EDIT: reformulating this question as I've managed to get the basics to work, but still experience problems.
I'm trying to emulate a USB device (bar code scanner) for testing purposes using usb-vhci, and I'm having some problems.
To give some context: the device is a CDC abstract modem, and the client - a java program - communicates with it over the serial line using AT commands.
Basically, I've got my device up and running, it registers itself correctly and I'm able to receive commands from and respond to the client.
The main problem appears to be that as soon as the device starts up or receives a bulk transfer from the host it triggers an ongoing stream of bulk and interrupt IN transfers (massive amounts, my usbmon log grows to 100 MB in a few seconds).
First at startup, where it keeps spewing out (mainly) bulk IN transfers until I receive the SET_CONTROL_LINE_STATE request and then they stop. Then, when the client sends the commands (AT command via the serial device) it starts again.
I'm guessing this is because I'm not responding correctly to some transfer, but I can't figure out what it is.
I've been comparing the usbmon output of my device with that of the real device, but so far I haven't been able to detect any difference that would explain why my emulated device behaves like this and the real one doesn't.
I basically started out with the example code found in libusb_vhci/examples/virtual_device2.c and adapted it to mimic the actual device. First off the device descriptors:
const uint8_t dev_desc[] = {
/* Device Descriptor */
0x12, //bLength 18
0x01, //bDescriptorType 1
0x00, 0x02, //bcdUSB 2.00
0x02, //bDeviceClass 2 Communications
0x00, //bDeviceSubClass 0
0x00, //bDeviceProtocol 0
0x40, //bMaxPacketSize0 64
0x5a, 0x06, //idVendor 065a
0x02, 0xa0, //idProduct a002
0x00, 0x01, //bcdDevice 1.00
0x00, //iManufacturer 0
0x01, //iProduct 1
0x00, //iSerial 0
0x01 //bNumConfigurations 1
};
const uint8_t conf_desc[] = {
/* Configuration Descriptor */
0x09, //bLength 9
0x02, //bDescriptorType 2
0x43, 0x00, //wTotalLength 67 ??
0x02, //bNumInterfaces 2
0x01, //bConfigurationValue 1
0x00, //iConfiguration 0
0x80, //bmAttributes (Bus Powered) 0x80
250, //MaxPower 500mA
/* Interface Descriptor 0 */
0x09, //bLength 9
0x04, //bDescriptorType 4
0x00, //bInterfaceNumber 0
0x00, //bAlternateSetting 0
0x01, //bNumEndpoints 1
0x02, //bInterfaceClass 2 Communications
0x02, //bInterfaceSubClass 2 Abstract (modem)
0x00, //bInterfaceProtocol 0 None
0x00, //iInterface 0
/* CDC Header */
0x05, //bLength 7
0x24, //bDescriptorType 5
0x00, //bEndpointAddress 0x01 EP 1 OUT
0x10, //bcdCDC 1.10
0x01, //"
/* CDC Call Management */
0x05, //bLength 3
0x24, //CDC_CS_INTERFACE
0x01, //CDC_CALL_MANAGEMENT
0x01, //bmCapabilities 0x01
0x00, //bDataInterface 0
/* CDC ACM */
0x04, //bLength 2
0x24, //CDC_CS_INTERFACE
0x02, //CDC_ABSTRACT_CONTROL_MANAGEMENT
0x02, //bmCapabilities 0x02
/* CDC Union */
0x05, //bLength 3
0x24, //CDC_CS_INTERFACE
0x06, //CDC_UNION
0x00, //bMasterInterface 0
0x01, //bSlaveInterface 1
/* Endpoint Descriptor */
0x07, //bLength 7
0x05, //bDescriptorType 5
0x83, //bEndpointAddress 0x83 EP 3 IN
0x03, //bmAttributes 3
0x40, 0x00, //wMaxPacketSize 0x0040 1x 64 bytes
0x0a, //bInterval 10
/* Interface Descriptor 1 */
0x09, //bLength 9
0x04, //bDescriptorType 4
0x01, //bInterfaceNumber 1
0x00, //bAlternateSetting 0
0x02, //bNumEndpoints 2
0x0a, //bInterfaceClass 10 CDC Data
0x00, //bInterfaceSubClass 0
0x00, //bInterfaceProtocol 0
0x00, //iInterface 0
/* Endpoint Descriptor */
0x07, //bLength 7
0x05, //bDescriptorType 5
0x01, //bEndpointAddress 0x01 EP 1 OUT
0x02, //bmAttributes 2
0x40, 0x00, //wMaxPacketSize 0x0040 1x 64 bytes
0x00, //bInterval 0
/* Endpoint Descriptor */
0x07, //bLength 7
0x05, //bDescriptorType 5
0x82, //bEndpointAddress 0x82 EP 2 IN
0x02, //bmAttributes 2
0x40,0x00, //wMaxPacketSize 0x0040 1x 64 bytes
0x00 //bInterval 0
};
const uint8_t str0_desc[] = {
0x04, //bLength 4
0x03, //bDescriptorType 3
0x09, 0x04 //bLanguage 0409 US
};
const uint8_t *str1_desc =
(uint8_t *)"\x36\x03O\0p\0t\0i\0c\0o\0n\0 \0U\0S\0B\00\0B\0a\0r\0c\0o\0d\0e\0 \0R\0e\0a\0d\0e\0r";
The main function is the same as in the example, but the process_urb() function is what has mainly been changed. The control section is largely intact, but I've added handling for some additional setup packets:
uint8_t rt = urb->bmRequestType;
uint8_t r = urb->bRequest;
if(rt == 0x00 && r == URB_RQ_SET_CONFIGURATION)
{
devlog("URB_RQ_SET_CONFIGURATION\n");
urb->status = USB_VHCI_STATUS_SUCCESS;
}
else if(rt == 0x00 && r == URB_RQ_SET_INTERFACE)
{
devlog("URB_RQ_SET_INTERFACE\n");
urb->status = USB_VHCI_STATUS_SUCCESS;
}
else if (rt == 0x21 && r == 0x20)
{
devlog("URB_CDC_SET_LINE_CODING\n");
urb->status = USB_VHCI_STATUS_SUCCESS;
}
else if (rt == 0x21 && r == 0x22)
{
devlog("URB_CDC_SET_CONTROL_LINE_STATE\n");
urb->status = USB_VHCI_STATUS_SUCCESS;
}
else if(rt == 0x80 && r == URB_RQ_GET_DESCRIPTOR)
{
int l = urb->wLength;
uint8_t *buffer = urb->buffer;
devlog("GET_DESCRIPTOR ");
switch(urb->wValue >> 8)
{
case 0:
puts("WTF_DESCRIPTOR");
urb->status = USB_VHCI_STATUS_SUCCESS;
break;
case 1:
puts("DEV_DESC");
if(dev_desc[0] < l) l = dev_desc[0];
memcpy(buffer, dev_desc, l);
urb->buffer_actual = l;
urb->status = USB_VHCI_STATUS_SUCCESS;
break;
case 2:
puts("CONF_DESC");
if(conf_desc[2] < l) l = conf_desc[2];
memcpy(buffer, conf_desc, l);
urb->buffer_actual = l;
urb->status = USB_VHCI_STATUS_SUCCESS;
break;
case 3:
devlog(" Reading string %d\n", urb->wValue & 0xff);
switch(urb->wValue & 0xff)
{
case 0:
if(str0_desc[0] < l) l = str0_desc[0];
memcpy(buffer, str0_desc, l);
urb->buffer_actual = l;
urb->status = USB_VHCI_STATUS_SUCCESS;
break;
case 1:
if(str1_desc[0] < l) l = str1_desc[0];
memcpy(buffer, str1_desc, l);
urb->buffer_actual = l;
urb->status = USB_VHCI_STATUS_SUCCESS;
break;
default:
devlog(" Trying to read unknown string: %d\n",urb->wValue & 0xff);
urb->status = USB_VHCI_STATUS_STALL;
break;
}
break;
default:
devlog(" UNKNOWN: wValue=%d (%d)\n",urb->wValue, urb->wValue >> 8);
urb->status = USB_VHCI_STATUS_STALL;
break;
}
}
else
{
devlog("OTHER bmRequestType %x bRequest %x\n", rt, r);
urb->status = USB_VHCI_STATUS_STALL;
}
The main issue is in handling the non-control transfers though. Here's my current implementation:
/* handle non-control sequences */
if(!usb_vhci_is_control(urb->type)) {
/* if we have a BULK OUT transfer */
if (usb_vhci_is_bulk(urb->type) && usb_vhci_is_out(urb->epadr)) {
/* we have a bulk out transfer, i.e. a command from client */
int cmd = get_at_command(urb->buffer, urb->buffer_actual);
if (cmd == COMMAND_Z1) {
/* we have request for version, need to wait for the BULK IN transfer */
last_command = cmd;
}
urb->status = USB_VHCI_STATUS_SUCCESS;
return;
}
/* if we have a BULK IN transfer */
if (usb_vhci_is_bulk(urb->type) && usb_vhci_is_in(urb->epadr)) {
/* we have a BULK IN transfer, use it to respond to any buffered commands */
if (last_command) {
/* send version */
memcpy(urb->buffer, VERSION_STR, strlen(VERSION_STR));
urb->buffer_actual = strlen(VERSION_STR);
last_command = 0;
urb->status = USB_VHCI_STATUS_SUCCESS;
return;
}
}
urb->status = USB_VHCI_STATUS_SUCCESS;
return;
}
Here's a snippet of the usbmon log I get as my device is starting up:
ffff880510727900 266671312 S Bi:5:002:2 -115 128 <
ffff880510727f00 266671315 C Bi:5:002:2 0 0
ffff880510727f00 266671316 S Bi:5:002:2 -115 128 <
ffff880510727cc0 266671319 C Ii:5:002:3 0:8 0
ffff880510727cc0 266671321 S Ii:5:002:3 -115:8 64 <
ffff880514d80900 266671323 S Co:5:002:0 s 21 22 0000 0000 0000 0
ffff880510727780 266671324 C Bi:5:002:2 0 0
ffff880510727780 266671325 S Bi:5:002:2 -115 128 <
ffff8805101096c0 266671329 C Bi:5:002:2 0 0
ffff8805101096c0 266671333 S Bi:5:002:2 -115 128 <
ffff8805107273c0 266671339 C Bi:5:002:2 0 0
ffff8805107273c0 266671344 S Bi:5:002:2 -115 128 <
ffff880510109b40 266671348 C Bi:5:002:2 0 0
ffff880510109b40 266671350 S Bi:5:002:2 -115 128 <
ffff880510109000 266671354 C Bi:5:002:2 0 0
ffff880510109000 266671357 S Bi:5:002:2 -115 128 <
ffff880510727d80 266671360 C Bi:5:002:2 0 0
ffff880510727d80 266671361 S Bi:5:002:2 -115 128 <
ffff880510109a80 266671363 C Bi:5:002:2 0 0
ffff880510109c00 266671370 C Bi:5:002:2 0 0
...
So, this is basically where I'm stuck. I've got a nearly functioning device, but the massive amounts of transfers basically chokes my system rendering it useless. Any help or info would be greatly appreciated!
It seems I have been able to resolve most of my issues now, and the problem was indeed me not responding correctly to events.
After doing some more detailed analysis of the usbmon output for the real device I noticed that it was responding to the superfluous interrupt transfers with -ENOENT, whereas I was responding with 0 (i.e. success). Some more digging into the usb-vhci code revealed that this error code corresponded to USB_VHCI_STATUS_CANCELED, and once I started responding with this I got the same behavior in my device as with the real device. Essentially I added this to my non-control section of process_urb:
/* if we have a INTERRUPT transfer */
if (usb_vhci_is_int(urb->type)) {
urb->status = USB_VHCI_STATUS_CANCELED;
return;
}
I'm not entirely out of the woods yet though. I noticed that the same thing seemed to apply for bulk IN transfers; I'm getting a ton of them during startup (which stop as soon as setup is complete) which - again - does not appear to be the case for the real device, and the real device - again - responds to these (superfluous) transfers with -ENOENT. I tried doing this, and it appears to work fine. The additional transfers do stop and it behaves just as the real device, but unfortunately it also results in my device not being able to send data back to the client. I modified my bulk IN handling code as follows:
/* if we have a BULK IN transfer */
if (usb_vhci_is_bulk(urb->type) && usb_vhci_is_in(urb->epadr)) {
if (last_command) {
// send version
memcpy(urb->buffer, VERSION_STR, strlen(VERSION_STR));
urb->buffer_actual = strlen(VERSION_STR);
last_command = 0;
urb->status = USB_VHCI_STATUS_SUCCESS;
} else {
urb->status = USB_VHCI_STATUS_CANCELED;
}
return;
}
I figure this should work, i.e. if I received a command in the previous bulk OUT transfer I should be able to use the IN transfer to respond (as I've been doing all along) and if there was no command I just respond with -ENOENT. For some reason this does not work though and I'm not sure why.
Another thing I noticed regarding the trace from the real device: although it does respond to these bulk transfers with -ENOENT, they send the response more than 10 seconds (!) after they received the request! Not sure what that's all about, but if anyone has an idea I'd be most grateful.
Related
I'm coming to you because I'm desperate about my problem. I've already made a previous post (xQueue ESP32 can't send two command) but I've tried but I don't have the expected result.
Result :
len : -1184515756
data :
I also tried these two tutorials but still nothing works...
https://freertos.org/a00018.html
https://controllerstech.com/freertos-tutorial-5-using-queue/
After several tries without really understanding how I got something almost working except I end up with a Guru Err Load Prohibited.
Code :
void app_main() {
/* Create the queue used to send complete struct message structures. */
uart_queue = xQueueCreate(20, sizeof(struct message));
xMutexUart = xSemaphoreCreateMutex();
....
struct message *ptx_msg =pvPortMalloc(sizeof (struct message));
//Set Channel
sprintf(ptx_msg->cmd,"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",0x01, 0x02, 0x10, 0x21, 0x02, 0x10, 0x02, 0x14, 0x2D, 0x02, 0x10, 0x02, 0x10, 0x02, 0x18, 0x02, 0x10, 0x03);
ptx_msg->len = strlen(ptx_msg->cmd);
printf("len : %d\n", ptx_msg->len);
for(int i=0;i<ptx_msg->len;i++){
printf("%02x ",ptx_msg->cmd[i]);
}
xSemaphoreTake(xMutexUart, portMAX_DELAY);
xQueueSend(uart_queue, (void*)&ptx_msg, portMAX_DELAY);
xSemaphoreGive(xMutexUart);
//repeat X time
....
}
void vTaskSend( void * pvParameters )
{
struct message *rx_msg;
int err;
while(1){
xSemaphoreTake(xMutexUart, portMAX_DELAY);
if (xQueueReceive(uart_queue, &rx_msg, portMAX_DELAY) == pdPASS){
printf("len : %d\n", rx_msg->len);
for(int i=0;i<rx_msg->len;i++){
printf("%02x ",rx_msg->cmd[i]);
}
printf("\n");
err = uart_write_bytes(uart_num, (const char *)rx_msg->cmd, rx_msg->len); // Write data to UART.
ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 5000)); // wait timeout is 5000 RTOS ticks (TickType_t)
if(err != rx_msg->len){
printf("Err, not all bytes send : %d/%d\n",err,rx_msg->len);
}
vPortFree(rx_msg); // free the structure memory*/
vTaskDelay(100 / portTICK_RATE_MS);
//taskYIELD();
}
xSemaphoreGive(xMutexUart);
vTaskDelay(100 / portTICK_RATE_MS);
}
}
Result :
len : 19
data : 01 02 10 21 02 10 02 14 2d 02 10 02 10 02 18 02 10 03 0a
I would like to correct my problem please!
EDIT :
Here is the code that I have and the result is :
len : 54516257561
data :
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <esp_system.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <driver/uart.h>
#define COMMAND_DEVICE_LIST 0x15
#define COMMAND_STATUS 0x00
#define COMMAND_DESCRIPTION 0x43
#define COMMAND_UNRECOGNIZED 0x11
#define COMMAND_ADDGROUP 0x60
#define COMMAND_BIND 0x30
#define SIZE_TYPE_DEVICE 2
#define SIZE_CLUSTER 2
#define SIZE_SHORT_ADD 2
#define SIZE_IEEE_ADD 8
#define START_DATA 7
#define SIZE_MIN_NB_DEVICE 2
#define CHAR_END_FRAME 0x03
#define CHAR_CONFIG_FRAME 0x02
#define CHAR_VAL_MAX_CONFIG_FRAME 0x10
#define STACK_SIZE 2048
//storage info
struct device{
char type_id[SIZE_TYPE_DEVICE];
char manufacturer[32]; //attribute 4
char name[32]; // attribute 5
char short_add[SIZE_SHORT_ADD];
char ieee_add[SIZE_IEEE_ADD];
char cluster_input[32][SIZE_CLUSTER];
char cluster_output[32][SIZE_CLUSTER];
};
struct get_device_list{
int len;
struct device device[];
}dev_list;
//list device type
char list_device_type[2][SIZE_TYPE_DEVICE]={{0x01,0x0C},{0x08,0x20}};
// Setup UART buffered IO with event queue
const int uart_buffer_size = 2048;
// Queue used to send and receive complete struct message structures.
QueueHandle_t uart_queue = NULL;
const int uart_num = UART_NUM_2;
struct message{
char cmd[128];
int len;
};
SemaphoreHandle_t xMutexUart;
/************************************************CONFIG***********************************************/
/**
* #brief Configuraiton of UART and set pin that uart use.
*
* #return [int] 0 if is successed
*/
int uart_setup()
{
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
// Configure UART parameters
ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, 18, 19, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// Install UART driver using an event queue here
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, uart_buffer_size, \
uart_buffer_size, 20, &uart_queue, 0));
return 0;
}
/************************************************UTILS***********************************************/
int cmdlen(char* cmd){
int i = 0;
while(cmd[i]!='\n'){
i++;
}
return i+1;
}
int device_shortadd_compare(char* search_add){
int res=-1,val=0;
for(int i=0;i < dev_list.len;i++){
val = 0;
for(int l=0;l<SIZE_TYPE_DEVICE;l++){
if(search_add[l]==dev_list.device[i].short_add[l]){
val++;
}
}
if(val == 2){
res=i;
}
}
return res;
}
int parse_translate(char* data,int len,char res[][64]){
int i=0,j=0,end=0,index=0;
while(len - j != 0){
if (data[j]==CHAR_CONFIG_FRAME)
{
j++; //skip 02
data[j]^=0x10; //10xor10=00
res[end][i-index]= data[j];
printf("%02x ",res[end][i-index]);
i++;
}else if (data[j]==CHAR_END_FRAME)
{
res[end][i-index]=data[j];
res[end][i+1-index]='\n';
res[end][i+2-index]='\0';
printf("%02x\n",res[end][i-index]);
i=i+2;
index=i;
end++;
}else{
res[end][i-index]= data[j];
printf("%02x ",res[end][i-index]);
i++;
}
j++;
}
return end;
}
/************************************************COMMUNICATION***********************************************/
/**
* #brief [Thread]Send commande to Zigbee module by UART
*
*/
void vTaskSend( void * pvParameters )
{
while(1){
int err;
struct message rx_msg;
if (xQueueReceive(uart_queue, &rx_msg, portMAX_DELAY) == pdPASS){
printf("len : %d\n", rx_msg.len);
for(int i=0;i< rx_msg.len;i++){
printf("%02x ", rx_msg.cmd[i]);
}
printf("\n");
xSemaphoreTake(xMutexUart, portMAX_DELAY);
err = uart_write_bytes(uart_num, (const char *) rx_msg.cmd, rx_msg.len); // Write data to UART.
ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 5000)); // wait timeout is 5000 RTOS ticks (TickType_t)
xSemaphoreGive(xMutexUart);
if(err != rx_msg.len){
printf("Err, not all bytes send : %d/%d\n",err, rx_msg.len);
}
//vPortFree(rx_msg); // free the structure memory*/
vTaskDelay(100 / portTICK_RATE_MS);
//taskYIELD();
}
xSemaphoreGive(xMutexUart);
vTaskDelay(100 / portTICK_RATE_MS);
}
}
/**
* #brief [Thread]Read response from UART
*
*/
void vTaskRead( void * pvParameters )
{
char data[512];
int length = 0;
while(1){
xSemaphoreTake(xMutexUart, portMAX_DELAY);
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
length = uart_read_bytes(uart_num, (uint8_t*)data, length, 100);
xSemaphoreGive(xMutexUart);
if(length>0){
printf("[R] message size : %d\n ",length);
char res[8][64];
int nb_cmd=parse_translate(data,length,res);
for(int i=0;i<nb_cmd;i++){
switch(res[i][2]){
case COMMAND_STATUS:
if(res[6]==COMMAND_STATUS){
printf("Command status success\n");
}
break;
case COMMAND_DEVICE_LIST:
dev_list.len=(cmdlen(res[i])-8)/13;
printf("Nb device find : %d\n",dev_list.len);
int start = START_DATA; //start of device info in frame
for(int j=0;j<dev_list.len;j++){
struct device dev;
printf("short: ");
for(int j=0;j<SIZE_SHORT_ADD;j++){
dev.short_add[j] = res[i][start+j];
printf("%02x ",dev.short_add[j]);
}
printf("\n");
printf("ieee: ");
for(int l=0;l<SIZE_IEEE_ADD;l++){
dev.ieee_add[l]=res[i][start+2+l];
printf("%02x ",dev.ieee_add[l]);
}
printf("\n");
dev_list.device[j]=dev;
vTaskDelay(100 / portTICK_RATE_MS);
start=start+SIZE_SHORT_ADD+SIZE_IEEE_ADD+2+1; //info of device 1 not used + id device 2 not used
}
break;
case COMMAND_DESCRIPTION:
printf(" ");
char add[SIZE_TYPE_DEVICE];
for(int j=0;j<SIZE_TYPE_DEVICE;j++){
add[j]=res[i][8+j];
}
int id = device_shortadd_compare(add);
if(id != -1){
printf("type: ");
for(int j=0;j<SIZE_TYPE_DEVICE;j++){
dev_list.device[id].type_id[j]=res[i][14+j];
printf("%02x ",dev_list.device[id].type_id[j]);
}
printf("\n");
if(dev_list.device[id].type_id[0]==0x00 && dev_list.device[id].type_id[1] ==0x00){
printf("Device %d not found, if it's a button press it\n",i);
}
}
break;
case COMMAND_ADDGROUP:
printf("Device add to group!\n");
break;
case COMMAND_BIND:
printf("Press the button, hurry up!!\n");
break;
}
}
}
vTaskDelay(100 / portTICK_RATE_MS);
//taskYIELD();
}
vTaskDelay(1000 / portTICK_RATE_MS);
ESP_ERROR_CHECK(uart_flush(uart_num));
}
/************************************************ZIGBEE***********************************************/
/**
* #brief Configuration of Zigbee module (channel,type) and start network
*
*/
void zigbee_config_with_pemit_join(){
struct message *ptx_msg =pvPortMalloc(sizeof (struct message));
//Set Channel
sprintf(ptx_msg->cmd,"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",0x01, 0x02, 0x10, 0x21, 0x02, 0x10, 0x02, 0x14, 0x2D, 0x02, 0x10, 0x02, 0x10, 0x02, 0x18, 0x02, 0x10, 0x03);
ptx_msg->len = strlen(ptx_msg->cmd);
printf("len : %d\n", ptx_msg->len);
for(int i=0;i<ptx_msg->len;i++){
printf("%02x ",ptx_msg->cmd[i]);
}
xQueueSend(uart_queue, (void*)ptx_msg, portMAX_DELAY);
printf("[S] Set channel to 11\n");
vTaskDelay(100 / portTICK_RATE_MS);
//Set Type
//ptx_msg = pvPortMalloc(sizeof (struct message));
sprintf(ptx_msg->cmd,"%c%c%c%c%c%c%c%c%c%c%c%c\n",0x01, 0x02, 0x10, 0x23, 0x02, 0x10, 0x02, 0x11, 0x22, 0x02, 0x10, 0x03);
ptx_msg->len =strlen(ptx_msg->cmd);
xQueueSend(uart_queue, (void*)ptx_msg, portMAX_DELAY);
printf("[S] Set as Coordinator\n");
vTaskDelay(100 / portTICK_RATE_MS);
//Start Network
//ptx_msg =pvPortMalloc(sizeof (struct message));
sprintf(ptx_msg->cmd,"%c%c%c%c%c%c%c%c%c%c\n",0x01, 0x02, 0x10, 0x24, 0x02, 0x10, 0x02, 0x10, 0x24, 0x03);
ptx_msg->len =strlen(ptx_msg->cmd);
xQueueSend(uart_queue, (void*)ptx_msg, portMAX_DELAY);
printf("[S] Start Network\n");
//Permit join
//ptx_msg =pvPortMalloc(sizeof (struct message));
sprintf(ptx_msg->cmd,"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",0x01, 0x02, 0x10, 0x49, 0x02, 0x10, 0x02, 0x14, 0xB0, 0xFF, 0xFC, 0xFF, 0x02, 0x10, 0x03);
ptx_msg->len =strlen(ptx_msg->cmd);
xQueueSend(uart_queue, (void*)ptx_msg, portMAX_DELAY);
printf("[S] Permit join: If you have button press it 2sec.\n");
vTaskDelay(6000 / portTICK_RATE_MS);
}
void vTaskZigbee( void * pvParameters )
{
if (uart_setup() == -1){
printf("Err during uart setup\n");
}
zigbee_config_with_pemit_join();
//taskYIELD();
while(1){
vTaskDelay(100000 / portTICK_RATE_MS);
}
}
/************************************************MAIN***********************************************/
void app_main() {
/* Create the queue used to send complete struct message structures. */
uart_queue = xQueueCreate(20, sizeof(struct message ));
xMutexUart = xSemaphoreCreateMutex();
BaseType_t xReturned;
TaskHandle_t xHandle = NULL;
/* Create the task, storing the handle. */
xReturned = xTaskCreate(
vTaskZigbee, /* Function that implements the task. */
"Zigbee", /* Text name for the task. */
STACK_SIZE, /* Stack size in words, not bytes. */
NULL, /* Parameter passed into the task. */
tskIDLE_PRIORITY,/* Priority at which the task is created. */
&xHandle); /* Used to pass out the created task's handle. */
xReturned = xTaskCreate( vTaskSend, "Send", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle);
xReturned = xTaskCreate( vTaskRead, "Read", STACK_SIZE*2, NULL, tskIDLE_PRIORITY, &xHandle);
}
I have a Variscite DART-MX8M evaluation board that I'm trying to use as a USB gadget, so I've written a user-mode gadget driver which talks to gadgetfs (mounted on /dev/gadget). It always returns a device descriptor to the host machine, but sometimes fails when responding to a requests for a string descriptor. The symptom is that when I write the string back to endpoint 0, I get EBUSY; it doesn't help to retry, as the subsequent attempt gets ESRCH.
It doesn't happen on the first request for the string descriptor, as you see lower down where the host gets the manufacturer and vendor (via udev rules, I think) but lsusb fails to retrieve them. But when it does fail, it's always at the same point in the execution.
I've done some print-tracing in the kernel (5.4.85, Variscite's "Dunfell" release) and found where the EBUSY originates:
/* we share one TRB for ep0/1 */
if (!list_empty(&dep->pending_list)) {
ret = -EBUSY;
goto out;
}
But I don't understand why we might hit this.
I made the simplest driver I could to reproduce this - it has just one configuration, with no endpoints (not counting ep0):
#include <linux/usb/ch9.h>
#include <linux/usb/gadgetfs.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define USB_DEV "/dev/gadget/dwc3-gadget"
enum string_id {
STRINGID_MANUFACTURER = 1,
STRINGID_PRODUCT,
STRINGID_SERIAL,
};
static const char *get_string_descriptor(enum string_id sid)
{
static const char mfr[] = { 10, USB_DT_STRING, 84, 0, 111, 0, 98, 0, 121, 0};
static const char prod[] = { 16, USB_DT_STRING, 119, 0, 105, 0, 100, 0, 103, 0, 101, 0, 116, 0};
static const char ser[] = { 8, USB_DT_STRING, 118, 0, 49, 0};
switch (sid) {
case STRINGID_MANUFACTURER:
return mfr;
case STRINGID_PRODUCT:
return prod;
case STRINGID_SERIAL:
return ser;
}
static const char fallback[] = { 20, USB_DT_STRING, 40, 0, 109, 0, 105, 0,
115, 0, 115, 0, 105, 0, 110, 0, 103, 0, 41, 0 };
return fallback;
}
static int write_string_descriptor(int fd, enum string_id sid) {
const char *buf = get_string_descriptor(sid);
return write(fd, buf, buf[0]);
}
static void handle_setup_request(int fd, struct usb_ctrlrequest* setup)
{
int status;
uint8_t buffer[512];
pthread_t thread;
printf("Setup request %d\n", setup->bRequest);
switch (setup->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
if (setup->bRequestType != USB_DIR_IN)
goto stall;
switch (setup->wValue >> 8) {
case USB_DT_STRING:
printf("Get string id #%d(max length %d)\n", setup->wValue & 0xff,
setup->wLength);
status = write_string_descriptor(fd, setup->wValue & 0xff);
// Error
if (status < 0) {
fprintf(stderr, "Failed to write string descriptor %d\n", setup->wValue & 0xff);
}
return;
default:
fprintf(stderr, "Cannot return descriptor %d\n", (setup->wValue >> 8));
}
break;
case USB_REQ_SET_CONFIGURATION:
printf("Ignoring configuration value %d\n", setup->wValue);
// Just ACK
status = read(fd, &status, 0);
return;
case USB_REQ_GET_INTERFACE:
printf("GET_INTERFACE\n");
buffer[0] = 0;
write(fd, buffer, 1);
return;
case USB_REQ_SET_INTERFACE:
printf("SET_INTERFACE\n");
// ACK
status = read(fd, &status, 0);
return;
}
stall:
fprintf(stderr, "Stalled\n");
// Error
if (setup->bRequestType & USB_DIR_IN)
read (fd, &status, 0);
else
write (fd, &status, 0);
}
static void handle_ep0(int fd)
{
while (1) {
struct usb_gadgetfs_event events[5];
int readsize = read(fd, &events, sizeof events);
if (readsize < 0) {
fprintf(stderr, "Read error %d(%m)\n", readsize);
return;
}
const int nevents = readsize / sizeof events[0];
int surplus = readsize % sizeof events[0];
printf("%d event(s) and %d extra\n", nevents, surplus);
for (int i = 0; i < nevents; ++i) {
switch (events[i].type) {
case GADGETFS_CONNECT:
printf("EP0 CONNECT\n");
break;
case GADGETFS_DISCONNECT:
printf("EP0 DISCONNECT\n");
break;
case GADGETFS_SETUP:
printf("EP0 SETUP\n");
handle_setup_request(fd, &events[i].u.setup);
break;
case GADGETFS_NOP:
case GADGETFS_SUSPEND:
break;
}
}
}
}
int main()
{
const int fd = open(USB_DEV, O_RDWR|O_SYNC|O_EXCL);
if (fd < 0) {
fprintf(stderr, "Unable to open %s(%m)\n", USB_DEV);
return EXIT_FAILURE;
}
const struct usb_device_descriptor device_descriptor =
{ .bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.idVendor = 0xAA00, // Fake vendor ID
.idProduct = 0xBB, // Fake product ID
.bcdDevice = 0x0200, // Version
.iManufacturer = STRINGID_MANUFACTURER,
.iProduct = STRINGID_PRODUCT,
.iSerialNumber = STRINGID_SERIAL,
.bNumConfigurations = 1
};
const struct usb_interface_descriptor if_descriptor = {
.bLength = sizeof if_descriptor,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0
};
const struct usb_config_descriptor config = {
.bLength = sizeof config,
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = sizeof config + sizeof if_descriptor,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = 1
};
uint8_t init_config[2048];
uint8_t* p = init_config;
printf("Start init\n");
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 0;
#define FETCH(desc) \
memcpy(p, &desc, desc.bLength); \
p += desc.bLength;
FETCH(config);
FETCH(if_descriptor);
FETCH(config); /* again for a high-speed i/f */
FETCH(if_descriptor);
FETCH(device_descriptor);
// Configure ep0
int send_size = p - init_config;
int sent = write(fd, init_config, send_size);
if (sent != send_size) {
fprintf(stderr, "Write error %d/%d (%m)\n", sent/send_size);
return EXIT_FAILURE;
}
printf("ep0 configured\n");
handle_ep0(fd);
}
Without changing the code, I unpredictably get two different results as seen from a Linux host using lsusb -v -d aa00:00bb. When it works, I get:
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 2 Communications
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0xaa00
idProduct 0x00bb
bcdDevice 2.00
iManufacturer 1 Toby
iProduct 2 widget
iSerial 3 v1
bNumConfigurations 1
Device Qualifier (for other device speed):
bLength 10
bDescriptorType 6
bcdUSB 2.00
bDeviceClass 2 Communications
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
bNumConfigurations 1
Device Status: 0x0000
(Bus Powered)
And when it fails:
Bus 001 Device 027: ID aa00:00bb Toby widget
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 2 Communications
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0xaa00
idProduct 0x00bb
bcdDevice 2.00
iManufacturer 1 (error)
iProduct 2 (error)
iSerial 3 (error)
bNumConfigurations 1
And it won't work again until I unload the dwc3 driver and re-load it.
Here's the relevant kernel log messages for the failing case (I enabled CONFIG_USB_GADGET_VERBOSE to get these):
[342748.061482] gadgetfs: bound to dwc3-gadget driver
[342748.066303] gadgetfs: bound to gadget device
[342748.071482] gadgetfs: ep0_read wait
[342748.344072] gadgetfs: connected
[342748.347308] gadgetfs: event[0] = 1
[342748.347929] gadgetfs: ep0_read wait
[342748.352265] gadgetfs: disconnected
[342748.355755] gadgetfs: event[0] = 2
[342748.356315] gadgetfs: ep0_read wait
[342748.504041] gadgetfs: connected
[342748.507269] gadgetfs: event[0] = 1
[342748.507842] gadgetfs: ep0_read wait
[342748.510596] gadgetfs: delegate req80.06 v0300 i0000 l255
[342748.510600] gadgetfs: event[0] = 3
[342748.511208] gadgetfs: usb_ep_queue returned 0
[342748.511213] gadgetfs: ep0_read wait
[342748.511557] gadgetfs: delegate req80.06 v0302 i0028 l255
[342748.511563] gadgetfs: event[0] = 3
[342748.511630] gadgetfs: usb_ep_queue returned 0
[342748.511635] gadgetfs: ep0_read wait
[342748.511934] gadgetfs: delegate req80.06 v0301 i0028 l255
[342748.511940] gadgetfs: event[0] = 3
[342748.511989] gadgetfs: usb_ep_queue returned 0
[342748.511995] gadgetfs: ep0_read wait
[342748.512181] gadgetfs: delegate req80.06 v0303 i0028 l255
[342748.512187] gadgetfs: event[0] = 3
[342748.512232] gadgetfs: usb_ep_queue returned 0
[342748.512238] gadgetfs: ep0_read wait
[342748.512711] gadgetfs: configuration #1
[342748.516551] gadgetfs: event[0] = 3
[342748.517139] gadgetfs: ep0_read wait
[342748.953968] gadgetfs: delegate req80.06 v0300 i0000 l4
[342748.953974] gadgetfs: event[0] = 3
[342748.954544] dwc3 38100000.usb: ep0out: pending_list empty
[342748.960062] gadgetfs: usb_ep_queue returned -16
[342748.960108] gadgetfs: ep0_read wait
(The line from dwc is the one I added to the ret = -EBUSY shown above).
A Wireshark trace perhaps points the finger at SET_CONFIGURATION, as it all works until that point:
No. Time delta Source Length Info Descriptor no
1 0.000000 0.000000 host 64 GET DESCRIPTOR Request DEVICE 0x00
2 0.004048 0.004048 1.113.0 82 GET DESCRIPTOR Response DEVICE
3 0.004074 0.000026 host 64 GET DESCRIPTOR Request CONFIGURATION 0x00
4 0.004669 0.000595 1.113.0 73 GET DESCRIPTOR Response CONFIGURATION
5 0.004682 0.000013 host 64 GET DESCRIPTOR Request CONFIGURATION 0x00
6 0.005918 0.001236 1.113.0 110 GET DESCRIPTOR Response CONFIGURATION
7 0.005948 0.000030 host 64 GET DESCRIPTOR Request STRING 0x00
8 0.570791 0.564843 1.113.0 68 GET DESCRIPTOR Response STRING
9 0.570822 0.000031 host 64 GET DESCRIPTOR Request STRING 0x02
10 0.571913 0.001091 1.113.0 86 GET DESCRIPTOR Response STRING
11 0.571933 0.000020 host 64 GET DESCRIPTOR Request STRING 0x01
12 0.572662 0.000729 1.113.0 84 GET DESCRIPTOR Response STRING
13 0.572680 0.000018 host 64 GET DESCRIPTOR Request STRING 0x03
14 0.573539 0.000859 1.113.0 98 GET DESCRIPTOR Response STRING
15 0.573806 0.000267 host 64 SET CONFIGURATION Request
16 0.578297 0.004491 1.113.0 64 SET CONFIGURATION Response
17 0.578325 0.000028 host 64 GET DESCRIPTOR Request STRING 0x04
18 5.703230 5.124905 1.113.0 64 GET DESCRIPTOR Response
19 5.703244 0.000014 host 64 GET DESCRIPTOR Request STRING 0x04
20 10.823171 5.119927 1.113.0 64 GET DESCRIPTOR Response
What could be causing this failure, and what do I need to do to avert it?
I found something that seems to fix this. I don't much like it, and there ought to be something not involving an arbitrary sleep!
I changed the set-configuration handler to wait for 10ms before replying, and also to test the result of writing the ACK. Either one of these alone was insufficient.
case USB_REQ_SET_CONFIGURATION:
printf("Ignoring configuration value %d\n", setup->wValue);
usleep(10'000);
// Just ACK
status = read(fd, &status, 0);
if (status < 0) {
goto stall;
}
return;
I'm trying to prototype some code on Arduino which simulates USB composite device - which consist of audio and HID functionality. What I'd like to achieve is to have ability to control volume by pressing physical button on Arduino - which will generate HID volume up/down event to host. I'd expect that changing volume will set audio even if Arduino is not selected as default device.
On the Windows I can control volume even if Arduino is not default audio device - which means I can connect two or more of Arduino devices and set valid volume independently. On the Linux (with ALSA/PulseAudio) it seems that HID Volume Up/Down works ONLY for default device - which means I cannot use two Arduino devices and control their volume independently.
I can also observe that if default audio device is different then Arduino there is no USB message with SET_CUR from the host.
Is there anything that I could configure in the ALSA/PulseAudio to achieve desired result? Or maybe HID approach should be reworked?
Below is definition of HID message I am using:
static const uint8_t JoystickHIDDescriptor[] =
{
0x05, 0x0c, // USAGE_PAGE (Consumer Devices)
0x09, 0x01, // USAGE (Consumer Control)
0xa1, 0x01, // COLLECTION (Application)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x09, 0xe9, // USAGE (Volume Up)
0x09, 0xea, // USAGE (Volume Down)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0x09, 0xe2, // USAGE (Mute)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x06, // INPUT (Data,Var,Rel)
0x95, 0x05, // REPORT_COUNT (5)
0x81, 0x07, // INPUT (Data,Var,Rel)
0xc0 // END_COLLECTION
};
If you use HID then you may check this one
https://github.com/pzprovi/hid
Take a look here entire project. There is two keyboard void functions. you may test keyboard2() example is just for you.
https://github.com/pzprovi/hid/blob/master/MultiKey/MultiKey.c
Just replace it and try to case all your array elements from JoystickHIDDescriptor like this one example at the bottom
void Keyboard2(void)
{
if(!HIDTxHandleBusy(lastINTransmission))
{
switch(getInput())
{
case INPUT_NONE:
// if we don't send this, the vol +/- will continue to roll on and on...
hid_report_in[0] = 0x00;
hid_report_in[1] = 0x00;
hid_report_in[2] = 0x00;
lastINTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
break;
case INPUT_VOLUME_UP:
hid_report_in[0] = 0x01;
hid_report_in[1] = 0xE9;
hid_report_in[2] = 0x00;
lastINTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
break;
case INPUT_VOLUME_DOWN:
hid_report_in[0] = 0x01;
hid_report_in[1] = 0xEA;
hid_report_in[2] = 0x00;
lastINTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
break;
case INPUT_VOLUME_MUTE:
hid_report_in[0] = 0x01;
hid_report_in[1] = 0xE2;
hid_report_in[2] = 0x00;
lastINTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
break;
default:
// no need to send anything.
break;
}
}
}
0x09, 0xe9, // USAGE (Volume Up) 0x09, 0xea,
// USAGE (Volume Down)
Put break point and check all your hardware buttons on your device. Then add for vol up
this code:
hid_report_in[0] = 0x01;
hid_report_in[1] = 0xE9;
hid_report_in[2] = 0x00;
lastINTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
and vol down this one:
hid_report_in[0] = 0x01;
hid_report_in[1] = 0xEA;
hid_report_in[2] = 0x00;
lastINTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
I want to test the linux usb HID gadget "g_hid" modules. But while inserting it is giving error as "no such device". After referring the Linux Documentation
http://elixir.free-electrons.com/linux/v4.13.11/source/Documentation/usb/gadget_hid.txt, I got to know the following code we need to add in the platform code
#include <linux/platform_device.h>
#include <linux/usb/g_hid.h>
/* hid descriptor for a keyboard */
static struct hidg_func_descriptor my_hid_data = {
.subclass = 0, /* No subclass */
.protocol = 1, /* Keyboard */
.report_length = 8,
.report_desc_length = 63,
.report_desc = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x06, /* USAGE (Keyboard) */
0xa1, 0x01, /* COLLECTION (Application) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */
0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x08, /* REPORT_COUNT (8) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
0x95, 0x05, /* REPORT_COUNT (5) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */
0x29, 0x05, /* USAGE_MAXIMUM (Kana) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x03, /* REPORT_SIZE (3) */
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
0x95, 0x06, /* REPORT_COUNT (6) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x65, /* LOGICAL_MAXIMUM (101) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0x00, /* USAGE_MINIMUM (Reserved) */
0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
0xc0 /* END_COLLECTION */
}
};
static struct platform_device my_hid = {
.name = "hidg",
.id = 0,
.num_resources = 0,
.resource = 0,
.dev.platform_data = &my_hid_data,
};
Where shall I add the above code in Linux source code? I am using TI-AM57xx board.
You are not supposed to modify any kernel source!
These HID descriptors are what goes into your USB device firmware.
Is it possible to pack an executable into a shared library and upon calling a function inside the said library:
unpack the executable
use the executable through fork
The reason I am asking is because I was recently faced with a situation where my shared library was being loaded in a "sandbox" environment (maybe chroot based) and I would have really like the possibility of spawning a separate process for an executable (loose coupling).
As long as you have permission to write to a directory on a filesystem that isn't mounted noexec, then you could just store the executable in a large array of unsigned char and write it out with fwrite, then use fork/exec to run it.
Really though, the best solution is just to use fork() without exec - just have the child side call into a different function after the fork() (and then exit with _exit() when that function is done).
Completely plausible.
static const char program[] = {
0x7f, 0x45, 0x4c, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x43, 0x05, 0x02, 0x00, 0x03, 0x00, 0x1a, 0x00, 0x43, 0x05,
0x1a, 0x00, 0x43, 0x05, 0x04, 0x00, 0x00, 0x00, 0xb9, 0x31, 0x00, 0x43,
0x05, 0xb2, 0x0d, 0xcd, 0x80, 0x25, 0x20, 0x00, 0x01, 0x00, 0x93, 0xcd,
0x80, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c,
0x64, 0x0a
};
void hello(void) {
int fd;
pid_t child;
char name[1024];
char *tmp = getenv("TEMP") ?: getenv("TMP") ?: "/tmp";
if (strlen(tmp) > sizeof(name) - 8) return;
sprintf(name, "%s/XXXXXX", tmp);
fd = mkstemp(name);
if (fd == -1) return;
if (write(fd, program, sizeof(program)) < sizeof(program)) {
close(fd);
unlink(name);
return;
}
fchmod(fd, 0700);
close(fd);
(child = fork()) ? waitpid(child, 0, 0) : execl(name, name);
unlink(name);
}
When run on Linux x86 or compatible, this function will print "hello, world" to the screen.
However, I would definitely not recommend this. If you want a separate binary, just ship a separate binary, and require that it be installed in the sandbox along with your library.