Release candidate
This commit is contained in:
parent
bd7f96e1ea
commit
076a2b83c5
62
Makefile
Normal file
62
Makefile
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# ******************************************************************************
|
||||||
|
#
|
||||||
|
# @file Makefile
|
||||||
|
#
|
||||||
|
# @brief Makefile for MCTP library
|
||||||
|
#
|
||||||
|
# @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
|
||||||
|
#
|
||||||
|
# @date Mar 2024
|
||||||
|
# @author Barrett Edwards <code@jrlabs.io>
|
||||||
|
#
|
||||||
|
# ******************************************************************************
|
||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS= -g3 -O0 -Wall -Wextra
|
||||||
|
MACROS=-D MCTP_VERBOSE
|
||||||
|
INCLUDE_DIR=/usr/local/include
|
||||||
|
LIB_DIR=/usr/local/lib
|
||||||
|
INCLUDE_PATH=-I $(INCLUDE_DIR)
|
||||||
|
LIB_PATH=-L $(LIB_DIR)
|
||||||
|
LIBS=-l uuid -l ptrqueue -l arrayutils -l fmapi -l emapi -l timeutils
|
||||||
|
TARGET=mctp
|
||||||
|
|
||||||
|
all: server client lib$(TARGET).a
|
||||||
|
|
||||||
|
client: client.c main.o threads.o ctrl.o
|
||||||
|
$(CC) $^ $(CFLAGS) $(MACROS) $(INCLUDE_PATH) $(LIB_PATH) $(LIBS) -o $@
|
||||||
|
|
||||||
|
server: server.c main.o threads.o ctrl.o
|
||||||
|
$(CC) $^ $(CFLAGS) $(MACROS) $(INCLUDE_PATH) $(LIB_PATH) $(LIBS) -o $@
|
||||||
|
|
||||||
|
lib$(TARGET).a: main.o threads.o ctrl.o
|
||||||
|
ar rcs $@ $^
|
||||||
|
|
||||||
|
ctrl.o: ctrl.c main.o
|
||||||
|
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
|
||||||
|
|
||||||
|
threads.o: threads.c main.o
|
||||||
|
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
|
||||||
|
|
||||||
|
main.o: main.c main.h
|
||||||
|
$(CC) -c $< $(CFLAGS) $(MACROS) $(INCLUDE_PATH) -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf ./*.o ./*.a server client
|
||||||
|
|
||||||
|
doc:
|
||||||
|
doxygen
|
||||||
|
|
||||||
|
install: lib$(TARGET).a main.h
|
||||||
|
sudo cp lib$(TARGET).a $(LIB_DIR)/
|
||||||
|
sudo cp main.h $(INCLUDE_DIR)/$(TARGET).h
|
||||||
|
|
||||||
|
# List all non file name targets as PHONY
|
||||||
|
.PHONY: all clean doc install
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
# $^ Will expand to be all the sensitivity list
|
||||||
|
# $< Will expand to be the frist file in sensitivity list
|
||||||
|
# $@ Will expand to be the target name (the left side of the ":" )
|
||||||
|
# -c gcc will compile but not try and link
|
||||||
579
client.c
Normal file
579
client.c
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
/**
|
||||||
|
* @file client.c
|
||||||
|
*
|
||||||
|
* @brief Code file client example of MCTP Transport Library
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
|
||||||
|
*
|
||||||
|
* @date Jan 2024
|
||||||
|
* @author Barrett Edwards <code@jrlabs.io>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* INCLUDES ==================================================================*/
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <endian.h>
|
||||||
|
|
||||||
|
#include <arrayutils.h>
|
||||||
|
#include <ptrqueue.h>
|
||||||
|
|
||||||
|
#include <fmapi.h>
|
||||||
|
|
||||||
|
#include "mctp.h"
|
||||||
|
|
||||||
|
/* MACROS ====================================================================*/
|
||||||
|
|
||||||
|
#define MCTP_MEM_ALIGNMENT 4096
|
||||||
|
#define MCTP_RECV_BUFFER_COUNT 1024
|
||||||
|
#define MCTP_PORT 2508
|
||||||
|
#define MCTP_MAX_NUM_PACKETS 1024
|
||||||
|
|
||||||
|
/* ENUMERATIONS ==============================================================*/
|
||||||
|
|
||||||
|
/* STRUCTS ===================================================================*/
|
||||||
|
|
||||||
|
/* GLOBAL VARIABLES ==========================================================*/
|
||||||
|
|
||||||
|
/* PROTOTYPES ================================================================*/
|
||||||
|
|
||||||
|
void *client_thread(void *arg);
|
||||||
|
int test_ctrl_set_eid(struct mctp *m);
|
||||||
|
int test_ctrl_get_eid(struct mctp *m);
|
||||||
|
int test_ctrl_get_msg_type_support(struct mctp *m);
|
||||||
|
int test_ctrl_get_version_support(struct mctp *m);
|
||||||
|
int test_ctrl_get_endpoint_uuid(struct mctp *m);
|
||||||
|
int test_fmapi_identify_switch(struct mctp *m);
|
||||||
|
|
||||||
|
/* FUNCTIONS =================================================================*/
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
struct mctp *m;
|
||||||
|
|
||||||
|
m = mctp_init();
|
||||||
|
if (m == NULL) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Message Handler Thread function
|
||||||
|
mctp_set_mh(m, client_thread);
|
||||||
|
|
||||||
|
// Set verbosity levels
|
||||||
|
mctp_set_verbosity(m, mctp_get_verbosity(m) | MCTP_VERBOSE_ERROR);
|
||||||
|
mctp_set_verbosity(m, mctp_get_verbosity(m) | MCTP_VERBOSE_THREADS);
|
||||||
|
mctp_set_verbosity(m, mctp_get_verbosity(m) | MCTP_VERBOSE_STEPS);
|
||||||
|
mctp_set_verbosity(m, mctp_get_verbosity(m) | MCTP_VERBOSE_PACKET);
|
||||||
|
|
||||||
|
// Run
|
||||||
|
mctp_run(m, MCTP_PORT, 0, MCRM_CLIENT, 1, 0);
|
||||||
|
|
||||||
|
printf("mctp_run() completed\n");
|
||||||
|
|
||||||
|
|
||||||
|
// Free memory
|
||||||
|
mctp_free(m);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message Handler Thread that performs client actions
|
||||||
|
*
|
||||||
|
* This thread function is called in place of the message_handler() function
|
||||||
|
*
|
||||||
|
* TESTS
|
||||||
|
* 1: Set EID
|
||||||
|
* 2: Get EID
|
||||||
|
* 3: Get Version support
|
||||||
|
* 4: Get Message Type Support
|
||||||
|
* 5: Get Endpoint UUID
|
||||||
|
*/
|
||||||
|
void *client_thread(void *arg)
|
||||||
|
{
|
||||||
|
struct message_handler *self;
|
||||||
|
struct mctp *m;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
self = (struct message_handler*) arg;
|
||||||
|
m = self->m;
|
||||||
|
|
||||||
|
printf("%s Started \n", __FUNCTION__);
|
||||||
|
|
||||||
|
// TEST 1: Set EID
|
||||||
|
printf("-----------------------------------------------------------------\n");
|
||||||
|
printf("TEST 1: Set EID\n");
|
||||||
|
rv = test_ctrl_set_eid(m);
|
||||||
|
if ( rv != 0 ) {
|
||||||
|
printf("%s test_ctrl_set_eid failed rv:%d\n", __FUNCTION__, rv);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST 2: Get EID
|
||||||
|
printf("-----------------------------------------------------------------\n");
|
||||||
|
printf("TEST 2: Get EID\n");
|
||||||
|
rv = test_ctrl_get_eid(m);
|
||||||
|
if ( rv != 0 ) {
|
||||||
|
printf("%s test_ctrl_get_eid failed rv:%d\n", __FUNCTION__, rv);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST 3: Get Version Support
|
||||||
|
printf("-----------------------------------------------------------------\n");
|
||||||
|
printf("TEST 4: Get Version Support\n");
|
||||||
|
rv = test_ctrl_get_version_support(m);
|
||||||
|
if ( rv != 0 ) {
|
||||||
|
printf("%s test_ctrl_version_support failed rv:%d\n", __FUNCTION__, rv);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST 4: Get Message Type Support
|
||||||
|
printf("-----------------------------------------------------------------\n");
|
||||||
|
printf("TEST 4: Get Message Type Support\n");
|
||||||
|
rv = test_ctrl_get_msg_type_support(m);
|
||||||
|
if ( rv != 0 ) {
|
||||||
|
printf("%s test_ctrl_get_msg_type_support failed rv:%d\n", __FUNCTION__, rv);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST 5: Get Endpoint UUID
|
||||||
|
printf("-----------------------------------------------------------------\n");
|
||||||
|
printf("TEST 4: Get Endpoint UUID\n");
|
||||||
|
rv = test_ctrl_get_endpoint_uuid(m);
|
||||||
|
if ( rv != 0 ) {
|
||||||
|
printf("%s test_ctrl_get_endpoint_uuid failed rv:%d\n", __FUNCTION__, rv);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST 6: FMAPI - Identify Switch Device
|
||||||
|
printf("-----------------------------------------------------------------\n");
|
||||||
|
printf("TEST 6: FMAPI - Identify Switch Device\n");
|
||||||
|
rv = test_fmapi_identify_switch(m);
|
||||||
|
if ( rv != 0 ) {
|
||||||
|
printf("%s test_fmapi_identify_switch failed rv:%d\n", __FUNCTION__, rv);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(20);
|
||||||
|
end:
|
||||||
|
|
||||||
|
// Tell Threads to stop
|
||||||
|
pthread_mutex_lock(&m->mtx);
|
||||||
|
{
|
||||||
|
m->stop_threads = 2;
|
||||||
|
pthread_cond_signal(&m->cond);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&m->mtx);
|
||||||
|
|
||||||
|
printf("%s Ending \n", __FUNCTION__);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the ability to get the Enpoint UUDI
|
||||||
|
*
|
||||||
|
* Return 0 upon success, non-zero error condition otherwise
|
||||||
|
*/
|
||||||
|
int test_ctrl_get_endpoint_uuid(struct mctp *m)
|
||||||
|
{
|
||||||
|
struct mctp_msg *mm;
|
||||||
|
|
||||||
|
/* STEPS
|
||||||
|
* 1: Get an mctp_msg from the queue
|
||||||
|
* 2: Set MCTP Message Header
|
||||||
|
* 3: Configure message type
|
||||||
|
* 4: Configure MCTP Control header
|
||||||
|
* 5: Configure command specific fields
|
||||||
|
* 6: Put message into send queue
|
||||||
|
* 7: Get response from the server
|
||||||
|
* 8: Print the received message
|
||||||
|
* 9: Put the response message back into the recv queue
|
||||||
|
*/
|
||||||
|
|
||||||
|
// STEP 1: Get an mctp_msg from the queue
|
||||||
|
mm = pq_pop(m->msgs, 1);
|
||||||
|
if (mm == NULL) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 2: Set MCTP message header (DST, SRC, TO, TAG)
|
||||||
|
mctp_fill_msg_hdr(mm, 0x02, 0x01, 1, 0);
|
||||||
|
|
||||||
|
// STEP 3: Configure message type
|
||||||
|
mm->type = MCMT_CONTROL;
|
||||||
|
|
||||||
|
// STEP 4: Configure MCTP Control header (REQ, DATAGRAM, INST, CMD)
|
||||||
|
mctp_fill_ctrl(mm, 1, 0, 0, MCCM_GET_ENDPOINT_UUID);
|
||||||
|
|
||||||
|
mm->len = MCLN_TYPE + mctp_len_ctrl((__u8*)mctp_get_ctrl(mm));
|
||||||
|
|
||||||
|
// STEP 5: Configure command specific fields
|
||||||
|
|
||||||
|
// STEP 6: Put message into send queue
|
||||||
|
pq_push(m->tmq, mm);
|
||||||
|
|
||||||
|
printf("========== Waiting for response ==========\n");
|
||||||
|
|
||||||
|
// STEP 7: Get response from the server
|
||||||
|
mm = pq_pop(m->rmq, 1);
|
||||||
|
if (mm == 0) {
|
||||||
|
printf("%s pq_pop() returned an error\n", __FUNCTION__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 8: Print the received message
|
||||||
|
mctp_prnt_msg(mm);
|
||||||
|
|
||||||
|
// STEP 9: Put the response message back into the recv queue
|
||||||
|
pq_push(m->msgs, mm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test the ability to set the EID of the remote endpoint
|
||||||
|
*
|
||||||
|
* Return 0 upon success, non-zero error condition otherwise
|
||||||
|
*/
|
||||||
|
int test_ctrl_set_eid(struct mctp *m)
|
||||||
|
{
|
||||||
|
struct mctp_msg *mm;
|
||||||
|
|
||||||
|
/* STEPS
|
||||||
|
* 1: Get an mctp_msg from the queue
|
||||||
|
* 2: Set MCTP Message Header
|
||||||
|
* 3: Configure message type
|
||||||
|
* 4: Configure MCTP Control header
|
||||||
|
* 5: Configure command specific fields
|
||||||
|
* 6: Put message into send queue
|
||||||
|
* 7: Get response from the server
|
||||||
|
* 8: Print the received message
|
||||||
|
* 9: Put the response message back into the recv queue
|
||||||
|
*/
|
||||||
|
|
||||||
|
// STEP 1: Get an mctp_msg from the queue
|
||||||
|
mm = pq_pop(m->msgs, 1);
|
||||||
|
if (mm == NULL) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 2: Set MCTP message header (DST, SRC, TO, TAG)
|
||||||
|
mctp_fill_msg_hdr(mm, 0x02, 0x01, 1, 0);
|
||||||
|
|
||||||
|
// STEP 3: Configure message type
|
||||||
|
mm->type = MCMT_CONTROL;
|
||||||
|
|
||||||
|
// STEP 4: Configure MCTP Control header (REQ, DATAGRAM, INST, CMD)
|
||||||
|
mctp_fill_ctrl(mm, 1, 0, 0, MCCM_SET_ENDPOINT_ID);
|
||||||
|
|
||||||
|
struct mctp_ctrl_msg *mc;
|
||||||
|
|
||||||
|
// STEP 5: Configure command specific fields
|
||||||
|
mc = (struct mctp_ctrl_msg*) &mm->payload[1];
|
||||||
|
|
||||||
|
mctp_ctrl_fill_set_eid(mc, 0x02);
|
||||||
|
|
||||||
|
mm->len = MCLN_TYPE + mctp_len_ctrl((__u8*)mctp_get_ctrl(mm));
|
||||||
|
|
||||||
|
// STEP 6: Put message into send queue
|
||||||
|
pq_push(m->tmq, mm);
|
||||||
|
|
||||||
|
printf("========== Waiting for response ==========\n");
|
||||||
|
|
||||||
|
// STEP 7: Get response from the server
|
||||||
|
mm = pq_pop(m->rmq, 1);
|
||||||
|
if (mm == 0) {
|
||||||
|
printf("%s pq_pop() returned an error\n", __FUNCTION__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 8: Print the received message
|
||||||
|
mctp_prnt_msg(mm);
|
||||||
|
|
||||||
|
// STEP 9: Put the response message back into the recv queue
|
||||||
|
pq_push(m->msgs, mm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_ctrl_get_eid(struct mctp *m)
|
||||||
|
{
|
||||||
|
struct mctp_msg *mm;
|
||||||
|
|
||||||
|
/* STEPS
|
||||||
|
* 1: Get an mctp_msg from the queue
|
||||||
|
* 2: Set MCTP Message Header
|
||||||
|
* 3: Configure message type
|
||||||
|
* 4: Configure MCTP Control header
|
||||||
|
* 5: Configure command specific fields
|
||||||
|
* 6: Put message into send queue
|
||||||
|
* 7: Get response from the server
|
||||||
|
* 8: Print the received message
|
||||||
|
* 9: Put the response message back into the recv queue
|
||||||
|
*/
|
||||||
|
|
||||||
|
// STEP 1: Get an mctp_msg from the queue
|
||||||
|
mm = pq_pop(m->msgs, 1);
|
||||||
|
if (mm == NULL) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 2: Set MCTP message header (DST, SRC, TO, TAG)
|
||||||
|
mctp_fill_msg_hdr(mm, 0x02, 0x01, 1, 0);
|
||||||
|
|
||||||
|
// STEP 3: Configure message type
|
||||||
|
mm->type = MCMT_CONTROL;
|
||||||
|
|
||||||
|
// STEP 4: Configure MCTP Control header (REQ, DATAGRAM, INST, CMD)
|
||||||
|
mctp_fill_ctrl(mm, 1, 0, 0, MCCM_GET_ENDPOINT_ID);
|
||||||
|
|
||||||
|
// STEP 5: Configure command specific fields
|
||||||
|
|
||||||
|
mm->len = MCLN_TYPE + mctp_len_ctrl((__u8*)mctp_get_ctrl(mm));
|
||||||
|
|
||||||
|
// STEP 6: Put message into send queue
|
||||||
|
pq_push(m->tmq, mm);
|
||||||
|
|
||||||
|
printf("========== Waiting for response ==========\n");
|
||||||
|
|
||||||
|
// STEP 7: Get response from the server
|
||||||
|
mm = pq_pop(m->rmq, 1);
|
||||||
|
if (mm == 0) {
|
||||||
|
printf("%s pq_pop() returned an error\n", __FUNCTION__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 8: Print the received message
|
||||||
|
mctp_prnt_msg(mm);
|
||||||
|
|
||||||
|
// STEP 9: Put the response message back into the recv queue
|
||||||
|
pq_push(m->msgs, mm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_ctrl_get_msg_type_support(struct mctp *m)
|
||||||
|
{
|
||||||
|
struct mctp_msg *mm;
|
||||||
|
|
||||||
|
/* STEPS
|
||||||
|
* 1: Get an mctp_msg from the queue
|
||||||
|
* 2: Set MCTP Message Header
|
||||||
|
* 3: Configure message type
|
||||||
|
* 4: Configure MCTP Control header
|
||||||
|
* 5: Configure command specific fields
|
||||||
|
* 6: Put message into send queue
|
||||||
|
* 7: Get response from the server
|
||||||
|
* 8: Print the received message
|
||||||
|
* 9: Put the response message back into the recv queue
|
||||||
|
*/
|
||||||
|
|
||||||
|
// STEP 1: Get an mctp_msg from the queue
|
||||||
|
mm = pq_pop(m->msgs, 1);
|
||||||
|
if (mm == NULL) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 2: Set MCTP message header (DST, SRC, TO, TAG)
|
||||||
|
mctp_fill_msg_hdr(mm, 0x02, 0x01, 1, 0);
|
||||||
|
|
||||||
|
// STEP 3: Configure message type
|
||||||
|
mm->type = MCMT_CONTROL;
|
||||||
|
|
||||||
|
// STEP 4: Configure MCTP Control header (REQ, DATAGRAM, INST, CMD)
|
||||||
|
mctp_fill_ctrl(mm, 1, 0, 0, MCCM_GET_MESSAGE_TYPE_SUPPORT);
|
||||||
|
|
||||||
|
// STEP 5: Configure command specific fields
|
||||||
|
|
||||||
|
mm->len = MCLN_TYPE + mctp_len_ctrl((__u8*)mctp_get_ctrl(mm));
|
||||||
|
|
||||||
|
// STEP 6: Put message into send queue
|
||||||
|
pq_push(m->tmq, mm);
|
||||||
|
|
||||||
|
printf("========== Waiting for response ==========\n");
|
||||||
|
|
||||||
|
// STEP 7: Get response from the server
|
||||||
|
mm = pq_pop(m->rmq, 1);
|
||||||
|
if (mm == 0) {
|
||||||
|
printf("%s pq_pop() returned an error\n", __FUNCTION__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 8: Print the received message
|
||||||
|
mctp_prnt_msg(mm);
|
||||||
|
|
||||||
|
// STEP 9: Put the response message back into the recv queue
|
||||||
|
pq_push(m->msgs, mm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_ctrl_get_version_support(struct mctp *m)
|
||||||
|
{
|
||||||
|
struct mctp_msg *mm;
|
||||||
|
__u8 *data;
|
||||||
|
|
||||||
|
/* STEPS
|
||||||
|
* 1: Get an mctp_msg from the queue
|
||||||
|
* 2: Set MCTP Message Header
|
||||||
|
* 3: Configure message type
|
||||||
|
* 4: Configure MCTP Control header
|
||||||
|
* 5: Configure command specific fields
|
||||||
|
* 6: Put message into send queue
|
||||||
|
* 7: Get response from the server
|
||||||
|
* 8: Print the received message
|
||||||
|
* 9: Put the response message back into the recv queue
|
||||||
|
*/
|
||||||
|
|
||||||
|
// STEP 1: Get an mctp_msg from the queue
|
||||||
|
mm = pq_pop(m->msgs, 1);
|
||||||
|
if (mm == NULL) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 2: Set MCTP message header (DST, SRC, TO, TAG)
|
||||||
|
mctp_fill_msg_hdr(mm, 0x02, 0x01, 1, 0);
|
||||||
|
|
||||||
|
// STEP 3: Configure message type
|
||||||
|
mm->type = MCMT_CONTROL;
|
||||||
|
|
||||||
|
// STEP 4: Configure MCTP Control header (REQ, DATAGRAM, INST, CMD)
|
||||||
|
mctp_fill_ctrl(mm, 1, 0, 0, MCCM_GET_VERSION_SUPPORT);
|
||||||
|
|
||||||
|
// STEP 5: Configure command specific fields
|
||||||
|
data = mm->payload + sizeof(struct mctp_ctrl);
|
||||||
|
data[0] = MCMT_BASE;
|
||||||
|
|
||||||
|
mm->len = MCLN_TYPE + mctp_len_ctrl((__u8*)mctp_get_ctrl(mm));
|
||||||
|
|
||||||
|
// STEP 6: Put message into send queue
|
||||||
|
pq_push(m->tmq, mm);
|
||||||
|
|
||||||
|
printf("========== Waiting for response ==========\n");
|
||||||
|
|
||||||
|
// STEP 7: Get response from the server
|
||||||
|
mm = pq_pop(m->rmq, 1);
|
||||||
|
if (mm == 0) {
|
||||||
|
printf("%s pq_pop() returned an error\n", __FUNCTION__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 8: Print the received message
|
||||||
|
printf("print message -------------------------------------\n");
|
||||||
|
mctp_prnt_msg(mm);
|
||||||
|
|
||||||
|
// STEP 9: Put the response message back into the recv queue
|
||||||
|
pq_push(m->msgs, mm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
end:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test: FMAPI - Identify Switch Device
|
||||||
|
*
|
||||||
|
* STEPS
|
||||||
|
* 1: Get an mctp_msg from the queue
|
||||||
|
* 2: Set MCTP Message Header
|
||||||
|
* 3: Configure message type
|
||||||
|
* 4: Configure MCTP Control header
|
||||||
|
* 5: Configure command specific fields
|
||||||
|
* 6: Serialize Request Cammand specific fields into message buffer
|
||||||
|
* 7: Put message into send queue
|
||||||
|
* 8: Get response from the server
|
||||||
|
* 9: Print the received message
|
||||||
|
* 10: Put the response message back into the recv queue
|
||||||
|
*/
|
||||||
|
int test_fmapi_identify_switch(struct mctp *m)
|
||||||
|
{
|
||||||
|
struct fmapi_hdr fh;
|
||||||
|
struct mctp_msg *mm;
|
||||||
|
|
||||||
|
// STEP 1: Get an mctp_msg from the queue
|
||||||
|
mm = pq_pop(m->msgs, 1);
|
||||||
|
if (mm == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// STEP 2: Set MCTP message header (DST, SRC, TO, TAG)
|
||||||
|
mctp_fill_msg_hdr(mm, 0x02, 0x01, 1, 0);
|
||||||
|
|
||||||
|
// STEP 3: Configure message type
|
||||||
|
mm->type = MCMT_CXLFMAPI;
|
||||||
|
|
||||||
|
// STEP 4: Configure MCTP Control header (REQ, DATAGRAM, INST, CMD)
|
||||||
|
fmapi_fill_hdr(&fh, FMMT_REQ, 0, FMOP_PSC_ID, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
// STEP : Serialize Request Cammand specific fields into message buffer
|
||||||
|
fmapi_serialize(mm->payload, &fh, FMOB_HDR);
|
||||||
|
|
||||||
|
// STEP 5: Configure command specific fields
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 6: Serialize Request Cammand specific fields into message buffer
|
||||||
|
|
||||||
|
mm->len = FMLN_HDR;
|
||||||
|
|
||||||
|
// STEP 7: Put message into send queue
|
||||||
|
pq_push(m->tmq, mm);
|
||||||
|
|
||||||
|
printf("========== Waiting for response ==========\n");
|
||||||
|
|
||||||
|
// STEP 8: Get response from the server
|
||||||
|
mm = pq_pop(m->rmq, 1);
|
||||||
|
if (mm == 0) {
|
||||||
|
printf("%s pq_pop() returned an error\n", __FUNCTION__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 9: Print the received message
|
||||||
|
printf("print message -------------------------------------\n");
|
||||||
|
mctp_prnt_msg(mm);
|
||||||
|
|
||||||
|
// STEP : Print the FM API Object
|
||||||
|
{
|
||||||
|
struct fmapi_psc_id_rsp id;
|
||||||
|
fmapi_deserialize(&id, mm->payload + FMLN_HDR, FMOB_PSC_ID_RSP, NULL);
|
||||||
|
fmapi_prnt(&id, FMOB_PSC_ID_RSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 10: Put the response message back into the recv queue
|
||||||
|
pq_push(m->msgs, mm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
end:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
835
main.c
Normal file
835
main.c
Normal file
@ -0,0 +1,835 @@
|
|||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
/**
|
||||||
|
* @file mctp.c
|
||||||
|
*
|
||||||
|
* @brief Code file for MCTP transport library
|
||||||
|
*
|
||||||
|
* @details As per MCTP specification, all MCTP fields are Big Endian
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
|
||||||
|
*
|
||||||
|
* @date Jan 2024
|
||||||
|
* @author Barrett Edwards <code@jrlabs.io>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* INCLUDES ==================================================================*/
|
||||||
|
|
||||||
|
/* gettid()
|
||||||
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
/* exit()
|
||||||
|
*/
|
||||||
|
//#include <stdlib.h>
|
||||||
|
|
||||||
|
/* errno
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* printf()
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* free()
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* memset()
|
||||||
|
* memcpy()
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* pthread_t
|
||||||
|
* pthread_create()
|
||||||
|
* pthread_join()
|
||||||
|
* pthread_getthreadid_np()
|
||||||
|
*/
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/* __u32
|
||||||
|
*/
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* sem_t
|
||||||
|
* sem_init()
|
||||||
|
* sem_timedwait()
|
||||||
|
*/
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
/* autl_prnt_buf()
|
||||||
|
*/
|
||||||
|
#include <arrayutils.h>
|
||||||
|
#include <timeutils.h>
|
||||||
|
#include <ptrqueue.h>
|
||||||
|
|
||||||
|
#include "mctp.h"
|
||||||
|
|
||||||
|
/* MACROS ====================================================================*/
|
||||||
|
|
||||||
|
//#define MCTP_VERBOSE
|
||||||
|
#ifdef MCTP_VERBOSE
|
||||||
|
#define INIT unsigned step = 0;
|
||||||
|
#define ENTER if (m->verbose & MCTP_VERBOSE_THREADS) printf("%d:%s Enter\n", gettid(), __FUNCTION__);
|
||||||
|
#define STEP step++; if (m->verbose & MCTP_VERBOSE_STEPS) printf("%d:%s STEP: %u\n", gettid(), __FUNCTION__, step);
|
||||||
|
#define HEX32(k, i) if (m->verbose & MCTP_VERBOSE_STEPS) printf("%d:%s STEP: %u %s: 0x%x\n", gettid(), __FUNCTION__, step, k, i);
|
||||||
|
#define INT32(k, i) if (m->verbose & MCTP_VERBOSE_STEPS) printf("%d:%s STEP: %u %s: %d\n", gettid(), __FUNCTION__, step, k, i);
|
||||||
|
#define ERR32(k, i) if (m->verbose & MCTP_VERBOSE_ERROR) printf("%d:%s STEP: %u ERR: %s: %d\n", gettid(), __FUNCTION__, step, k, i);
|
||||||
|
#define EXIT(rc) if (m->verbose & MCTP_VERBOSE_THREADS) printf("%d:%s Exit: %d\n", gettid(), __FUNCTION__,rc);
|
||||||
|
#else
|
||||||
|
#define INIT
|
||||||
|
#define ENTER
|
||||||
|
#define STEP
|
||||||
|
#define HEX32(k, i)
|
||||||
|
#define INT32(k, i)
|
||||||
|
#define ERR32(k, i)
|
||||||
|
#define EXIT(rc)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ENUMERATIONS ==============================================================*/
|
||||||
|
|
||||||
|
/* STRUCTS ===================================================================*/
|
||||||
|
|
||||||
|
/* GLOBAL VARIABLES ==========================================================*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation of MCTP Threads Run Mode (RM)
|
||||||
|
*/
|
||||||
|
const char *STR_MCRM[] = {
|
||||||
|
"Server", // MCRM_SERVER = 0,
|
||||||
|
"Client" // MCRM_CLIENT = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
/* String representation of MCTP Message Type Codes (MT)
|
||||||
|
*
|
||||||
|
* See DSP0239 v1.9.0 Table 1.
|
||||||
|
*/
|
||||||
|
const char *STR_MCMT[] = {
|
||||||
|
"CONTROL", // MCMT_CONTROL = 0x00,
|
||||||
|
"PLDM", // MCMT_PLDM = 0x01,
|
||||||
|
"NCSI", // MCMT_NCSI = 0x02,
|
||||||
|
"ETHERNET", // MCMT_ETHERNET = 0x03,
|
||||||
|
"NVMEMI", // MCMT_NVMEMI = 0x04,
|
||||||
|
"SPDM", // MCMT_SPDM = 0x05,
|
||||||
|
"SECURE", // MCMT_SECURE = 0x06,
|
||||||
|
"CXLFMAPI", // MCMT_CXLFMAPI = 0x07,
|
||||||
|
"CXLCCI ", // MCMT_CXLCCI = 0x08,
|
||||||
|
"VDM_PCI", // MCMT_VDM_PCI = 0x7E,
|
||||||
|
"VDM_IANA" // MCMT_VDM_IANA = 0x7F
|
||||||
|
};
|
||||||
|
|
||||||
|
/* PROTOTYPES ================================================================*/
|
||||||
|
|
||||||
|
/* FUNCTIONS =================================================================*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to fill MCTP Header fields
|
||||||
|
*
|
||||||
|
* @param mm struct mctp_msg* to fill
|
||||||
|
* @param dest Destination EID
|
||||||
|
* @param src Source EID
|
||||||
|
* @param owner Bit indicating if SRC is the owner
|
||||||
|
* @param tag Tag ID to track multiple outstanding commands
|
||||||
|
*/
|
||||||
|
void mctp_fill_msg_hdr(
|
||||||
|
struct mctp_msg *mm,
|
||||||
|
__u8 dest,
|
||||||
|
__u8 src,
|
||||||
|
__u8 owner,
|
||||||
|
__u8 tag)
|
||||||
|
{
|
||||||
|
mm->dst = dest;
|
||||||
|
mm->src = src;
|
||||||
|
mm->owner = owner;
|
||||||
|
mm->tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free memory allocated by init function
|
||||||
|
*
|
||||||
|
* STEPS
|
||||||
|
* 1: Verify input
|
||||||
|
* 2: Close socket connection
|
||||||
|
* 3: Destroy Mutexes
|
||||||
|
* 4: Free queues
|
||||||
|
* 5: Free mctp struct memory
|
||||||
|
*/
|
||||||
|
int mctp_free(struct mctp *m)
|
||||||
|
{
|
||||||
|
INIT
|
||||||
|
struct mctp_version *head, *curr, *next;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
ENTER
|
||||||
|
|
||||||
|
// Initialize variables
|
||||||
|
rv = 1;
|
||||||
|
|
||||||
|
STEP // 1: Verify input
|
||||||
|
if (m == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
rv = -1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
STEP // 2: Close socket connection
|
||||||
|
if (m->conn != m->sock)
|
||||||
|
close(m->conn);
|
||||||
|
close(m->sock);
|
||||||
|
|
||||||
|
STEP // 3: Destroy Mutexes
|
||||||
|
pthread_mutex_destroy(&m->mtx);
|
||||||
|
pthread_cond_destroy(&m->cond);
|
||||||
|
pthread_mutex_destroy(&m->tags_mtx);
|
||||||
|
|
||||||
|
STEP // 4: Free queues
|
||||||
|
pq_free(m->rpq);
|
||||||
|
pq_free(m->rmq);
|
||||||
|
pq_free(m->tpq);
|
||||||
|
pq_free(m->tmq);
|
||||||
|
pq_free(m->taq);
|
||||||
|
pq_free(m->acq);
|
||||||
|
pq_free(m->pkts);
|
||||||
|
pq_free(m->msgs);
|
||||||
|
pq_free(m->actions);
|
||||||
|
|
||||||
|
STEP // 5 Free MCTP Versions array
|
||||||
|
head = m->mctp_versions;
|
||||||
|
while (head != NULL)
|
||||||
|
{
|
||||||
|
curr = head;
|
||||||
|
head = head->next_type;
|
||||||
|
while (curr != NULL)
|
||||||
|
{
|
||||||
|
next = curr->next_entry;
|
||||||
|
free(curr);
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STEP // 6: Free mctp struct memory
|
||||||
|
free(m);
|
||||||
|
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
EXIT(rv);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the verbosity bit mask
|
||||||
|
*/
|
||||||
|
__u32 mctp_get_verbosity(struct mctp *m)
|
||||||
|
{
|
||||||
|
return m->verbose;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize an mctp object
|
||||||
|
*
|
||||||
|
* STEPS
|
||||||
|
* 1: Allocate memory for mctp struct
|
||||||
|
* 2: Initialize message handlers
|
||||||
|
* 3: Initialize message_handler thread
|
||||||
|
* 4: Initialize UUID
|
||||||
|
* 5: Initialize mutex variables
|
||||||
|
*/
|
||||||
|
struct mctp *mctp_init()
|
||||||
|
{
|
||||||
|
struct mctp *m;
|
||||||
|
|
||||||
|
// STEP 1: Allocate memory for mctp struct
|
||||||
|
m = (struct mctp*) calloc (1, sizeof(struct mctp));
|
||||||
|
if (m == NULL)
|
||||||
|
{
|
||||||
|
errno = EFAULT;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 2: Initialize message handlers
|
||||||
|
m->handlers[MCMT_CONTROL] = mctp_ctrl_handler;
|
||||||
|
|
||||||
|
// STEP 3: Initialize message_handler thread
|
||||||
|
m->fn_sr = mctp_socket_reader;
|
||||||
|
m->fn_pr = mctp_packet_reader;
|
||||||
|
m->fn_mh = mctp_message_handler;
|
||||||
|
m->fn_pw = mctp_packet_writer;
|
||||||
|
m->fn_sw = mctp_socket_writer;
|
||||||
|
m->fn_st = mctp_submission_thread;
|
||||||
|
m->fn_ct = mctp_completion_thread;
|
||||||
|
|
||||||
|
// STEP 4: Initialize UUID
|
||||||
|
uuid_generate(m->uuid);
|
||||||
|
memcpy(m->state.uuid, m->uuid, MCLN_UUID);
|
||||||
|
|
||||||
|
// STEP 5: Initialize mutex variables
|
||||||
|
pthread_mutex_init(&m->mtx, NULL);
|
||||||
|
pthread_cond_init(&m->cond, NULL);
|
||||||
|
pthread_mutex_init(&m->tags_mtx, NULL);
|
||||||
|
|
||||||
|
// STEP 6: Initialize mctp_versions array
|
||||||
|
m->mctp_versions = NULL;
|
||||||
|
|
||||||
|
mctp_set_version(m, MCMT_BASE, 0xF1,0xF3,0xF1,0x00);
|
||||||
|
mctp_set_version(m, MCMT_CONTROL, 0xF1,0xF3,0xF1,0x00);
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the number of packets needed for this MCTP Message
|
||||||
|
*
|
||||||
|
* @return the number of packets, 0 if error
|
||||||
|
*/
|
||||||
|
int mctp_pkt_count(struct mctp_msg *mm)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
// Initialize variables
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
switch (mm->type)
|
||||||
|
{
|
||||||
|
case MCMT_CONTROL: rv = 1; break; // All MCTP control messages are 1 packet long
|
||||||
|
case MCMT_PLDM:
|
||||||
|
case MCMT_NCSI:
|
||||||
|
case MCMT_ETHERNET:
|
||||||
|
case MCMT_NVMEMI:
|
||||||
|
case MCMT_SPDM:
|
||||||
|
case MCMT_SECURE:
|
||||||
|
case MCMT_CXLFMAPI:
|
||||||
|
case MCMT_CSE:
|
||||||
|
case MCMT_CXLCCI:
|
||||||
|
case MCMT_VDM_PCI:
|
||||||
|
case MCMT_VDM_IANA:
|
||||||
|
{
|
||||||
|
// Compute the number of MCLN_BTU sized packets needed
|
||||||
|
rv = mm->len / MCLN_BTU;
|
||||||
|
if ((mm->len % MCLN_BTU) > 0 )
|
||||||
|
rv++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print an MCTP Transport Header
|
||||||
|
*/
|
||||||
|
void mctp_prnt_hdr(struct mctp_hdr *mh)
|
||||||
|
{
|
||||||
|
if (mh == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf("MCTP Header:\n");
|
||||||
|
printf("Header version: 0x%x\n", mh->ver);
|
||||||
|
printf("Destination EID: 0x%02x\n", mh->dest);
|
||||||
|
printf("Source EID: 0x%02x\n", mh->src);
|
||||||
|
printf("Start of Message: %d\n", mh->som);
|
||||||
|
printf("End of Message: %d\n", mh->eom);
|
||||||
|
printf("Packet Sequence #: 0x%x\n", mh->seq);
|
||||||
|
printf("Tag Owner: %d\n", mh->owner);
|
||||||
|
printf("Tag: 0x%x\n", mh->tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print MCTP Pkt
|
||||||
|
*/
|
||||||
|
void mctp_prnt_pkt(struct mctp_pkt *mp)
|
||||||
|
{
|
||||||
|
if (mp == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Print the MCTP Transport Header
|
||||||
|
mctp_prnt_hdr(&mp->hdr);
|
||||||
|
|
||||||
|
// Print the payload
|
||||||
|
autl_prnt_buf(mp, sizeof(struct mctp_pkt), 4, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print MCTP Packet Wrapper
|
||||||
|
*/
|
||||||
|
void mctp_prnt_pkt_wrapper(struct mctp_pkt_wrapper *pw)
|
||||||
|
{
|
||||||
|
if (pw == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf("MCTP Packet Wrapper:\n");
|
||||||
|
timespec_print(&pw->ts);
|
||||||
|
printf("Next: %p\n", pw->next);
|
||||||
|
mctp_prnt_pkt(&pw->pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print an MCTP Message Type
|
||||||
|
*/
|
||||||
|
void mctp_prnt_type(struct mctp_type *mt)
|
||||||
|
{
|
||||||
|
printf("MCTP Type:\n");
|
||||||
|
printf("Integrity Check: %d\n", mt->IC);
|
||||||
|
printf("Message Type: 0x%02x %s\n", mt->type, mcmt(mt->type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the current MCTP Endpoint Configuration
|
||||||
|
*/
|
||||||
|
void mctp_prnt_state(struct mctp_state *ms)
|
||||||
|
{
|
||||||
|
char buf[37];
|
||||||
|
// Convert UUID into String for printing
|
||||||
|
uuid_unparse(ms->uuid, buf);
|
||||||
|
|
||||||
|
printf("MCTP State:\n");
|
||||||
|
printf("Endpoint ID: %02x\n", ms->eid);
|
||||||
|
printf("Bus Owner EID: %02x\n", ms->bus_owner_eid);
|
||||||
|
printf("Verbose Flags: %08x\n", ms->verbose);
|
||||||
|
printf("UUID: %s\n", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print MCTP Message
|
||||||
|
*/
|
||||||
|
void mctp_prnt_msg(struct mctp_msg *mm)
|
||||||
|
{
|
||||||
|
if (mm == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf("MCTP Message:\n");
|
||||||
|
printf("Destination EID: 0x%02x\n", mm->dst);
|
||||||
|
printf("Source EID: 0x%02x\n", mm->src);
|
||||||
|
printf("Type: 0x%02x - %s\n", mm->type, mcmt(mm->type));
|
||||||
|
printf("Tag Owner: %d\n", mm->owner);
|
||||||
|
printf("Tag: %d\n", mm->tag);
|
||||||
|
printf("Payload Len: %d\n", mm->len);
|
||||||
|
printf("Payload:\n");
|
||||||
|
|
||||||
|
// Print the payload in bytes
|
||||||
|
autl_prnt_buf(mm->payload, mm->len, 4, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retire an MCTP action
|
||||||
|
*
|
||||||
|
* Checks in the mctp_msg and mctp_action to the central free pools
|
||||||
|
* @param m struct mctp*
|
||||||
|
* @param a struct mctp_action*
|
||||||
|
*/
|
||||||
|
void mctp_retire(struct mctp* m, struct mctp_action *a)
|
||||||
|
{
|
||||||
|
struct mctp_pkt_wrapper *pw, *next;
|
||||||
|
|
||||||
|
// Check in msg
|
||||||
|
if (a->req != NULL)
|
||||||
|
pq_push(m->msgs, a->req);
|
||||||
|
if (a->rsp != NULL)
|
||||||
|
pq_push(m->msgs, a->rsp);
|
||||||
|
|
||||||
|
if (a->pw != NULL)
|
||||||
|
{
|
||||||
|
pw = a->pw;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
next = pw->next;
|
||||||
|
pw->next = NULL;
|
||||||
|
pq_push(m->pkts, pw);
|
||||||
|
pw = next;
|
||||||
|
} while (pw != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear action
|
||||||
|
memset(a, 0, sizeof(struct mctp_action));
|
||||||
|
|
||||||
|
// Check in action
|
||||||
|
pq_push(m->actions, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the threads
|
||||||
|
*
|
||||||
|
* @return 0 on success
|
||||||
|
* -1 on socket create failure (both)
|
||||||
|
* -2 on socket bind failure (server)
|
||||||
|
* -3 on socket connect failure (client)
|
||||||
|
* 1 on pthread_create() failure (both)
|
||||||
|
* 2 on connection_thread start failure (both)
|
||||||
|
*
|
||||||
|
* STEPS
|
||||||
|
* 1: Store parameters in mctp object
|
||||||
|
* 2: Create socket
|
||||||
|
* 3: Set parameters for server socket
|
||||||
|
* 4: Configure socket
|
||||||
|
* 5: Start connection thread
|
||||||
|
*/
|
||||||
|
int mctp_run(struct mctp *m, int port, __u32 address, int mode, int use_threads, int dontblock)
|
||||||
|
{
|
||||||
|
INIT
|
||||||
|
int rv;
|
||||||
|
sem_t sem;
|
||||||
|
struct timespec ts, delta;
|
||||||
|
|
||||||
|
ENTER
|
||||||
|
|
||||||
|
// Initialize variables
|
||||||
|
rv = -1;
|
||||||
|
delta.tv_sec = 1;
|
||||||
|
delta.tv_nsec = 0;
|
||||||
|
|
||||||
|
STEP // 1: Store parameters in mctp object
|
||||||
|
m->port = port;
|
||||||
|
m->mode = mode;
|
||||||
|
m->use_threads = use_threads;
|
||||||
|
m->wait = use_threads;
|
||||||
|
|
||||||
|
STEP // 2: Create socket
|
||||||
|
m->sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if ( m->sock < 0 )
|
||||||
|
{
|
||||||
|
ERR32("Could not create socket. rv:", m->sock);
|
||||||
|
rv = -1;
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
|
||||||
|
STEP // 3: Set parameters for server socket
|
||||||
|
memset( &m->sa_server, 0, sizeof(struct sockaddr_in));
|
||||||
|
m->sa_server.sin_family = AF_INET;
|
||||||
|
m->sa_server.sin_port = htons(port);
|
||||||
|
m->sa_server.sin_addr.s_addr = address;
|
||||||
|
|
||||||
|
STEP // 4: Configure Socket
|
||||||
|
if ( mode == MCRM_SERVER )
|
||||||
|
{
|
||||||
|
// Bind to socket
|
||||||
|
rv = bind(m->sock, (struct sockaddr *) &m->sa_server, sizeof(struct sockaddr_in));
|
||||||
|
if ( rv < 0 )
|
||||||
|
{
|
||||||
|
ERR32("Could not bind socket. rv", rv);
|
||||||
|
rv = -2;
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen on socket
|
||||||
|
listen(m->sock,5);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Connect to the server as a client
|
||||||
|
rv = connect(m->sock, (struct sockaddr *) &m->sa_server, sizeof(struct sockaddr_in));
|
||||||
|
if ( rv < 0 )
|
||||||
|
{
|
||||||
|
ERR32("Socket connect failed. rv:", rv);
|
||||||
|
rv = -3;
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
m->conn = m->sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set struct mctp pointer in Connection Handler object
|
||||||
|
m->ch.m = m;
|
||||||
|
m->ch.dontblock = dontblock;
|
||||||
|
m->ch.sem = NULL;
|
||||||
|
|
||||||
|
STEP // 5: Start Connection Handler Thread
|
||||||
|
// If the user specified dontblock, then start the connection handler thread function as a independent thread and return
|
||||||
|
if (dontblock)
|
||||||
|
{
|
||||||
|
// Initialize sempahore
|
||||||
|
sem_init(&sem, 0, 0);
|
||||||
|
m->ch.sem = &sem;
|
||||||
|
|
||||||
|
// Start thread
|
||||||
|
rv = pthread_create( &m->pt_ch, NULL, mctp_connection_handler, (void*) &m->ch);
|
||||||
|
if ( rv != 0 )
|
||||||
|
{
|
||||||
|
ERR32("Could not create Connection Handler Thread", rv);
|
||||||
|
rv = 1;
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute timeout to wait for semaphore
|
||||||
|
timespec_get(&ts, CLOCK_MONOTONIC);
|
||||||
|
timespec_add(&ts, &delta, &ts);
|
||||||
|
|
||||||
|
// Pend on a semaphore until all the threads are running
|
||||||
|
rv = sem_timedwait(&sem, &ts);
|
||||||
|
|
||||||
|
sem_destroy(&sem);
|
||||||
|
if (rv != 0)
|
||||||
|
{
|
||||||
|
ERR32("Threads failed to start", rv);
|
||||||
|
rv = 2;
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mctp_connection_handler(&m->ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
close:
|
||||||
|
|
||||||
|
close(m->sock);
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
EXIT(rv)
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the function to call for a MCTP Message type
|
||||||
|
*/
|
||||||
|
void mctp_set_handler (
|
||||||
|
struct mctp *m,
|
||||||
|
int type,
|
||||||
|
int (*func)(struct mctp *m, struct mctp_action *ma))
|
||||||
|
{
|
||||||
|
if (type < MCMT_MAX)
|
||||||
|
m->handlers[type] = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the function to be called as the message handler thread
|
||||||
|
*/
|
||||||
|
void mctp_set_mh(struct mctp *m, void *(*fn)(void*arg))
|
||||||
|
{
|
||||||
|
m->fn_mh = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the verbosity bit mask
|
||||||
|
*/
|
||||||
|
void mctp_set_verbosity(struct mctp *m, __u32 level)
|
||||||
|
{
|
||||||
|
m->verbose = level;
|
||||||
|
m->state.verbose = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to request mctp threads to stop
|
||||||
|
*
|
||||||
|
* This is called by the thread functions to say they exited abnormally
|
||||||
|
* This is called when a thread has experienced an error and needs to tell the main thread to stop all the other threads
|
||||||
|
* Pend upon the mutex which will unlock when the main thread calls pthread_cond_wait()
|
||||||
|
* When the lock is obtained, tell the main thread to stop all the threads by setting a bit,
|
||||||
|
* then issue signal, then unlock, then exit
|
||||||
|
*/
|
||||||
|
void mctp_request_stop(struct mctp *m)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&m->mtx);
|
||||||
|
{
|
||||||
|
m->stop_threads = 2;
|
||||||
|
pthread_cond_signal(&m->cond);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&m->mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instruct all threads to stop, wait, join
|
||||||
|
*
|
||||||
|
* This can only be called by an external thread, not by any of the child mctp threads
|
||||||
|
*
|
||||||
|
* @detail Pend upon the mutex which will unlock when the main thread calls pthread_cond_wait()
|
||||||
|
* When the lock is obtained, tell the main thread to stop all the threads by setting a bit,
|
||||||
|
* then issue signal, then unlock, then exit
|
||||||
|
*/
|
||||||
|
int mctp_stop(struct mctp *m)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&m->mtx);
|
||||||
|
{
|
||||||
|
// If we get the mutex and the threads haven't started,
|
||||||
|
// then the connection thread has pended on the accept() and won't
|
||||||
|
// return, so cancel the thread
|
||||||
|
if ( ( m->use_threads != 0 ) && ( m->all_threads_started == 0 ) ) {
|
||||||
|
pthread_cancel(m->pt_ch);
|
||||||
|
close(m->sock);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m->stop_threads = 1;
|
||||||
|
pthread_cond_signal(&m->cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&m->mtx);
|
||||||
|
|
||||||
|
// Join with the connection handler thread before exiting function
|
||||||
|
pthread_join(m->pt_ch, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit an object for transmission
|
||||||
|
*
|
||||||
|
* @param m struct mctp*
|
||||||
|
* @param type mctp message type
|
||||||
|
* @param obj Pointer to serialized data buffer to send
|
||||||
|
* @param len Length of object in bytes
|
||||||
|
* @param retry Number of attempts to send the object. -1=forever, -2=default
|
||||||
|
* @param user_data void* to a user data object to keep with the action until completion
|
||||||
|
* @param fn_submitted Function to call when action is submitted to tmq
|
||||||
|
* @param fn_completed Function to call when response to action is received
|
||||||
|
* @param fn_failed Function to call when retry attempts have elapsed
|
||||||
|
* @return struct mctp_action* of the action submitted. NULL on error and sets errno
|
||||||
|
*
|
||||||
|
* STEPS
|
||||||
|
* 1: Validate inputs
|
||||||
|
* 2. Prepare message
|
||||||
|
* 3. Prepare action
|
||||||
|
* 4. Submit action
|
||||||
|
*/
|
||||||
|
struct mctp_action *mctp_submit(
|
||||||
|
struct mctp *m,
|
||||||
|
int type,
|
||||||
|
void *obj,
|
||||||
|
size_t len,
|
||||||
|
int retry,
|
||||||
|
struct timespec *delta,
|
||||||
|
void *user_data,
|
||||||
|
void (*fn_submitted)(struct mctp *m, struct mctp_action *a),
|
||||||
|
void (*fn_completed)(struct mctp *m, struct mctp_action *a),
|
||||||
|
void (*fn_failed)(struct mctp *m, struct mctp_action *a)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
INIT
|
||||||
|
struct mctp_action *ma;
|
||||||
|
struct mctp_msg *mm;
|
||||||
|
sem_t sem;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
ENTER
|
||||||
|
|
||||||
|
// Initialize varialbes
|
||||||
|
rv = 1;
|
||||||
|
ma = NULL;
|
||||||
|
|
||||||
|
STEP // 1: Validate inputs
|
||||||
|
if (m == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (obj == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
STEP // 2. Prepare Message
|
||||||
|
|
||||||
|
// Check out msg
|
||||||
|
mm = pq_pop(m->msgs, 1);
|
||||||
|
if (mm == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// Fill out msg
|
||||||
|
mm->owner = 1;
|
||||||
|
mm->type = type;
|
||||||
|
mm->len = len;
|
||||||
|
memcpy(&mm->payload, obj, len);
|
||||||
|
|
||||||
|
STEP // 3. Prepare Action
|
||||||
|
|
||||||
|
// Check out action
|
||||||
|
ma = pq_pop(m->actions, 1);
|
||||||
|
if (ma == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// Fill out action
|
||||||
|
memset(ma, 0, sizeof(struct mctp_action));
|
||||||
|
ma->valid = 1;
|
||||||
|
ma->req = mm;
|
||||||
|
|
||||||
|
if (retry < -1)
|
||||||
|
ma->max = MCTP_ACTION_DEFAULT_RETRY_NUM;
|
||||||
|
else
|
||||||
|
ma->max = retry;
|
||||||
|
|
||||||
|
timespec_get(&ma->created, CLOCK_MONOTONIC);
|
||||||
|
|
||||||
|
ma->user_data = user_data;
|
||||||
|
ma->fn_submitted = fn_submitted;
|
||||||
|
ma->fn_completed = fn_completed;
|
||||||
|
ma->fn_failed = fn_failed;
|
||||||
|
|
||||||
|
// Set mctp_action.sem to NULL if we are not going to pend on the sempahore
|
||||||
|
if (delta == NULL)
|
||||||
|
ma->sem = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Initialize Semaphore
|
||||||
|
sem_init(&sem, 0, 0);
|
||||||
|
|
||||||
|
ma->sem = &sem;
|
||||||
|
}
|
||||||
|
|
||||||
|
STEP // 4. Submit action
|
||||||
|
|
||||||
|
rv = pq_push(m->taq, ma);
|
||||||
|
if (rv != 0)
|
||||||
|
{
|
||||||
|
ma = NULL;
|
||||||
|
errno = EBUSY;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
STEP // 5: Pend on semaphore
|
||||||
|
if (delta != NULL)
|
||||||
|
{
|
||||||
|
// Compute absolute timeout
|
||||||
|
timespec_get(&ma->timeout, CLOCK_MONOTONIC);
|
||||||
|
timespec_add(&ma->timeout, delta, &ma->timeout);
|
||||||
|
|
||||||
|
// Pend on semaphore
|
||||||
|
rv = sem_timedwait(&sem, &ma->timeout);
|
||||||
|
|
||||||
|
// Clean up the one time use semaphore
|
||||||
|
sem_destroy(&sem);
|
||||||
|
|
||||||
|
// Check response. If the semaphore timedpout, then return NULL to caller
|
||||||
|
if (rv != 0)
|
||||||
|
ma = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
EXIT(rv)
|
||||||
|
|
||||||
|
return ma;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Functions to return a string representation of an object*/
|
||||||
|
const char *mcmt(unsigned u)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
switch (u)
|
||||||
|
{
|
||||||
|
case MCMT_CONTROL: rv = 0; break; // 0x00
|
||||||
|
case MCMT_PLDM: rv = 1; break; // 0x01
|
||||||
|
case MCMT_NCSI: rv = 2; break; // 0x02
|
||||||
|
case MCMT_ETHERNET: rv = 3; break; // 0x03
|
||||||
|
case MCMT_NVMEMI: rv = 4; break; // 0x04
|
||||||
|
case MCMT_SPDM: rv = 5; break; // 0x05
|
||||||
|
case MCMT_SECURE: rv = 6; break; // 0x06
|
||||||
|
case MCMT_CXLFMAPI: rv = 7; break; // 0x07
|
||||||
|
case MCMT_CXLCCI: rv = 8; break; // 0x08
|
||||||
|
case MCMT_VDM_PCI: rv = 9; break; // 0x7E
|
||||||
|
case MCMT_VDM_IANA: rv = 10; break; // 0x7F
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
return STR_MCMT[rv];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *mcrm(unsigned u)
|
||||||
|
{
|
||||||
|
if (u >= MCRM_MAX)
|
||||||
|
return NULL;
|
||||||
|
return STR_MCRM[u];
|
||||||
|
}
|
||||||
|
|
||||||
883
main.h
Normal file
883
main.h
Normal file
@ -0,0 +1,883 @@
|
|||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
/**
|
||||||
|
* @file mctp.h
|
||||||
|
*
|
||||||
|
* @brief Header file for MCTP transport library
|
||||||
|
*
|
||||||
|
* @details As per MCTP specification, all MCTP fields are Big Endian
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
|
||||||
|
*
|
||||||
|
* @date Jan 2024
|
||||||
|
* @author Barrett Edwards <code@jrlabs.io>
|
||||||
|
*
|
||||||
|
* Macro / Enumeration Prefixes
|
||||||
|
* MCCC - MCTP Control Completion Codes (CC)
|
||||||
|
* MCCM - MCTP Control Command IDs (CM)
|
||||||
|
* MCEP - MCTP Control - Get Endpoint EID - Endpoint Typea (EP)
|
||||||
|
* MCID - Special Endpoint ID values (ID)
|
||||||
|
* MCIT - MCTP Control - Get Endpoint EID - Endpoint ID Type (IT)
|
||||||
|
* MCMT - MCTP Message Type Codes (MT)
|
||||||
|
* MCRM - Run Mode for the MCTP Threads (RM)
|
||||||
|
* MCSE - MCTP Control Set EID Operations (SE)
|
||||||
|
* MCLN - Message Data Lengths for MCTP Control Messages (LN)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _MCTP_H
|
||||||
|
#define _MCTP_H
|
||||||
|
|
||||||
|
/* INCLUDES ==================================================================*/
|
||||||
|
|
||||||
|
/* __u8
|
||||||
|
* __u32
|
||||||
|
*/
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* useconds_t
|
||||||
|
*/
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/* struct sockaddr_in
|
||||||
|
*/
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
/* uuid_t
|
||||||
|
*/
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
|
||||||
|
/* timepsec
|
||||||
|
*/
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* sem_t
|
||||||
|
*/
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
/* MACROS ====================================================================*/
|
||||||
|
|
||||||
|
// Serialized length of MCTP Header
|
||||||
|
#define MCLN_HDR 4
|
||||||
|
// Serialized length of MCTP BTU
|
||||||
|
#define MCLN_BTU 64
|
||||||
|
// Serialized length of MCTP Packet
|
||||||
|
#define MCLN_PKT (MCLN_HDR + MCLN_BTU)
|
||||||
|
// Serialized length of MCTP Type
|
||||||
|
#define MCLN_TYPE 1
|
||||||
|
// Serialized length of MCTP Control UUID
|
||||||
|
#define MCLN_UUID 16
|
||||||
|
|
||||||
|
#define MCLN_MSG_PAYLOAD 8192
|
||||||
|
#define MCLN_MSG (MCLN_HDR + MCLN_TYPE + MCLN_MSG_PAYLOAD)
|
||||||
|
|
||||||
|
#define MCTP_MAX_INPROCESS_MESSAGES 8
|
||||||
|
#define MCTP_MAX_PACKET_NUM 1024
|
||||||
|
#define MCTP_MAX_MESSAGE_NUM 16
|
||||||
|
|
||||||
|
#define MCTP_NUM_TAGS 8
|
||||||
|
|
||||||
|
#define MCTP_RPQ_SIZE 1024
|
||||||
|
#define MCTP_TPQ_SIZE 1024
|
||||||
|
#define MCTP_RMQ_SIZE 128
|
||||||
|
#define MCTP_TMQ_SIZE 128
|
||||||
|
#define MCTP_TAQ_SIZE 128
|
||||||
|
#define MCTP_ACQ_SIZE 128
|
||||||
|
|
||||||
|
#define MCTP_PKT_POOL_SIZE 1024
|
||||||
|
#define MCTP_MSG_POOL_SIZE 128
|
||||||
|
#define MCTP_ACTION_POOL_SIZE 128
|
||||||
|
#define MCTP_ACTION_DEFAULT_RETRY_NUM 8
|
||||||
|
|
||||||
|
// Verbose bit fields
|
||||||
|
#define MCTP_VERBOSE_ERROR (0x01 << 0)
|
||||||
|
#define MCTP_VERBOSE_THREADS (0x01 << 1)
|
||||||
|
#define MCTP_VERBOSE_STEPS (0x01 << 2)
|
||||||
|
#define MCTP_VERBOSE_PACKET (0x01 << 3)
|
||||||
|
#define MCTP_VERBOSE_MESSAGE (0x01 << 4)
|
||||||
|
|
||||||
|
/* Action Macros */
|
||||||
|
#define MCTP_ACTION_DELTA_SEC 0
|
||||||
|
#define MCTP_ACTION_DELTA_NSEC 100000000
|
||||||
|
|
||||||
|
/* Threads Macros */
|
||||||
|
#define MCTP_THREAD_ERROR_USLEEP 1000
|
||||||
|
#define MCTP_THREAD_SUBMIT_NSLEEP 1000000
|
||||||
|
|
||||||
|
/* MCTP Control Macros */
|
||||||
|
#define SET_EID_ACCEPTED 0
|
||||||
|
#define SET_EID_REJECTED 1
|
||||||
|
|
||||||
|
// Length of MCTP_VERSIONS[] array
|
||||||
|
#define MCTP_VERSIONS_NUM 4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message Data Lengths for MCTP Control Messages (LN)
|
||||||
|
* The Response lengths include the 1 byte completion code length
|
||||||
|
*/
|
||||||
|
#define MCLN_CTRL 2
|
||||||
|
#define MCLN_CTRL_SET_EID_REQ 2
|
||||||
|
#define MCLN_CTRL_SET_EID_RESP 4
|
||||||
|
#define MCLN_CTRL_GET_EID_REQ 0
|
||||||
|
#define MCLN_CTRL_GET_EID_RESP 4
|
||||||
|
#define MCLN_CTRL_GET_UUID_REQ 0
|
||||||
|
#define MCLN_CTRL_GET_UUID_RESP 17
|
||||||
|
#define MCLN_CTRL_GET_VER_SUPPORT_REQ 1
|
||||||
|
#define MCLN_CTRL_GET_VER_SUPPORT_RESP 2
|
||||||
|
#define MCLN_CTRL_GET_MSG_TYPE_SUPPORT_REQ 0
|
||||||
|
#define MCLN_CTRL_GET_MSG_TYPE_SUPPORT_RESP 2
|
||||||
|
|
||||||
|
/* ENUMERATIONS ==============================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Message Type Codes (MT)
|
||||||
|
*
|
||||||
|
* See DSP0239 v1.9.0 Table 1.
|
||||||
|
*/
|
||||||
|
enum _MCMT
|
||||||
|
{
|
||||||
|
MCMT_CONTROL = 0x00,
|
||||||
|
MCMT_PLDM = 0x01,
|
||||||
|
MCMT_NCSI = 0x02,
|
||||||
|
MCMT_ETHERNET = 0x03,
|
||||||
|
MCMT_NVMEMI = 0x04,
|
||||||
|
MCMT_SPDM = 0x05,
|
||||||
|
MCMT_SECURE = 0x06,
|
||||||
|
MCMT_CXLFMAPI = 0x07,
|
||||||
|
MCMT_CXLCCI = 0x08,
|
||||||
|
MCMT_CSE = 0x70,
|
||||||
|
MCMT_VDM_PCI = 0x7E,
|
||||||
|
MCMT_VDM_IANA = 0x7F,
|
||||||
|
MCMT_MAX = 0xFF
|
||||||
|
};
|
||||||
|
#define MCMT_BASE 0xFF
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCTP Threads Run Mode (RM)
|
||||||
|
*/
|
||||||
|
enum _MCRM
|
||||||
|
{
|
||||||
|
MCRM_SERVER = 0,
|
||||||
|
MCRM_CLIENT = 1,
|
||||||
|
MCRM_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control Completion Codes (CC)
|
||||||
|
*
|
||||||
|
* See DSP0236 v1.3.0 Table 13.
|
||||||
|
*/
|
||||||
|
enum _MCCC
|
||||||
|
{
|
||||||
|
MCCC_SUCCESS = 0x00,
|
||||||
|
MCCC_ERROR = 0x01,
|
||||||
|
MCCC_ERROR_INVALID_DATA = 0x02,
|
||||||
|
MCCC_ERROR_INVALID_LENGTH = 0x03,
|
||||||
|
MCCC_ERROR_NOT_READY = 0x04,
|
||||||
|
MCCC_ERROR_UNSUPPORTED_CMD = 0x05,
|
||||||
|
MCCC_MAX = 0x06
|
||||||
|
/* 0x80 - 0xFF are command specific */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control Command IDs (CM)
|
||||||
|
*
|
||||||
|
* See DSP0236 v1.3.0 Table 12.
|
||||||
|
*/
|
||||||
|
enum _MCCM
|
||||||
|
{
|
||||||
|
MCCM_RESERVED = 0x00,
|
||||||
|
MCCM_SET_ENDPOINT_ID = 0x01,
|
||||||
|
MCCM_GET_ENDPOINT_ID = 0x02,
|
||||||
|
MCCM_GET_ENDPOINT_UUID = 0x03,
|
||||||
|
MCCM_GET_VERSION_SUPPORT = 0x04,
|
||||||
|
MCCM_GET_MESSAGE_TYPE_SUPPORT = 0x05,
|
||||||
|
MCCM_GET_VENDOR_MESSAGE_SUPPORT = 0x06,
|
||||||
|
MCCM_RESOLVE_ENDPOINT_ID = 0x07,
|
||||||
|
MCCM_ALLOCATE_ENDPOINT_IDS = 0x08,
|
||||||
|
MCCM_ROUTING_INFO_UPDATE = 0x09,
|
||||||
|
MCCM_GET_ROUTING_TABLE_ENTRIES = 0x0A,
|
||||||
|
MCCM_PREPARE_ENDPOINT_DISCOVERY = 0x0B,
|
||||||
|
MCCM_ENDPOINT_DISCOVERY = 0x0C,
|
||||||
|
MCCM_DISCOVERY_NOTIFY = 0x0D,
|
||||||
|
MCCM_GET_NETWORK_ID = 0x0E,
|
||||||
|
MCCM_QUERY_HOP = 0x0F,
|
||||||
|
MCCM_RESOLVE_UUID = 0x10,
|
||||||
|
MCCM_QUERY_RATE_LIMIT = 0x11,
|
||||||
|
MCCM_REQUEST_TX_RATE_LIMIT = 0x12,
|
||||||
|
MCCM_UPDATE_RATE_LIMIT = 0x13,
|
||||||
|
MCCM_QUERY_SUPPORTED_INTERFACES = 0x14,
|
||||||
|
MCCM_MAX = 0x15
|
||||||
|
/* 0xF0 - 0xFF are transport specific */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Get Endpoint EID - Endpoint Type (EP)
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 15
|
||||||
|
*/
|
||||||
|
enum _MCEP
|
||||||
|
{
|
||||||
|
MCEP_SIMPLE_ENDPOINT = 0,
|
||||||
|
MCEP_BRIDGE = 1,
|
||||||
|
MCEP_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special Endpoint ID values (ID)
|
||||||
|
*
|
||||||
|
* See DSP0236 v1.3.1 Table 2.
|
||||||
|
*/
|
||||||
|
enum _MCID
|
||||||
|
{
|
||||||
|
MCID_NULL = 0,
|
||||||
|
MCID_BROADCAST = 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Get Endpoint EID - Endpoint ID Type (IT)
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 15
|
||||||
|
*/
|
||||||
|
enum _MCIT
|
||||||
|
{
|
||||||
|
MCIT_DYNAMIC = 0,
|
||||||
|
MCIT_STATIC = 1,
|
||||||
|
MCIT_STATIC_CURRENT = 2,
|
||||||
|
MCIT_STATIC_DIFFERENT = 3,
|
||||||
|
MCIT_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control Set EID Operations (SE)
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 14
|
||||||
|
*/
|
||||||
|
enum _MCSE
|
||||||
|
{
|
||||||
|
MCSE_SET = 0,
|
||||||
|
MCSE_FORCE = 1,
|
||||||
|
MCSE_RESET = 2,
|
||||||
|
MCSE_DISCOVER = 3,
|
||||||
|
MCSE_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
/* STRUCTS ===================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Transport Header
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 1
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_hdr
|
||||||
|
{
|
||||||
|
__u8 ver : 4; //!< MCTP Header version
|
||||||
|
__u8 rsvd1 : 4;
|
||||||
|
|
||||||
|
__u8 dest; //!< Destination Endpoint ID
|
||||||
|
__u8 src; //!< Source Endpoint ID
|
||||||
|
|
||||||
|
__u8 tag : 3; //!< tag to track outstanding commands
|
||||||
|
__u8 owner : 1; //!< Requester is the originator of this cmd
|
||||||
|
__u8 seq : 2; //!< Packet Sequence number modulo 4
|
||||||
|
__u8 eom : 1; //!< End of Message flag
|
||||||
|
__u8 som : 1; //!< Start of Message flag
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Packet
|
||||||
|
*
|
||||||
|
* This is a packed structure for transmission
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 1
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_pkt
|
||||||
|
{
|
||||||
|
struct mctp_hdr hdr;
|
||||||
|
__u8 payload[MCLN_BTU];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCTP Packet Wrapper object for software use. Not packed. Cannot be sent directly
|
||||||
|
*/
|
||||||
|
struct mctp_pkt_wrapper
|
||||||
|
{
|
||||||
|
struct timespec ts; //!< Time when this packet was received
|
||||||
|
struct mctp_pkt_wrapper* next; //!< The next mctp_packet in a linked list
|
||||||
|
struct mctp_pkt pkt; //!< The data of this object
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Type Header
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 1
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_type
|
||||||
|
{
|
||||||
|
__u8 type :7; //!< MCTP Message Type [MCMT]
|
||||||
|
__u8 IC :1; //!< Integrity Check Field
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCTP Message
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 1
|
||||||
|
*/
|
||||||
|
struct mctp_msg
|
||||||
|
{
|
||||||
|
__u8 src;
|
||||||
|
__u8 dst;
|
||||||
|
__u8 type;
|
||||||
|
__u8 owner;
|
||||||
|
__u8 tag;
|
||||||
|
__u16 len;
|
||||||
|
struct timespec ts;
|
||||||
|
__u8 payload[MCLN_MSG_PAYLOAD];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State of the MCTP endpoint
|
||||||
|
*
|
||||||
|
* This is used to track MCTP related configuration
|
||||||
|
*/
|
||||||
|
struct mctp_state
|
||||||
|
{
|
||||||
|
__u8 eid;
|
||||||
|
__u8 bus_owner_eid;
|
||||||
|
__u32 verbose;
|
||||||
|
uuid_t uuid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCPT Control Message Fields (Request)
|
||||||
|
* DSP0236 1.3.1 Table 10
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ctrl
|
||||||
|
{
|
||||||
|
__u8 inst : 5; //!< Instance ID
|
||||||
|
__u8 rsvd : 1;
|
||||||
|
__u8 datagram : 1; //!< Datagram bit
|
||||||
|
__u8 req : 1; //!< Request Bit
|
||||||
|
|
||||||
|
__u8 cmd; //!< MCTP Control Command [MCCM]
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Set EID Request
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 14
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ctrl_set_eid_req
|
||||||
|
{
|
||||||
|
__u8 operation : 2;
|
||||||
|
__u8 rsvd1 : 6;
|
||||||
|
|
||||||
|
__u8 eid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Set EID response
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 14
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ctrl_set_eid_resp
|
||||||
|
{
|
||||||
|
__u8 comp_code;
|
||||||
|
|
||||||
|
__u8 allocation : 2;
|
||||||
|
__u8 rsvd2 : 2;
|
||||||
|
__u8 assignment : 2;
|
||||||
|
__u8 rsvd1 : 2;
|
||||||
|
|
||||||
|
__u8 eid;
|
||||||
|
|
||||||
|
__u8 pool_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Get Endpoint ID Response
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 15
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ctrl_get_eid_resp
|
||||||
|
{
|
||||||
|
__u8 comp_code; // MCCC
|
||||||
|
|
||||||
|
__u8 eid;
|
||||||
|
|
||||||
|
__u8 id_type : 2; // MCIT
|
||||||
|
__u8 rsvd2 : 2;
|
||||||
|
__u8 endpoint_type : 2; // MCEP
|
||||||
|
__u8 rsvd1 : 2;
|
||||||
|
|
||||||
|
__u8 medium_specific;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Get UUID Response
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 16
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ctrl_get_uuid_resp
|
||||||
|
{
|
||||||
|
__u8 comp_code; // MCCC
|
||||||
|
|
||||||
|
__u8 uuid[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Version Support
|
||||||
|
*
|
||||||
|
* This struct is used in the global variable to define what versions the emulator supports
|
||||||
|
* This is not serialized over the wire
|
||||||
|
* It is used in a linked list
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 18
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_version
|
||||||
|
{
|
||||||
|
__u8 major;
|
||||||
|
__u8 minor;
|
||||||
|
__u8 update;
|
||||||
|
__u8 alpha;
|
||||||
|
__u8 type; // MCTC
|
||||||
|
struct mctp_version *next_entry;
|
||||||
|
struct mctp_version *next_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Version Support
|
||||||
|
*
|
||||||
|
* This struct is sent over the wire.
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 18
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ver
|
||||||
|
{
|
||||||
|
__u8 major;
|
||||||
|
__u8 minor;
|
||||||
|
__u8 update;
|
||||||
|
__u8 alpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Get Version Support Request
|
||||||
|
*
|
||||||
|
* This struct is sent over the wire.
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 18
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ctrl_get_ver_req
|
||||||
|
{
|
||||||
|
__u8 type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Get Version Support Response
|
||||||
|
*
|
||||||
|
* This struct is sent over the wire.
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 18
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ctrl_get_ver_resp
|
||||||
|
{
|
||||||
|
__u8 comp_code; // MCCC
|
||||||
|
__u8 count; // Number of entries returned
|
||||||
|
struct mctp_ver versions[15]; // Versions supported
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MCTP Control - Get Message Type Support Response
|
||||||
|
*
|
||||||
|
* This struct is sent over the wire.
|
||||||
|
*
|
||||||
|
* DSP0236 1.3.1 Table 18
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ctrl_get_msg_type_resp
|
||||||
|
{
|
||||||
|
__u8 comp_code; // MCCC
|
||||||
|
__u8 count; // Number of entries returned
|
||||||
|
__u8 list[59]; // Versions supported
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCTP Control Message Object
|
||||||
|
*/
|
||||||
|
struct __attribute__((__packed__)) mctp_ctrl_msg
|
||||||
|
{
|
||||||
|
struct mctp_ctrl hdr; //!< MCTP Control Message Header
|
||||||
|
union {
|
||||||
|
struct mctp_ctrl_set_eid_req set_eid_req;
|
||||||
|
struct mctp_ctrl_set_eid_resp set_eid_rsp;
|
||||||
|
struct mctp_ctrl_get_eid_resp get_eid_rsp;
|
||||||
|
struct mctp_ctrl_get_uuid_resp get_uuid_rsp;
|
||||||
|
struct mctp_ctrl_get_ver_req get_ver_req;
|
||||||
|
struct mctp_ctrl_get_ver_resp get_ver_rsp;
|
||||||
|
struct mctp_ctrl_get_msg_type_resp get_msg_type_rsp;
|
||||||
|
} obj;
|
||||||
|
__u8 len; //!< Object Payload length in bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Establish there is an mctp object so other objects can have a pointer to it */
|
||||||
|
struct mctp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submission action object
|
||||||
|
*/
|
||||||
|
struct mctp_action
|
||||||
|
{
|
||||||
|
struct mctp_msg *req; //!< Request Message payload
|
||||||
|
struct mctp_msg *rsp; //!< Response Message payload
|
||||||
|
struct mctp_pkt_wrapper *pw;//!< Linked list of packets
|
||||||
|
|
||||||
|
struct timespec created; //!< Time stamp when action was created
|
||||||
|
struct timespec submitted; //!< Time of last submission
|
||||||
|
struct timespec completed; //!< Time when response was received
|
||||||
|
|
||||||
|
int valid; //!< Bool if this object is 1=valid or 0=not
|
||||||
|
int completion_code; //!< 0=Success, Failure Code otherwise
|
||||||
|
int num; //!< Number of transmission attempted
|
||||||
|
int max; //!< Maximum number of transmission attempts
|
||||||
|
void *user_data; //!< Pointer to user data kept with action until completion
|
||||||
|
|
||||||
|
sem_t *sem; //!< Semaphore to pend on until action has completed
|
||||||
|
struct timespec timeout; //!< Absolute time when the semaphore will expire
|
||||||
|
|
||||||
|
//!< Function to call when this action is submitted
|
||||||
|
void (*fn_submitted)(struct mctp *m, struct mctp_action *a);
|
||||||
|
|
||||||
|
//!< Function to call when this action has completed
|
||||||
|
void (*fn_completed)(struct mctp *m, struct mctp_action *a);
|
||||||
|
|
||||||
|
//!< Function to call if this action fails to complete
|
||||||
|
void (*fn_failed)(struct mctp *m, struct mctp_action *a);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object passed to Socket Writer thread function
|
||||||
|
*/
|
||||||
|
struct socket_writer
|
||||||
|
{
|
||||||
|
// Parent pointer
|
||||||
|
struct mctp *m;
|
||||||
|
|
||||||
|
// State fields
|
||||||
|
__u32 loop;
|
||||||
|
|
||||||
|
// Thread fields
|
||||||
|
pid_t threadid;
|
||||||
|
|
||||||
|
// State fields
|
||||||
|
__u64 packet_count;
|
||||||
|
__u64 dropped_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object passed to Packet Writer thread function
|
||||||
|
*/
|
||||||
|
struct packet_writer
|
||||||
|
{
|
||||||
|
// Parent pointer
|
||||||
|
struct mctp *m;
|
||||||
|
|
||||||
|
// State fields
|
||||||
|
__u32 loop;
|
||||||
|
|
||||||
|
// Thread fields
|
||||||
|
pid_t threadid;
|
||||||
|
useconds_t sleep_usec;
|
||||||
|
|
||||||
|
// State fields
|
||||||
|
__u8 pkt_seq;
|
||||||
|
__u64 packet_count;
|
||||||
|
__u64 message_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object passed to Message Handler thread function
|
||||||
|
*/
|
||||||
|
struct message_handler
|
||||||
|
{
|
||||||
|
// Parent pointer
|
||||||
|
struct mctp *m;
|
||||||
|
|
||||||
|
// State fields
|
||||||
|
__u32 loop;
|
||||||
|
|
||||||
|
// Thread fields
|
||||||
|
pid_t threadid;
|
||||||
|
useconds_t sleep_usec;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object passed to Packet Reader thread function
|
||||||
|
*/
|
||||||
|
struct packet_reader
|
||||||
|
{
|
||||||
|
// Parent pointer
|
||||||
|
struct mctp *m;
|
||||||
|
|
||||||
|
// Thread fields
|
||||||
|
pid_t threadid;
|
||||||
|
|
||||||
|
// State fields
|
||||||
|
__u32 loop;
|
||||||
|
__u8 pkt_seq;
|
||||||
|
__u64 packet_count;
|
||||||
|
__u64 message_count;
|
||||||
|
__u64 dropped_version;
|
||||||
|
__u64 dropped_seqnum;
|
||||||
|
__u64 dropped_noeom;
|
||||||
|
__u64 dropped_nosom;
|
||||||
|
__u64 dropped_wrongto;
|
||||||
|
|
||||||
|
// In process Messages
|
||||||
|
struct mctp_msg *tags[MCTP_NUM_TAGS];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object passed to Socket Reader thread function
|
||||||
|
*/
|
||||||
|
struct socket_reader
|
||||||
|
{
|
||||||
|
// Parent pointer
|
||||||
|
struct mctp *m;
|
||||||
|
|
||||||
|
// Thread fields
|
||||||
|
pid_t threadid;
|
||||||
|
useconds_t sleep_usec;
|
||||||
|
|
||||||
|
// State fields
|
||||||
|
__u32 loop;
|
||||||
|
__u64 sleep_count;
|
||||||
|
__u64 packet_count;
|
||||||
|
__u64 dropped_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object passed to Connection Handler thread function
|
||||||
|
*/
|
||||||
|
struct connection_handler
|
||||||
|
{
|
||||||
|
struct mctp *m;
|
||||||
|
pid_t threadid;
|
||||||
|
__u32 loop;
|
||||||
|
int dontblock;
|
||||||
|
sem_t *sem;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object passed to Submission Thread function
|
||||||
|
*/
|
||||||
|
struct submission_thread
|
||||||
|
{
|
||||||
|
struct mctp *m; //!< Parent pointer
|
||||||
|
pid_t threadid; //!< Threadid of this thread
|
||||||
|
__u32 loop; //!< Thread step / loop counter
|
||||||
|
|
||||||
|
struct timespec action_delta; //!< Relative time to wait on an action before resubmitting
|
||||||
|
struct timespec thread_delta; //!< Relative time for thread to wait when sleeping
|
||||||
|
struct timespec thread_timeout; //!< Absolute time when to wake from pthread_cond_wait()
|
||||||
|
|
||||||
|
pthread_mutex_t mtx; //!< Thread sleep mutex
|
||||||
|
pthread_cond_t cond; //!< Thread sleep condition
|
||||||
|
int wake; //!< Request to wake the thread
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object passed to Action Completion Thread function
|
||||||
|
*/
|
||||||
|
struct completion_thread
|
||||||
|
{
|
||||||
|
struct mctp *m; //!< Parent pointer
|
||||||
|
pid_t threadid; //!< Threadid of this thread
|
||||||
|
__u32 loop; //!< Thread step / loop counter
|
||||||
|
__u64 completed_actions; //!< Number of actions completed
|
||||||
|
__u64 successful_actions; //!< Number of actions that completed successfully
|
||||||
|
__u64 failed_actions; //!< Number of actions that failed
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State representation of a set of MCTP threads
|
||||||
|
*/
|
||||||
|
struct mctp
|
||||||
|
{
|
||||||
|
struct mctp_state state;
|
||||||
|
uuid_t uuid;
|
||||||
|
struct mctp_version *mctp_versions;
|
||||||
|
|
||||||
|
int (*handlers[MCMT_MAX]) (struct mctp *m, struct mctp_action *ma);
|
||||||
|
|
||||||
|
// Thread control
|
||||||
|
pthread_mutex_t mtx;
|
||||||
|
pthread_cond_t cond; // Condition to wake up main thread if there is a failure with the worker threads
|
||||||
|
__u32 verbose;
|
||||||
|
int use_threads;
|
||||||
|
int wait;
|
||||||
|
int all_threads_started;
|
||||||
|
int stop_threads;
|
||||||
|
int dummy;
|
||||||
|
|
||||||
|
// Outstanding commands array
|
||||||
|
struct mctp_action *tags[MCTP_NUM_TAGS];
|
||||||
|
pthread_mutex_t tags_mtx;
|
||||||
|
|
||||||
|
// Thread handles
|
||||||
|
pthread_t pt_ch; //!< PThread handle for Connection Thread
|
||||||
|
pthread_t pt_sr; //!< PThread handle for Socket Reader Thread
|
||||||
|
pthread_t pt_pr; //!< PThread handle for Packet Reader Thread
|
||||||
|
pthread_t pt_mh; //!< PThread handle for Message Handler Thread
|
||||||
|
pthread_t pt_pw; //!< PThread handle for Packet Writer Thread
|
||||||
|
pthread_t pt_sw; //!< PThread handle for Socket Writer Thread
|
||||||
|
pthread_t pt_st; //!< PThread handle for Submission Thread
|
||||||
|
pthread_t pt_ct; //!< PThread handle for Action Completion Thread
|
||||||
|
|
||||||
|
// Thread state
|
||||||
|
struct connection_handler ch;
|
||||||
|
struct socket_reader sr;
|
||||||
|
struct packet_reader pr;
|
||||||
|
struct message_handler mh;
|
||||||
|
struct packet_writer pw;
|
||||||
|
struct socket_writer sw;
|
||||||
|
struct submission_thread st;
|
||||||
|
struct completion_thread ct;
|
||||||
|
|
||||||
|
// Thread functions
|
||||||
|
void *(*fn_sr)(void *arg);
|
||||||
|
void *(*fn_pr)(void *arg);
|
||||||
|
void *(*fn_mh)(void *arg);
|
||||||
|
void *(*fn_pw)(void *arg);
|
||||||
|
void *(*fn_sw)(void *arg);
|
||||||
|
void *(*fn_st)(void *arg);
|
||||||
|
void *(*fn_ct)(void *arg);
|
||||||
|
|
||||||
|
// Object Pools
|
||||||
|
struct ptr_queue *pkts;
|
||||||
|
struct ptr_queue *msgs;
|
||||||
|
struct ptr_queue *actions;
|
||||||
|
|
||||||
|
// Queue fields
|
||||||
|
struct ptr_queue *rpq; //!< Receive Packet Queue
|
||||||
|
struct ptr_queue *tpq; //!< Transmit Packet Queue
|
||||||
|
struct ptr_queue *rmq; //!< Receive Message Queue
|
||||||
|
struct ptr_queue *tmq; //!< Transmit Message Queue
|
||||||
|
struct ptr_queue *taq; //!< Transmit Action Queue
|
||||||
|
struct ptr_queue *acq; //!< Action Completed Queue
|
||||||
|
|
||||||
|
// Socket fields
|
||||||
|
int port;
|
||||||
|
int mode;
|
||||||
|
int sock;
|
||||||
|
int conn;
|
||||||
|
socklen_t client_len;
|
||||||
|
struct sockaddr_in sa_server;
|
||||||
|
struct sockaddr_in sa_client;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* GLOBAL VARIABLES ==========================================================*/
|
||||||
|
|
||||||
|
/* PROTOTYPES ================================================================*/
|
||||||
|
|
||||||
|
/* External API */
|
||||||
|
struct mctp *mctp_init();
|
||||||
|
int mctp_run(struct mctp *m, int port, __u32 address, int mode, int use_threads, int dontblock);
|
||||||
|
int mctp_stop(struct mctp *m);
|
||||||
|
void mctp_request_stop(struct mctp *m);
|
||||||
|
int mctp_free(struct mctp *m);
|
||||||
|
void mctp_retire(struct mctp* m, struct mctp_action *a);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit an object for transmission
|
||||||
|
*
|
||||||
|
* Call will pend on a semaphore for a time sepcified in timespec delta if provided.
|
||||||
|
* If delta is not provided, call will submit and return immediately
|
||||||
|
*
|
||||||
|
* @param m struct mctp*
|
||||||
|
* @param type mctp message type
|
||||||
|
* @param obj Pointer to serialized data buffer to send
|
||||||
|
* @param len Length of object in bytes
|
||||||
|
* @param retry Number of attempts to send the object. -1=forever, -2=default
|
||||||
|
* @param delta struct timespec* Time to wait for a response.
|
||||||
|
* @param user_data void* to a user data object to keep with the action until completion
|
||||||
|
* @param fn_submitted Function to call when action is submitted to tmq
|
||||||
|
* @param fn_completed Function to call when response to action is received
|
||||||
|
* @param fn_failed Function to call when retry attempts have elapsed
|
||||||
|
* @return struct mctp_action* of the action submitted. NULL on error and sets errno
|
||||||
|
*
|
||||||
|
* STEPS
|
||||||
|
* 1: Validate inputs
|
||||||
|
* 2. Prepare message
|
||||||
|
* 3. Prepare action
|
||||||
|
* 4. Submit action
|
||||||
|
*/
|
||||||
|
struct mctp_action *mctp_submit(
|
||||||
|
struct mctp *m,
|
||||||
|
int type,
|
||||||
|
void *obj,
|
||||||
|
size_t len,
|
||||||
|
int retry,
|
||||||
|
struct timespec *delta,
|
||||||
|
void *user_data,
|
||||||
|
void (*fn_submitted)(struct mctp *m, struct mctp_action *a),
|
||||||
|
void (*fn_completed)(struct mctp *m, struct mctp_action *a),
|
||||||
|
void (*fn_failed)(struct mctp *m, struct mctp_action *a)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verbosity levels
|
||||||
|
void mctp_set_verbosity(struct mctp *m, __u32 level);
|
||||||
|
__u32 mctp_get_verbosity(struct mctp *m);
|
||||||
|
|
||||||
|
int mctp_set_version(struct mctp *m, __u8 type, __u8 major, __u8 minor, __u8 update, __u8 alpha);
|
||||||
|
void mctp_prnt_ver(struct mctp_version *mv, int indent);
|
||||||
|
int mctp_sprnt_ver(char *buf, struct mctp_version *mv);
|
||||||
|
int vercmp(struct mctp_version *lhs, struct mctp_version *rhs);
|
||||||
|
|
||||||
|
// Set handlers
|
||||||
|
void mctp_set_handler(struct mctp *m, int type, int (*func)(struct mctp *m, struct mctp_action *ma));
|
||||||
|
void mctp_set_mh(struct mctp *m, void *(*fn)(void*arg));
|
||||||
|
|
||||||
|
// Functions to populate common MCTP structs
|
||||||
|
void mctp_fill_msg_hdr(struct mctp_msg *mm, __u8 dest, __u8 src, __u8 owner, __u8 tag);
|
||||||
|
void mctp_fill_ctrl(struct mctp_msg *mm, __u8 req, __u8 datagram, __u8 inst, __u8 cmd);
|
||||||
|
|
||||||
|
int mctp_ctrl_fill_get_eid(struct mctp_ctrl_msg *m);
|
||||||
|
int mctp_ctrl_fill_get_type(struct mctp_ctrl_msg *m);
|
||||||
|
int mctp_ctrl_fill_get_ver(struct mctp_ctrl_msg *m, int type);
|
||||||
|
int mctp_ctrl_fill_get_uuid(struct mctp_ctrl_msg *m);
|
||||||
|
int mctp_ctrl_fill_set_eid(struct mctp_ctrl_msg *m, int eid);
|
||||||
|
|
||||||
|
int mctp_pkt_count(struct mctp_msg *mm);
|
||||||
|
|
||||||
|
/* MCTP Control Message Functions */
|
||||||
|
int mctp_ctrl_handler(struct mctp *m, struct mctp_action *ma);
|
||||||
|
struct mctp_ctrl *mctp_get_ctrl(struct mctp_msg *mm);
|
||||||
|
__u8 *mctp_get_ctrl_payload(struct mctp_msg *mm);
|
||||||
|
unsigned int mctp_len_ctrl(__u8 *ptr);
|
||||||
|
|
||||||
|
/* Thread Functions */
|
||||||
|
void *mctp_connection_handler(void *arg);
|
||||||
|
void *mctp_socket_reader(void *arg);
|
||||||
|
void *mctp_packet_reader(void *arg);
|
||||||
|
void *mctp_message_handler(void *arg);
|
||||||
|
void *mctp_socket_writer(void *arg);
|
||||||
|
void *mctp_packet_writer(void *arg);
|
||||||
|
void *mctp_submission_thread(void *arg);
|
||||||
|
void *mctp_completion_thread(void *arg);
|
||||||
|
|
||||||
|
/* Print methods for mctp objects*/
|
||||||
|
void mctp_prnt_hdr(struct mctp_hdr *mh);
|
||||||
|
void mctp_prnt_pkt_wrapper(struct mctp_pkt_wrapper *pw);
|
||||||
|
void mctp_prnt_pkt(struct mctp_pkt *mp);
|
||||||
|
void mctp_prnt_type(struct mctp_type *mt);
|
||||||
|
void mctp_prnt_msg(struct mctp_msg *mm);
|
||||||
|
void mctp_prnt_state(struct mctp_state *ms);
|
||||||
|
|
||||||
|
/* Return a string representation of enum entries */
|
||||||
|
const char *mcmt(unsigned u);
|
||||||
|
const char *mcrm(unsigned u);
|
||||||
|
const char *mccc(unsigned u);
|
||||||
|
const char *mccm(unsigned u);
|
||||||
|
const char *mcep(unsigned u);
|
||||||
|
const char *mcid(unsigned u);
|
||||||
|
const char *mcit(unsigned u);
|
||||||
|
const char *mcse(unsigned u);
|
||||||
|
|
||||||
|
#endif //ifndef _MCTP_H
|
||||||
274
server.c
Normal file
274
server.c
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
/**
|
||||||
|
* @file server.c
|
||||||
|
*
|
||||||
|
* @brief Code file to implement server example of MCTP transport library
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
|
||||||
|
*
|
||||||
|
* @date Jan 2024
|
||||||
|
* @author Barrett Edwards <code@jrlabs.io>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* INCLUDES ==================================================================*/
|
||||||
|
|
||||||
|
/* exit()
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* printf()
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* memset()
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* errno
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* __u8
|
||||||
|
* __u32
|
||||||
|
* __u64
|
||||||
|
*/
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <ptrqueue.h>
|
||||||
|
|
||||||
|
#include <fmapi.h>
|
||||||
|
|
||||||
|
#include "mctp.h"
|
||||||
|
|
||||||
|
/* MACROS ====================================================================*/
|
||||||
|
|
||||||
|
#define MCTP_PORT 2508
|
||||||
|
|
||||||
|
//#define MCTP_VERBOSE
|
||||||
|
#ifdef MCTP_VERBOSE
|
||||||
|
#define VERBOSE(v, m, t) ({ if(v) printf("%d:%s %s\n", t, __FUNCTION__, m ); })
|
||||||
|
#define VERBOSE_INT(v, m, t, i) ({ if(v) printf("%d:%s %s %d\n", t, __FUNCTION__, m, i); })
|
||||||
|
#define VERBOSE_STR(v, m, t, s) ({ if(v) printf("%d:%s %s %s\n", t, __FUNCTION__, m, s); })
|
||||||
|
#else
|
||||||
|
#define VERBOSE(v, m, t)
|
||||||
|
#define VERBOSE_INT(v, m, i)
|
||||||
|
#define VERBOSE_STR(v, m, s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ENUMERATIONS ==============================================================*/
|
||||||
|
|
||||||
|
/* STRUCTS ===================================================================*/
|
||||||
|
|
||||||
|
/* GLOBAL VARIABLES ==========================================================*/
|
||||||
|
|
||||||
|
/* PROTOTYPES ================================================================*/
|
||||||
|
|
||||||
|
int fmapi_handler(struct mctp *, struct mctp_action *ma);
|
||||||
|
int fmop_identify_switch_device(struct mctp_state *state, struct mctp_msg *req, struct mctp_msg *resp);
|
||||||
|
|
||||||
|
/* FUNCTIONS =================================================================*/
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
struct mctp *m;
|
||||||
|
|
||||||
|
// Create the threads object
|
||||||
|
m = mctp_init();
|
||||||
|
if (m == NULL) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Message handler functions
|
||||||
|
mctp_set_handler(m, MCMT_CXLFMAPI, fmapi_handler);
|
||||||
|
|
||||||
|
// Set verbosity levels
|
||||||
|
mctp_set_verbosity(m, mctp_get_verbosity(m) | MCTP_VERBOSE_ERROR);
|
||||||
|
mctp_set_verbosity(m, mctp_get_verbosity(m) | MCTP_VERBOSE_THREADS);
|
||||||
|
mctp_set_verbosity(m, mctp_get_verbosity(m) | MCTP_VERBOSE_STEPS);
|
||||||
|
mctp_set_verbosity(m, mctp_get_verbosity(m) | MCTP_VERBOSE_PACKET);
|
||||||
|
mctp_set_verbosity(m, mctp_get_verbosity(m) | MCTP_VERBOSE_MESSAGE);
|
||||||
|
|
||||||
|
// Run the threads
|
||||||
|
mctp_run(m, MCTP_PORT, 0, MCRM_SERVER, 1, 1);
|
||||||
|
|
||||||
|
|
||||||
|
printf("Main thread sleeping ###########################3\n");
|
||||||
|
sleep(10);
|
||||||
|
printf("Main thread calling stop ###########################3\n");
|
||||||
|
mctp_stop(m);
|
||||||
|
printf("Main thread calling free ###########################3\n");
|
||||||
|
|
||||||
|
// Free memory
|
||||||
|
mctp_free(m);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return 0 indicates that the response message should NOT be sent
|
||||||
|
* 1 indicates that the response message should be se sent
|
||||||
|
* STEPS
|
||||||
|
* 1: Verify type of message is CXL FMAPI
|
||||||
|
* 2: Deserialize buffer into local Request FM API Header object
|
||||||
|
* 3: Verify FM API Message Category
|
||||||
|
* 4: Fill Response MCTP Transport Header: dst, src, owner, tag, type
|
||||||
|
* 5: Handle Opcode
|
||||||
|
* 6: Handle simple response case
|
||||||
|
*/
|
||||||
|
int fmapi_handler(struct mctp *m, struct mctp_action *ma)
|
||||||
|
{
|
||||||
|
struct mctp_msg *mr, *mm;
|
||||||
|
struct fmapi_hdr req_fh, resp_fh;
|
||||||
|
int rv, rc, ret;
|
||||||
|
unsigned long len;
|
||||||
|
|
||||||
|
VERBOSE(1, "", 0);
|
||||||
|
|
||||||
|
// Initialize variables
|
||||||
|
len = 0;
|
||||||
|
ret = 0;
|
||||||
|
rc = FMRC_UNSUPPORTED;
|
||||||
|
mm = ma->req;
|
||||||
|
|
||||||
|
// : Get mctp_msg buffer for the response
|
||||||
|
mr = pq_pop(m->msgs, 1);
|
||||||
|
if (mr == NULL)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// STEP 1: Verify type of message is CXL FMAPI
|
||||||
|
if ( mm->type != MCMT_CXLFMAPI )
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// STEP 2: Deserialize buffer into local Request FM API Header object
|
||||||
|
rv = fmapi_deserialize(&req_fh, mm->payload, FMOB_HDR, NULL);
|
||||||
|
if (rv == 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// STEP 3: Verify FM API Message Category
|
||||||
|
if (req_fh.category != FMMT_REQ)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// STEP 4: Fill Response MCTP Transport Header: dst, src, owner, tag, type
|
||||||
|
mctp_fill_msg_hdr(mr, mm->src, mm->dst, 0, mm->tag);
|
||||||
|
mr->type = mm->type;
|
||||||
|
|
||||||
|
// STEP 5: Handle Opcode
|
||||||
|
switch(req_fh.opcode)
|
||||||
|
{
|
||||||
|
case FMOP_PSC_ID: // 0x5100
|
||||||
|
ret = fmop_identify_switch_device(&m->state, mm, mr);
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
default:
|
||||||
|
len = 0;
|
||||||
|
ret = 1;
|
||||||
|
rc = FMRC_UNSUPPORTED;
|
||||||
|
goto send;
|
||||||
|
}
|
||||||
|
|
||||||
|
send:
|
||||||
|
// STEP 6: Handle simple response case
|
||||||
|
|
||||||
|
// Fill Response FM API HDR
|
||||||
|
mr->len = fmapi_fill_hdr(&resp_fh, FMMT_RESP, req_fh.tag, req_fh.opcode, 0, len, rc, 0);
|
||||||
|
|
||||||
|
// Serialize response fmapi_hdr into response message data buffer
|
||||||
|
rv = fmapi_serialize(mr->payload, &resp_fh, FMOB_HDR);
|
||||||
|
if (rv == 0)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
ma->rsp = mr;
|
||||||
|
|
||||||
|
pq_push(m->tmq, ma);
|
||||||
|
|
||||||
|
end:
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the FM API Opcode: Identify Switch Device (Opcode 5100h)
|
||||||
|
*
|
||||||
|
* @return 1 to send response message, 0 to not send a response
|
||||||
|
*
|
||||||
|
* STEPS:
|
||||||
|
* 1: Deserialize buffer into local Request FM API Header object
|
||||||
|
* 2: Deserialize requset buffer into local object (if needed)
|
||||||
|
* 3: Obtain lock on switch state
|
||||||
|
* 4: Populate response object with data
|
||||||
|
* 5: Release lock on switch state
|
||||||
|
* 6: Compute FM API Payload Length
|
||||||
|
* 7: Fill Response FM API HDR
|
||||||
|
* 8: Serialize response fmapi_hdr into response message data buffer
|
||||||
|
* 9: Fill in opcode specifc response data
|
||||||
|
*/
|
||||||
|
int fmop_identify_switch_device(struct mctp_state *state, struct mctp_msg *req, struct mctp_msg *resp)
|
||||||
|
{
|
||||||
|
struct fmapi_hdr req_fh, resp_fh;
|
||||||
|
int rv, rc;
|
||||||
|
unsigned long len;
|
||||||
|
struct fmapi_psc_id_rsp id;
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
rc = FMRC_SUCCESS;
|
||||||
|
state->verbose = state->verbose;
|
||||||
|
|
||||||
|
// STEP 1: Deserialize buffer into local Request FM API Header object
|
||||||
|
rv = fmapi_deserialize(&req_fh, req->payload, FMOB_HDR, NULL);
|
||||||
|
if (rv == 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// STEP 2: Deserialize requset buffer into local object (if needed)
|
||||||
|
if (rv == 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// STEP 3: Obtain lock on switch state
|
||||||
|
// TBD
|
||||||
|
|
||||||
|
// STEP 4: Populate response object with data
|
||||||
|
memset(&id, 0, sizeof(struct fmapi_psc_id_rsp));
|
||||||
|
id.ingress_port = 1; //!< Ingress Port ID
|
||||||
|
id.num_ports = 32; //!< Total number of physical ports
|
||||||
|
id.num_vcss = 16; //!< Max number of VCSs
|
||||||
|
id.active_ports[0] = 0xFF; //!< Active physical port bitmask: enabled (1), disabled (0)
|
||||||
|
id.active_ports[1] = 0xFF; //!< Active physical port bitmask: enabled (1), disabled (0)
|
||||||
|
id.active_ports[2] = 0xFF; //!< Active physical port bitmask: enabled (1), disabled (0)
|
||||||
|
id.active_ports[3] = 0xFF; //!< Active physical port bitmask: enabled (1), disabled (0)
|
||||||
|
id.active_vcss[0] = 0xFF; //!< Active vcs bitmask: enabled (1), disabled (0)
|
||||||
|
id.active_vcss[1] = 0xFF; //!< Active vcs bitmask: enabled (1), disabled (0)
|
||||||
|
id.num_vppbs = 32; //!< Max number of vPPBs
|
||||||
|
id.active_vppbs = 32; //!< Number of active vPPBs
|
||||||
|
id.num_decoders = 1; //!< Number of HDM decoders available per USP
|
||||||
|
|
||||||
|
// STEP 5: Release lock on switch state
|
||||||
|
|
||||||
|
// STEP 6: Compute FM API Payload Length
|
||||||
|
len = FMLN_PSC_IDENTIFY_SWITCH;
|
||||||
|
|
||||||
|
// STEP 7: Fill Response FM API HDR
|
||||||
|
fmapi_fill_hdr(&resp_fh, FMMT_RESP, req_fh.tag, req_fh.opcode, 0, len, rc, 0);
|
||||||
|
|
||||||
|
// STEP 8: Serialize response fmapi_hdr into response message data buffer
|
||||||
|
rv = fmapi_serialize(resp->payload, &resp_fh, FMOB_HDR);
|
||||||
|
if (rv == 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// STEP 9: Fill in opcode specifc response data
|
||||||
|
rv = fmapi_serialize(resp->payload + FMLN_HDR, &id , FMOB_PSC_ID_RSP);
|
||||||
|
if (rv == 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
resp->len = FMLN_HDR + FMLN_PSC_IDENTIFY_SWITCH;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
end:
|
||||||
|
// 1 implies to send this response message back to requestor
|
||||||
|
return 0; // 0 implies to not send a response back to the requestor
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user