VBUS APIs for a Hosted App

Introduction

VBUS (vehicle bus) APIs provide the ability for applications running on the LMU-55XX platform to send and receive data to/from connected CalAmp VBUS devices.

This page defines the hosted application VBUS API interface to the LMU application running on the CalAmp LMU-55XX Linux-based product and explains the supported API for VBUS activity on the LMU-55XX platform, including some code examples.

The messaging service enables a Linux application to send and receive data to/from the connected CalAmp VBUS device (VPOD2, JPOD2).

Use calamp_app_vbus_sdk_est to demonstrate all VBUS API functionality.

Note that the VBUS API currently supports only JPOD2.

Background

The LMU-55XX is a Linux platform composed of several application processes. One of the processes is the CalAmp LMU application that is responsible for location and messaging services using PEG and LMDirect.

Additionally, the CalAmp LMU application supports the vehicle bus devices JPOD1/JPOD2 and VPOD1/VPOD2. Other hosted application processes running the Linux platform should be able to access the VBUS (JPOD2 and VPOD2) services within the LMU app.

Specifically, one or more hosted application processes running alongside the LMU app need to be able to send and receive JPOD2 or VPOD2 data, which is sent to the LMU inbound server (typically the customer’s server) using the LMDirect user message format.

This document details the CalAmp LMU application VBUS API, which customer-hosted applications can use to receive JPOD2 or VPOD2 data.

The CalAmp LMU Application

The VBUS API requires a CalAmp LMU application.

LMU App VBUS API Architecture Overview and Flow

Communications between the LMU app and other processes wanting to access the VBUS JPOD2 or VPOD2 messaging are implemented using the Linux System V IPC message queue methods.

For messaging services, the LMU app supports a single VBUS SndMsg queue through which an external process can register itself with the LMU application and receive JPOD2 or VPOD2 data from the LMU. When the external process sends a registration notification to the LMU app via the SndMsg queue, the buffer contains the external process’s RcvMsg queued for receiving supported VBUS JPOD2/VPOD2 responses. The external process’s RcvMsg queue must be created as a shared system resource so that the LMU app can pass data to the requesting hosted app.

Here is an overview illustration of the hosted app, CalAmp LMU, and VBUS devices:

          Hosted application (IPC clients)
                 |
          CalAmp LMU application VBUS API (IPC server)
                 |
          CalAmp LMU application VBUS device handler
                 |
          Physical VBUS device (JPOD2 or VPOD2)
                 |
          Physical vehicle bus

The sequence for a hosted application to send a VBUS API request and to set or get back the JPOD2 or VPOD2 response data is as follows:

  1. Get access to the LMU app VBUS API request IPC message queue (/etc/calamp/lmu/vbus_sdk_queue).
  2. Create a client VBUS API response IPC message queue (/etc/calamp/lmu/vbus_sdk_client_queue).
  3. Fill in the VBUS param read, VBUS param write, VBUS action, or VBUS data group IPC request structure, which will be passed to the LMU app.
  4. Send the IPC message to the LMU app via the Linux IPC function msgsnd().
  5. Wait for the response via the Linux IPC function msgrcv().
  6. If the response message indicates that the response data is larger than a response message payload, wait and retrieve the incoming Linux IPC messages via a msgrcv() loop.
  7. As part of the hosted application termination handling, the hosted app should destroy the client VBUS API response IPC message queue that was created in step 2.

LMU App VBUS API Components

The components of the VBUS API consist of the following:

  • CalAmp LMU application:
    • VBUS API header file: lmuAppVBusSdk.h
    • CalAmp LMU VBUS IPC message queue: /etc/calamp/lmu/vbus_sdk_queue
    • CalAmp LMU VBUS API request and response handling code
  • Client-hosted application:
    • VBUS API header file: lmuAppVBusSdk.h
    • Client-hosted application VBUS IPC message queue: /etc/calamp/lmu/vbus_sdk_client_queue
    • Hosted app VBUS API request and response handling code

VBUS API Header File

The CalAmp VBUS API header file is lmuAppVBusSdk.h .

VBUS API IPC Message Queues

The LMU app VBUS API send request IPC message queue is defined as follows:
#define LMU_APP_VBUS_SDK_FILE ("/etc/calamp/lmu/vbus_sdk_queue")

This IPC message queue is created by the LMU app and used by the client hosted app program to send a supported message to the LMU application.

The LMU app VBUS API client response IPC message queue is defined as follows:
#define LMU_APP_VBUS_CLIENT_FILE ("/etc/calamp/lmu/vbus_sdk_client_queue")

This IPC message queue is created by the client hosted app program and passed to the LMU app. This IPC queue is where the LMU app will send response messages to the client hosted app program.

VBUS API IPC Message Request Operations

OpcodeValueDescription
LMU_APP_VBUS_CMD_READ256Read a VBUS (JPOD2) parameter.
LMU_APP_VBUS_CMD_WRITE257Read a VBUS (JPOD2) parameter.
LMU_APP_VBUS_CMD_ACTION258Run a VBUS (JPOD2) action.
LMU_APP_VBUS_CMD_REQ_DATA_GROUP259Query a VBUS (JPOD2) data group message.

VBUS API IPC Message Request

The LMU VBUS API uses the standard Linux IPC message queue mechanism so that a client (hosted application) can send a request to the LMU application and receive a response from it.

The LMU VBUS API message send packet structures and message response packet structures are defined in the header file lmuAppVBusSdk.h.

There is a specific IPC message queue packet structure that the hosted application needs to initialize for each supported VBUS API operation and then send to the LMU app:

  • VBUS param read request IPC message
  • VBUS param write request IPC message
  • VBUS action request IPC message
  • VBUS data group message request IPC message

The first part of all Linux IPC message queue messages must be an msg header. For the VBUS API, the msg header is as follows:

/* First part of all VBUS SDK IPc message queue messages */
struct _lmu_app_peg_vbus_hdr
{
    int32_t msg_type;	/* linux sysv message type = 1 for lmu, >2= client number */
    int32_t cmd;	/* sdk command (read, write, peg action, data group) */
    int32_t transaction;	/* client transaction id */
};

VBUS API Read VBUS Param Request IPC Message

VBUS JPOD2 parameters can be read or written via this VBUS API.

Here is the VBUS param read request IPC message:

struct _lmu_app_peg_vbus_read_param_request	/* 128 byte structure */
{
    struct _lmu_app_peg_vbus_hdr header;	/* mtext[0] ... mtext[7] */
    int32_t client_qid;				/* mtext[8] */
    int32_t param;      			/* mtext[12] */
    int32_t index;				/* mtext[16] */
    int32_t mtype_src;	/* client number	   mtext[20] */
    unsigned int spare[24];			/* mtext[24] ... mtext[119] */
    int32_t flags;                              /* mtext[120] */
};

To read a specific VBUS param, the following struct _lmu_app_peg_vbus_read_param_request fields need to be initialized:

struct  _lmu_app_peg_vbus_read_param_request  req;
       req.header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;

This will send this IPC message to the LMU app VBUS API message queue:

req.header.cmd = LMU_APP_VBUS_CMD_READ;

This will specify to the listening LMU app VBUS API message queue the requested read param operation.

req.header.transaction = seqid++;

Set this to an application sequence number that increments for every request. This will facilitate identifying the follow-on response — which will have this same sequence id value.

req.client_qid = client_qid;

Set this to the IPC client queue id created in the hosted application.

req.param = param;

Set this to the LMU VBUS parameter number of the param to read.

req.index = index;

Set this to the LMU VBUS parameter index of the param to read. To read back all parameter indexes for this parameter, set this field to -1.

req.mtype_src = ClientNum;

Set this to a unique number that represents the hosted app client number. This hosted app client number will also be returned in the IPC response message. Note that there can be multiple VBUS param read requests from the same hosted app client number, or from multiple different client numbers.

The LMU app will return the response data and the hosted app client number, the request opcode, and the sequence ID. With these values, the hosted application can match up LMU app responses with hosted application requests.

req.msgid = unique_value;

Additionally, the VBUS API request structures have an optional msgid field that can be set to a user-defined unique unsigned int value. The msgid is also returned in the LMU app response and is an additional optional field to use to match LMU app responses to hosted application requests.

VBUS API Read VBUS Param Jumbo Payload Request IPC Message

To enable a jumbo payload read VBUS param request, three things need to be done:

  1. Create a jumbo payload IPC client queue:
lmuAppVBusSdk.h
#define LMU_APP_VBUS_CLIENT_JFILE ( "/etc/calamp/lmu/vbus_sdk_client_jqueue")
keyid = ftok (LMU_APP_VBUS_CLIENT_JFILE, 1);
clieint_jqid = msgget(keyid, (IPC_CREAT | 0666 ));
  1. Set VBUS_APP_VBUS_SDK_JUMBO_PAYLOAD in the request flag field:
req.flags  |= (LMU_APP_VBUS_CMD_READ  | VBUS_APP_VBUS_SDK_JUMBO_PAYLOAD)
  1. Send the request and look for the response in your jumbo IPC client queue:
rc = msgsnd(lmu_qid, (void *)req, req_len, IPC_NOWAIT);

static LMU_APP_VBUS_SDK_RP_JUMBO_RSP lmuRPjRsp;
struct _lmu_app_peg_vbus_read_param_jumbo_response *jrsp;
jrsp = (struct _lmu_app_peg_vbus_read_param_jumbo_response *)&lmuRPjRsp;

rsp_len = msgrcv(client_jqid, (void *)&lmuRPjRsp, jmsg_len, ClientNum, 0);

VBUS API Write VBUS Param Request IPC Message

VBUS JPOD2 parameters can be read or written via this VBUS API.

Here is the VBUS param write request IPC message:

struct _lmu_app_peg_vbus_write_param_request	/* 128 byte structure */
{
    struct _lmu_app_peg_vbus_hdr header;	/* mtext[0] ... mtext[7] */
    int32_t client_qid;    			/* mtext[8] */
    int32_t param;         			/* mtext[12] */
    int32_t index;     				/* mtext[16] */
    int32_t mtype_src;	/* client number	   mtext[20] */
    int32_t valbyte_cnt;			/* mtext[24] */
    unsigned char valbytes[MAX_PARAM_VALUE_BYTES]; /* mtext[28]...mtext[39] */
    unsigned int spare[19];			/* mtext[40] ... mtext[119] */
    int32_t flags;				/* mtext[120] */
};

To write a specific VBUS param, the following struct _lmu_app_peg_vbus_write_param_request fields need to be initialized:

struct  _lmu_app_peg_vbus_write_param_request  req;

       req.header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;

This will send this IPC message to the LMU app VBUS API message queue:

req.header.cmd = LMU_APP_VBUS_CMD_WRITE;

This will specify to the listening LMU app VBUS API message queue the requested write param operation.

req.header.transaction = seqid++;

Set this to an application sequence number that increments for every request. This will facilitate identifying the follow-on response — which will have this same sequence ID value.

req.client_qid = client_qid;

Set this to the IPC client queue ID created in the hosted application.

req.param = param;

Set this to the LMU VBUS parameter number of the param to write.

req.index = index;

Set this to the LMU VBUS parameter index of the param to write.

req.valbyte_cnt = valbyte_cnt;

Set this to the number of parameter values to write for this parameter/index. For example, the parameter/index 5007,0,65 has one value, 65.

/ 5007,0,65
param,index,value
then req.valbyte_cnt = 1
/

Here's another example: The parameter/index 5011,0,6,0,0,0,0,0,19,136,0,0,0,0 has 12 values, 6,0,0,0,0,0,19,136,0,0,0,0.

/ 5011,0,6,0,0,0,0,0,19,136,0,0,0,0
param,index,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12
then req.valbyte_cnt = 12
/

or (i = 0; i < valbyte_cnt; i++)
       {
           req.valbytes[i] = valbytes[i];
       }

Assign the parameter/index values to the request struct array req.valbytes[].

req.mtype_src = ClientNum;

Set this to a unique number that represents the hosted app client number. This client number will also be returned in the IPC response message. Note that there can be multiple VBUS param write requests from the same hosted app client number, or from multiple different client numbers.

The LMU app response will return the response data and the hosted app client number, the request opcode, and the sequence ID. With these values, the hosted application can match up LMU app responses with hosted application requests.

req.msgid = unique_value;

Additionally, the VBUS API request structures have an optional msgid field that can be set to a user-defined unique unsigned int value. The msgid is also returned in the LMU app response and is an additional optional field to use to match LMU app responses to hosted application requests.

VBUS API Action Request IPC Message

This header file contains the list of supported JPOD2 and VPOD2 request data messages:

#define VBUS_API_ACT_VBUS_ACTION				99
#define VBUS_API_ACT_VBUS_GET_DATA_GROUP		100

The VBUS_API_ACT_VBUS_ACTION request requires an action modifier that defines the VBUS action request. Here are the supported VBUS API action modifiers:

#define VBUS_API_ACT_MOD_NONE				0
#define VBUS_API_ACT_MOD_RESET				1
#define VBUS_API_ACT_MOD_HOLD_IN_RESET			2
#define VBUS_API_ACT_MOD_POWER_ON			3
#define VBUS_API_ACT_MOD_POWER_OFF			4
#define VBUS_API_ACT_MOD_DETECT_VEHICLE			5
#define VBUS_API_ACT_MOD_VEHICLE_ID_RPT			6
#define VBUS_API_ACT_MOD_RESET_TRIP			7
#define VBUS_API_ACT_MOD_ALL_DTC_RPTS			8
#define VBUS_API_ACT_MOD_UNSENT_DTC_RPTS		9
#define VBUS_API_ACT_MOD_SET_FACTORY_DEFAULTS		10
#define VBUS_API_ACT_MOD_START_SLEEP			11
#define VBUS_API_ACT_MOD_STOP_BUS_POLL			14
#define VBUS_API_ACT_MOD_RESUME_BUS_POLL		15
#define VBUS_API_ACT_MOD_REGISTER_DEVICE			16
#define VBUS_API_ACT_MOD_VEHICLE_DISC_RPT		17
#define VBUS_API_ACT_MOD_START_VIN_DECODE		18
#define VBUS_API_ACT_MOD_FREEZE_FRAME_START_TRUE	19
#define VBUS_API_ACT_MOD_FREEZE_FRAME_GET		20
#define VBUS_API_ACT_MOD_RUN_CMD_BY_TAG_TRUE		21
#define VBUS_API_ACT_MOD_RUN_CMD_BY_TAG_FALSE		22
#define VBUS_API_ACT_MOD_FREEZE_FRAME_START_FALSE	23
#define VBUS_API_ACT_MOD_STOP_USING_OBD			24
#define VBUS_API_ACT_MOD_VEHICLE_BUS_DATA_RPT_MAINT	25
#define VBUS_API_ACT_MOD_VEHICLE_BUS_DATA_RPT_INB	26
#define VBUS_API_ACT_MOD_REMOTE_OBD_GET_DATA		27
#define VBUS_API_ACT_MOD_GET_REMOTE_OBD_DATA		28
#define VBUS_API_ACT_MOD_OBD_PASSIVE_MODE_TRUE		29
#define VBUS_API_ACT_MOD_OBD_PASSIVE_MODE_FALSE		30
#define VBUS_API_ACT_MOD_ALL_DTC_WITH_TYPE_RPT		31
#define VBUS_API_ACT_MOD_UNSENT_DTC_WITH_TYPE_RPT	32

Here is the VBUS action request IPC message:

