initial commit

This commit is contained in:
2025-08-13 06:37:47 -04:00
commit 77fe11c758
19 changed files with 1728 additions and 0 deletions
+369
View File
@@ -0,0 +1,369 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "ble/gatt-service/hids_device.h"
#include "btstack_ring_buffer.h"
#include "btstack_defines.h"
#include "usb_device.h"
#include "usb_host.h"
#include "hid_report.h"
static uint8_t buffer_storage[REPORT_BUF_SIZE];
static btstack_ring_buffer_t report_buf;
static uint8_t desc_hid_report[HID_DESCRIPTOR_SIZE];
static uint16_t desc_hid_report_len=0;
static struct report_desc *descriptors;
static struct report_desc* report_desc_alloc(void);
static void report_desc_init(struct report_desc *descriptor);
static void report_desc_free(struct report_desc *descriptor);
static struct report_desc* report_desc_find(uint8_t dev_addr, uint8_t instance);
static struct report_dict* report_dict_alloc(struct report_desc *descriptor);
static void report_dict_init(struct report_dict *mapping);
static void report_dict_free(struct report_dict *mapping, struct report_desc *descriptor);
static struct report_dict* find_mapping(uint8_t dev_addr, uint8_t instance, uint8_t report_id);
extern hci_con_handle_t con_handle;
// start listening to HID input reports on all mounted devices
bool request_hid_reports_all(void) {
// send request to receive reports on all mounted devices
struct report_desc * current;
for (current=descriptors; current != NULL; current=current->next) {
if (! current->listening) {
if(request_hid_report(current->dev_addr, current->instance)) {
char tempbuf[40];
size_t count = sprintf(tempbuf, "Listening to input reports on [%u:%u]\n", current->dev_addr, current->instance);
cdc_print_str(tempbuf, count);
current->listening = true;
} else {
return false;
}
}
}
return true;
}
// stop listening to HID input reports on all mounted devices
bool stop_hid_reports_all(void) {
// send request to stop reports on all mounted devices
struct report_desc * current;
for (current=descriptors; current != NULL; current=current->next) {
if (current->listening) {
if(stop_hid_report(current->dev_addr, current->instance)) {
char tempbuf[40];
size_t count = sprintf(tempbuf, "Stopping input reports on [%u:%u]\n", current->dev_addr, current->instance);
cdc_print_str(tempbuf, count);
current->listening = false;
} else {
return false;
}
}
}
return true;
}
// used to send next HID input report on the BLE interface
void send_report(){
// process queue and send next report
if (btstack_ring_buffer_bytes_available(&report_buf)) {
uint8_t dev_addr;
uint8_t instance;
uint8_t len8[2];
uint16_t len;
uint32_t num_bytes_read;
// retrieve dev_addr from ring_buffer
btstack_ring_buffer_read(&report_buf, &dev_addr, 1, &num_bytes_read);
// retrieve instance from ring buffer
btstack_ring_buffer_read(&report_buf, &instance, 1, &num_bytes_read);
// retrieve length as two uint8_t and turn into uint16_t
btstack_ring_buffer_read(&report_buf, len8, 2, &num_bytes_read);
memcpy(&len, len8, 2);
// retrieve report from ring buffer
uint8_t report[len];
btstack_ring_buffer_read(&report_buf, report, len, &num_bytes_read);
// find report id mapping
struct report_dict * mapping = find_mapping(dev_addr, instance, report[0]);
char tempbuf[16];
size_t count=sprintf(tempbuf, "[%04x](%u) ", con_handle, mapping->ble_id);
cdc_print_str(tempbuf, count);
// send hid report to ble interface
if (mapping != NULL) {
if (mapping->report_id==0) {
hids_device_send_input_report_for_id(con_handle, mapping->ble_id, report, len);
cdc_print_hex(report, len);
} else {
// replace report ID in original report before sending
hids_device_send_input_report_for_id(con_handle, mapping->ble_id, &report[1], len-1);
cdc_print_hex(&report[1], len-1);
}
}
// request send of next report
hids_device_request_can_send_now_event(con_handle);
}
}
// add report to the BTstack ring buffer for sending
void queue_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {
if (con_handle != HCI_CON_HANDLE_INVALID) {
// convert len to array of two uint8_t from single uint16_t
uint8_t len8[2];
memcpy(len8, &len, 2);
if (btstack_ring_buffer_bytes_free(&report_buf) >= len+4) {
// put instance, length, and report into ring buffer if space available
btstack_ring_buffer_write(&report_buf, &dev_addr, 1);
btstack_ring_buffer_write(&report_buf, &instance, 1);
btstack_ring_buffer_write(&report_buf, len8, 2);
btstack_ring_buffer_write(&report_buf, report, len);
}
// request send on BLE HID interface
hids_device_request_can_send_now_event(con_handle);
}
}
// allocate memory for HID report ring buffer
void init_report_buf(void) {
btstack_ring_buffer_init(&report_buf, buffer_storage, sizeof(buffer_storage));
}
// allocate memory for USB interface report descriptor
static struct report_desc * report_desc_alloc(void) {
struct report_desc *ret = REPORT_DESC_ALLOC();
if (ret != NULL) {
report_desc_init(ret);
if (descriptors == NULL) {
descriptors = ret;
} else {
struct report_desc *last;
for (last = descriptors; last->next != NULL; last=last->next);
last->next = ret;
}
}
return ret;
}
// initialize report descriptor struct
static void report_desc_init(struct report_desc *descriptor) {
memset(descriptor, 0, sizeof(struct report_desc));
descriptor->next = NULL;
descriptor->mappings = NULL;
}
// free memory and teardown usb->bt report ID mappings for report descriptor struct
static void report_desc_free(struct report_desc *descriptor) {
if (descriptor != NULL) {
if (descriptors == descriptor) {
descriptors = descriptor->next;
} else {
struct report_desc *last;
for (last = descriptors; last->next != NULL; last = last->next) {
if ((last->next) == descriptor) {
last->next = descriptor->next;
break;
}
}
}
while (descriptor->mappings != NULL) {
report_dict_free(descriptor->mappings, descriptor);
}
free(descriptor);
}
}
// add report descriptor for new HID interface
bool add_descriptor(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) {
struct report_desc * descriptor = report_desc_alloc();
if (descriptor == NULL) {
return false;
}
memcpy(descriptor->descriptor, desc_report, desc_len);
descriptor->desc_len = desc_len;
descriptor->dev_addr = dev_addr;
descriptor->instance = instance;
descriptor->listening = false;
return true;
}
// generate new report descriptor for BLE device and save mappings for report IDs
bool generate_report_descriptor(void) {
memset(desc_hid_report, 0, sizeof(desc_hid_report));
desc_hid_report_len=0;
uint8_t num_mounted = 0;
struct report_desc * current;
for (current=descriptors; current != NULL; current=current->next) {
if (desc_hid_report_len + (current->desc_len) + 2 < HID_DESCRIPTOR_SIZE) {
memcpy(&desc_hid_report[desc_hid_report_len], current->descriptor, current->desc_len);
} else {
cdc_print_msg("Err: HID report descriptor too long\n");
return false;
}
bool no_id = true;
// search for report ID (0x85) in report descriptor
for (int i=0; i<(current->desc_len); i++) {
if(current->descriptor[i] == 0x85) {
// use report ID found in descriptor
no_id=false;
i++;
// modify report ID and save mapping
struct report_dict * report_map = report_dict_alloc(current);
if (report_map == NULL) {
return false;
}
num_mounted++;
report_map->report_id = current->descriptor[i];
report_map->ble_id = num_mounted;
desc_hid_report[desc_hid_report_len+i]=num_mounted;
}
}
// no report ID found in descriptor for the interface
if (no_id) {
// add report id field to beginning of descriptor after Collection (0xa1) field
uint8_t col_pos=0;
for (int i=0; i<(current->desc_len); i++) {
if(current->descriptor[i] == 0xa1) {
col_pos=i;
break;
}
}
desc_hid_report[desc_hid_report_len+col_pos+2] = 0x85;
desc_hid_report[desc_hid_report_len+col_pos+3] = num_mounted+1;
memcpy(&desc_hid_report[desc_hid_report_len+col_pos+4], &current->descriptor[col_pos+2], current->desc_len-col_pos);
desc_hid_report_len += current->desc_len+2;
// store mapping with report ID of 0
struct report_dict * report_map = report_dict_alloc(current);
num_mounted++;
report_map->report_id = 0;
report_map->ble_id = num_mounted;
} else {
desc_hid_report_len += current->desc_len;
}
}
if (num_mounted > NUM_REPORT_IDS) {
cdc_print_msg("Error: too many report IDs\n");
return false;
}
btstack_ring_buffer_reset(&report_buf);
return true;
}
// remove report descriptor for HID interface
void remove_instance(uint8_t dev_addr, uint8_t instance) {
struct report_desc *descriptor = report_desc_find(dev_addr, instance);
if (descriptor != NULL) {
report_desc_free(descriptor);
}
}
// find report descriptor by device address and instance
static struct report_desc * report_desc_find(uint8_t dev_addr, uint8_t instance) {
struct report_desc *descriptor;
for (descriptor = descriptors; descriptor != NULL; descriptor = descriptor->next) {
if (descriptor->dev_addr==dev_addr && descriptor->instance==instance) {
break;
}
}
return descriptor;
}
// allocate memory for usb->bt report id mapping
static struct report_dict * report_dict_alloc(struct report_desc * descriptor) {
struct report_dict *ret = REPORT_DICT_ALLOC();
if (ret != NULL) {
report_dict_init(ret);
if (descriptor->mappings == NULL) {
descriptor->mappings = ret;
} else {
struct report_dict *last;
for (last = descriptor->mappings; last->next != NULL; last=last->next);
last->next = ret;
}
}
return ret;
}
// initialize usb->bt report id mapping struct
static void report_dict_init(struct report_dict *mapping) {
memset(mapping, 0, sizeof(struct report_dict));
mapping->next = NULL;
}
// free memory from report id mapping
static void report_dict_free(struct report_dict *mapping, struct report_desc *descriptor) {
if (mapping != NULL) {
if (descriptor->mappings == mapping) {
descriptor->mappings = mapping->next;
} else {
struct report_dict *last;
for (last = descriptor->mappings; last->next != NULL; last = last->next) {
if ((last->next) == mapping) {
last->next = mapping->next;
break;
}
}
}
free(mapping);
}
}
// find the usb->bt report id mapping struct for given device, instance, and usb report id
static struct report_dict * find_mapping(uint8_t dev_addr, uint8_t instance, uint8_t report_id) {
struct report_desc * descriptor;
descriptor = report_desc_find(dev_addr, instance);
if (descriptor != NULL) {
struct report_dict * mapping;
for (mapping = descriptor->mappings; mapping != NULL; mapping = mapping->next) {
if (mapping->report_id ==0 || mapping->report_id == report_id) {
return mapping;
}
}
}
return NULL;
}
uint16_t get_desc_hid_report_len(void) {
return desc_hid_report_len;
}
uint8_t const* get_desc_hid_report(void) {
return desc_hid_report;
}