1330 lines
30 KiB
C
1330 lines
30 KiB
C
/* SPDX-License-Identifier: Apache-2.0 */
|
|
/**
|
|
* @file mctp_ctrl.c
|
|
*
|
|
* @brief Code file for MCTP Control Functions
|
|
*
|
|
* @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved.
|
|
*
|
|
* @date Jan 2024
|
|
* @author Barrett Edwards <code@jrlabs.io>
|
|
*
|
|
*/
|
|
|
|
/* INCLUDES ==================================================================*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
/* printf()
|
|
*/
|
|
#include <stdio.h>
|
|
|
|
/* memset()
|
|
* memcpy()
|
|
*/
|
|
#include <string.h>
|
|
|
|
/* calloc()
|
|
*/
|
|
#include <stdlib.h>
|
|
|
|
/* errno
|
|
*/
|
|
#include <errno.h>
|
|
|
|
/* Needed for gettid()
|
|
*/
|
|
#define _GNU_SOURCE
|
|
|
|
/* gettid()
|
|
*/
|
|
#include <unistd.h>
|
|
|
|
//#include <sys/types.h>
|
|
|
|
/* __u8
|
|
* __u32
|
|
* __u64
|
|
*/
|
|
#include <linux/types.h>
|
|
|
|
/* be32toh()
|
|
*/
|
|
#include <endian.h>
|
|
|
|
/* autl_prnt_buf()
|
|
*/
|
|
#include <arrayutils.h>
|
|
#include <ptrqueue.h>
|
|
|
|
#include "main.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 Special Endpoint ID values (ID)
|
|
*
|
|
* See DSP0236 v1.3.1 Table 2.
|
|
*/
|
|
const char *STR_MCID[] = {
|
|
"Null", // MCID_NULL = 0,
|
|
"Broadcast" // MCID_BROADCAST = 0xff
|
|
};
|
|
|
|
/*
|
|
* String representation of MCTP Control Completion Codes (CC)
|
|
*
|
|
* See DSP0236 v1.3.0 Table 13.
|
|
*/
|
|
const char *STR_MCCC[] = {
|
|
"Success", // MCCC_SUCCESS = 0x00,
|
|
"Error", // MCCC_ERROR = 0x01,
|
|
"Error Invalid Data", // MCCC_ERROR_INVALID_DATA = 0x02,
|
|
"Error Invalid Length", // MCCC_ERROR_INVALID_LENGTH = 0x03,
|
|
"Error Not Ready", // MCCC_ERROR_NOT_READY = 0x04,
|
|
"Error Unsupported CMD" // MCCC_ERROR_UNSUPPORTED_CMD = 0x05
|
|
};
|
|
|
|
/*
|
|
* MCTP Control Command IDs (CM)
|
|
*
|
|
* See DSP0236 v1.3.0 Table 12.
|
|
*/
|
|
const char *STR_MCCM[] = {
|
|
"Reserved", // MCCM_RESERVED = 0x00,
|
|
"Set Endpoint ID", // MCCM_SET_ENDPOINT_ID = 0x01,
|
|
"Get Endpoint ID", // MCCM_GET_ENDPOINT_ID = 0x02,
|
|
"Get Endpoint UUID", // MCCM_GET_ENDPOINT_UUID = 0x03,
|
|
"Get Version Support", // MCCM_GET_VERSION_SUPPORT = 0x04,
|
|
"Get Message Type Support", // MCCM_GET_MESSAGE_TYPE_SUPPORT = 0x05,
|
|
"Get Vendor Message Support", // MCCM_GET_VENDOR_MESSAGE_SUPPORT = 0x06,
|
|
"Resolve Endpoint ID", // MCCM_RESOLVE_ENDPOINT_ID = 0x07,
|
|
"Allocate Endpoint IDs", // MCCM_ALLOCATE_ENDPOINT_IDS = 0x08,
|
|
"Routing Info Update", // MCCM_ROUTING_INFO_UPDATE = 0x09,
|
|
"Get Routing Table Entries", // MCCM_GET_ROUTING_TABLE_ENTRIES = 0x0A,
|
|
"Prepare Endpoint Discovery", // MCCM_PREPARE_ENDPOINT_DISCOVERY = 0x0B,
|
|
"Endpoint Discover", // MCCM_ENDPOINT_DISCOVERY = 0x0C,
|
|
"Discovery Notify", // MCCM_DISCOVERY_NOTIFY = 0x0D,
|
|
"Get Network ID", // MCCM_GET_NETWORK_ID = 0x0E,
|
|
"Query hop", // MCCM_QUERY_HOP = 0x0F,
|
|
"Resolve UUID", // MCCM_RESOLVE_UUID = 0x10,
|
|
"Query Rate Limit", // MCCM_QUERY_RATE_LIMIT = 0x11,
|
|
"Request RX Rate Limit", // MCCM_REQUEST_TX_RATE_LIMIT = 0x12,
|
|
"Update Rate Limit", // MCCM_UPDATE_RATE_LIMIT = 0x13,
|
|
"Query Supported Interfaces" // MCCM_QUERY_SUPPORTED_INTERFACES = 0x14,
|
|
};
|
|
|
|
/*
|
|
* String representation of MCTP Control Set EID Operations (SE)
|
|
*
|
|
* DSP0236 1.3.1 Table 14
|
|
*/
|
|
const char *STR_MCSE[] = {
|
|
"Set", // MCSE_SET = 0,
|
|
"Force", // MCSE_FORCE = 1,
|
|
"Reset", // MCSE_RESET = 2,
|
|
"Discover" // MCSE_DISCOVER = 3,
|
|
};
|
|
|
|
/*
|
|
* String representation of MCTP Control - Get Endpoint EID - Endpoint Typea (EP)
|
|
*
|
|
* DSP0236 1.3.1 Table 15
|
|
*/
|
|
const char *STR_MCEP[] = {
|
|
"Endpoint", // MCEP_SIMPLE_ENDPOINT = 0,
|
|
"Bridge" // MCEP_BRIDGE = 1,
|
|
};
|
|
|
|
/*
|
|
* String representation of MCTP Control - Get Endpoint EID - Endpoint ID Type (IT)
|
|
*
|
|
* DSP0236 1.3.1 Table 15
|
|
*/
|
|
const char *STR_MCIT[] = {
|
|
"Dynamic", // MCIT_DYNAMIC = 0,
|
|
"Static", // MCIT_STATIC = 1,
|
|
"Static Current", // MCIT_STATIC_CURRENT = 2,
|
|
"Static Different" // MCIT_STATIC_DIFFERENT = 3
|
|
};
|
|
|
|
/* PROTOTYPES ================================================================*/
|
|
|
|
// Implemented
|
|
static int set_eid (struct mctp *m, struct mctp_action *ma);
|
|
static int get_eid (struct mctp *m, struct mctp_action *ma);
|
|
static int get_ver_support (struct mctp *m, struct mctp_action *ma);
|
|
static int get_type_support (struct mctp *m, struct mctp_action *ma);
|
|
static int get_uuid (struct mctp *m, struct mctp_action *ma);
|
|
|
|
// Unimplemented
|
|
int get_vendor_msg_type_support (struct mctp *m, struct mctp_action *ma);
|
|
int resolve_endpoint_id (struct mctp *m, struct mctp_action *ma);
|
|
int allocate_endpoint_ids (struct mctp *m, struct mctp_action *ma);
|
|
int routing_info_update (struct mctp *m, struct mctp_action *ma);
|
|
int get_routing_table_entries (struct mctp *m, struct mctp_action *ma);
|
|
int prepare_for_endpoint_discovery (struct mctp *m, struct mctp_action *ma);
|
|
int endpoint_discovery (struct mctp *m, struct mctp_action *ma);
|
|
int discovery_notify (struct mctp *m, struct mctp_action *ma);
|
|
int get_network_id (struct mctp *m, struct mctp_action *ma);
|
|
int query_hop (struct mctp *m, struct mctp_action *ma);
|
|
int resolve_uuid (struct mctp *m, struct mctp_action *ma);
|
|
int query_rate_limit (struct mctp *m, struct mctp_action *ma);
|
|
int request_tx_rate_limit (struct mctp *m, struct mctp_action *ma);
|
|
int update_rate_limit (struct mctp *m, struct mctp_action *ma);
|
|
int query_supported_interfaces (struct mctp *m, struct mctp_action *ma);
|
|
|
|
/* FUNCTIONS =================================================================*/
|
|
|
|
/**
|
|
* Handler of incoming MCTP Control messages
|
|
*
|
|
* @param[in/out] m Pointer to an mctp_state object
|
|
* @param[in] ma Pointer to an mctp_action that is the request
|
|
* @return 0 upon success, 1 upon failure
|
|
*
|
|
* STEPS
|
|
* 1: Get Message body
|
|
* 2: Verify request is from the tag owner, if not discard
|
|
* 3: Verify request bit, if not a request, discard
|
|
* 4: Verify EID
|
|
* 5: Handle each MCTP Control Command
|
|
*/
|
|
int mctp_ctrl_handler(struct mctp *m, struct mctp_action *ma)
|
|
{
|
|
INIT
|
|
int rv;
|
|
struct mctp_ctrl *mc;
|
|
|
|
ENTER
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Get Message body
|
|
mc = (struct mctp_ctrl*) ma->req->payload;
|
|
|
|
STEP // 2: Verify request is from the tag owner, if not discard
|
|
if ( ma->req->owner == 0)
|
|
goto end;
|
|
|
|
STEP // 3: Verify request bit, if not a request, discard
|
|
if ( mc->req == 0 )
|
|
goto end;
|
|
|
|
STEP // 4: Verify EID
|
|
//If new req isn't a Broadcast and the EID has been set, and the new req EID doesn't match, discard
|
|
if ( (ma->req->dst != MCID_NULL) && (ma->req->dst != MCID_BROADCAST) )
|
|
if (ma->req->dst != m->state.eid)
|
|
goto end;
|
|
|
|
STEP // 5: Handle each MCTP Control Command
|
|
switch (mc->cmd)
|
|
{
|
|
case MCCM_RESERVED: break; // 0x00
|
|
case MCCM_SET_ENDPOINT_ID: rv = set_eid(m, ma); break; // 0x01
|
|
case MCCM_GET_ENDPOINT_ID: rv = get_eid(m, ma); break; // 0x02
|
|
case MCCM_GET_ENDPOINT_UUID: rv = get_uuid(m, ma); break; // 0x03
|
|
case MCCM_GET_VERSION_SUPPORT: rv = get_ver_support(m, ma); break; // 0x04
|
|
case MCCM_GET_MESSAGE_TYPE_SUPPORT: rv = get_type_support(m, ma); break; // 0x05
|
|
case MCCM_GET_VENDOR_MESSAGE_SUPPORT: break; // 0x06
|
|
case MCCM_RESOLVE_ENDPOINT_ID: break; // 0x07
|
|
case MCCM_ALLOCATE_ENDPOINT_IDS: break; // 0x08
|
|
case MCCM_ROUTING_INFO_UPDATE: break; // 0x09
|
|
case MCCM_GET_ROUTING_TABLE_ENTRIES: break; // 0x0A
|
|
case MCCM_PREPARE_ENDPOINT_DISCOVERY: break; // 0x0B
|
|
case MCCM_ENDPOINT_DISCOVERY: break; // 0x0C
|
|
case MCCM_DISCOVERY_NOTIFY: break; // 0x0D
|
|
case MCCM_GET_NETWORK_ID: break; // 0x0E
|
|
case MCCM_QUERY_HOP: break; // 0x0F
|
|
case MCCM_RESOLVE_UUID: break; // 0x10
|
|
case MCCM_QUERY_RATE_LIMIT: break; // 0x11
|
|
case MCCM_REQUEST_TX_RATE_LIMIT: break; // 0x12
|
|
case MCCM_UPDATE_RATE_LIMIT: break; // 0x13
|
|
case MCCM_QUERY_SUPPORTED_INTERFACES: break; // 0x14
|
|
default: rv = 0; break;
|
|
}
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
/**
|
|
* Prepare an MCTP Control Message - Get EID
|
|
*
|
|
* @param m emapi_msg* to fill
|
|
* @return 0 upon success, non zero otherwise
|
|
*/
|
|
int mctp_ctrl_fill_get_eid(struct mctp_ctrl_msg *m)
|
|
{
|
|
int rv;
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
// Validate Inputs
|
|
if (m == NULL)
|
|
goto end;
|
|
|
|
// Clear Header
|
|
memset(&m->hdr, 0, sizeof(struct mctp_ctrl_msg));
|
|
|
|
// Set header
|
|
m->hdr.cmd = MCCM_GET_ENDPOINT_ID;
|
|
|
|
// Set object
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Prepare an MCTP Control Message - Get Message Type Support
|
|
*
|
|
* @param m emapi_msg* to fill
|
|
* @return 0 upon success, non zero otherwise
|
|
*/
|
|
int mctp_ctrl_fill_get_type(struct mctp_ctrl_msg *m)
|
|
{
|
|
int rv;
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
// Validate Inputs
|
|
if (m == NULL)
|
|
goto end;
|
|
|
|
// Clear Header
|
|
memset(&m->hdr, 0, sizeof(struct mctp_ctrl_msg));
|
|
|
|
// Set header
|
|
m->hdr.cmd = MCCM_GET_MESSAGE_TYPE_SUPPORT;
|
|
|
|
// Set object
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Prepare an MCTP Control Message - Get Message Version Support
|
|
*
|
|
* @param m emapi_msg* to fill
|
|
* @return 0 upon success, non zero otherwise
|
|
*/
|
|
int mctp_ctrl_fill_get_ver(struct mctp_ctrl_msg *m, int type)
|
|
{
|
|
int rv;
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
// Validate Inputs
|
|
if (m == NULL)
|
|
goto end;
|
|
|
|
// Clear Header
|
|
memset(&m->hdr, 0, sizeof(struct mctp_ctrl_msg));
|
|
|
|
// Set header
|
|
m->hdr.cmd = MCCM_GET_VERSION_SUPPORT;
|
|
|
|
// Set object
|
|
m->obj.get_ver_req.type = type;
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Prepare an MCTP Control Message - Get Endpoint UUID
|
|
*
|
|
* @param m emapi_msg* to fill
|
|
* @return 0 upon success, non zero otherwise
|
|
*/
|
|
int mctp_ctrl_fill_get_uuid(struct mctp_ctrl_msg *m)
|
|
{
|
|
int rv;
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
// Validate Inputs
|
|
if (m == NULL)
|
|
goto end;
|
|
|
|
// Clear Header
|
|
memset(&m->hdr, 0, sizeof(struct mctp_ctrl_msg));
|
|
|
|
// Set header
|
|
m->hdr.cmd = MCCM_GET_ENDPOINT_UUID;
|
|
|
|
// Set object
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Prepare an MCTP Control Message - Set Endpoint UUID
|
|
*
|
|
* @param m emapi_msg* to fill
|
|
* @return 0 upon success, non zero otherwise
|
|
*/
|
|
int mctp_ctrl_fill_set_eid(struct mctp_ctrl_msg *m, int eid)
|
|
{
|
|
int rv;
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
// Validate Inputs
|
|
if (m == NULL)
|
|
goto end;
|
|
|
|
// Clear Header
|
|
memset(&m->hdr, 0, sizeof(struct mctp_ctrl_msg));
|
|
|
|
// Set header
|
|
m->hdr.cmd = MCCM_SET_ENDPOINT_ID;
|
|
|
|
// Set object
|
|
m->obj.set_eid_req.eid = eid;
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Convenience function to fill MCTP Control object
|
|
*/
|
|
void mctp_fill_ctrl(
|
|
struct mctp_msg *mm,
|
|
__u8 req,
|
|
__u8 datagram,
|
|
__u8 inst,
|
|
__u8 cmd)
|
|
{
|
|
struct mctp_ctrl *mc;
|
|
mc = mctp_get_ctrl(mm);
|
|
mc->req = req;
|
|
mc->datagram = datagram;
|
|
mc->inst = inst;
|
|
mc->cmd = cmd;
|
|
}
|
|
|
|
/**
|
|
* Convenience Function to return a pointer to the MCTP Control object
|
|
*/
|
|
struct mctp_ctrl *mctp_get_ctrl(struct mctp_msg *mm)
|
|
{
|
|
return (struct mctp_ctrl*) mm->payload;
|
|
}
|
|
|
|
/**
|
|
* Convenience function to get a pointer to the 4th byte of a MCTP Control Message
|
|
*/
|
|
__u8 *mctp_get_ctrl_payload(struct mctp_msg *mm)
|
|
{
|
|
return mm->payload + MCLN_CTRL;
|
|
}
|
|
|
|
/**
|
|
* Perform MCTP Control - Get Endpoint ID Command
|
|
*
|
|
* @param m struct mctp*
|
|
* @param ma struct mctp_action* inbound message to handle
|
|
* @return 0 upon success, 1 upon failure
|
|
*
|
|
* STEPS
|
|
* 1: Get response mctp_msg
|
|
* 2: Set payload pointers
|
|
* 3: Validate Inputs
|
|
* 4: Perform Action
|
|
* 5: Prepare Response Object
|
|
* 6: Prepare Response Header
|
|
* 7: Prepare MCTP Header
|
|
* 8: Submit message to Transmit Message Queue
|
|
*/
|
|
static int get_eid(struct mctp *m, struct mctp_action *ma)
|
|
{
|
|
INIT
|
|
struct mctp_ctrl_msg *req, *rsp;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize Variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Get response mctp_msg
|
|
ma->rsp = pq_pop(m->msgs, 1);
|
|
if (ma->rsp == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Set payload pointers
|
|
req = (struct mctp_ctrl_msg*) &ma->req->payload;
|
|
rsp = (struct mctp_ctrl_msg*) &ma->rsp->payload;
|
|
|
|
STEP // 3: Validate Inputs
|
|
|
|
STEP // 4: Perform Action
|
|
|
|
STEP // 5: Prepare Response Object
|
|
rsp->obj.get_eid_rsp.comp_code = MCCC_SUCCESS;
|
|
rsp->obj.get_eid_rsp.eid = m->state.eid;
|
|
rsp->obj.get_eid_rsp.endpoint_type = MCEP_SIMPLE_ENDPOINT;
|
|
rsp->obj.get_eid_rsp.id_type = MCIT_DYNAMIC;
|
|
|
|
STEP // 6: Prepare Response Header
|
|
memcpy(&rsp->hdr, &req->hdr, sizeof(struct mctp_ctrl));
|
|
rsp->hdr.req = 0;
|
|
|
|
STEP // 7 : Prepare MCTP Header
|
|
ma->rsp->dst = ma->req->src;
|
|
ma->rsp->src = ma->req->dst;
|
|
ma->rsp->type = ma->req->type;
|
|
ma->rsp->len = MCLN_CTRL + MCLN_CTRL_GET_EID_RESP;
|
|
|
|
STEP // 8: Submit message to Transmit Message Queue
|
|
pq_push(m->tmq, ma);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Perform MCTP Control - Get Endpoint UUID Command
|
|
*
|
|
* @param m struct mctp*
|
|
* @param ma struct mctp_action* inbound message to handle
|
|
* @return 0 upon success, 1 upon failure
|
|
*
|
|
* STEPS
|
|
* 1: Get response mctp_msg
|
|
* 2: Set payload pointers
|
|
* 3: Validate Inputs
|
|
* 4: Perform Action
|
|
* 5: Prepare Response Object
|
|
* 6: Prepare Response Header
|
|
* 7: Prepare MCTP Header
|
|
* 8: Submit message to Transmit Message Queue
|
|
*/
|
|
static int get_uuid(struct mctp *m, struct mctp_action *ma)
|
|
{
|
|
INIT
|
|
struct mctp_ctrl_msg *req, *rsp;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Get response mctp_msg
|
|
ma->rsp = pq_pop(m->msgs, 1);
|
|
if (ma->rsp == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Set payload pointers
|
|
req = (struct mctp_ctrl_msg*) &ma->req->payload;
|
|
rsp = (struct mctp_ctrl_msg*) &ma->rsp->payload;
|
|
|
|
STEP // 3: Validate Inputs
|
|
|
|
STEP // 4: Perform Action
|
|
|
|
STEP // 5: Prepare Response Object
|
|
rsp->obj.get_uuid_rsp.comp_code = MCCC_SUCCESS;
|
|
memcpy(rsp->obj.get_uuid_rsp.uuid, m->state.uuid, MCLN_UUID);
|
|
|
|
STEP // 6: Prepare Response Header
|
|
memcpy(&rsp->hdr, &req->hdr, sizeof(struct mctp_ctrl));
|
|
rsp->hdr.req = 0;
|
|
|
|
STEP // 7 : Prepare MCTP Header
|
|
ma->rsp->dst = ma->req->src;
|
|
ma->rsp->src = ma->req->dst;
|
|
ma->rsp->type = ma->req->type;
|
|
ma->rsp->len = MCLN_CTRL + MCLN_CTRL_GET_UUID_RESP;
|
|
|
|
STEP // 7: Submit message to Transmit Message Queue
|
|
pq_push(m->tmq, ma);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Perform MCTP Control - Get MCTP Message Type Support Command
|
|
*
|
|
* @param m struct mctp*
|
|
* @param ma struct mctp_action* inbound message to handle
|
|
* @return 0 upon success, 1 upon failure
|
|
*
|
|
* STEPS
|
|
* 1: Get response mctp_msg
|
|
* 2: Set payload pointers
|
|
* 3: Validate Inputs
|
|
* 4: Perform Action
|
|
* 5: Prepare Response Object
|
|
* 6: Prepare Response Header
|
|
* 7: Prepare MCTP Header
|
|
* 8: Submit message to Transmit Message Queue
|
|
*/
|
|
static int get_type_support(struct mctp *m, struct mctp_action *ma)
|
|
{
|
|
INIT
|
|
struct mctp_ctrl_msg *req, *rsp;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize Variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Get response mctp_msg
|
|
ma->rsp = pq_pop(m->msgs, 1);
|
|
if (ma->rsp == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Set payload pointers
|
|
req = (struct mctp_ctrl_msg*) &ma->req->payload;
|
|
rsp = (struct mctp_ctrl_msg*) &ma->rsp->payload;
|
|
|
|
STEP // 3: Validate Inputs
|
|
|
|
STEP // 4: Perform Action
|
|
|
|
STEP // 5: Prepare Response Object
|
|
rsp->obj.get_msg_type_rsp.comp_code = MCCC_SUCCESS;
|
|
rsp->obj.get_msg_type_rsp.count = 2;
|
|
rsp->obj.get_msg_type_rsp.list[0] = MCMT_CXLFMAPI;
|
|
rsp->obj.get_msg_type_rsp.list[1] = MCMT_CXLCCI;
|
|
|
|
STEP // 6: Prepare Response Header
|
|
memcpy(&rsp->hdr, &req->hdr, sizeof(struct mctp_ctrl));
|
|
rsp->hdr.req = 0;
|
|
|
|
STEP // 7 : Prepare MCTP Header
|
|
ma->rsp->dst = ma->req->src;
|
|
ma->rsp->src = ma->req->dst;
|
|
ma->rsp->type = ma->req->type;
|
|
ma->rsp->len = MCLN_CTRL + MCLN_CTRL_GET_MSG_TYPE_SUPPORT_RESP + rsp->obj.get_msg_type_rsp.count;
|
|
|
|
STEP // 8: Submit message to Transmit Message Queue
|
|
pq_push(m->tmq, ma);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Perform MCTP Control - Get MCTP Version Support Command
|
|
*
|
|
* @param m struct mctp*
|
|
* @param ma struct mctp_action* inbound message to handle
|
|
* @return 0 upon success, 1 upon failure
|
|
*
|
|
* STEPS
|
|
* 1: Get response mctp_msg
|
|
* 2: Set payload pointers
|
|
* 3: Validate Inputs
|
|
* 4: Perform Action
|
|
* 5: Prepare Response Object
|
|
* 6: Prepare Response Header
|
|
* 7: Prepare MCTP Header
|
|
* 8: Submit message to Transmit Message Queue
|
|
*/
|
|
static int get_ver_support(struct mctp *m, struct mctp_action *ma)
|
|
{
|
|
INIT
|
|
struct mctp_ctrl_msg *req, *rsp;
|
|
struct mctp_version *head, *mv;
|
|
int rv;
|
|
|
|
int count;
|
|
|
|
ENTER
|
|
|
|
// Initialize Variables
|
|
rv = 1;
|
|
count = 0;
|
|
|
|
STEP // 1: Get response mctp_msg
|
|
ma->rsp = pq_pop(m->msgs, 1);
|
|
if (ma->rsp == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Set payload pointers
|
|
req = (struct mctp_ctrl_msg*) &ma->req->payload;
|
|
rsp = (struct mctp_ctrl_msg*) &ma->rsp->payload;
|
|
|
|
STEP // 3: Validate Inputs
|
|
|
|
STEP // 4: Perform Action
|
|
|
|
STEP // 5: Prepare Response Object
|
|
|
|
// Search linked list for entries of requested type
|
|
head = m->mctp_versions;
|
|
while (head != NULL)
|
|
{
|
|
if (head->type < req->obj.get_ver_req.type)
|
|
{
|
|
head = head->next_type;
|
|
continue;
|
|
}
|
|
else if (head->type == req->obj.get_ver_req.type)
|
|
{
|
|
mv = head;
|
|
while (mv != NULL)
|
|
{
|
|
rsp->obj.get_ver_rsp.versions[count].major = mv->major;
|
|
rsp->obj.get_ver_rsp.versions[count].minor = mv->minor;
|
|
rsp->obj.get_ver_rsp.versions[count].update = mv->update;
|
|
rsp->obj.get_ver_rsp.versions[count].alpha = mv->alpha;
|
|
count++;
|
|
|
|
// Break if we have reached the maximum number of entries returnable in a 64B MTU
|
|
if (count >= 14)
|
|
break;
|
|
|
|
mv = mv->next_entry;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Populate response buffer from DSP0236 1.3.1 Table 18
|
|
if (count > 0)
|
|
rsp->obj.get_ver_rsp.comp_code = MCCC_SUCCESS;
|
|
else
|
|
rsp->obj.get_ver_rsp.comp_code = 0x80;
|
|
rsp->obj.get_ver_rsp.count = count;
|
|
|
|
STEP // 6: Prepare Response Header
|
|
memcpy(&rsp->hdr, &req->hdr, sizeof(struct mctp_ctrl));
|
|
rsp->hdr.req = 0;
|
|
|
|
STEP // 7 : Prepare MCTP Header
|
|
ma->rsp->dst = ma->req->src;
|
|
ma->rsp->src = ma->req->dst;
|
|
ma->rsp->type = ma->req->type;
|
|
ma->rsp->len = MCLN_CTRL + MCLN_CTRL_GET_VER_SUPPORT_RESP + (count * 4);
|
|
|
|
STEP // 8: Submit message to Transmit Message Queue
|
|
pq_push(m->tmq, ma);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Perform MCTP Control - Set Endpoint ID Command
|
|
*
|
|
* @param m struct mctp*
|
|
* @param ma struct mctp_action* inbound message to handle
|
|
* @return 0 upon success, 1 upon failure
|
|
*
|
|
* STEPS
|
|
* 1: Get response mctp_msg
|
|
* 2: Set payload pointers
|
|
* 3: Validate Inputs
|
|
* 4: Perform Action
|
|
* 5: Prepare Response Object
|
|
* 6: Prepare Response Header
|
|
* 7: Prepare MCTP Header
|
|
* 8: Submit message to Transmit Message Queue
|
|
*/
|
|
static int set_eid(struct mctp *m, struct mctp_action *ma)
|
|
{
|
|
INIT
|
|
struct mctp_ctrl_msg *req, *rsp;
|
|
int rv;
|
|
|
|
ENTER
|
|
|
|
// Initialize Variables
|
|
rv = 1;
|
|
|
|
STEP // 1: Get response mctp_msg
|
|
ma->rsp = pq_pop(m->msgs, 1);
|
|
if (ma->rsp == NULL)
|
|
goto end;
|
|
|
|
STEP // 2: Set payload pointers
|
|
req = (struct mctp_ctrl_msg*) &ma->req->payload;
|
|
rsp = (struct mctp_ctrl_msg*) &ma->rsp->payload;
|
|
|
|
STEP // 3: Validate Inputs
|
|
|
|
// Reject unsupported Set EID opereations
|
|
// This endpoint doesn't support static EIDs, so fail if they try and do a reset
|
|
if (req->obj.set_eid_req.operation == MCSE_RESET)
|
|
{
|
|
rsp->obj.set_eid_rsp.comp_code = MCCC_ERROR_INVALID_DATA;
|
|
rsp->obj.set_eid_rsp.assignment = SET_EID_REJECTED;
|
|
rsp->obj.set_eid_rsp.eid = m->state.eid;
|
|
goto fail;
|
|
}
|
|
|
|
// This endpoint doesn't support discovery, so fail if requestor performs a MCSE_DISCOVER
|
|
if (req->obj.set_eid_req.operation == MCSE_DISCOVER)
|
|
{
|
|
rsp->obj.set_eid_rsp.comp_code = MCCC_ERROR_INVALID_DATA;
|
|
rsp->obj.set_eid_rsp.assignment = SET_EID_REJECTED;
|
|
rsp->obj.set_eid_rsp.eid = m->state.eid;
|
|
goto fail;
|
|
}
|
|
|
|
// Reject invalid EIDs
|
|
if ( (req->obj.set_eid_req.eid == MCID_NULL) || (req->obj.set_eid_req.eid == MCID_BROADCAST) )
|
|
{
|
|
rsp->obj.set_eid_rsp.comp_code = MCCC_ERROR_INVALID_DATA;
|
|
rsp->obj.set_eid_rsp.assignment = SET_EID_REJECTED;
|
|
rsp->obj.set_eid_rsp.eid = m->state.eid;
|
|
goto fail;
|
|
}
|
|
|
|
STEP // 4: Perform Action
|
|
m->state.eid = req->obj.set_eid_req.eid;
|
|
m->state.bus_owner_eid = ma->req->src;
|
|
|
|
// Print the MCTP endpoint state
|
|
if (m->verbose & MCTP_VERBOSE_STEPS)
|
|
mctp_prnt_state(&m->state);
|
|
|
|
STEP // 5: Prepare Response Object
|
|
|
|
rsp->obj.set_eid_rsp.comp_code = MCCC_SUCCESS;
|
|
rsp->obj.set_eid_rsp.assignment = SET_EID_ACCEPTED;
|
|
rsp->obj.set_eid_rsp.allocation = 0;
|
|
rsp->obj.set_eid_rsp.eid = m->state.eid;
|
|
rsp->obj.set_eid_rsp.pool_size = 0;
|
|
|
|
// Set the SRC EID in the MCTP Hdr to the new value
|
|
ma->rsp->src = m->state.eid;
|
|
|
|
STEP // 6: Prepare Response Header
|
|
memcpy(&rsp->hdr, &req->hdr, sizeof(struct mctp_ctrl));
|
|
rsp->hdr.req = 0;
|
|
|
|
STEP // 7 : Prepare MCTP Header
|
|
ma->rsp->dst = ma->req->src;
|
|
ma->rsp->src = ma->req->dst;
|
|
ma->rsp->type = ma->req->type;
|
|
ma->rsp->len = MCLN_CTRL + MCLN_CTRL_SET_EID_RESP;
|
|
|
|
STEP // 8: Submit message to Transmit Message Queue
|
|
pq_push(m->tmq, ma);
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
|
|
fail:
|
|
|
|
ma->completion_code = 1;
|
|
mctp_retire(m, ma);
|
|
|
|
EXIT(rv)
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Determine length in bytes of MCTP Control Message
|
|
*
|
|
* @param ptr unsigned char pointer to a buffer. The first item in the buffer
|
|
* is expected to be a mctp_ctrl struct followed by the command
|
|
* payload
|
|
* @return The length of the mctp_control + data in bytes
|
|
*/
|
|
unsigned int mctp_len_ctrl(__u8 *ptr)
|
|
{
|
|
unsigned len;
|
|
struct mctp_ctrl *mc;
|
|
__u8 *data;
|
|
|
|
len = 0;
|
|
mc = (struct mctp_ctrl*) ptr;
|
|
data = ptr + sizeof(struct mctp_ctrl);
|
|
|
|
switch(mc->cmd)
|
|
{
|
|
case MCCM_RESERVED: goto end; // 0x00
|
|
case MCCM_SET_ENDPOINT_ID: // 0x01
|
|
{
|
|
if (mc->req) len = MCLN_CTRL_SET_EID_REQ;
|
|
else len = MCLN_CTRL_SET_EID_RESP;
|
|
}
|
|
break;
|
|
|
|
case MCCM_GET_ENDPOINT_ID: // 0x02
|
|
{
|
|
if (mc->req) len = MCLN_CTRL_GET_EID_REQ;
|
|
else len = MCLN_CTRL_GET_EID_RESP;
|
|
}
|
|
break;
|
|
|
|
case MCCM_GET_ENDPOINT_UUID: // 0x03
|
|
{
|
|
if (mc->req) len = MCLN_CTRL_GET_UUID_REQ;
|
|
else len = MCLN_CTRL_GET_UUID_RESP;
|
|
}
|
|
break;
|
|
|
|
case MCCM_GET_VERSION_SUPPORT: // 0x04
|
|
{
|
|
if (mc->req)
|
|
len = MCLN_CTRL_GET_VER_SUPPORT_REQ;
|
|
else {
|
|
// Get the Version Number Entry Count from byte 2 of the data
|
|
len = MCLN_CTRL_GET_VER_SUPPORT_RESP + data[1] * 4;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MCCM_GET_MESSAGE_TYPE_SUPPORT: // 0x05
|
|
{
|
|
if (mc->req)
|
|
len = MCLN_CTRL_GET_MSG_TYPE_SUPPORT_REQ;
|
|
else {
|
|
// Get the Message Type Count from byte 2 of the data
|
|
len = MCLN_CTRL_GET_MSG_TYPE_SUPPORT_RESP + data[1] * 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MCCM_GET_VENDOR_MESSAGE_SUPPORT: goto end; // 0x06
|
|
case MCCM_RESOLVE_ENDPOINT_ID: goto end; // 0x07
|
|
case MCCM_ALLOCATE_ENDPOINT_IDS: goto end; // 0x08
|
|
case MCCM_ROUTING_INFO_UPDATE: goto end; // 0x09
|
|
case MCCM_GET_ROUTING_TABLE_ENTRIES: goto end; // 0x0A
|
|
case MCCM_PREPARE_ENDPOINT_DISCOVERY: goto end; // 0x0B
|
|
case MCCM_ENDPOINT_DISCOVERY: goto end; // 0x0C
|
|
case MCCM_DISCOVERY_NOTIFY: goto end; // 0x0D
|
|
case MCCM_GET_NETWORK_ID: goto end; // 0x0E
|
|
case MCCM_QUERY_HOP: goto end; // 0x0F
|
|
case MCCM_RESOLVE_UUID: goto end; // 0x10
|
|
case MCCM_QUERY_RATE_LIMIT: goto end; // 0x11
|
|
case MCCM_REQUEST_TX_RATE_LIMIT: goto end; // 0x12
|
|
case MCCM_UPDATE_RATE_LIMIT: goto end; // 0x13
|
|
case MCCM_QUERY_SUPPORTED_INTERFACES: goto end; // 0x14
|
|
default: goto end;
|
|
}
|
|
|
|
// Add the length of the MCTP Control Message Header
|
|
len += MCLN_CTRL;
|
|
|
|
return len;
|
|
|
|
end:
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* BCD Digit Compare
|
|
* @return -1 lhs comes before rhs
|
|
* 0 lhs == rhs
|
|
* +1 lhs comes after rhs
|
|
*/
|
|
int dgtcmp(__u8 lhs, __u8 rhs)
|
|
{
|
|
if (lhs == rhs)
|
|
return 0;
|
|
if (lhs == 0x0F && rhs != 0x0F)
|
|
return -1;
|
|
if (rhs == 0x0F && lhs != 0x0F)
|
|
return 1;
|
|
if (lhs < rhs)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* MCTP Version Compare
|
|
* @return -1 lhs comes before rhs
|
|
* 0 lhs == rhs
|
|
* +1 lhs comes after rhs
|
|
*/
|
|
int vercmp(struct mctp_version *lhs, struct mctp_version *rhs)
|
|
{
|
|
// Compare upper digit of Major
|
|
switch( dgtcmp(lhs->major >> 4, rhs->major >> 4) )
|
|
{
|
|
case -1: return -1;
|
|
case 1: return 1;
|
|
}
|
|
// Compare lower digit of Major
|
|
switch( dgtcmp(lhs->major & 0x0F, rhs->major & 0x0F) )
|
|
{
|
|
case -1: return -1;
|
|
case 1: return 1;
|
|
}
|
|
|
|
// Compare upper digit of Minor
|
|
switch( dgtcmp(lhs->minor >> 4, rhs->minor >> 4) )
|
|
{
|
|
case -1: return -1;
|
|
case 1: return 1;
|
|
}
|
|
// Compare lower digit of Minor
|
|
switch( dgtcmp(lhs->minor & 0x0F, rhs->minor & 0x0F) )
|
|
{
|
|
case -1: return -1;
|
|
case 1: return 1;
|
|
}
|
|
|
|
// Compare upper digit of Update
|
|
switch( dgtcmp(lhs->update >> 4, rhs->update >> 4) )
|
|
{
|
|
case -1: return -1;
|
|
case 1: return 1;
|
|
}
|
|
// Compare lower digit of Update
|
|
switch( dgtcmp(lhs->update & 0x0F, rhs->update & 0x0F) )
|
|
{
|
|
case -1: return -1;
|
|
case 1: return 1;
|
|
}
|
|
|
|
return dgtcmp(lhs->alpha, rhs->alpha);
|
|
}
|
|
|
|
/**
|
|
* SPrint the MCTP version into a character buffer
|
|
* @param buf char* to a buffer of at least 11 length (includes null terminator)
|
|
* @poaram mv struct mctp_version*
|
|
* @return The number of characters printed - not including the null terminator
|
|
*/
|
|
int mctp_sprnt_ver(char *buf, struct mctp_version *mv)
|
|
{
|
|
int i;
|
|
|
|
i = 0;
|
|
|
|
// Major Upper Digit
|
|
if ((mv->major & 0xF0) != 0xF0)
|
|
i += sprintf(&buf[i], "%d", (mv->major >> 4) & 0x0F);
|
|
|
|
// Major Lower Digit
|
|
i += sprintf(&buf[i], "%d.", (mv->major & 0x0F));
|
|
|
|
// Minor Upper Digit
|
|
if ((mv->minor & 0xF0) != 0xF0)
|
|
i += sprintf(&buf[i], "%d", (mv->minor >> 4) & 0x0F);
|
|
|
|
// Minor Lower
|
|
i += sprintf(&buf[i], "%d", (mv->minor & 0x0F));
|
|
|
|
// Don't print anything for the update if it is 0xFF
|
|
if (mv->update != 0xFF)
|
|
{
|
|
i += sprintf(&buf[i], ".");
|
|
|
|
// Update Upper Digit
|
|
if ((mv->update & 0xF0) != 0xF0)
|
|
i += sprintf(&buf[i], "%d", (mv->update >> 4) & 0x0F);
|
|
|
|
// Update Lower Digit
|
|
i += sprintf(&buf[i], "%d", (mv->update & 0x0F));
|
|
}
|
|
|
|
// Don't print anything for the alpha if it is 0x00
|
|
if (mv->alpha != 0)
|
|
i += sprintf(&buf[i], "%c", mv->alpha);
|
|
|
|
buf[i] = 0;
|
|
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
* Print a struct mctp_version
|
|
*
|
|
*Format: "type: major.minor.update.alpha
|
|
*/
|
|
void mctp_prnt_ver(struct mctp_version *mv, int indent)
|
|
{
|
|
char space[] = " ";
|
|
char buf[11];
|
|
space[indent] = 0;
|
|
|
|
mctp_sprnt_ver(buf, mv);
|
|
|
|
printf("%s0x%02x: %s\n", space, mv->type, buf);
|
|
}
|
|
|
|
/**
|
|
* Print the linked list array of mctp_verion objects
|
|
*/
|
|
void mctp_prnt_vers(struct mctp_version *mv)
|
|
{
|
|
struct mctp_version *head, *curr;
|
|
|
|
head = mv;
|
|
|
|
while (head != NULL)
|
|
{
|
|
mctp_prnt_ver(head,0);
|
|
|
|
curr = head->next_entry;
|
|
while (curr != NULL)
|
|
{
|
|
mctp_prnt_ver(curr,4);
|
|
curr = curr->next_entry;
|
|
}
|
|
head = head->next_type;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add an entry to the list of supported MCTP Message versions
|
|
*/
|
|
int mctp_set_version(struct mctp *m, __u8 type, __u8 major, __u8 minor, __u8 update, __u8 alpha)
|
|
{
|
|
struct mctp_version *new, *curr, *prev;
|
|
int rv;
|
|
|
|
// Initialize variables
|
|
rv = 1;
|
|
|
|
// Allocate and clear memory for new struct
|
|
new = calloc (1, sizeof(struct mctp_version));
|
|
if (new == NULL)
|
|
goto end;
|
|
|
|
// Store new values in new struct
|
|
new->type = type;
|
|
new->major = major;
|
|
new->minor = minor;
|
|
new->update = update;
|
|
new->alpha = alpha;
|
|
|
|
// Determine if this is the first version to be created
|
|
// If there is an existing linked list, insert at the head of the list
|
|
if (m->mctp_versions == NULL)
|
|
{
|
|
m->mctp_versions = new;
|
|
goto end;
|
|
}
|
|
|
|
// If the new version is less than the first types entry, set it to the first
|
|
prev = NULL;
|
|
curr = m->mctp_versions;
|
|
if (new->type < curr->type)
|
|
{
|
|
new->next_type = curr;
|
|
m->mctp_versions = new;
|
|
goto end;
|
|
}
|
|
|
|
if (new->type == curr->type)
|
|
{
|
|
if (vercmp(new, curr) < 0)
|
|
{
|
|
new->next_type = curr->next_type;
|
|
new->next_entry = curr;
|
|
m->mctp_versions = new;
|
|
goto end;
|
|
}
|
|
if (vercmp(new, curr) == 0)
|
|
{
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
// Find head of type list
|
|
while ( curr != NULL )
|
|
{
|
|
if (new->type < curr->type)
|
|
{
|
|
new->next_type = curr;
|
|
prev->next_type = new;
|
|
goto end;
|
|
}
|
|
else if (new->type == curr->type)
|
|
{
|
|
// if new version less than curr version, insert ahead of it in the sub array
|
|
if (vercmp(new, curr) < 0)
|
|
{
|
|
prev->next_type = new;
|
|
new->next_type = curr->next_type;
|
|
new->next_entry = curr;
|
|
curr->next_type = NULL;
|
|
goto end;
|
|
}
|
|
|
|
// Drop new if version equal to head of sub list
|
|
if (vercmp(new, curr) == 0)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
prev = curr;
|
|
curr = curr->next_entry;
|
|
|
|
while (1)
|
|
{
|
|
// If we have reached the end of the list, append
|
|
if (curr == NULL)
|
|
{
|
|
prev->next_entry = new;
|
|
goto end;
|
|
}
|
|
|
|
if (vercmp(new, curr) < 0)
|
|
{
|
|
prev->next_entry = new;
|
|
new->next_entry = curr;
|
|
goto end;
|
|
}
|
|
|
|
// Drop new if version equal to curr of sub list
|
|
if (vercmp(new, curr) == 0)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
prev = curr;
|
|
curr = curr->next_entry;
|
|
}
|
|
}
|
|
else if (curr->next_type == NULL)
|
|
{
|
|
curr->next_type = new;
|
|
goto end;
|
|
}
|
|
|
|
prev = curr;
|
|
curr = curr->next_type;
|
|
}
|
|
|
|
rv = 0;
|
|
|
|
end:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* Functions to return a string representation of an object */
|
|
const char *mccc(unsigned u)
|
|
{
|
|
if (u >= MCCC_MAX)
|
|
return NULL;
|
|
return STR_MCCC[u];
|
|
}
|
|
|
|
const char *mccm(unsigned u)
|
|
{
|
|
if (u >= MCCM_MAX)
|
|
return NULL;
|
|
return STR_MCCM[u];
|
|
}
|
|
|
|
const char *mcep(unsigned u)
|
|
{
|
|
if (u >= MCEP_MAX)
|
|
return NULL;
|
|
return STR_MCEP[u];
|
|
}
|
|
|
|
const char *mcid(unsigned u)
|
|
{
|
|
int rv;
|
|
switch(u)
|
|
{
|
|
case MCID_NULL: rv = 0; break; // 0x00
|
|
case MCID_BROADCAST: rv = 1; break; // 0xff
|
|
default: return NULL;
|
|
}
|
|
return STR_MCID[rv];
|
|
}
|
|
|
|
const char *mcit(unsigned u)
|
|
{
|
|
if (u >= MCIT_MAX)
|
|
return NULL;
|
|
return STR_MCIT[u];
|
|
}
|
|
|
|
const char *mcse(unsigned u)
|
|
{
|
|
if (u >= MCSE_MAX)
|
|
return NULL;
|
|
return STR_MCSE[u];
|
|
}
|
|
|