struct _lmu_app_peg_vbus_action_request		/* 128 byte structure */
{
    struct _lmu_app_peg_vbus_hdr header;	/* mtext[0] ... mtext[7] */
    int32_t client_qid;				/* mtext[8] */
    int32_t action;				/* mtext[12] */
    int32_t modifier;				/* mtext[16] */
    int32_t mtype_src;	/* client number	   mtext[20] */
    unsigned int spare[23];			/* mtext[24] ... mtext[115] */
    unsigned int runcmd;			/* mtest[116] */
    int32_t flags;				/* mtext[120] */
};

To send a specific VBUS action request, the following struct _lmu_app_peg_vbus_action_request fields need to be initialized:

struct  _lmu_app_peg_vbus_action_request  req;

       req.header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;

This will send this IPC message to the LMU app VBUS API message queue:

req.header.cmd = LMU_APP_VBUS_CMD_REQ_DATA_GROUP;

This will specify to the listening LMU app VBUS API message queue the requested action operation.

req.header.transaction = seqid++;

Set this to an application sequence number that increments for every request. This will facilitate identifying the follow-on response — which will have this same sequence ID value.

req.client_qid = client_qid;

Set this to the IPC client queue ID created in the hosted application.

req.index = data_group_number;

Set this to the LMU VBUS data group message number.

req.mtype_src = ClientNum;

Set this to a unique number that represents the hosted app client number. This client number will also be returned in the IPC response message. Note that there can be multiple VBUS data group read requests from the same hosted app client number, or from multiple different client numbers.

The LMU app response will return the response data and the hosted app client number, the request opcode, and the sequence ID. With these values, the hosted application can match up LMU app responses with hosted application requests.

req.msgid = unique_value;

VBUS API Data Group Message Jumbo Payload Request IPC Message

The normal response payload size is 256 bytes. However, for VBUS param data group message requests, the hosted application can specify a response payload size of 1024. This is useful to reduce response data payload fragmentation for VBUS parameter requests that return more than 256 bytes of data. For example, the VBUS JPOD2 data group message 2 can return more than 256 bytes of data. Here's an example: If VBUS JPOD2 data group message 2 returned 415 bytes of data, with a nonjumbo payload request, this would trigger two IPC message response messages — one with 256 bytes of data and another one with 159 bytes of data. With a jumbo payload request, this would trigger one IPC message response message with a data size of 415 in the jumbo 1024 payload buffer.

To enable a Jumbo payload read VBUS param request, three new thing need to be done:

  1. Create a jumbo payload IPC client queue:
lmuAppVBusSdk.h
#define LMU_APP_VBUS_CLIENT_JFILE ( "/etc/calamp/lmu/vbus_sdk_client_jqueue")
keyid = ftok (LMU_APP_VBUS_CLIENT_JFILE, 1);
clieint_jqid = msgget(keyid, (IPC_CREAT | 0666 ));
  1. Set VBUS_APP_VBUS_SDK_JUMBO_PAYLOAD in the request flags field:
req.flags  |= (LMU_APP_VBUS_CMD_REQ_DATA_GROUP  | VBUS_APP_VBUS_SDK_JUMBO_PAYLOAD)
  1. Send the request and look for the response in your jumbo IPC client queue:
rc = msgsnd(lmu_qid, (void *)req, req_len, IPC_NOWAIT);
static LMU_APP_VBUS_SDK_DG_JUMBO_RSP lmuDGjRsp;
struct _lmu_app_peg_vbus_read_data_group_jumbo_response *jrsp;
jrsp = (struct _lmu_app_peg_vbus_read_data_group_jumbo_response *)&lmuDGjRsp;
rsp_len = msgrcv(client_jqid, (void *)&lmuDGjRsp, jmsg_len, ClientNum, 0);

VBUS API Write VBUS Param Request IPC Message

VBUS JPOD2 parameters can be read or written via this VBUS API.

Here is the VBUS param write request IPC message:

struct _lmu_app_peg_vbus_write_param_request	/* 128 byte structure */
{
    struct _lmu_app_peg_vbus_hdr header;	/* mtext[0] ... mtext[7] */
    int32_t client_qid;    			/* mtext[8] */
    int32_t param;         			/* mtext[12] */
    int32_t index;     				/* mtext[16] */
    int32_t mtype_src;	/* client number	   mtext[20] */
    int32_t valbyte_cnt;			/* mtext[24] */
    unsigned char valbytes[MAX_PARAM_VALUE_BYTES]; /* mtext[28]...mtext[39] */
    unsigned int spare[19];			/* mtext[40] ... mtext[119] */
    int32_t flags;				/* mtext[120] */
};

To write a specific VBUS param, the following struct _lmu_app_peg_vbus_write_param_request fields need to be initialized:

struct  _lmu_app_peg_vbus_write_param_request  req;

       req.header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;

This will send this IPC message to the LMU app VBUS API message queue:

req.header.cmd = LMU_APP_VBUS_CMD_WRITE;

This will specify to the listening LMU app VBUS API message queue the requested write param operation.

req.header.transaction = seqid++;

Set this to an application sequence number that increments for every request. This will facilitate identifying the follow-on response — which will have this same sequence ID value.

req.client_qid = client_qid;

Set this to the IPC client queue ID created in the hosted application.

req.param = param;

Set this to the LMU VBUS parameter number of the param to write.

req.index = index;

Set this to the LMU VBUS parameter index of the param to write.

req.valbyte_cnt = valbyte_cnt;

Set this to the number of parameter values to write for this parameter/index. For example, the parameter/index 5007,0,65 has one value, 65.

/ 5007,0,65
param,index,value
then req.valbyte_cnt = 1
/

Here's another example: The parameter/index 5011,0,6,0,0,0,0,0,19,136,0,0,0,0 has 12 values, 6,0,0,0,0,0,19,136,0,0,0,0.

/ 5011,0,6,0,0,0,0,0,19,136,0,0,0,0
param,index,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12
then req.valbyte_cnt = 12
/

for (i = 0; i < valbyte_cnt; i++)
       {
           req.valbytes[i] = valbytes[i];
       }

Assign the parameter/index values to the request struct array req.valbytes[].

req.mtype_src = ClientNum;

Set this to a unique number that represents the hosted app client number. This client number will also be returned in the IPC response message. Note that there can be multiple VBUS param write requests from the same hosted app client number, or from multiple different client numbers.

The LMU app response will return the response data and the hosted app client number, the request opcode, and the sequence ID. With these values, the hosted application can match up LMU app responses with hosted application requests.

req.msgid = unique_value;

Additionally, the VBUS API request structures have an optional msgid field that can be set to a user-defined unique unsigned int value. The msgid is also returned in the LMU app response and is an additional optional field to use to match LMU app responses to hosted application requests.

VBUS API Action Request IPC Message

This header file contains the list of supported JPOD2 and VPOD2 request data messages:

#define VBUS_API_ACT_VBUS_ACTION				99
#define VBUS_API_ACT_VBUS_GET_DATA_GROUP		100

The VBUS_API_ACT_VBUS_ACTION request requires an action modifier that defines the VBUS action request. Here are the supported VBUS API action modifiers:

#define VBUS_API_ACT_MOD_NONE				0
#define VBUS_API_ACT_MOD_RESET				1
#define VBUS_API_ACT_MOD_HOLD_IN_RESET			2
#define VBUS_API_ACT_MOD_POWER_ON			3
#define VBUS_API_ACT_MOD_POWER_OFF			4
#define VBUS_API_ACT_MOD_DETECT_VEHICLE			5
#define VBUS_API_ACT_MOD_VEHICLE_ID_RPT			6
#define VBUS_API_ACT_MOD_RESET_TRIP			7
#define VBUS_API_ACT_MOD_ALL_DTC_RPTS			8
#define VBUS_API_ACT_MOD_UNSENT_DTC_RPTS		9
#define VBUS_API_ACT_MOD_SET_FACTORY_DEFAULTS		10
#define VBUS_API_ACT_MOD_START_SLEEP			11
#define VBUS_API_ACT_MOD_STOP_BUS_POLL			14
#define VBUS_API_ACT_MOD_RESUME_BUS_POLL		15
#define VBUS_API_ACT_MOD_REGISTER_DEVICE			16
#define VBUS_API_ACT_MOD_VEHICLE_DISC_RPT		17
#define VBUS_API_ACT_MOD_START_VIN_DECODE		18
#define VBUS_API_ACT_MOD_FREEZE_FRAME_START_TRUE	19
#define VBUS_API_ACT_MOD_FREEZE_FRAME_GET		20
#define VBUS_API_ACT_MOD_RUN_CMD_BY_TAG_TRUE		21
#define VBUS_API_ACT_MOD_RUN_CMD_BY_TAG_FALSE		22
#define VBUS_API_ACT_MOD_FREEZE_FRAME_START_FALSE	23
#define VBUS_API_ACT_MOD_STOP_USING_OBD			24
#define VBUS_API_ACT_MOD_VEHICLE_BUS_DATA_RPT_MAINT	25
#define VBUS_API_ACT_MOD_VEHICLE_BUS_DATA_RPT_INB	26
#define VBUS_API_ACT_MOD_REMOTE_OBD_GET_DATA		27
#define VBUS_API_ACT_MOD_GET_REMOTE_OBD_DATA		28
#define VBUS_API_ACT_MOD_OBD_PASSIVE_MODE_TRUE		29
#define VBUS_API_ACT_MOD_OBD_PASSIVE_MODE_FALSE		30
#define VBUS_API_ACT_MOD_ALL_DTC_WITH_TYPE_RPT		31
#define VBUS_API_ACT_MOD_UNSENT_DTC_WITH_TYPE_RPT	32

Here is the VBUS action request IPC message:

struct _lmu_app_peg_vbus_action_request		/* 128 byte structure */
{
    struct _lmu_app_peg_vbus_hdr header;	/* mtext[0] ... mtext[7] */
    int32_t client_qid;			/* mtext[8] */
    int32_t action;				/* mtext[12] */
    int32_t modifier;				/* mtext[16] */
    int32_t mtype_src;	/* client number	   mtext[20] */
    unsigned int spare[23];			/* mtext[24] ... mtext[115] */
    unsigned int runcmd;			/* mtest[116] */
    int32_t flags;				/* mtext[120] */
};

To send a specific VBUS action request, the following struct _lmu_app_peg_vbus_action_request fields need to be initialized:

struct  _lmu_app_peg_vbus_action_request  req;

       req.header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;

This will send this IPC message to the LMU app VBUS API message queue:

req.header.cmd = LMU_APP_VBUS_CMD_ACTION;

This will specify to the listening LMU app VBUS API message queue the requested action operation.

req.header.transaction = seqid++;

Set this to an application sequence number that increments for every request. This will facilitate identifying the follow-on response — which will have this same sequence ID value.

req.client_qid = client_qid;

Set this to the IPC client queue ID created in the hosted application.

req.action = VBUS_API_ACT_VBUS_ACTION;

Set this to the LMU VBUS action number to run. Currently, only action 99, VBUS_API_ACT_VBUS_ACTION, is supported.

req.modifier = modifier;

Set this field with one of the VBUS action modifiers listed above from the header file lmuAppVBusSdk.h.

req.mtype_src = ClientNum;

Set this to a unique number that represents the hosted app client number. This client number will also be returned in the IPC response message. Note that there can be multiple VBUS action requests from the same hosted app client number, or from multiple different client numbers.

The LMU app response will return the response data and the hosted app client number, the request opcode, and the Sequence ID. With these values, the hosted application can match up LMU app responses with hosted application requests.

req.msgid = unique_value;

Additionally, the VBUS API request structures have an optional msgid field that can be set to a user-defined unique unsigned int value. The msgid is also returned in the LMU app response and is an additional optional field to use to match LMU app responses to hosted application requests.

Each VBUS action that generates a report will have a default destination for the report — either the maintenance server (defined in PEG param 2320) or the inbound server (defined in PEG param 2319). Additionally, with the VBUS API, the hosted application can have the report sent instead back to itself if the request structure flags field is set to VBUS_APP_VBUS_SDK_SEND_TO_HA.

req.flags |= VBUS_APP_VBUS_SDK_SEND_TO_HA;

VBUS API VBUS Data Group Request IPC Message

The VBUS_API_ACT_VBUS_GET_DATA_GROUP request retrieves JPOD2 or VPOD2 data group messages that are defined. Data group messages 0-31 are supported.

The following is the VBUS data group request IPC message:

struct _lmu_app_peg_vbus_read_data_group_request /* 128 byte structure */
{
    struct _lmu_app_peg_vbus_hdr header;	/* mtext[0] ... mtext[7] */
    int32_t client_qid;			/* mtext[8] */
    int32_t index;  	/* param index		   mtext[12] */
    int32_t space;				/* mtext[16] */
    int32_t mtype_src;	/* client number	   mtext[20] */
    unsigned int spare[23];			/* mtext[24] ... mtext[115] */
    unsigned int runcmd;			/* mtest[116] */
    int32_t flags;				/* mtext[120] */
};

To send a specific VBUS data group request, the following struct _lmu_app_peg_vbus_read_data_group_request fields need to be initialized:

struct  _lmu_app_peg_vbus_read_data_group_request  req;

       req.header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;

This will send this IPC message to the LMU app VBUS API message queue:

req.header.cmd = LMU_APP_VBUS_CMD_REQ_DATA_GROUP;

This will specify to the listening LMU app VBUS API message queue the requested action operation.

req.header.transaction = seqid++;

Set this to an application sequence number that increments for every request. This will facilitate identifying the follow-on response — which will have this same sequence ID value.

VBUS API IPC Message Response

There are four types of VBUS API IPC response messages:

  • The response for the VBUS param read request
  • The response for the VBUS param write request
  • The response for the VBUS action request
  • The response for the VBUS read data group message request

VBUS API IPC Message Response Payload

The default VBUS API IPC response message payload size is 256 bytes. However, for VBUS API parameter read and VBUS API data group message requests, there is an option to request a 1024-byte payload response:

#define LMU_APP_VBUS_MAX_MESSAGE_LEN		256
#define LMU_APP_VBUS_MAX_JUMBO_MESSAGE_LEN	1024

VBUS API Read VBUS Param Response

The LMU VBUS API IPC response messages are defined as follows.

Here are the VBUS param read response IPC messages:

  • Nonjumbo 256-byte payload response message:
