Related
I'm building a small "macro" style keyboard using rust, STM32 chips, usb-device and usbd-hid. The device is correctly detected by my PC and the manufacturer strings etc are visible in device manager. I can use the device as a keyboard.
However when I try to write from host to device using rusb or hidapi, it fails with an IO error. When I debug the C code it gives an "Access Denied" error when it tries to WriteFile to the USB device. It seems on further research that this is a limitation of Windows, in that I can't access HID mice and keyboards as they're system reserved:
the native Windows HID driver is supported by libusb, but there are some limitations, such as not being able to access HID mice and keyboards since they are system reserved
However I can't find any documentation or examples of how to work around this - it seems I should be able to add another configuration, interface or endpoint in my USB descriptors that isn't exclusively held by Windows. Anybody have any hints on where to start?
My current USB device configuration (using usbd-hid's macro, which I'm happy to abandon if I need to) looks like this:
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = KEYBOARD) = {
(usage_page = KEYBOARD, usage_min = 0xE0, usage_max = 0xE7) = {
#[packed_bits 8] #[item_settings data,variable,absolute] modifier=input;
};
(usage_min = 0x00, usage_max = 0xFF) = {
#[item_settings constant,variable,absolute] reserved=input;
};
(usage_page = LEDS, usage_min = 0x01, usage_max = 0x05) = {
#[packed_bits 5] #[item_settings data,variable,absolute] leds=output;
};
(usage_page = KEYBOARD, usage_min = 0x00, usage_max = 0xDD) = {
#[item_settings data,array,absolute] keycodes=input;
};
(usage_page = 0xFF17, usage_min = 0x01, usage_max = 0xFF) = {
#[item_settings data,variable,absolute] command=output;
};
(usage_page = 0xFF17, usage_min = 0x01, usage_max = 0xFF) = {
#[item_settings data,variable,absolute] data=output;
};
}
)]
#[allow(dead_code)]
pub struct CustomKeyboardReport {
pub modifier: u8,
pub reserved: u8,
pub leds: u8,
pub keycodes: [u8; 6],
pub command: u8,
pub data: u8,
}
Which produces a descriptor that looks like this
0x05 0x01 0x09 0x06 0xA1 0x01 0x05 0x07
0x19 0xE0 0x29 0xE7 0x15 0x00 0x25 0x01
0x75 0x01 0x95 0x08 0x81 0x02 0x19 0x00
0x29 0xFF 0x26 0xFF 0x00 0x75 0x08 0x95
0x01 0x81 0x03 0x05 0x08 0x19 0x01 0x29
0x05 0x25 0x01 0x75 0x01 0x95 0x05 0x91
0x02 0x95 0x03 0x91 0x03 0x05 0x07 0x19
0x00 0x29 0xDD 0x26 0xFF 0x00 0x75 0x08
0x95 0x06 0x81 0x00 0x06 0x17 0xFF 0x19
0x01 0x29 0xFF 0x95 0x01 0x91 0x02 0x06
0x17 0xFF 0x19 0x01 0x29 0xFF 0x91 0x02
0xC0
Related: 1
I resolved this by creating a separate report:
#[gen_hid_descriptor(
(collection = LOGICAL, usage_page = VENDOR_DEFINED_START, usage = 0x00) = {
(usage_page = 0xFF17, usage_min = 0x01, usage_max = 0xFF) = {
#[item_settings data,array,absolute] command=output;
};
}
)]
pub struct CommandReport {
pub command: [u8; 2],
}
Then I created a separate HID interface with only an OUT endoint:
// for the keyboard
let hid = HIDClass::new(&alloc, CustomKeyboardReport::desc(), 10);
// for comms from the host -> device
let command = HIDClass::new_ep_out(&alloc, CommandReport::desc(), 10);
In my poll function I check both:
bus.poll(&mut [&mut hid, &mut command])
And then I can read the data by pulling raw output:
let mut buffer: [u8; 64] = [0; 64];
match command.pull_raw_output(&mut buffer) {
Ok(size) => handleCommand(buffer, size),
Err(UsbError::WouldBlock) => {
// no pending data
}
Err(err) => panic!("Error receiving data {:?}", err),
}
As there is only 1 report ID, I did not have to prepend the reportId in the data I sent from the host using libusb.
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 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.
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.
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.