typedef struct _lmu_app_peg_vbus_read_param_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;        /* request status , 0=good */
    int32_t param;         /* vbus parameter */
    int32_t index;         /* vbus parameter index */
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    rsptext[LMU_APP_VBUS_MAX_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_RP_RSP;
  • Jumbo 1024-byte payload response message:
typedef struct _lmu_app_peg_vbus_read_param_jumbo_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;        /* request status , 0=good */
    int32_t param;         /* vbus parameter */
    int32_t index;         /* vbus parameter index */
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    rsptext[LMU_APP_VBUS_MAX_JUMBO_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_RP_JUMBO_RSP;

VBUS API Write VBUS Param Response

The following is the VBUS param write response IPC message:

typedef struct _lmu_app_peg_vbus_write_param_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;       /* request status , 0=good */
    int32_t param;	  /* vbus parameter */
    int32_t index;        /* accumulator index ranges from 0 to 15 */
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    rsptext[LMU_APP_VBUS_MAX_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_WP_RSP;

VBUS API VBUS Action Response

Here is the VBUS action response IPC message:

typedef struct _lmu_app_peg_vbus_action_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;
    int32_t action;
    int32_t modifier;
    unsigned long msgid;
    unsigned int  spare[10];
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;		/* total transfer size */
    char    acttext[LMU_APP_VBUS_MAX_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_ACT_RSP;

VBUS API VBUS Data Group Message Response

Here are the VBUS data group response IPC messages:

  • Nonjumbo 256-byte payload response message:
typedef struct _lmu_app_peg_vbus_read_data_group_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;		/* request status , 0=good */
    int32_t index;
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;	/* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    dgtext[LMU_APP_VBUS_MAX_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_DG_RSP;
  • Jumbo 1024-byte payload response message:
typedef struct _lmu_app_peg_vbus_read_data_group_jumbo_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;		/* request status , 0=good */
    int32_t index;
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;	/* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    dgtext[LMU_APP_VBUS_MAX_JUMBO_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_DG_JUMBO_RSP;

VBUS API RunCmd Automated Response Feature

The VBUS API has the ability for the hosted application to register a data group message ID request in the VBUS API. After the registration, the JPOD2 response to the specific data group message ID will automatically respond at a frequency specified by the hosted application. To specify the frequency of the automated response, use the runcmd field in the VBUS API data group message request structure, as follows:

struct _lmu_app_peg_vbus_read_data_group_request /* 128 byte structure */
{
    struct _lmu_app_peg_vbus_hdr header;	/* mtext[0] ... mtext[7] */
    int32_t client_qid;			/* mtext[8] */
    int32_t index;  	/* param index		   mtext[12] */
    int32_t space;				/* mtext[16] */
    int32_t mtype_src;	/* client number	   mtext[20] */
    unsigned int spare[23];			/* mtext[24] ... mtext[115] */
    unsigned int runcmd;			/* mtest[116]   -------------- frequency of the RunCmd response */
    int32_t flags;				/* mtext[120] */
};

To register a data group message request to the VBUS API, set the VBUS API flag, VBUS_APP_VBUS_SDK_RUN_CMD_ADD, in the flags field of the request structure:

req.index = 2
req.flags  |= (LMU_APP_VBUS_CMD_REQ_DATA_GROUP  | VBUS_APP_VBUS_SDK_JUMBO_PAYLOAD | VBUS_APP_VBUS_SDK_RUN_CMD_ADD)

Also, set the frequency field, runcmd, in the request:

req.runcmd = VBUS_APP_VBUS_SDK_RUN_CMD_250MSEC

Possible frequency options are the following:

#define VBUS_APP_VBUS_SDK_RUN_CMD_50MSEC             50           /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_100MSEC            100          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_125MSEC            125          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_150MSEC            150          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_200MSEC            200          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_250MSEC            250          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_300MSEC            300          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_400MSEC            400          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_500MSEC            500          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_600MSEC            600          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_700MSEC            700          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_800MSEC            800          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_900MSEC            900          /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_1SEC               1000        /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_2SEC               2000        /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_3SEC               3000        /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_4SEC               4000        /*msec*/
#define VBUS_APP_VBUS_SDK_RUN_CMD_5SEC               5000        /*msec*/

Currently, do not pick any frequency less than 200 ms.

Here's an example: Setting the runcmd field to VBUS_APP_VBUS_SDK_RUN_CMD_250MSEC instructs the VBUS API to issue the data group message for ID # every 250 ms (that is, four times a second).

The hosted app can issue one of these data group message for ID # requests and then wait for the automatic responses.

The new request flag, VBUS_APP_VBUS_SDK_RUN_CMD_DELETE, is used to stop the automatic data group message for ID #.

req.index = 2
req.flags  |= (LMU_APP_VBUS_CMD_REQ_DATA_GROUP  | VBUS_APP_VBUS_SDK_RUN_CMD_DELETE)

The hosted application can register more than one data group message request for several IDs. Just note that there is no threshold for the amount of outstanding automated data group requests and no threshold on frequencies of each automated request. That being said, know that the JPOD2 hardware can handle only five or so outstanding data group request ID 2 requests or eight data group request ID 1 requests per second. This is because of the time it takes for the JPOD2 hardware to run a data group request ID 2 (about 130 to 200 ms) and a data group request ID 1 (about 40 ms to 60 ms).

The hosted application can also query the VBUS API to see all the registered VBUS API data group message automated requests. This is done by issuing a data group message (any ID) request and specifying the request flags option, VBUS_APP_VBUS_SDK_RUN_CMD_STAT, as follows:

req.flags  |= (LMU_APP_VBUS_CMD_REQ_DATA_GROUP  | VBUS_APP_VBUS_SDK_RUN_CMD_STAT)

Here is an example of the returned data from querying the VBUS API. Note that a string is returned in the response payload. In the following example, there are three registered automated data group messages: DGM#2, automatically issued every 500ms; DGM#1, automatically issued every 500ms;and DGM#0, automatically issued every 300ms.

RunCmd Cmd Data Group Msg, index 2, interval 500 ms
RunCmd Cmd Data Group Msg, index 1, interval 500 ms
RunCmd Cmd Data Group Msg, index 0, interval 300 ms

Here is an example of the returned data from querying the VBUS API when there are no RunCmds running:

No RunCmds Registered

VBUS API IPC Message Response

There are four types of VBUS API IPC response messages:

  • The response for the VBUS param read request
  • The response for the VBUS param write request
  • The response for the VBUS action request
  • The response for the VBUS read data group message request

VBUS API IPC Message Response Payload

The default VBUS API IPC response message payload size is 256 bytes. However, for VBUS API parameter read and VBUS API data group message requests, there is an option to request a 1024-byte payload response:

#define LMU_APP_VBUS_MAX_MESSAGE_LEN		256
#define LMU_APP_VBUS_MAX_JUMBO_MESSAGE_LEN	1024

VBUS API Read VBUS Param Response

The LMU VBUS API IPC response messages are defined as follows:

Here are the VBUS param read response IPC messages:

  • Nonjumbo 256-byte payload response message:
typedef struct _lmu_app_peg_vbus_read_param_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;        /* request status , 0=good */
    int32_t param;         /* vbus parameter */
    int32_t index;         /* vbus parameter index */
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;    /* total transfer size  */
    char    rsptext[LMU_APP_VBUS_MAX_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_RP_RSP;
  • Jumbo 1024-byte payload response message:
typedef struct _lmu_app_peg_vbus_read_param_jumbo_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;        /* request status , 0=good */
    int32_t param;         /* vbus parameter */
    int32_t index;         /* vbus parameter index */
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    rsptext[LMU_APP_VBUS_MAX_JUMBO_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_RP_JUMBO_RSP;

VBUS API Write VBUS Param Response

The following is the VBUS param write response IPC message:

typedef struct _lmu_app_peg_vbus_write_param_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;       /* request status , 0=good */
    int32_t param;	  /* vbus parameter */
    int32_t index;        /* accumulator index ranges from 0 to 15 */
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    rsptext[LMU_APP_VBUS_MAX_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_WP_RSP;

VBUS API VBUS Action Response

Here is the VBUS action response IPC message:

typedef struct _lmu_app_peg_vbus_action_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;
    int32_t action;
    int32_t modifier;
    unsigned long msgid;
    unsigned int  spare[10];
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;		/* total transfer size */
    char    acttext[LMU_APP_VBUS_MAX_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_ACT_RSP;

VBUS API VBUS Data Group Message Response

Here are the VBUS data group response IPC messages:

  • Nonjumbo 256-byte payload response message:
typedef struct _lmu_app_peg_vbus_read_data_group_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;		/* request status , 0=good */
    int32_t index;
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;	/* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    dgtext[LMU_APP_VBUS_MAX_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_DG_RSP;
  • Jumbo 1024-byte payload response message:
typedef struct _lmu_app_peg_vbus_read_data_group_jumbo_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;		/* request status , 0=good */
    int32_t index;
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;	/* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    dgtext[LMU_APP_VBUS_MAX_JUMBO_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_DG_JUMBO_RSP;

VBUS API IPC Message Response Data

The first part of all Linux IPC message queue messages must be an msg header. For the VBUS API, the msg header is as follows:

/* First part of all VBUS SDK IPc message queue messages */
struct _lmu_app_peg_vbus_hdr
{
    int32_t msg_type;	/* linux sysv message type = 1 for lmu, >2= client number */
    int32_t cmd;		/* sdk command (vbus param read, vbus param write, vbus action, data group msg read) */
    int32_t transaction;	/* client transaction id */
};

The LMU app VBUS API per-response maximum payload size is defined as follows:

#define LMU_APP_VBUS_MAX_MESSAGE_LEN		256

Each type of LMU app VBUS API response message structure (read param, write param, action, read data group message) has common structure fields to describe the response data.

Here is the VBUS param read response IPC message:

typedef struct _lmu_app_peg_vbus_read_param_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;        /* request status , 0=good */
    …
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;   /* total transfer size */
    char    rsptext[LMU_APP_VBUS_MAX_MESSAGE_LEN];	/*  vbus param read response in ASCII */
} LMU_APP_VBUS_SDK_RP_RSP;

Here is the VBUS param write response IPC message:

typedef struct _lmu_app_peg_vbus_write_param_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;       /* request status , 0=good */
    …
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;   /* total transfer size */
    char    rsptext[LMU_APP_VBUS_MAX_MESSAGE_LEN];	/*  vbus param write response in ASCII */
} LMU_APP_VBUS_SDK_WP_RSP;

The following is the VBUS action response IPC message:

typedef struct _lmu_app_peg_vbus_action_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;
    …
    unsigned short buflen;        /* message buffer len */
    unsigned short total_size;	/* total transfer size  */
    char    acttext[LMU_APP_VBUS_MAX_MESSAGE_LEN];	/* vbus action response in ASCII, “OK” */
} LMU_APP_VBUS_SDK_ACT_RSP;

The following is the VBUS data group response IPC message:

{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;		/* request status , 0=good */
    …
    char    dgtext[LMU_APP_VBUS_MAX_MESSAGE_LEN];	/* data group message response */
    unsigned short buflen;	/* message buffer len */
    unsigned short total_size;   /* total transfer size */
    char    dgtext[LMU_APP_VBUS_MAX_MESSAGE_LEN];	/* data group message response */
} LMU_APP_VBUS_SDK_DG_RSP;

The IPC message response data is in the message payload buffer:

char    rsptext[LMU_APP_VBUS_MAX_MESSAGE_LEN];	/*  vbus param write response in ASCII */

The size of the data in the payload buffer is set in the field:

unsigned short buflen;        /* message buffer len */

The total size of the entire response data is set in the field:

unsigned short total_size;   /* total transfer size */

If, for example, the total response is 834 bytes, there will be four VBUS API IPC message responses sent to the Hosted application client IPC message queue. The first three IPC response messages will have a payload size of 256, and the fourth IPC response message will have a payload size of 66 bytes.

LMU App VBus API total response size:  834 bytes
LMU App VBus API IPC message #1:
    rsp->header.transaction = 0;    
    rsp->buflen = 256;
    rsp->total_size = 834;
LMU App VBus API IPC message #2:
    rsp->header.transaction = 1;   
    rsp->buflen = 256;
    rsp->total_size = 834;
LMU App VBus API IPC message #3:
    rsp->header.transaction = 2;    
    rsp->buflen = 256;
    rsp->total_size = 834;

LMU App VBus API IPC message #4:
    rsp->header.transaction = 3; 
    rsp->buflen = 66;
    rsp->total_size = 834;

VBUS API IPC Message Response Data Fragmentation

The maximum response total data size, as defined by the LMU app VBUS drivers, is 8192 (8KB) bytes.

#define LMU_APP_VBUS_MAX_DATA_BUFFER_LEN	LMU_APP_VBUS_MAX_MESSAGE_LEN*4*8

The LMU app VBUS API per-response IPC message maximum payload size is defined as follows:

#define LMU_APP_VBUS_MAX_MESSAGE_LEN		256

If, for example, the LMU app response provides 8KB of data, the LMU VBUS API layer will send back 32 IPC message responses to the hosted application, each with a data payload size of 256 bytes.

8KB / 256 bytes = 32 IPC message responses, with a 256-byte payload sent to the hosted application.

The VBUS API will return the total response data size in the IPC response field:

typedef struct _lmu_app_peg_vbus_action_response
{
    …
    unsigned short total_size;		/* total transfer size */
    …
} LMU_APP_VBUS_SDK_ACT_RSP;


typedef struct _lmu_app_peg_vbus_read_param_response
{
    …
    unsigned short total_size;    /* total transfer size */
    …
} LMU_APP_VBUS_SDK_RP_RSP;

typedef struct _lmu_app_peg_vbus_write_param_response
{
    …
    unsigned short total_size;    /* total transfer size */
    …
} LMU_APP_VBUS_SDK_WP_RSP;

typedef struct _lmu_app_peg_vbus_read_data_group_response
{
    …
    unsigned short total_size;    /* total transfer size */
    …
} LMU_APP_VBUS_SDK_DG_RSP;

If a data response payload is smaller than the maximum payload size of 256 bytes, no data fragmentation flags apply.

Most JPOD2 or VPOD2 data responses are less than 256 bytes and therefore fit within one response message.

If the response data is larger than 256 bytes, the response data is sent via several IPC response messages, in which each response message is marked with the flags below.

The LMU app VBUS API response data fragmentation flags are as follows:

#define VBUS_APP_VBUS_SDK_FRAG_DATA		0x00000001
#define VBUS_APP_VBUS_SDK_FRAG_FIRST_BLK		0x00000002
#define VBUS_APP_VBUS_SDK_FRAG_MID_BLK		0x00000004
#define VBUS_APP_VBUS_SDK_FRAG_LAST_BLK		0x00000008

If a response data is larger than the maximum IPC response message payload size of 256 bytes, all the response data messages for the reply are marked with the flag VBUS_APP_VBUS_SDK_FRAG_DATA.

The first response data message is marked with the flag VBUS_APP_VBUS_SDK_FRAG_DATA. Subsequent response data messages are marked with the flag VBUS_APP_VBUS_SDK_FRAG_MID_BLK. The last response data message is marked with the flag VBUS_APP_VBUS_SDK_FRAG_LAST_BLK.

The response data messages can be reassembled by the hosted app by the following fields in the response message:

  1. Linux IPC client number
  2. VBUS API command
  3. VBUS API response data packet sequence number for large, fragmented response data
  4. Total message returned response size
  5. Optional msgid for a user-defined unique unsigned int value

Look specifically at the VBUS data group response IPC message; the data identification fields are found as follows:

/* First part of all VBUS SDK IPc message queue messages */
struct _lmu_app_peg_vbus_hdr
{

1. Linux IPC client number

int32_t msg_type;	/* linux sysv message type = 1 for lmu, >2= client number */

2. VBUS API command

int32_t cmd;		/* sdk command (read, write, peg action, data group) */

3. VBUS API response data packet sequence number

int32_t transaction;	/* client transaction id */
};

4. Additional reassembly helper fields in all VBUS API response structures:

unsigned short total_size;    /* total transfer size */
   unsigned long msgid;   	 /* for user-defined unique unsigned int value */

Here is an example of reassembly fields in the data group message response struct:

typedef struct _lmu_app_peg_vbus_read_data_group_response
{
    struct _lmu_app_peg_vbus_hdr header;
    int32_t flags;
    int32_t status;		/* request status , 0=good */
    int32_t index;
    unsigned long msgid;
    unsigned int spare[10];
    unsigned short buflen;	/* message buffer len */
    unsigned short total_size;    /* total transfer size */
    char    dgtext[LMU_APP_VBUS_MAX_MESSAGE_LEN];
} LMU_APP_VBUS_SDK_DG_RSP;

Reference the example C code provided below for various methods of handling response packet reassembly.

VBUS API Details

The best way to understand how a hosted application can retrieve JPOD2 or VPOD2 data using the LMU VBUS API is by following working C code examples.

The sequence for a hosted application to send a VBUS API request and to get back the JPOD2 or VPOD2 response data is as follows:

  1. Get access to the LMU app VBUS API request IPC message queue (/etc/calamp/lmu/vbus_sdk_queue).
  2. Create a client VBUS API response IPC message queue (/etc/calamp/lmu/vbus_sdk_client_queue).
  3. Fill in the VBUS read param, VBUS write param, VBUS action, or VBUS data group IPC request structure, which will be passed to the LMU app.
  4. Send the IPC message to the LMU app via the Linux IPC function msgsnd().
  5. Wait for the response via the Linux IPC function msgrcv().
  6. If the response message indicates that the response data is larger than a response message payload, wait and retrieve the incoming Linux IPC messages via a msgrcv() loop.
  7. As part of the hosted application termination handling, the hosted app should destroy the client VBUS API response IPC message queue that was created in step 2.

Appendix: Code Examples

/*
 * Copyright (c) 2016, 2017 CalAmp Corporation.  All rights reserved.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <pthread.h>
#include <signal.h>

#include "calamp/lmuAppVBusSdk.h"

static void QuitHandler(int32_t sig);
void DEBUG_HexDump(unsigned char *b, int length);
int32_t LMU_Init(void);
int32_t Client_Init(char *filename);
int32_t Client_Close(void);
int32_t Client_GetArgs(void);
int32_t DGM_msgrcv_create_thread(void);
int32_t DGM_msgrcv_create_jumbo_thread(void);
void *DGM_msgrcv_Thread(void *data);
void *DGM_msgrcv_Jumbo_Thread(void *data);
int32_t DGM_snd_rcv_message(void);
int32_t client_read_data_group_msg(void);

const char *progname;

static LMU_APP_VBUS_SDK_REQ lmuReq;
static LMU_APP_VBUS_SDK_DG_RSP lmuDGRsp;
static LMU_APP_VBUS_SDK_DG_JUMBO_RSP lmuDGjRsp;
static int32_t lmu_qid = -1;
static int32_t client_qid = -1;
static int32_t client_jqid = -1;
static int32_t client_sqid = -1;
static int32_t seqid = 0;
int32_t m_runcmd, m_jumbo, m_enable, m_interval, m_statcmd, m_active;
int32_t m_index = -1;
int32_t do_debug = 0;

static pthread_t m_DMG_msgrcv_tid = -1;
static pthread_t m_DMG_msgrcv_jumbo_tid = -1;
static int32_t m_DMG_msgrcv_Thread_Running = 0;
static int32_t m_DMG_msgrcv_Jumbo_Thread_Running = 0;

int32_t ClientNum = LMU_APP_PEG_VBUS_MSGTYPE_CLIENT;	/* clientnum 2 */

static void QuitHandler(int32_t sig)
{
	Client_Close();
	exit(sig);
}

void DEBUG_HexDump(unsigned char *b, int length)
{
	int i;
	char sbuf[10];
	char tbuf[80];

	if ( b == NULL )
	{
		printf("DEBUG_HexDump: buffer is NULL");
		return;
	}

	/* limit traffic so msg queue does not back up */
	tbuf[0] = 0;
	for (i=0; i<length;)
	{
		sprintf(sbuf,"%02X ",*(b+i));
		strcat(tbuf, sbuf);
		i++;
		/* send CR-LF every 20 bytes */
		if ((i%20) == 0)
		{
			printf("%s", tbuf);
			printf("\n");
			tbuf[0] = 0;
		}
	}
	printf("%s", tbuf);
	printf("\n");
}

int32_t DGM_msgrcv_create_thread(void)
{
	int32_t status;

	status = pthread_create(&m_DMG_msgrcv_tid, 0,
				&DGM_msgrcv_Thread, NULL );
	if (status != 0)
		printf("%s, pthread create error %d\n", __func__, status);
	else
		printf("%s, msgrcv pthread created.\n", __func__);

	pthread_detach (m_DMG_msgrcv_tid);

	return status;
}

int32_t DGM_msgrcv_create_jumbo_thread(void)
{
	int32_t status;

	status = pthread_create(&m_DMG_msgrcv_jumbo_tid, 0,
				&DGM_msgrcv_Jumbo_Thread, NULL);
	if (status != 0)
		printf("%s, pthread create error %d\n", __func__, status);
	else
		printf("%s, msgrcv jumbo pthread created.\n", __func__);

	pthread_detach (m_DMG_msgrcv_jumbo_tid);

	return status;
}

void *DGM_msgrcv_Thread(void *data)
{
	int32_t rsp_len = 0;
	int32_t msg_len = 0;
	struct _lmu_app_peg_vbus_read_data_group_response *rsp;
	rsp = ( struct _lmu_app_peg_vbus_read_data_group_response *)&lmuDGRsp;
	msg_len = sizeof( LMU_APP_VBUS_SDK_DG_RSP ) - sizeof(int32_t);

	memset((char *)&lmuDGRsp, 0, sizeof(LMU_APP_VBUS_SDK_DG_RSP));

	m_DMG_msgrcv_Thread_Running = 1;

	while (m_DMG_msgrcv_Thread_Running)
	{
		rsp_len = msgrcv(client_qid, (void *)&lmuDGRsp,
				 msg_len, ClientNum, 0 );
		if (rsp_len > 0)
		{
			if (rsp->buflen > 0)
			{
				if (do_debug)
				{
					printf("%s: Msg flags = 0x%08x, transaction = %d\n", __func__, rsp->flags, rsp->header.transaction);
					DEBUG_HexDump((unsigned char *)rsp->dgtext, rsp->buflen);
				}
			}
			else
				printf("%s: error buflen = %d\n",
						__func__, rsp->buflen);

			memset((char *)&lmuDGRsp, 0,
					sizeof(LMU_APP_VBUS_SDK_DG_RSP));
		}
	}

	/* exit thread */
	pthread_exit(NULL);

	return NULL;
}

void *DGM_msgrcv_Jumbo_Thread(void *data)
{
	int32_t rsp_len = 0;
	int32_t msg_len = 0;
	struct _lmu_app_peg_vbus_read_data_group_jumbo_response *jrsp;
	jrsp = (struct _lmu_app_peg_vbus_read_data_group_jumbo_response *)&lmuDGjRsp;
	msg_len = sizeof(LMU_APP_VBUS_SDK_DG_JUMBO_RSP) - sizeof(int32_t);

	memset((char *)&lmuDGjRsp, 0, sizeof(LMU_APP_VBUS_SDK_DG_JUMBO_RSP));

	m_DMG_msgrcv_Jumbo_Thread_Running = 1;

	while (m_DMG_msgrcv_Jumbo_Thread_Running)
	{
		rsp_len = msgrcv(client_jqid, (void *)&lmuDGjRsp,
                          	 msg_len, ClientNum, 0 );
		if ( rsp_len > 0 )
		{
			if (jrsp->buflen > 0)
			{
				if (do_debug)
				{
					printf("%s: Msg flags = 0x%08x, transaction = %d\n", __func__, jrsp->flags, jrsp->header.transaction);
					DEBUG_HexDump((unsigned char *)jrsp->dgtext, jrsp->buflen);
				}
			}
			else
				printf("%s: error buflen = %d\n",
						__func__, jrsp->buflen);

			memset((char *)&lmuDGjRsp, 0,
					sizeof(LMU_APP_VBUS_SDK_DG_JUMBO_RSP));
		}
	}

	/* exit thread */
	pthread_exit(NULL);

	return NULL;
}

int32_t DGM_snd_rcv_message(void)
{
	struct _lmu_app_peg_vbus_read_data_group_request *req;
	int32_t req_len = 0;
	int32_t rc = 0;

	memset ( (char *)&lmuReq, 0, sizeof( LMU_APP_VBUS_SDK_REQ ));
	req = ( struct _lmu_app_peg_vbus_read_data_group_request *)&lmuReq;
  1. Fill in the action or data group IPC request structure, which will be passed to the LMU app.
/* setup read_accum request */
	req->header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;
	req->header.cmd = LMU_APP_VBUS_CMD_REQ_DATA_GROUP;
	req->header.transaction = seqid++;
	req->index = m_index;   ------------- Data Group Message number

	req->mtype_src = ClientNum;

	if (m_runcmd)
	{
		req->runcmd = m_interval;
		if (m_enable)
		{ ------------- Start a RunCmd automated request
			printf("RunCmd start, interval %d msec.\n",
							(int32_t)m_interval);
			req->flags |= VBUS_APP_VBUS_SDK_RUN_CMD_ADD;
		}
		else
		{  ------------- Stop a RunCmd automated request
			printf("RunCmd stop.\n");
			req->flags |= VBUS_APP_VBUS_SDK_RUN_CMD_DELETE;
		}
	}

	if (m_jumbo)
	{
		req->client_qid = client_jqid;  ------------- if Jumbo 1024 payload request
		req->flags |= VBUS_APP_VBUS_SDK_JUMBO_PAYLOAD;
	}
	else if (m_statcmd)
	{
		printf("Send RunCmd STATUS Cmd.\n");
		req->client_qid = client_sqid;  ------------- else if RunCmd status request

		req->flags |= VBUS_APP_VBUS_SDK_RUN_CMD_STAT;
	}
	else
		req->client_qid = client_qid;  ------------- else non-Jumbo 256 payload request


	req_len = sizeof( lmuReq ) - sizeof(int32_t);
	printf("read_data_group_test: %d, reqlen = %d\n", m_index, req_len );
  1. Send the IPC message to the LMU app via the Linux IPC function msgsnd().
/* send message */
	rc = msgsnd(lmu_qid, (void *)req, req_len, IPC_NOWAIT);
	printf("read_data_group_test: msgsnd rc = %d \n", rc );

	if (!m_runcmd)
	{  ------------- If not a RunCmd request, wait for the response
		int32_t rsp_len = 0;
		int32_t msg_len = 0;
		int32_t jmsg_len = 0;
		int32_t retry_count = 0;
		struct _lmu_app_peg_vbus_read_data_group_response *rsp;
		struct _lmu_app_peg_vbus_read_data_group_jumbo_response *jrsp;
	
		rsp = (struct _lmu_app_peg_vbus_read_data_group_response *)&lmuDGRsp;
		jrsp = (struct _lmu_app_peg_vbus_read_data_group_jumbo_response *)&lmuDGjRsp;

		msg_len = sizeof( LMU_APP_VBUS_SDK_DG_RSP ) - sizeof(int32_t);
		jmsg_len = sizeof( LMU_APP_VBUS_SDK_DG_JUMBO_RSP ) - sizeof(int32_t);

		memset((char *)&lmuDGRsp, 0, sizeof( LMU_APP_VBUS_SDK_DG_RSP ));
		memset((char *)&lmuDGjRsp, 0, sizeof( LMU_APP_VBUS_SDK_DG_JUMBO_RSP ));
  1. Wait for the response via the Linux IPC function msgrcv().
/* now wait for the response */
		if (!rc)
		{
			int32_t all_pkts_received = 0;
			int32_t tcount = 0;
			int32_t pcount = 0;

			rsp_len = 0;
			retry_count = 0;
			while (( rsp_len == 0) || (retry_count < 10) || (all_pkts_received == 0))
			{
				if (m_jumbo) 
				{  ------------- use Jumbo payload IPC client queue
					rsp_len = msgrcv(client_jqid,
							(void *)&lmuDGjRsp,
							jmsg_len, ClientNum,
							0 );
				}
				else if (m_statcmd)
				{ ------------- use RunCmd status IPC client queue
					rsp_len = msgrcv(client_sqid,
							(void *)&lmuDGRsp,
							msg_len, ClientNum,
							0 );
				}
				else
				{  ------------- use non-Jumbo payload IPC client queue
					rsp_len = msgrcv(client_qid,
							(void *)&lmuDGRsp,
							msg_len, ClientNum,
							0 );
				}

				if (m_jumbo && rsp_len > 0)
				{
					printf("index %d, status %d!\n", jrsp->index, rsp->status);
					if (jrsp->buflen > 0)
					{
						tcount += jrsp->buflen;
						pcount = jrsp->total_size;
						if (do_debug)
						{
							printf("Msg flags = 0x%08x, transaction = %d\n", jrsp->flags, jrsp->header.transaction);
							DEBUG_HexDump((unsigned char *)jrsp->dgtext, jrsp->buflen);
						}
					}
					else
						printf("error buflen = %d\n", jrsp->buflen);
				}
				else if (rsp_len > 0)
				{
					printf("index %d, status %d!\n", rsp->index, rsp->status);
					if (rsp->buflen > 0)
					{
						tcount += rsp->buflen;
						pcount = rsp->total_size;
						if (m_statcmd)
							printf("\n%s\n\n", (char *)rsp->dgtext);
						else
						{
							if (do_debug)
							{
								printf("Msg flags = 0x%08x, transaction = %d\n", rsp->flags, rsp->header.transaction);
								DEBUG_HexDump((unsigned char *)rsp->dgtext, rsp->buflen);
							}
						}
					}
					else
						printf("error buflen = %d\n", rsp->buflen);
				}
  1. If the response message indicates that the response data is larger than a response message payload, wait and retrieve the incoming Linux IPC messages via a msgrcv() loop.
if (tcount == pcount)
					break;
				retry_count++;
				usleep( 50 * 1000 );
			} /* end wait for receive */
          	}
		else
		{
			printf("msgsnd error %s \n", strerror(errno));
		}
	}

	return rc;
}

/* get access to lmu app API request IPC message queue */
int32_t LMU_Init(void)
{
	key_t keyid = -1;
	int32_t rc = 0;

	keyid = ftok(LMU_APP_VBUS_SDK_FILE, 1 );
	if (keyid != -1) {
		lmu_qid = msgget(keyid, 0666);
		if (lmu_qid != -1) {
			printf("lmu app sdk queue access ok \n");
		} else {
			printf("msgget lmu sdk queue: errno %s \n", strerror(errno));
			return rc;
		}
	} else {
		printf("ftok sdk queue errno %s \n", strerror(errno));
		return rc;
	}

	return rc;
}

/* create client response IPC message queue */ 
int32_t Client_Init(char *filename)
{
	key_t keyid = -1;
	struct stat statbfr;
	FILE *fd;
	int32_t qid;
	int32_t rc;

	rc = stat(filename, &statbfr);
	if (rc == -1)
	{
		/* create the file */
		fd = fopen(filename, "w+");
		if (fd != NULL) {
			/* file created ok */
			fclose(fd);
		} else {
			/* why can't we do this? huh? */
			printf("fopen err %s \n", strerror(errno));
			return -1;
		}
	}
	keyid = ftok (filename, 1);
	if (keyid != -1)
	{
		qid = msgget(keyid, (IPC_CREAT | 0666 ));
		if (qid != -1) {
			printf("client queue %s created ok, qid = %d \n",
							filename, qid);
		} else {
			printf("client_qid msgget : errno %s \n",
							strerror(errno));
			return -1;
		}
	} else {
		printf("ftok client queue errno %s \n", strerror(errno));
		return -1;
	}

	return qid;
}

int32_t Client_Close(void)
{
	struct msqid_ds qidInfo;
	int32_t rc = 0;

	/* stop the receive threads */
	m_DMG_msgrcv_Thread_Running = 0;
	m_DMG_msgrcv_Jumbo_Thread_Running = 0;
	usleep(1000 * 1000);	/* 1 sec. */

	printf("appSdkTest: destroy client queue ...\n");
	rc = msgctl ( client_qid, IPC_RMID, &qidInfo );

	printf("appSdkTest: destroy client jumbo queue ...\n");
	rc = msgctl ( client_jqid, IPC_RMID, &qidInfo );

	printf("appSdkTest: destroy client status queue ...\n");
	rc = msgctl ( client_sqid, IPC_RMID, &qidInfo );

	return rc;
}

/* Note, for this example code, there is no argument checking. */
/* Customer should add these checks. */
int32_t Client_GetArgs(void)
{
	char inputIndece[16];
	char *end_ptr = NULL;

	m_index = 0;
	memset(inputIndece, 0, sizeof(inputIndece));
	fflush ( stdout );
	fflush ( stdin );
	printf("\n");
	printf( "----------- LMU READ DATA GROUP MSG TEST ----------------\n");
	printf( "Enter Data Group Msg ID (0-31) - (default 0): ");
	fgets(inputIndece, sizeof(inputIndece), stdin);
	if (strlen( inputIndece) > 1)
		m_index = strtol(inputIndece, &end_ptr, 10);

	m_jumbo = 0;
	memset( inputIndece, 0, sizeof(inputIndece));
	fflush ( stdout );
	fflush ( stdin );
	printf("Enter 'y' to use jumbo payload size (default 'n'): ");
	fgets(inputIndece, sizeof(inputIndece), stdin);
	if (strlen( inputIndece) > 1)
	{
		if (0 == strncmp(inputIndece, "y", 1))
			m_jumbo = 1;
		else
			m_jumbo = 0;
	}

	/* STATUS command */
	m_statcmd = 0;
	memset(inputIndece, 0, sizeof(inputIndece));
	fflush ( stdout );
	fflush ( stdin );
	printf( "Enter 'y' to run RunCmd Stat cmd (default 'n'): ");
	fgets( inputIndece, sizeof(inputIndece), stdin );
	if (strlen( inputIndece) > 1)
	{
		if (0 == strncmp(inputIndece, "y", 1))
			m_statcmd = 1;
		else
			m_statcmd = 0;
	}

	/* RunCmd enable/disable and frequency interval */
	m_runcmd = 0;
	m_enable = 0;
	m_interval = 0;
	if (!m_statcmd)
	{
		memset(inputIndece, 0, sizeof(inputIndece));
		fflush ( stdout );
		fflush ( stdin );
		printf("Run 'y' or don't run 'n' RunCmd for this DGM %d (default is 'n') : ", m_index);
		fgets(inputIndece, sizeof(inputIndece), stdin);
		m_runcmd = m_enable = 0;  /* default is do not run RunCmd */
		if (strlen( inputIndece) > 1)
		{
			if (0 == strncmp(inputIndece, "y", strlen("y")))
				m_runcmd = 1;
		}

		if (m_runcmd)
		{
			memset(inputIndece, 0, sizeof(inputIndece));
			fflush ( stdout );
			fflush ( stdin );
			printf("Start 'y' or Stop 'n' RunCmd for this DGM %d (default is start) : ", m_index);
			fgets(inputIndece, sizeof(inputIndece), stdin);
			m_enable = 1;     /* default is enable */
			if (strlen( inputIndece) > 1)
			{
				if (0 == strncmp(inputIndece, "n", strlen("n")))
					m_enable = 0;
				else
					m_enable = 1;
			}

			if (m_enable)
			{
				memset(inputIndece, 0, sizeof(inputIndece));
				fflush ( stdout );
				fflush ( stdin );
				printf("Enter delay between DGM %d requests in ms (default is 250) : ", m_index);
				fgets(inputIndece, sizeof(inputIndece), stdin);
				if (strlen( inputIndece) > 1)
					m_interval =
					     strtol(inputIndece, &end_ptr, 10);
				else
					m_interval =
					     VBUS_APP_VBUS_SDK_RUN_CMD_250MSEC;
			}
		}
	}

	return 0;
}

int32_t client_read_data_group_msg(void)
{
	int32_t rc = 0;

	/* setup */
	Client_GetArgs();	/* Note, skip args verification */

	if (m_runcmd)
	{ ------------- If RunCmd automated request command, then create background IPC msgrcv thread to read the RunCmd automated responses
		/* create receiving response thread */
		if (m_jumbo)
			DGM_msgrcv_create_jumbo_thread();
		else
			DGM_msgrcv_create_thread();
	}

	/* send the request, get the response */
	rc = DGM_snd_rcv_message();

	return rc;
}

int32_t main(int32_t argc, char *argv[])
{
	int32_t command = 0;
	char inputCmd[80];
	char *end_ptr = NULL;
	int32_t rc = 0;

	(progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]);

	signal(SIGINT, QuitHandler);
	signal(SIGTERM, QuitHandler);
	signal(SIGALRM, QuitHandler);
  1. Get access to the LMU app VBUS API request IPC message queue (/etc/calamp/lmu/vbus_sdk_queue).
LMU_Init();
  1. Create a client VBUS API response IPC message queue (/etc/calamp/lmu/vbus_sdk_client_queue).
client_qid  = Client_Init(LMU_APP_VBUS_CLIENT_FILE);
	client_jqid = Client_Init(LMU_APP_VBUS_CLIENT_JFILE);
	client_sqid = Client_Init(LMU_APP_VBUS_CLIENT_SFILE);

	if (lmu_qid == -1)
	{
		printf("lmu_qid not accessible!! done\n");
		return -1;
	}
	if (client_qid == -1 || client_jqid == -1 || client_sqid == -1)
	{
		printf("client_qid not accessible ...done\n");
		return -2;
	}

	m_active = 1;
	while (m_active)
	{
		fflush ( stdout );
		fflush ( stdin );
		memset ( inputCmd, 0, sizeof(inputCmd));

		printf ("\n");
		printf ("--------------- LMU APP SDK TEST -------------------\n");
		printf ("  1 ...... set debug on \n");
		printf ("  2 ...... set debug off \n");
		printf ("  3 ...... VBus read Data Group Msg x \n");
		printf ("  4 ...... quit \n");

		printf ("\n");
		printf (" Enter command: ");
		fgets (inputCmd, sizeof(inputCmd) , stdin);
		if (strlen( inputCmd ) > 1)
		{
			command = strtol( inputCmd, &end_ptr, 10);
			printf("command entered = %d\n", command);
			switch (command)
			{
				case 1: /* set debug on */
					do_debug = 1;
					break;
				case 2: /* set debug on */
					do_debug = 0;
					break;
				case 3: /* read data group msg 'n'  */
					rc = client_read_data_group_msg();
					break;
				case 4: /* quit */
					m_active = 0;
					rc = 0;
					printf("QUIT command entered.. bye!\n");
					break;
				default:
					printf("UNKNOWN command %d entered...retry \n", command);
					break;
			}
			if (rc)
			printf("%s: operation failed\n", progname);
		}/* end if command entered */
	}/* end while active */
  1. As part of the hosted application termination handling, the hosted app should destroy the client VBUS API response IPC message queue that was created:
Client_Close();
	printf("Done... bye bye!\n");

	return rc;
}
/*
 * Copyright (c) 2016, 2017 CalAmp Corporation.  All rights reserved.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/sysinfo.h>
#include <stddef.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <errno.h>
#include <syslog.h>
#include <stdbool.h>
#include <signal.h>
#include <getopt.h>

#include "calamp/lmuAppVBusSdk.h"

/* #define VBUS_API_DEBUG */

typedef unsigned int uint32_t;
typedef unsigned char uint8_t;

/*------------------------------------------------------------------
 function prototypes 
--------------------------------------------------------------------*/
int32_t Client_Init(void);
int32_t Client_Close(void);
int32_t CLI_mode(char *buf);
void MENU_mode(void);
static void QuitHandler(int sig);
static void Usage(const char *progname);
static void prt_version(void);
static int32_t set_client_number(void);
static void ConvertCmdString(char *cmdbuf);

static int32_t client_read_vbus_param(uint32_t mode, char *buf);
static int32_t client_write_vbus_param(uint32_t mode, char *buf);

/*--------------------------------------------------------------------
 Defines 
--------------------------------------------------------------------*/
#define OPTION_VERSION		0x00000001
#define OPTION_MENU_MODE	0x00000002
#define OPTION_CLI_MODE		0x00000004
#define OPTION_DEBUG_ON		0x00000008
#define OPTION_SEND_TO_HA	0x00000010

#define MAX_OPTIONS		MAX_PARAM_VALUE_BYTES	

/* parser special characters */
#define PRSR_END	0xC0
#define PRSR_ESC	0xDB
#define PRSR_ESC_END	0xDC
#define PRSR_ESC_ESC	0xDD
#define PRSR_CR		0x0D
#define PRSR_LF		0x0A
#define PRSR_BS		0x08
#define PRSR_SP		0x20
#define PRSR_DQUOTE	0x22	/* double quote */
#define PRSR_SUBST	0x1F	/* substitue char for a Space in a quoted string */
#define PRSR_ESCAPE	0x5C	/* Escape char to insert " inside param */

#define PBUF_LEN		300	/* print buffer length */
#define MAX_CMD_LINE_LENGTH	120

typedef struct s_options
        {
            char *options[MAX_OPTIONS];
            int options_count;
        } OPTIONS;

/*-------------------------------------------------------------------
 global variables
--------------------------------------------------------------------*/
extern int optind;
extern int opterr;
extern char *optarg;

const char *progname;
unsigned int option_flag = 0;
int32_t lmu_qid = -1;
int32_t client_qid = -1;
int32_t seqid = 0;

char *version = "1.02";

/*------------------------------------------------------------------
 local variables
--------------------------------------------------------------------*/
static LMU_APP_VBUS_SDK_REQ lmuReq;
static LMU_APP_VBUS_SDK_RSP lmuRsp;
static LMU_APP_VBUS_SDK_RP_RSP lmuRPRsp;	/* vbus read param response */
static LMU_APP_VBUS_SDK_WP_RSP lmuWPRsp;	/* vbus write param response */

static bool do_debug = false;

static int32_t ClientNum = LMU_APP_PEG_VBUS_MSGTYPE_CLIENT;

static uint8_t VBusDataResponse[LMU_APP_VBUS_MAX_DATA_BUFFER_LEN];
static uint8_t *VBus_APIbuf = VBusDataResponse;

static void QuitHandler(int sig)
{
    exit(sig);
}

static void Usage(const char *progname)
{
    fprintf(stderr,
            "usage: %s \n"
            "           [-o]  <at$app .....> \n"
            "           [-i]  interactive mode \n"
            "           [-v]  version \n"
            "           [-d]  turn on application debug  \n"
            "           [-a]  send report response to HA \n"
            "examples: %s -o \"at\\$app vbus param? 5007,0,*\"\n"
            "          %s -o \"at\\$app vbus param? 5011,*\"\n"
            "          %s -o \"at\\$app vbus param 5007,0,245\"\n",
            progname,
            progname,
            progname,
            progname);

    exit(1);
}

static void prt_version(void)
{
    fprintf(stderr, "%s version %s\n", progname, version);
}

void DEBUG_SendASCII(char *sndstr)
{
    if (do_debug == false) return;

    if ( sndstr == NULL )
    {
        printf("%s: buffer is NULL", progname);
        return;
    }

    printf("%s", sndstr);
    printf("\n");
}

void DEBUG_HexDump(uint8_t *b, int length)
{
    int i;
    char sbuf[10];
    char tbuf[80];

    if (do_debug == false) return;

    if ( b == NULL )
    {
        printf("%s: buffer is NULL", progname);
        return;
    }

    /* limit traffic so msg queue does not back up */
    tbuf[0] = 0;
    for (i=0; i<length;)
    {
        sprintf(sbuf,"%02X ",*(b+i));
        strcat(tbuf, sbuf);
        i++;
        /* send CR-LF every 20 bytes */
        if ((i%20) == 0)
        {
            DEBUG_SendASCII(tbuf);
            tbuf[0] = 0;
        }
    }
    DEBUG_SendASCII(tbuf);
}

/************************************************************************/
  CONVERT CMD STRING: converts string to uppercase except when inside
      double quotes.  Converts all Spaces inside double quotes to a 0x1F so
  that strings insides double quotes can contain spaces.
/************************************************************************/
static void ConvertCmdString(char *cmdbuf)
{
    bool bQuote=false;
    int i;

    for (i=0; i < MAX_CMD_LINE_LENGTH; i++)
    {
        if (cmdbuf[i] == 0)
            break;

        if (cmdbuf[i] == PRSR_DQUOTE)
                if ( i > 0 && cmdbuf[i-1] != PRSR_ESCAPE )
                 bQuote = !bQuote;   /* toggle quote flag */


        if (bQuote)
        {
            /* substitute a 0x1F for a space inside a double quote */
            if (cmdbuf[i] == PRSR_SP)
                cmdbuf[i] = PRSR_SUBST;
        }
        else
        {
            /* convert char to uppercase */
            if ((cmdbuf[i] >= 'a') && (cmdbuf[i] <= 'z'))
                cmdbuf[i] -= 0x20;
        }
    }
}

/*--------------------------------------------------------------------
 client_read_vbus_param
----------------------------------------------------------------------*/
static int32_t client_read_vbus_param ( uint32_t mode, char *buf )
{
    int32_t rc = 1;
    struct _lmu_app_peg_vbus_read_param_request *req;
    struct _lmu_app_peg_vbus_read_param_response *rsp;
    int32_t req_len = 0;
    int32_t rsp_len = 0;
    int32_t msg_len = 0;
    char inputData[16];
    char *end_ptr = NULL;
    int32_t param = -1;
    int32_t index = -1;
    int32_t retry_count = 0;

    /* setup */
    memset( inputData, 0, sizeof(inputData));
    memset ( (char *)&lmuReq, 0, sizeof( LMU_APP_VBUS_SDK_REQ ));
    memset ( (char *)&lmuRPRsp, 0, sizeof( LMU_APP_VBUS_SDK_RP_RSP ));
    req = ( struct _lmu_app_peg_vbus_read_param_request *)&lmuReq;
    rsp = ( struct _lmu_app_peg_vbus_read_param_response *)&lmuRPRsp;
    msg_len = sizeof( LMU_APP_VBUS_SDK_RP_RSP ) - sizeof(int32_t);

    if ( lmu_qid == -1 )
    {
         printf("%s: lmu_qid not accessible!! done\n", progname);
         return -1;
    }
    if ( client_qid == -1 )
    {
         printf("%s: client_qid not accessible ...done\n", progname);
         return -2;
    }

    memset(VBusDataResponse, 0, sizeof(VBusDataResponse));

    if (mode == OPTION_MENU_MODE)
    {
        /* query operator for vbus param index */
        fflush ( stdout );
        fflush ( stdin );
        printf("---------------- LMU READ VBUS PARAM TEST ----------------\n");
        printf("Enter VBus param (%d or greater): ", MIN_VBUS_PARAM_ID);
        fgets( inputData, sizeof(inputData), stdin );
        if ( strlen( inputData) > 1 )
           param = strtol( inputData, &end_ptr, 10 );

        memset ( inputData, 0, sizeof(inputData));
        fflush ( stdout );
        fflush ( stdin );
        printf("Enter VBus param index value, return for all param indexes : ");
        fgets ( inputData, sizeof(inputData), stdin );
        if ( strlen( inputData ) > 1 )
           index = strtoul( inputData, &end_ptr, 10 );
        else
           index = ALL_PARAM_INDEXES;
    }
    else if (mode == OPTION_CLI_MODE)
    {
        char *p = NULL;

        if (!buf) return 1;

        p = strtok(buf, ",");
        if (!p) return 1;
        param = strtoul( p, &end_ptr, 10 );

        p = strtok(NULL, ",");
        if (!p) return 1;
        if (p[0] == '*')
            index = -1; 
        else
            index = strtoul( p, &end_ptr, 10 ); 
        if (do_debug)
        {
            printf("%s: param = %d\n", __func__, param);
            printf("%s: index = %d\n", __func__, index);
        }
    }
    else
    {
        return 1;
    }

    if (param >= MIN_VBUS_PARAM_ID)
    {
       if (do_debug)
           printf("%s: sending VBus READ PARAM message to lmu app...\n", progname);
  1. Fill in the VBUS read param IPC request structure, which will be passed to the LMU app.
/* setup read vbus param request */
       req->header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;
       req->header.cmd = LMU_APP_VBUS_CMD_READ;
       req->header.transaction = seqid++;
       req->client_qid = client_qid;
       req->param = param;  ----------- Param number to read
       req->index = index; -------- Param index to read or -1 to read all the param indexes
       req->mtype_src = ClientNum;
       req_len = sizeof( lmuReq ) - sizeof(int32_t);
       if (do_debug)
           printf("%s: param = %d, index = %d, reqlen = %d\n", progname, param, index, req_len);
  1. Send the IPC message to the LMU app via the Linux IPC function msgsnd().
/* send message */
       rc = msgsnd( lmu_qid, (void *)req, req_len, IPC_NOWAIT);
       if (do_debug)
           printf("%s: msgsnd rc = %d \n", progname, rc );
  1. Wait for the response via the Linux IPC function msgrcv().
/* now wait for the response */
       if ( rc == 0 )
       {
           struct msqid_ds ids;
           int32_t qnum = 1;
           int32_t tcount = 0;
           int32_t all_pkts_received = 0;

           rsp_len = 0;

           while ( (rsp_len <= 0) || (qnum > 0) || (all_pkts_received == 0) )
           {
               rsp_len = msgrcv( client_qid, (void *)&lmuRPRsp, msg_len,
                          ClientNum, IPC_NOWAIT );
               /* printf("%s: rsp_len %d!\n", progname, rsp_len); */
               if ( rsp_len > 0 )
               {
                   if (do_debug)
                       printf("%s: param %d, status %d!\n", progname, rsp->param, rsp->status);
                   if (rsp->buflen > 0)
                   {
                       if (do_debug)
                           printf("%s: Msg flags = 0x%08x, transaction = %d, total_size = %d, msgid = %lu\n", progname, rsp->flags, rsp->header.transaction, rsp->total_size, rsp->msgid);
                       DEBUG_HexDump((uint8_t *)rsp->rsptext,rsp->buflen);
  1. If the response message indicates that the response data is larger than a response message payload, wait and retrieve the incoming Linux IPC messages via a msgrcv() loop.
memcpy(VBus_APIbuf+(rsp->header.transaction * LMU_APP_VBUS_MAX_MESSAGE_LEN), rsp->rsptext, rsp->buflen);
                       tcount += rsp->buflen;
                       if (do_debug)
                           printf("%s: total = %d, buflen = %d, tcount = %d\n", progname, rsp->total_size, rsp->buflen, tcount);
                   }
                   else
                   {
                       printf("%s: error buflen = %d\n", progname, rsp->buflen);
                       rc = 1;
                       break;
                   }

                   if (tcount == rsp->total_size)
                   {
                       rc = 0;
                       if (do_debug)
                           printf("%s: finished total = %d, buflen = %d, tcount = %d\n", progname, rsp->total_size, rsp->buflen, tcount);
                       all_pkts_received = 1;
                       break;	/* got all the pkts for this response, so exit */
                   }
               }
  1. If the response message indicates that the response data is larger than a response message payload, wait and retrieve the incoming Linux IPC messages via a msgrcv() loop.
usleep( 50 * 1000 );
               msgctl(client_qid, IPC_STAT, &ids);
               qnum = ids.msg_qnum;
               if (do_debug)
                   printf("Number of Queued Messages = %d\n",(int)ids.msg_qnum);
               if (retry_count++ > 50)
                   break;
           }/* end wait for receive */
       }
       else
       {
           printf("%s: msgsnd error %s \n", progname, strerror(errno));
       }
    }
    else
    {
       printf("%s: BAD param Indece %d entered...\n", progname, index);
       rc = -5;
    }

    if (retry_count >= 50)
        rc = 1;

    if (rc == 0)
    {
        if (do_debug)
    	    printf("\n\n%s: Read VBus Cfg Param = \n\n%s\n", progname, VBus_APIbuf);
        else
    	    printf("%s", VBus_APIbuf);
    }

    return rc;
}

/*--------------------------------------------------------------------
 client_write_vbus_param
----------------------------------------------------------------------*/
static int32_t client_write_vbus_param ( uint32_t mode, char *buf )
{
    int32_t rc = 1;
    struct _lmu_app_peg_vbus_write_param_request *req;
    struct _lmu_app_peg_vbus_write_param_response *rsp;
    int32_t req_len = 0;
    int32_t rsp_len = 0;
    int32_t msg_len = 0;
    char inputData[16];
    char *end_ptr = NULL;
    int32_t param = -1;
    int32_t index = -1;
    int32_t valbyte_cnt = -1;
    int32_t valbytes[MAX_PARAM_VALUE_BYTES] = {0};
    int32_t value;
    int32_t retry_count = 0;

    /* setup */
    memset( inputData, 0, sizeof(inputData));
    memset ( (char *)&lmuReq, 0, sizeof( LMU_APP_VBUS_SDK_REQ ));
    memset ( (char *)&lmuWPRsp, 0, sizeof( LMU_APP_VBUS_SDK_WP_RSP ));
    req = ( struct _lmu_app_peg_vbus_write_param_request *)&lmuReq;
    rsp = ( struct _lmu_app_peg_vbus_write_param_response *)&lmuWPRsp;
    msg_len = sizeof( LMU_APP_VBUS_SDK_WP_RSP ) - sizeof(int32_t);

    if ( lmu_qid == -1 )
    {
         printf("%s: lmu_qid not accessible!! done\n", progname);
         return -1;
    }
    if ( client_qid == -1 )
    {
         printf("%s: client_qid not accessible ...done\n", progname);
         return -2;
    }

    if (mode == OPTION_MENU_MODE)
    {
        memset(VBusDataResponse, 0, sizeof(VBusDataResponse));

        /* query operator for vbus param index */
        fflush ( stdout );
        fflush ( stdin );
        printf( "--------------- LMU WRITE VBUS PARAM TEST ----------------\n");
        printf( "Enter VBus param (%d or greater): ", MIN_VBUS_PARAM_ID);
        fgets( inputData, sizeof(inputData), stdin );
        if ( strlen( inputData) > 1 )
           param = strtol( inputData, &end_ptr, 10 );

        memset ( inputData, 0, sizeof(inputData));
        fflush ( stdout );
        fflush ( stdin );
        /* printf( "Enter VBus param index value, return for all param indexes : "); */
        printf( "Enter VBus param index value : ");
        fgets ( inputData, sizeof(inputData), stdin );
        if ( strlen( inputData ) > 1 )
           index = strtoul( inputData, &end_ptr, 10 );
        /* verify if * works for param write else */
           /* index = ALL_PARAM_INDEXES; */

        valbyte_cnt = 0;
        printf( "Enter VBus param byte value (0 - 255)\n");
        while (valbyte_cnt < MAX_PARAM_VALUE_BYTES)
        {
            memset ( inputData, 0, sizeof(inputData));
            value = 0;
            fflush ( stdout );
            fflush ( stdin );
            printf( "  enter byte value or <cr> to terminate input: ");
            fgets ( inputData, sizeof(inputData), stdin );
            if ( strlen( inputData ) > 1 )
            {
                value = strtoul( inputData, &end_ptr, 10 );
                if ((value >= 0) && (value <= 255))
                {
                     valbytes[valbyte_cnt] = (uint8_t)value; 
                     valbyte_cnt++;
                }
                else
                    printf("Illegal value, enter valid byte value (0 - 255)\n");
            }
            else
                break;
        }
    }
    else if (mode == OPTION_CLI_MODE)
    {
        char *p = NULL;

        if (!buf) return 1;

        /* 5007,0,65 */
        /* 5011,0,0,0,0,0,0,0,19,136,0,0,0,0 */
        /* ^ */
        p = strtok(buf, ",");
        if (!p) return 1;
        param = strtol( p, &end_ptr, 10 );

        p = strtok(NULL, ",");
        if (!p) return 1;
        index = strtoul( p, &end_ptr, 10 );

        if (do_debug)
        {
            printf("%s: param = %d\n", __func__, param);
            printf("%s: index = %d\n", __func__, index);
        }

        /* 5007,0,65 */
        /* 5011,0,0,0,0,0,0,0,19,136,0,0,0,0 */
        /*  ^ */
        p = strtok(NULL, ",");
        if (!p) return 1;

        valbyte_cnt = 0;
        while (valbyte_cnt < MAX_PARAM_VALUE_BYTES)
        {
            if (p != NULL)
            {
                value = strtoul( p, &end_ptr, 10 );
                valbytes[valbyte_cnt] = (uint8_t)value;
                if (do_debug)
                    printf("%s: option[%d] = %d\n", progname, valbyte_cnt, (int32_t)valbytes[valbyte_cnt]);
                valbyte_cnt++; 
                p = strtok(NULL, ",");
            }
            else
                break;
        }
    }
    else
    {
        return 1;
    }

    if (param >= MIN_VBUS_PARAM_ID)
    {
       int i;

       if (do_debug)
           printf("%s: sending VBus WRITE PARAM message to lmu app...\n", progname);
  1. Fill in the VBUS write param IPC request structure, which will be passed to the LMU app.
/* set up write vbus param request */
       req->header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;
       req->header.cmd = LMU_APP_VBUS_CMD_WRITE;
       req->header.transaction = seqid++;
       req->client_qid = client_qid;
       req->param = param; ------- Param number to write
       req->index = index; ------ Param index to write
       req->valbyte_cnt = valbyte_cnt;  ----- Number of values for this parameter/index.  
/* For example:   */
/*   5007,0,65 */
/*   param,index,value  */
		/*   then req->valbyte_cnt = 1 */
		
      		/*   5011,0,0,0,0,0,0,0,19,136,0,0,0,0 */
		/*   param,index,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12 */
        		/*   then req->valbyte_cnt = 12 */

       if (do_debug)
           printf("%s: req->valbyte_cnt = %d\n", progname, req->valbyte_cnt);
       for (i = 0; i < valbyte_cnt; i++)
       {
           req->valbytes[i] = valbytes[i];
           if (do_debug)
               printf("%s: req->valbytes[%d] = 0x%02x\n", progname, i, req->valbytes[i]);
       }
       req->mtype_src = ClientNum;
       req_len = sizeof( lmuReq ) - sizeof(int32_t);
       if (do_debug)
           printf("%s: param = %d, index = %d, reqlen = %d\n", progname, param, index, req_len);
  1. Send the IPC message to the LMU app via the Linux IPC function msgsnd().
/* send message */
       rc = msgsnd( lmu_qid, (void *)req, req_len, IPC_NOWAIT);
       if (do_debug)
           printf("%s: msgsnd rc = %d \n", progname, rc );
  1. Wait for the response via the Linux IPC function msgrcv().
/* now wait for the response */
       if ( rc == 0 )
       {
           struct msqid_ds ids;
           int32_t qnum = 1;
           int32_t tcount = 0;
           int32_t all_pkts_received = 0;

           rsp_len = 0;

           while ( (rsp_len <= 0) || (qnum > 0) || (all_pkts_received == 0) )
           {
               rsp_len = msgrcv( client_qid, (void *)&lmuWPRsp, msg_len,
                          ClientNum, IPC_NOWAIT );
               /* printf("%s: rsp_len %d!\n", progname, rsp_len); */
               if ( rsp_len > 0 )
               {
                   if (do_debug)
                       printf("%s: param %d, status %d!\n", progname, rsp->param, rsp->status);
                   if (rsp->buflen > 0)
                   {
                       if (do_debug)
                           printf("%s: Msg flags = 0x%08x, transaction = %d, total_size = %d\n", progname, rsp->flags, rsp->header.transaction, rsp->total_size);
                       DEBUG_HexDump((uint8_t *)rsp->rsptext,rsp->buflen);
  1. If the response message indicates that the response data is larger than a response message payload, wait and retrieve the incoming Linux IPC messages via a msgrcv() loop.
memcpy(VBus_APIbuf+(rsp->header.transaction * LMU_APP_VBUS_MAX_MESSAGE_LEN), rsp->rsptext, rsp->buflen);
                       tcount += rsp->buflen;
                       if (do_debug)
                           printf("%s: total = %d, buflen = %d, tcount = %d\n", progname, rsp->total_size, rsp->buflen, tcount);
                   }
                   else
                   {
                       printf("%s: error buflen = %d\n", progname, rsp->buflen);
                       rc = 1;
                       break;
                   }

                   if (tcount == rsp->total_size)
                   {
                       rc = 0;
                       if (do_debug)
                           printf("%s: finished total = %d, buflen = %d, tcount = %d\n", progname, rsp->total_size, rsp->buflen, tcount);
                       all_pkts_received = 1;
                       break;	/* got all the pkts for this response, so exit */
                   }
               }
  1. If the response message indicates that the response data is larger than a response message payload, wait and retrieve the incoming Linux IPC messages via a msgrcv() loop.
usleep( 50 * 1000 );
               msgctl(client_qid, IPC_STAT, &ids);
               qnum = ids.msg_qnum;
               if (do_debug)
                   printf("Number of Queued Messages = %d\n",(int)ids.msg_qnum);
               if (retry_count++ > 50)
                   break;
           }/* end wait for receive */
       }
       else
       {
           printf("%s: msgsnd error %s \n", progname, strerror(errno));
       }
    }
    else
    {
       printf("%s: BAD param Indece %d entered...\n", progname, index);
       rc = -5;
    }

    if (retry_count >= 50)
        rc = 1;

    if (rc == 0)
    {
        if (do_debug)
    	    printf("\n\n%s: Read VBus Cfg Param = \n\n%s\n", progname, VBus_APIbuf);
    }

    return rc;
}

int32_t set_client_number(void)
{
    char inputData[16];
    char *end_ptr = NULL;
    int32_t client_no = 1;
    int32_t rsp_len = 0;

    fflush ( stdout );
    fflush ( stdin );
    printf(" Enter client number ( 2-30 ): ");
    fgets( inputData, sizeof(inputData), stdin );
    if ( strlen (inputData ) > 1 )
    {
        client_no = strtol( inputData, &end_ptr, 10 );
    }
    fflush ( stdin );
    fflush ( stdout );

    if ((client_no < 1) || (client_no > 30))
        client_no = LMU_APP_PEG_VBUS_MSGTYPE_CLIENT;

    while (rsp_len == 0)
    {
        rsp_len = msgrcv( client_qid, (void *)&lmuRsp, sizeof(lmuRsp),
                          ClientNum, IPC_NOWAIT );
        printf("%s: rsp_len = %d\n", progname, rsp_len);
    }

    printf("%s: ClientNum = %d\n", progname, client_no);
    return client_no;
}

int32_t Client_Init(void)
{
    int32_t rsp_len = 0;
    key_t   keyid = -1;
    struct stat statbfr;
    FILE *fd;
    int32_t rc = 0;
  1. Get access to the LMU app VBUS API request IPC message queue (/etc/calamp/lmu/vbus_sdk_queue).
/* get access to lmu app sdk command queue */
    keyid = ftok ( LMU_APP_VBUS_SDK_FILE, 1 );
    if ( keyid != -1 )
    {
        lmu_qid = msgget ( keyid, 0666 );
        if ( lmu_qid != -1 )
        {
            if (do_debug)
                printf("lmu app sdk queue access ok \n ");
        }
        else
        {
            rc = errno;
            printf("msgget lmu sdk queue: errno %s \n", strerror(errno));
            return rc;
        }
    }
    else
    {
        rc = errno;
        printf("ftok sdk queue errno %s \n", strerror(errno));
        return rc;
)
  1. Create a client VBUS API response IPC message queue (/etc/calamp/lmu/vbus_sdk_client_queue).
/* create client queue */
    rc = stat( LMU_APP_VBUS_CLIENT_FILE, &statbfr );
    if ( rc == -1 )
    {
        /* create the file */
        fd = fopen ( LMU_APP_VBUS_CLIENT_FILE, "w+" );
        if ( fd != NULL )
        {
           /* file created ok */
           fclose ( fd );
        }
        else
        {     
           /* why can't we do this? huh? */
            rc = errno;
            printf("%s: fopen err %s \n" , progname, strerror(errno));
            return rc;
        }
    }

    keyid = ftok ( LMU_APP_VBUS_CLIENT_FILE, 1 );
    if ( keyid != -1 )
    {
        client_qid = msgget ( keyid, (IPC_CREAT | 0666 ) );
        if ( client_qid != -1 )
        {
            if (do_debug)
                printf("client queue created ok, qid = %d \n", client_qid);
        }
        else
        {
            rc = errno;
            printf("%s msgget : errno %s \n", progname, strerror(errno));
            return rc;
        }
    }
    else
    {
        rc = errno;
        printf("ftok client queue errno %s \n", strerror(errno));
        return rc;
    }
    while (rsp_len == 0)
    {
        rsp_len = msgrcv( client_qid, (void *)&lmuRsp, sizeof(lmuRsp),
                          ClientNum, IPC_NOWAIT );
        if (do_debug)
            printf("%s: rsp_len = %d\n", progname, rsp_len);
    }

    return rc;
}

int32_t CLI_mode(char *buf)
{
    int32_t i, rc = 0;
    char *p = NULL;
    OPTIONS opts;

    if (buf == NULL)
        return 1;

#if defined(VBUS_API_DEBUG)
    printf("%s: buf = %s\n", __func__, buf);
#endif

    memset((void *)&opts, 0, sizeof(opts));
    p = strtok(buf, " \r");

    if (!p)
        return 1;

    for (i = 0; i < MAX_OPTIONS; i++)
    {
        opts.options[i] = p;
        if (p != NULL) {
            opts.options_count++;
            p = strtok(NULL, " \r");
        }
    }

#if defined(VBUS_API_DEBUG)
    printf("%s: options_count = %d\n", __func__, opts.options_count);
#endif
    for (i=0; i<opts.options_count; i++)
    {
        ConvertCmdString((char *)opts.options[i]); 
#if defined(VBUS_API_DEBUG)
        printf("%s: opts.options[%d] = %s\n", __func__, i, opts.options[i]);
#endif
    }


    if (0 == strncmp((char *)opts.options[0], "AT$APP", strlen("AT$APP")))
    {
        if (0 == strncmp((char *)opts.options[1], "VBUS", strlen("VBUS")))
        {
            /* eg. at$app vbus param? 5007,*  */
            /* eg. at$app vbus param? 5011,*  */
            /* eg. at$app vbus param? 5011,0,*  */
            if ((opts.options_count = 4) && (0 == strncmp((char *)opts.options[2], "PARAM?", strlen("PARAM?"))))
            {
                rc = client_read_vbus_param(OPTION_CLI_MODE, opts.options[3]);
            }
            /* eg. at$app vbus param 5007,0,235 */
            /* eg. at$app vbus param 5011,0,0,0,0,0,0,0,19,136,0,0,0,0 */
            else if ((opts.options_count == 4) && (0 == strncmp((char *)opts.options[2], "PARAM", strlen("PARAM"))))
            {
                rc = client_write_vbus_param(OPTION_CLI_MODE, opts.options[3]);
            }
        }
    }

    return rc;
}

void MENU_mode(void)
{
    int32_t active = 1;
    int32_t command = 0;
    char    inputCmd[80]; 
    char    *end_ptr = NULL;
    int32_t rc = 0;

    /* setup */
    memset( inputCmd, 0, sizeof(inputCmd));

    /* check for command from console input */
    while ( active )
    {
          fflush ( stdout );
          fflush ( stdin );
          memset ( inputCmd, 0, sizeof(inputCmd));

          printf ("------------- LMU APP VBUS API TEST --------------\n");
          printf ("  1 ...... set debug on \n");
          printf ("  2 ...... set debug off \n");
          printf ("  3 ...... set client number x \n");
          printf ("  4 ...... VBus read param x \n");
          printf ("  5 ...... VBus write param x \n");
          printf ("  6 ...... quit \n");

          printf ("\n");
          printf (" Enter command: ");
          fgets (  inputCmd, sizeof(inputCmd) , stdin );
          if ( strlen( inputCmd ) > 1 )
          {
              command = strtol( inputCmd, &end_ptr, 10 ); 
              printf("command entered = %d\n", command );
              switch ( command )
              {
                  case 1: /* set debug  */
                       do_debug = true;
                       break;
                  case 2: /* set debug on */
                       do_debug = false;
                       break;
                  case 3: /* set client number */
                       ClientNum = set_client_number();
                       break;
                  case 4: /* read vbus param 'n' */
                       rc = client_read_vbus_param(OPTION_MENU_MODE, NULL);
                       break;
                  case 5: /* write vbus param 'n' */
                       rc = client_write_vbus_param(OPTION_MENU_MODE, NULL);
                       break;
                  case 6: /* quit */
                       active = 0;
                       rc = 0;
                       if (do_debug)
                           printf("QUIT command entered .. bye!\n");
                       break;
                  default:
                       printf("UNKNOWN command %d entered...retry \n", command);
                       break;
              }
              if (rc)
                  printf("%s: operation failed\n", progname);
          }/* end if command entered */ 
    }/* end while active */
}

int32_t Client_Close(void)
{
    struct msqid_ds qidInfo;
    int32_t rc = 0;
  1. As part of the hosted application termination handling, the hosted app should destroy the client VBUS API response IPC message queue that was created in step 2.
/* free queue */
    if (do_debug)
        printf("%s: destroy client queue ...\n", progname);
    rc = msgctl ( client_qid, IPC_RMID, &qidInfo );

    if (rc == 0)          
        client_qid = -1;
    else
        printf("%s: msgctl error %s \n", progname, strerror(errno));

    return rc;
}

/*----------------------------------------------------------
 main program runs with linux console 
------------------------------------------------------------*/
int32_t main ( int32_t argc, char *argv[] )
{
    int32_t rc = 0;
    int32_t op;
    char *buf = NULL;

    (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]);

    signal(SIGINT, QuitHandler);
    signal(SIGTERM, QuitHandler);
    signal(SIGALRM, QuitHandler);

    while ((op = getopt (argc, argv, "o:iadvh")) != -1)
    {
        switch (op)
        {
        case 'o':
            option_flag |= OPTION_CLI_MODE;
            buf = optarg;
            break;
        case 'i':
            option_flag |= OPTION_MENU_MODE;
            break;
        case 'd':
            option_flag |= OPTION_DEBUG_ON;
            break;
        case 'v':
            option_flag |= OPTION_VERSION;
            break;
        case 'a':
            option_flag |= OPTION_SEND_TO_HA;
            break;
        case 'h':
        case '?':
            Usage(progname);
            /* NOTREACHED */
            break;
        default:
            Usage(progname);
            /* NOTREACHED */
            break;
        }
    }

    if (option_flag & OPTION_VERSION)
    {
        prt_version();
        exit(0);
    }

    if (option_flag & OPTION_DEBUG_ON)
        do_debug = true;

    if (option_flag & OPTION_CLI_MODE)
    {
        rc = Client_Init();
        if (rc > 0)
            return rc;
        rc = CLI_mode(buf);
        Client_Close();
    }
    else if ((argc == 1) || (option_flag & OPTION_MENU_MODE))
    {
        rc = Client_Init();
        if (rc > 0)
            return rc;
        MENU_mode();
        Client_Close();
    }

    if (rc)
        printf("%s: operation failed.\n", progname);

    if (do_debug)
        printf("%s: done ... bye bye! \n", progname);

    return rc;
}
/*
 * Copyright (c) 2016, 2017 CalAmp Corporation.  All rights reserved.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/sysinfo.h>
#include <stddef.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <errno.h>
#include <syslog.h>
#include <stdbool.h>
#include <signal.h>
#include <getopt.h>

#include "calamp/lmuAppVBusSdk.h"

/* #define VBUS_API_DEBUG */

typedef unsigned int uint32_t;
typedef unsigned char uint8_t;

/*------------------------------------------------------------------
 function prototypes 
--------------------------------------------------------------------*/
int32_t Client_Init(void);
int32_t Client_Close(void);
int32_t CLI_mode(char *buf);
void MENU_mode(void);
static void QuitHandler(int sig);
static void Usage(const char *progname);
static void prt_version(void);
static int32_t set_client_number(void);
static void ConvertCmdString(char *cmdbuf);

static int32_t client_peg_action(uint32_t mode, char *buf);

/*------------------------------------------------------------------
 Defines 
--------------------------------------------------------------------*/
#define OPTION_VERSION		0x00000001
#define OPTION_MENU_MODE	0x00000002
#define OPTION_CLI_MODE		0x00000004
#define OPTION_DEBUG_ON		0x00000008
#define OPTION_SEND_TO_HA	0x00000010

#define MAX_OPTIONS		MAX_PARAM_VALUE_BYTES	

/* parser special characters */
#define PRSR_END	0xC0
#define PRSR_ESC	0xDB
#define PRSR_ESC_END	0xDC
#define PRSR_ESC_ESC	0xDD
#define PRSR_CR		0x0D
#define PRSR_LF		0x0A
#define PRSR_BS		0x08
#define PRSR_SP		0x20
#define PRSR_DQUOTE	0x22	/* double quote */
#define PRSR_SUBST	0x1F	/* substitue char for a Space in a quoted string */
#define PRSR_ESCAPE	0x5C	/* Escape char to insert " inside param */

#define PBUF_LEN		300	/* print buffer length
#define MAX_CMD_LINE_LENGTH	120

typedef struct s_options
        {
            char *options[MAX_OPTIONS];
            int options_count;
        } OPTIONS;

/*------------------------------------------------------------------
 global variables
--------------------------------------------------------------------*/
extern int optind;
extern int opterr;
extern char *optarg;

const char *progname;
unsigned int option_flag = 0;
int32_t lmu_qid = -1;
int32_t client_qid = -1;
int32_t seqid = 0;

char *version = "1.02";

/*------------------------------------------------------------------
 local variables
--------------------------------------------------------------------*/
static LMU_APP_VBUS_SDK_REQ lmuReq;
static LMU_APP_VBUS_SDK_RSP lmuRsp;
static LMU_APP_VBUS_SDK_ACT_RSP lmuACTRsp;	/* vbus action response */

static bool do_debug = false;

static int32_t ClientNum = LMU_APP_PEG_VBUS_MSGTYPE_CLIENT;

static uint8_t VBusDataResponse[LMU_APP_VBUS_MAX_DATA_BUFFER_LEN];
static uint8_t *VBus_APIbuf = VBusDataResponse;

static void QuitHandler(int sig)
{
    exit(sig);
}

static void Usage(const char *progname)
{
    fprintf(stderr,
            "usage: %s \n"
            "           [-o]  <at$app .....> \n"
            "           [-i]  interactive mode \n"
            "           [-v]  version \n"
            "           [-d]  turn on application debug  \n"
            "           [-a]  send report response to HA \n"
            "example:  %s -o \"at\\$app peg action 99 1\"\n",
            progname,
            progname);

    exit(1);
}

static void prt_version(void)
{
    fprintf(stderr, "%s version %s\n", progname, version);
}

void DEBUG_SendASCII(char *sndstr)
{
    /* int32_t strSize = 0; */

    if (do_debug == false) return;

    if ( sndstr == NULL )
    {
        printf("%s: buffer is NULL", progname);
        return;
    }

    /* strSize = strlen(sndstr); */
    printf("%s", sndstr);
    printf("\n");
}

void DEBUG_HexDump(uint8_t *b, int length)
{
    int i;
    char sbuf[10];
    char tbuf[80];

    if (do_debug == false) return;

    if ( b == NULL )
    {
        printf("%s: buffer is NULL", progname);
        return;
    }

    /* limit traffic so msg queue does not back up */
    tbuf[0] = 0;
    for (i=0; i<length;)
    {
        sprintf(sbuf,"%02X ",*(b+i));
        strcat(tbuf, sbuf);
        i++;
        /* send CR-LF every 20 bytes */
        if ((i%20) == 0)
        {
            DEBUG_SendASCII(tbuf);
            tbuf[0] = 0;
        }
    }
    DEBUG_SendASCII(tbuf);
  /* DEBUG_SendASCII(""); */
}

/************************************************************************/
  CONVERT CMD STRING: converts string to uppercase except when inside
      double quotes.  Converts all Spaces inside double quotes to a 0x1F so
  that strings insides double quotes can contain spaces.
/************************************************************************/
static void ConvertCmdString(char *cmdbuf)
{
    bool bQuote=false;
    int i;

    for (i=0; i < MAX_CMD_LINE_LENGTH; i++)
    {
        if (cmdbuf[i] == 0)
            break;

        if (cmdbuf[i] == PRSR_DQUOTE)
                if ( i > 0 && cmdbuf[i-1] != PRSR_ESCAPE )
                 bQuote = !bQuote;   /* toggle quote flag */


        if (bQuote)
        {
            /* substitute a 0x1F for a space inside a double quote */
            if (cmdbuf[i] == PRSR_SP)
                cmdbuf[i] = PRSR_SUBST;
        }
        else
        {
            /* convert char to uppercase */
            if ((cmdbuf[i] >= 'a') && (cmdbuf[i] <= 'z'))
                cmdbuf[i] -= 0x20;
        }
    }
}

/*--------------------------------------------------------------------
 client_peg_action
----------------------------------------------------------------------*/
static int32_t client_peg_action ( uint32_t mode, char *buf )
{
    int32_t rc = 0;
    struct _lmu_app_peg_vbus_action_request *req;
    struct _lmu_app_peg_vbus_action_response *rsp;
    int32_t req_len = 0;
    int32_t rsp_len = 0;
    int32_t msg_len = 0;
    int32_t action = 0;
    int32_t modifier = 0;
    int32_t send_to_HA = 0;
    char    *end_ptr = NULL;
    char    inputData[16];
    int32_t retry_count = 0;

    /* setup */
    req = ( struct _lmu_app_peg_vbus_action_request *)&lmuReq;
    rsp = ( struct _lmu_app_peg_vbus_action_response *)&lmuACTRsp;
    memset ( (char *)&lmuReq, 0, sizeof( LMU_APP_VBUS_SDK_REQ )); 
    memset ( (char *)&lmuACTRsp, 0, sizeof( LMU_APP_VBUS_SDK_ACT_RSP )); 
    msg_len = sizeof( LMU_APP_VBUS_SDK_ACT_RSP ) - sizeof(int32_t);

    if ( lmu_qid == -1 ) 
    {
         printf("%s: lmu_qid not accessible!! done\n", progname);
         return -1;
    }
    if ( client_qid == -1 )
    {
         printf("%s: client_qid not accessible ...done\n", progname);
         return -2;
    }

    if ((mode & OPTION_MENU_MODE) == OPTION_MENU_MODE)
    {
        /* query for action and modifier parameters */
        fflush ( stdout );
        fflush ( stdin );
        memset( inputData, 0, sizeof(inputData));
        printf("----------------- PEG ACTION 99 TEST ---------------------\n");
        /* printf(" Enter action command : "); */
        /* fgets( inputData, sizeof(inputData), stdin ); */
        /* if ( strlen (inputData ) > 1 ) */
        /*    action = strtol( inputData, &end_ptr, 10 ); */
        action = VBUS_API_ACT_VBUS_ACTION;	/* ACT_VBUS_ACTION */
        /* fflush ( stdin ); */
        /* fflush ( stdout ); */
        /* if (action != LMU_ACT_VBUS_ACTION) */
        /*    return 1; */

        printf(" Action command %d\n", action);

        memset ( inputData, 0, sizeof(inputData));
        fflush ( stdout );
        fflush ( stdin );
        memset( inputData, 0, sizeof(inputData ));
        printf(" Enter VBus action modifier: "); 
        fgets( inputData, sizeof(inputData), stdin );
        if ( strlen (inputData ) > 1 )
        {
            modifier = strtol( inputData, &end_ptr, 10 );
        }
        printf(" Action modifier %d\n", modifier);

        memset ( inputData, 0, sizeof(inputData));
        fflush ( stdout );
        fflush ( stdin );
        memset( inputData, 0, sizeof(inputData ));
        printf(" For VBus actions that generate data enter 'y' to send data to HA.\n"); 
        printf("or enter <cr> to send data to the default action location : "); 
        fgets( inputData, sizeof(inputData), stdin );
        if ( strlen (inputData ) > 1 )
        {
            if (0 == strncmp(inputData, "y", 1))
                send_to_HA = 1;
            else
                send_to_HA = 0;
        }
        else
            send_to_HA = 0;

        if (send_to_HA)
            printf(" Send response to HA\n");
        else
            printf(" Send response to default action location\n");
    }
    else if ((mode & OPTION_CLI_MODE) == OPTION_CLI_MODE)
    {
        char *p = NULL;

        if (!buf) return 1;

        p = strtok(buf, ",");
        if (!p) return 1;
        action = strtoul( p, &end_ptr, 10 );

        p = strtok(NULL, ",");
        if (!p) return 1;
        modifier = strtoul( p, &end_ptr, 10 ); 

        if ((action != VBUS_API_ACT_VBUS_ACTION) && (action != VBUS_API_ACT_VBUS_GET_DATA_GROUP))
            return 1;

        if (do_debug)
        {
            printf("%s: action = %d\n", __func__, action);
            printf("%s: modifier = %d\n", __func__, modifier);
        }
        if (mode & OPTION_SEND_TO_HA)
        {
            send_to_HA = 1;
            if (do_debug)
                printf(" Send response to HA\n");
        }
        else
        {
            send_to_HA = 0;
            if (do_debug)
                printf(" Send response to default action location\n");
        }

    }
    else
    {
        return 1;
    }
  1. Fill in the VBUS action IPC request structure, which will be passed to the LMU app.
/* set up disconnect request */
    req->header.msg_type = LMU_APP_PEG_VBUS_MSGTYPE_LMU;
    req->header.cmd = LMU_APP_VBUS_CMD_ACTION; 
    req->header.transaction = seqid++;
    req->client_qid = client_qid;
    req->action = action; ------- VBus Action 99
    req->modifier = modifier; ------ VBus Action modifier (1-31)
    req->mtype_src = ClientNum;
    if (send_to_HA)
        req->flags |= VBUS_APP_VBUS_SDK_SEND_TO_HA; ---- set flags to indicate to send the response data to this HA client program

    req_len = sizeof(struct _lmu_app_peg_vbus_action_request) - sizeof(int32_t);
    if (do_debug)
        printf("%s: %d,%d, reqlen = %d, ClientNum=%d, transaction=%d\n", progname, req->action,req->modifier,req_len,req->mtype_src,req->header.transaction);
  1. Send the IPC message to the LMU app via the Linux IPC function msgsnd().
/* generate peg action request */
    rc = msgsnd( lmu_qid, (void *)req, req_len, IPC_NOWAIT); 
    if (do_debug)
        printf("%s: msgsnd results = %d \n", progname, rc );
  1. Wait for the response via the Linux IPC function msgrcv().
/* now wait for the response */
    if ( rc == 0 )
    {
        struct msqid_ds ids;
        int32_t qnum = 1;
        int32_t tcount = 0;
        int32_t all_pkts_received = 0;

        rsp_len = 0;

        while ( (rsp_len <= 0) || (qnum > 0) || (all_pkts_received == 0) )
        {
            rsp_len = msgrcv( client_qid, (void *)&lmuACTRsp, msg_len,
                      ClientNum, IPC_NOWAIT );
            /* printf("%s: rsp_len %d!\n", progname, rsp_len); */
            if ( rsp_len > 0 )
            {
                if (do_debug)
                    printf("%s: status %d received!\n", progname, rsp->status);
                if (rsp->buflen > 0)
                {
                    if (do_debug)
                        printf("%s: Msg flags = 0x%08x, transaction = %d, total_size = %d, msgid = %lu\n", progname, rsp->flags, rsp->header.transaction, rsp->total_size, rsp->msgid);
                    DEBUG_HexDump((uint8_t *)rsp->acttext, rsp->buflen);
  1. If the response message indicates that the response data is larger than a response message payload, wait and retrieve the incoming Linux IPC messages via a msgrcv() loop.
memcpy(VBus_APIbuf+(rsp->header.transaction * LMU_APP_VBUS_MAX_MESSAGE_LEN), rsp->acttext, rsp->buflen);
                    tcount += rsp->buflen;
                    if (do_debug)
                        printf("%s: total = %d, buflen = %d, tcount = %d\n", progname, rsp->total_size, rsp->buflen, tcount);
                }
                else
                {
                    printf("%s: error buflen = %d\n", progname, rsp->buflen);
                    rc = 1;
                    break;
                }

                if (tcount == rsp->total_size)
                {
                    rc = 0;
                    if (do_debug)
                        printf("%s: finished total = %d, buflen = %d, tcount = %d\n", progname, rsp->total_size, rsp->buflen, tcount);
                    all_pkts_received = 1;
                    break;   /* got all the pkts for this response, so exit */
                }
            }
  1. If the response message indicates that the response data is larger than a response message payload, wait and retrieve the incoming Linux IPC messages via a msgrcv() loop.
usleep( 50 * 1000 );
            msgctl(client_qid, IPC_STAT, &ids);
            qnum = ids.msg_qnum;
            if (do_debug)
                printf("Number of Queued Messages = %d\n",(int)ids.msg_qnum);
            if (retry_count++ > 50)
                break;
        }/* end wait for receive */
    }
    else
    {
        printf("%s: operation error %s\n", progname, strerror(errno));
    }/* end if msgsnd okay */

    return rc;
}

int32_t set_client_number(void)
{
    char inputData[16];
    char *end_ptr = NULL;
    int32_t client_no = 1;
    int32_t rsp_len = 0;

    fflush ( stdout );
    fflush ( stdin );
    printf(" Enter client number ( 2-30 ): ");
    fgets( inputData, sizeof(inputData), stdin );
    if ( strlen (inputData ) > 1 )
    {
        client_no = strtol( inputData, &end_ptr, 10 );
    }
    fflush ( stdin );
    fflush ( stdout );

    if ((client_no < 1) || (client_no > 30))
        client_no = LMU_APP_PEG_VBUS_MSGTYPE_CLIENT;

    while (rsp_len == 0)
    {
        rsp_len = msgrcv( client_qid, (void *)&lmuRsp, sizeof(lmuRsp),
                          ClientNum, IPC_NOWAIT );
        printf("%s: rsp_len = %d\n", progname, rsp_len);
    }

    printf("%s: ClientNum = %d\n", progname, client_no);
    return client_no;
}

int32_t Client_Init(void)
{
    int32_t rsp_len = 0;
    key_t   keyid = -1;
    struct stat statbfr;
    FILE *fd;
    int32_t rc = 0;
  1. Get access to the LMU app VBUS API request IPC message queue (/etc/calamp/lmu/vbus_sdk_queue).
/* get access to lmu app sdk command queue */
    keyid = ftok ( LMU_APP_VBUS_SDK_FILE, 1 );
    if ( keyid != -1 )
    {
        lmu_qid = msgget ( keyid, 0666 );
        if ( lmu_qid != -1 )
        {
            if (do_debug)
                printf("lmu app vbus api queue access ok \n ");
        }
        else
        {
            rc = errno;
            printf("msgget lmu vbus api queue: errno %s \n", strerror(errno));
            return rc;
        }
    }
    else
    {
        rc = errno;
        printf("ftok vbus api queue errno %s \n", strerror(errno));
        return rc;
    }
  1. Create a client VBUS API response IPC message queue (/etc/calamp/lmu/vbus_sdk_client_queue).
/* create client queue */
    rc = stat( LMU_APP_VBUS_CLIENT_FILE, &statbfr );
    if ( rc == -1 )
    {
        /* create the file */
        fd = fopen ( LMU_APP_VBUS_CLIENT_FILE, "w+" );
        if ( fd != NULL )
        {
           /* file created  */
           fclose ( fd );
        }
        else
        {     
           /* why can't we do this? huh? */
            rc = errno;
            printf("%s: fopen err %s \n" , progname, strerror(errno));
            return rc;
        }
    }

    keyid = ftok ( LMU_APP_VBUS_CLIENT_FILE, 1 );
    if ( keyid != -1 )
    {
        client_qid = msgget ( keyid, (IPC_CREAT | 0666 ) );
        if ( client_qid != -1 )
        {
            if (do_debug)
                printf("client queue created ok, qid = %d \n", client_qid);
        }
        else
        {
            rc = errno;
            printf("client_qid msgget : errno %s \n", strerror(errno));
            return rc;
        }
    }
    else
    {
        rc = errno;
        printf("ftok client queue errno %s \n", strerror(errno));
        return rc;
    }
    while (rsp_len == 0)
    {
        rsp_len = msgrcv( client_qid, (void *)&lmuRsp, sizeof(lmuRsp),
                          ClientNum, IPC_NOWAIT );
        if (do_debug)
            printf("%s: rsp_len = %d\n", progname, rsp_len);
    }

    return rc;
}

int32_t CLI_mode(char *buf)
{
    int32_t i, rc = 0;
    char *p = NULL;
    OPTIONS opts;

    if (buf == NULL)
        return 1;

#if defined(VBUS_API_DEBUG)
    printf("%s: buf = %s\n", __func__, buf);
#endif

    memset((void *)&opts, 0, sizeof(opts));
    p = strtok(buf, " \r");

    if (!p)
        return 1;

    for (i = 0; i < MAX_OPTIONS; i++)
    {
        opts.options[i] = p;
        if (p != NULL) {
            opts.options_count++;
            p = strtok(NULL, " \r");
        }
    }

#if defined(VBUS_API_DEBUG)
    printf("%s: options_count = %d\n", __func__, opts.options_count);
#endif
    for (i=0; i<opts.options_count; i++)
    {
        ConvertCmdString((char *)opts.options[i]); 
#if defined(VBUS_API_DEBUG)
        printf("%s: opts.options[%d] = %s\n", __func__, i, opts.options[i]);
#endif
    }


    if (0 == strncmp((char *)opts.options[0], "AT$APP", strlen("AT$APP")))
    {
        if (0 == strncmp((char *)opts.options[1], "PEG", strlen("PEG")))
        {
            /* eg. at4app peg action 99 8 */
            if ((opts.options_count == 5) && (0 == strncmp((char *)opts.options[2], "ACTION", strlen("ACTION"))))
            {
                char buf[128] = {0};
                unsigned int actflags = 0;
                if (option_flag & OPTION_SEND_TO_HA)
                    actflags |= OPTION_SEND_TO_HA;
                sprintf(buf, "%s,%s,%s", opts.options[3], ",", opts.options[4]);
            	rc = client_peg_action((OPTION_CLI_MODE | actflags), buf);
            }
        }
    }

    return rc;
}

void MENU_mode(void)
{
    int32_t active = 1;
    int32_t command = 0;
    char    inputCmd[80]; 
    char    *end_ptr = NULL;
    int32_t rc = 0;

    /* setup */
    memset( inputCmd, 0, sizeof(inputCmd));

    /* check for command from console input */
    while ( active )
    {
          fflush ( stdout );
          fflush ( stdin );
          memset ( inputCmd, 0, sizeof(inputCmd));

          printf ("--------------- LMU APP SDK TEST -------------------\n");
          printf ("  1 ...... set debug on \n");
          printf ("  2 ...... set debug off \n");
          printf ("  3 ...... set client number x \n");
          printf ("  4 ...... VBus peg action x \n");
          printf ("  5 ...... quit \n");

          printf ("\n");
          printf (" Enter command: ");
          fgets (  inputCmd, sizeof(inputCmd) , stdin );
          if ( strlen( inputCmd ) > 1 )
          {
              command = strtol( inputCmd, &end_ptr, 10 ); 
              printf("command entered = %d\n", command );
              switch ( command )
              {
                  case 1: /* set debug on */
                       do_debug = true;
                       break;
                  case 2: /* set debug on */
                       do_debug = false;
                       break;
                  case 3: /* set client number */
                       ClientNum = set_client_number();
                       break;
                  case 4: /* peg action 'n' */
                       rc = client_peg_action(OPTION_MENU_MODE, NULL);
                       break;
                  case 5: /* quit */
                       active = 0;
                       rc = 0;
                       if (do_debug)
                           printf("QUIT command entered .. bye!\n");
                       break;
                  default:
                       printf("UNKNOWN command %d entered...retry \n", command);
                       break;
              }
              if (rc)
                  printf("%s: operation failed\n", progname);
          }/* end if command entered */
    }/* end while active */
}

int32_t Client_Close(void)
{
    struct msqid_ds qidInfo;
    int32_t rc = 0;
  1. As part of the hosted application termination handling, the hosted app should destroy the client VBUS API response IPC message queue that was created in step 2.
/* free queue */
    if (do_debug)
        printf("appSdkTest: destroy client queue ...\n");
    rc = msgctl ( client_qid, IPC_RMID, &qidInfo );

    if (rc == 0)          
        client_qid = -1;
    else
        printf("%s: msgctl error %s \n", progname, strerror(errno));

    return rc;
}

/*----------------------------------------------------------
 main program runs with linux console 
------------------------------------------------------------*/
int32_t main ( int32_t argc, char *argv[] )
{
    int32_t rc = 0;
    int32_t op;
    char *buf = NULL;

    (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]);

    signal(SIGINT, QuitHandler);
    signal(SIGTERM, QuitHandler);
    signal(SIGALRM, QuitHandler);

    while ((op = getopt (argc, argv, "o:iadvh")) != -1)
    {
        switch (op)
        {
        case 'o':
            option_flag |= OPTION_CLI_MODE;
            buf = optarg;
            break;
        case 'i':
            option_flag |= OPTION_MENU_MODE;
            break;
        case 'd':
            option_flag |= OPTION_DEBUG_ON;
            break;
        case 'v':
            option_flag |= OPTION_VERSION;
            break;
        case 'a':
            option_flag |= OPTION_SEND_TO_HA;
            break;
        case 'h':
        case '?':
            Usage(progname);
            /* NOTREACHED */
            break;
        default:
            Usage(progname);
            /* NOTREACHED */
            break;
        }
    }

    if (option_flag & OPTION_VERSION)
    {
        prt_version();
        exit(0);
    }

    if (option_flag & OPTION_DEBUG_ON)
        do_debug = true;

    if (option_flag & OPTION_CLI_MODE)
    {
        rc = Client_Init();
        if (rc > 0)
            return rc;
        rc = CLI_mode(buf);
        Client_Close();
    }
    else if ((argc == 1) || (option_flag & OPTION_MENU_MODE))
    {
        rc = Client_Init();
        if (rc > 0)
            return rc;
        MENU_mode();
        Client_Close();
    }

    if (rc)
        printf("%s: operation failed.\n", progname);

    if (do_debug)
        printf("%s: done ... bye bye! \n", progname);

    return rc;
}