From 3dc2b4c2d34ba30e78a50e1e2c6512d3716a3b88 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 13 Aug 2021 21:51:52 -0400 Subject: [PATCH] at least original functionality with new API --- locale/circuitpython.pot | 6 +- shared-bindings/usb_hid/Device.c | 49 +++++++----- shared-bindings/usb_hid/Device.h | 2 +- shared-bindings/usb_hid/__init__.c | 2 +- shared-module/usb_hid/Device.c | 97 ++++++++++++++-------- shared-module/usb_hid/Device.h | 4 +- shared-module/usb_hid/__init__.c | 124 ++++++++++++----------------- 7 files changed, 152 insertions(+), 132 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index a87bd419b9..6114dcfaea 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -91,6 +91,10 @@ msgstr "" msgid "%q length must be %q" msgstr "" +#: shared-bindings/usb_hid/Device.c +msgid "%q length must be >= 1" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "" @@ -1226,7 +1230,7 @@ msgstr "" msgid "Internal error #%d" msgstr "" -#: shared-bindings/sdioio/SDCard.c +#: shared-bindings/sdioio/SDCard.c shared-module/usb_hid/Device.c msgid "Invalid %q" msgstr "" diff --git a/shared-bindings/usb_hid/Device.c b/shared-bindings/usb_hid/Device.c index 25377dffed..718df73241 100644 --- a/shared-bindings/usb_hid/Device.c +++ b/shared-bindings/usb_hid/Device.c @@ -31,7 +31,7 @@ //| class Device: //| """HID Device specification""" //| -//| def __init__(self, *, descriptor: ReadableBuffer, usage_page: int, usage: int, in_report_lengths: Sequence[int], out_report_lengths: Sequence[int]) -> None: +//| def __init__(self, *, descriptor: ReadableBuffer, usage_page: int, usage: int, report_ids: Sequence[int], in_report_lengths: Sequence[int], out_report_lengths: Sequence[int]) -> None: //| """Create a description of a USB HID device. The actual device is created when you //| pass a `Device` to `usb_hid.enable()`. //| @@ -40,6 +40,7 @@ //| :param int usage_page: The Usage Page value from the descriptor. Must match what is in the descriptor. //| :param int usage: The Usage value from the descriptor. Must match what is in the descriptor. //| :param int report_ids: Sequence of report ids used by the descriptor. +//| If the ``report_descriptor`` does not have a report ID, use 0. //| :param int in_report_lengths: Sequence of sizes in bytes of the HIDs report sent to the host. //| The sizes are in order of the ``report_ids``. //| "IN" is with respect to the host. @@ -59,12 +60,12 @@ //| MOUSE: Device //| """Standard mouse device supporting five mouse buttons, X and Y relative movements from -127 to 127 //| in each report, and a relative mouse wheel change from -127 to 127 in each report. -//| Uses Report ID 2 for its IN reports. +//| Uses Report ID 2 for its IN report. //| """ //| //| CONSUMER_CONTROL: Device //| """Consumer Control device supporting sent values from 1-652, with no rollover. -//| Uses Report ID 3 for its IN reports.""" +//| Uses Report ID 3 for its IN report.""" //| STATIC mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -100,8 +101,12 @@ STATIC mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args mp_obj_t out_report_lengths = args[ARG_out_report_lengths].u_obj; size_t report_ids_count = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(report_ids)); - if (MP_OBJ_SMALL_INT_VALUE(mp_obj_len(in_report_lengths)) != report_ids_count || - MP_OBJ_SMALL_INT_VALUE(mp_obj_len(out_report_lengths)) != report_ids_count) { + if (report_ids_count < 1) { + mp_raise_ValueError_varg(translate("%q length must be >= 1"), MP_QSTR_report_ids); + } + + if ((size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(in_report_lengths)) != report_ids_count || + (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(out_report_lengths)) != report_ids_count) { mp_raise_ValueError_varg(translate("%q, %q, and %q must all be the same length"), MP_QSTR_report_ids, MP_QSTR_in_report_lengths, MP_QSTR_out_report_lengths); } @@ -136,7 +141,9 @@ STATIC mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args //| def send_report(self, buf: ReadableBuffer, report_id: Optional[int] = None) -> None: -//| """Send an HID report. +//| """Send an HID report. If the device descriptor specifies zero or one report id's, +//| you can supply `None` (the default) as the value of ``report_id``. +//| Otherwise you must specify which report id to use when sending the report. //| """ //| ... //| @@ -155,11 +162,12 @@ STATIC mp_obj_t usb_hid_device_send_report(size_t n_args, const mp_obj_t *pos_ar mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ); - uint8_t report_id = 0; + // -1 asks common_hal to determine the report id if possible. + mp_int_t report_id_arg = -1; if (args[ARG_report_id].u_obj != mp_const_none) { - const mp_int_t report_id_arg = mp_obj_int_get_checked(args[ARG_report_id].u_obj); - report_id = mp_arg_validate_int_range(report_id_arg, 1, 255, MP_QSTR_report_id); + report_id_arg = mp_obj_int_get_checked(args[ARG_report_id].u_obj); } + const uint8_t report_id = common_hal_usb_hid_device_validate_report_id(self, report_id_arg); common_hal_usb_hid_device_send_report(self, ((uint8_t *)bufinfo.buf), bufinfo.len, report_id); return mp_const_none; @@ -167,10 +175,10 @@ STATIC mp_obj_t usb_hid_device_send_report(size_t n_args, const mp_obj_t *pos_ar MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_device_send_report_obj, 2, usb_hid_device_send_report); //| def get_last_received_report(self, report_id: Optional[int] = None) -> bytes: -//| """Get the last received HID OUT report for the given report ID. -//| The report ID may be omitted if there is no report ID, or only one report ID. -//| Return `None` if nothing received. -//| """ +//| """Get the last received HID OUT report for the given report ID. +//| The report ID may be omitted if there is no report ID, or only one report ID. +//| Return `None` if nothing received. +//| """ //| ... //| STATIC mp_obj_t usb_hid_device_get_last_received_report(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -184,17 +192,18 @@ STATIC mp_obj_t usb_hid_device_get_last_received_report(size_t n_args, const mp_ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - uint8_t report_id = 0; + mp_int_t report_id_arg = -1; if (args[ARG_report_id].u_obj != mp_const_none) { - report_id = mp_obj_int_get_checked(args[ARG_report_id].u_obj); + report_id_arg = mp_obj_int_get_checked(args[ARG_report_id].u_obj); } + const uint8_t report_id = common_hal_usb_hid_device_validate_report_id(self, report_id_arg); return common_hal_usb_hid_device_get_last_received_report(self, report_id); } MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_device_get_last_received_report_obj, 1, usb_hid_device_get_last_received_report); //| last_received_report: bytes -//| """The HID OUT report as a `bytes`. (read-only). `None` if nothing received. +//| """The HID OUT report as a `bytes` (read-only). `None` if nothing received. //| Same as `get_last_received_report()` with no argument. //| //| Deprecated: will be removed in CircutPython 8.0.0. Use `get_last_received_report()` instead. @@ -203,7 +212,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_device_get_last_received_report_obj, 1, usb_h STATIC mp_obj_t usb_hid_device_obj_get_last_received_report_property(mp_obj_t self_in) { usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in); - return common_hal_usb_hid_device_get_last_received_report(self, 0); + // Get the sole report_id, if there is one. + const uint8_t report_id = common_hal_usb_hid_device_validate_report_id(self, -1); + return common_hal_usb_hid_device_get_last_received_report(self, report_id); } MP_DEFINE_CONST_FUN_OBJ_1(usb_hid_device_get_last_received_report_property_obj, usb_hid_device_obj_get_last_received_report_property); @@ -215,7 +226,7 @@ const mp_obj_property_t usb_hid_device_last_received_report_obj = { }; //| usage_page: int -//| """The usage page of the device as an `int`. Can be thought of a category. (read-only)""" +//| """The device usage page identifier, which designates a category of device. (read-only)""" //| STATIC mp_obj_t usb_hid_device_obj_get_usage_page(mp_obj_t self_in) { usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -231,7 +242,7 @@ const mp_obj_property_t usb_hid_device_usage_page_obj = { }; //| usage: int -//| """The functionality of the device as an int. (read-only) +//| """The device usage identifier, which designates a specific kind of device. (read-only) //| //| For example, Keyboard is 0x06 within the generic desktop usage page 0x01. //| Mouse is 0x02 within the same usage page.""" diff --git a/shared-bindings/usb_hid/Device.h b/shared-bindings/usb_hid/Device.h index d6069c370e..a5e94c4d52 100644 --- a/shared-bindings/usb_hid/Device.h +++ b/shared-bindings/usb_hid/Device.h @@ -38,6 +38,6 @@ void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t * mp_obj_t common_hal_usb_hid_device_get_last_received_report(usb_hid_device_obj_t *self, uint8_t report_id); uint8_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self); uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self); -bool common_hal_usb_hid_device_valid_report_id(usb_hid_device_obj_t *self, uint8_t report_id); +uint8_t common_hal_usb_hid_device_validate_report_id(usb_hid_device_obj_t *self, mp_int_t report_id); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_HID_DEVICE_H diff --git a/shared-bindings/usb_hid/__init__.c b/shared-bindings/usb_hid/__init__.c index f5a7c1cf72..1c8a45e4ea 100644 --- a/shared-bindings/usb_hid/__init__.c +++ b/shared-bindings/usb_hid/__init__.c @@ -71,7 +71,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(usb_hid_disable_obj, usb_hid_disable); //| //| If you enable too many devices at once, you will run out of USB endpoints. //| The number of available endpoints varies by microcontroller. -//| CircuitPython will go into safe mode after running boot.py to inform you if +//| CircuitPython will go into safe mode after running ``boot.py`` to inform you if //| not enough endpoints are available. //| """ //| ... diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c index 9690f86586..db92bd1c32 100644 --- a/shared-module/usb_hid/Device.c +++ b/shared-module/usb_hid/Device.c @@ -79,7 +79,7 @@ const usb_hid_device_obj_t usb_hid_device_keyboard_obj = { .usage = 0x06, .num_report_ids = 1, .report_ids = { 0x01, }, - .in_report_lengths = { 8, 0, 0, 0, }, + .in_report_lengths = { 8, 0, 0, 0, 0, 0, }, .out_report_lengths = { 1, }, }; @@ -129,7 +129,7 @@ const usb_hid_device_obj_t usb_hid_device_mouse_obj = { .usage = 0x02, .num_report_ids = 1, .report_ids = { 0x02, }, - .in_report_lengths = { 4, 0, 0, 0, }, + .in_report_lengths = { 4, 0, 0, 0, 0, 0, }, .out_report_lengths = { 0, }, }; @@ -158,18 +158,30 @@ const usb_hid_device_obj_t usb_hid_device_consumer_control_obj = { .usage = 0x01, .num_report_ids = 1, .report_ids = { 0x03 }, - .in_report_lengths = { 2, 0, 0, 0 }, - .out_report_lengths = { 0 }, + .in_report_lengths = { 2, 0, 0, 0, 0, 0, }, + .out_report_lengths = { 0, }, }; - -bool common_hal_usb_hid_device_valid_report_id(usb_hid_device_obj_t *self, uint8_t report_id) { +STATIC size_t get_report_id_idx(usb_hid_device_obj_t *self, size_t report_id) { for (size_t i = 0; i < self->num_report_ids; i++) { if (report_id == self->report_ids[i]) { - return true; + return i; } } - return false; + return MAX_REPORT_IDS_PER_DESCRIPTOR; +} + +// See if report_id is used by this device. If it is -1, then return the sole report id used by this device, +// which might be 0 if no report_id was supplied. +uint8_t common_hal_usb_hid_device_validate_report_id(usb_hid_device_obj_t *self, mp_int_t report_id_arg) { + if (report_id_arg == -1 && self->num_report_ids == 1) { + return self->report_ids[0]; + } + if (!(report_id_arg >= 0 && + get_report_id_idx(self, (size_t)report_id_arg) < MAX_REPORT_IDS_PER_DESCRIPTOR)) { + mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_report_id); + } + return (uint8_t)report_id_arg; } void common_hal_usb_hid_device_construct(usb_hid_device_obj_t *self, mp_obj_t report_descriptor, uint8_t usage_page, uint8_t usage, size_t num_report_ids, uint8_t *report_ids, uint8_t *in_report_lengths, uint8_t *out_report_lengths) { @@ -183,7 +195,7 @@ void common_hal_usb_hid_device_construct(usb_hid_device_obj_t *self, mp_obj_t re mp_get_buffer_raise(report_descriptor, &bufinfo, MP_BUFFER_READ); self->report_descriptor_length = bufinfo.len; - // Copy the raw the descriptor bytes into a heap obj. We don't keep the Python descriptor object. + // Copy the raw descriptor bytes into a heap obj. We don't keep the Python descriptor object. uint8_t *descriptor_bytes = gc_alloc(bufinfo.len, false, false); memcpy(descriptor_bytes, bufinfo.buf, bufinfo.len); @@ -206,8 +218,12 @@ uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self) { } void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *report, uint8_t len, uint8_t report_id) { - if (len != self->in_report_length) { - mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."), self->in_report_length); + // report_id and len have already been validated for this device. + size_t id_idx = get_report_id_idx(self, report_id); + + if (len != self->in_report_lengths[id_idx]) { + mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."), + self->in_report_lengths[id_idx]); } // Wait until interface is ready, timeout = 2 seconds @@ -225,14 +241,25 @@ void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t * } } +mp_obj_t common_hal_usb_hid_device_get_last_received_report(usb_hid_device_obj_t *self, uint8_t report_id) { + // report_id has already been validated for this deveice. + size_t id_idx = get_report_id_idx(self, report_id); + return mp_obj_new_bytes(self->out_report_buffers[id_idx], self->out_report_lengths[id_idx]); +} void usb_hid_device_create_report_buffers(usb_hid_device_obj_t *self) { - for (size_t i = 0; i < self->num_report_ids; count++) { - if (self->out_report_length > 0) { - self->out_report_buffers[i] = self->out_report_lengths[i] > 0 - ? gc_alloc(self->out_report_length, false, true /*long-lived*/) - : NULL; - } + for (size_t i = 0; i < self->num_report_ids; i++) { + // The IN buffers are used only for tud_hid_get_report_cb(), + // which is an unusual case. Normally we can just pass the data directly with tud_hid_report(). + self->in_report_buffers[i] = + self->in_report_lengths[i] > 0 + ? gc_alloc(self->in_report_lengths[i], false, true /*long-lived*/) + : NULL; + + self->out_report_buffers[i] = + self->out_report_lengths[i] > 0 + ? gc_alloc(self->out_report_lengths[i], false, true /*long-lived*/) + : NULL; } } @@ -254,25 +281,27 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t memcpy(buffer, hid_device->in_report_buffers[id_idx], reqlen); return reqlen; } + return 0; +} // Callbacks invoked when we received Set_Report request through control endpoint - void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) { - (void)itf; - if (report_type == HID_REPORT_TYPE_INVALID) { - report_id = buffer[0]; - buffer++; - bufsize--; - } else if (report_type != HID_REPORT_TYPE_OUTPUT) { - return; - } +void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) { + (void)itf; + if (report_type == HID_REPORT_TYPE_INVALID) { + report_id = buffer[0]; + buffer++; + bufsize--; + } else if (report_type != HID_REPORT_TYPE_OUTPUT) { + return; + } - usb_hid_device_obj_t *hid_device; - size_t id_idx; - // Find device with this report id, and get the report id index. - if (usb_hid_get_device_with_report_id(report_id, &hid_device, &id_idx)) { - // If a report of the correct size has been read, save it in the proper OUT report buffer. - if (hid_device && hid_device->out_report_lengths[id_idx] >= bufsize) { - memcpy(hid_device->out_report_buffers[id_idx], buffer, bufsize); - } + usb_hid_device_obj_t *hid_device; + size_t id_idx; + // Find device with this report id, and get the report id index. + if (usb_hid_get_device_with_report_id(report_id, &hid_device, &id_idx)) { + // If a report of the correct size has been read, save it in the proper OUT report buffer. + if (hid_device && hid_device->out_report_lengths[id_idx] >= bufsize) { + memcpy(hid_device->out_report_buffers[id_idx], buffer, bufsize); } } +} diff --git a/shared-module/usb_hid/Device.h b/shared-module/usb_hid/Device.h index cc185d3272..4bdd51d104 100644 --- a/shared-module/usb_hid/Device.h +++ b/shared-module/usb_hid/Device.h @@ -32,7 +32,8 @@ #include "py/obj.h" -// The most complicated one currently know of is the head and eye tracker, which requires 5: +// The most complicated device currently known of is the head and eye tracker, which requires 5 +// report ids. // https://usb.org/sites/default/files/hutrr74_-_usage_page_for_head_and_eye_trackers_0.pdf #define MAX_REPORT_IDS_PER_DESCRIPTOR (6) @@ -43,6 +44,7 @@ typedef struct { uint8_t report_ids[MAX_REPORT_IDS_PER_DESCRIPTOR]; uint8_t in_report_lengths[MAX_REPORT_IDS_PER_DESCRIPTOR]; uint8_t out_report_lengths[MAX_REPORT_IDS_PER_DESCRIPTOR]; + uint8_t *in_report_buffers[MAX_REPORT_IDS_PER_DESCRIPTOR]; uint8_t *out_report_buffers[MAX_REPORT_IDS_PER_DESCRIPTOR]; uint16_t report_descriptor_length; uint8_t usage_page; diff --git a/shared-module/usb_hid/__init__.c b/shared-module/usb_hid/__init__.c index 6f903bb657..2be9a646d6 100644 --- a/shared-module/usb_hid/__init__.c +++ b/shared-module/usb_hid/__init__.c @@ -197,10 +197,6 @@ size_t usb_hid_report_descriptor_length(void) { total_hid_report_descriptor_length += hid_devices[i].report_descriptor_length; } - // Don't need space for a report id if there's only one device. - if (num_hid_devices == 1) { - total_hid_report_descriptor_length -= 2; - } return total_hid_report_descriptor_length; } @@ -215,91 +211,69 @@ void usb_hid_build_report_descriptor(uint8_t *report_descriptor_space, size_t re for (mp_int_t i = 0; i < num_hid_devices; i++) { usb_hid_device_obj_t *device = &hid_devices[i]; // Copy the report descriptor for this device. - if (num_hid_devices == 1 && device->num_report_ids == 1) { - // There's only one device, with one report id, so remove it. - // Copy the descriptor, but splice out the report id indicator and value (2 bytes). - size_t report_id_idx = 0; - for (report_id_idx = 0; report_id_idx < device->report_descriptor_length; report_id_idx++) { - if (report_descriptor_start[report_id_idx] == 0x85) { - break; - } - } - if (report_id_idx < device->report_descriptor_length) { + memcpy(report_descriptor_start, device->report_descriptor, device->report_descriptor_length); + // Advance to the next free chunk for the next report descriptor.x + report_descriptor_start += device->report_descriptor_length; - memcpy(report_descriptor_start, device->report_descriptor, device->report_id_index - 1); - report_descriptor_start += device->report_id_index - 1; - memcpy(report_descriptor_start, device->report_descriptor + device->report_id_index + 1, - device->report_descriptor_length - device->report_id_index - 1); - } else { - // Copy the whole descriptor and fill in the report id. - memcpy(report_descriptor_start, device->report_descriptor, device->report_descriptor_length); - report_descriptor_start[device->report_id_index] = i + 1; - - // Remember the report id that was assigned. - device->report_id = i + 1; - - // Advance to the next free chunk for the next report descriptor.x - report_descriptor_start += device->report_descriptor_length; - } - // Clear the heap pointer to the bytes of the descriptor. - // We don't need it any more and it will get lost when the heap goes away. - device->report_descriptor = NULL; - } + // Clear the heap pointer to the bytes of the descriptor. + // We don't need it any more and it will get lost when the heap goes away. + device->report_descriptor = NULL; } +} // Call this after the heap and VM are finished. - void usb_hid_save_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length) { - if (!usb_hid_enabled()) { - return; - } - - // Allocate storage that persists across VMs to hold the combined report descriptor. - // and to remember the device details. - - // Copy the descriptor from the temporary area to a supervisor storage allocation that - // will leave between VM instantiations. - hid_report_descriptor_allocation = - allocate_memory(align32_size(report_descriptor_length), - /*high_address*/ false, /*movable*/ false); - memcpy((uint8_t *)hid_report_descriptor_allocation->ptr, report_descriptor_space, report_descriptor_length); +void usb_hid_save_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length) { + if (!usb_hid_enabled()) { + return; } - void usb_hid_gc_collect(void) { - gc_collect_ptr(hid_devices_tuple); + // Allocate storage that persists across VMs to hold the combined report descriptor. + // and to remember the device details. - // Mark possible heap pointers in the static device list as in use. - for (mp_int_t device_idx = 0; i < num_hid_devices; i++) { + // Copy the descriptor from the temporary area to a supervisor storage allocation that + // will leave between VM instantiations. + hid_report_descriptor_allocation = + allocate_memory(align32_size(report_descriptor_length), + /*high_address*/ false, /*movable*/ false); + memcpy((uint8_t *)hid_report_descriptor_allocation->ptr, report_descriptor_space, report_descriptor_length); +} - // Cast away the const for .report_descriptor. It could be in flash or on the heap. - // Constant report descriptors must be const so that they are used directly from flash - // and not copied into RAM. - gc_collect_ptr((void *)hid_devices[device_idx].report_descriptor); +void usb_hid_gc_collect(void) { + gc_collect_ptr(hid_devices_tuple); - // Collect all the OUT report buffers for this device. - for (size_t id_idx = 0; id_idx < hid_devices[device_idx].report_ids_count; id_idx++) { - gc_collect_ptr(hid_devices[i].out_report_buffers[id_idx]); + // Mark possible heap pointers in the static device list as in use. + for (mp_int_t device_idx = 0; device_idx < num_hid_devices; device_idx++) { + + // Cast away the const for .report_descriptor. It could be in flash or on the heap. + // Constant report descriptors must be const so that they are used directly from flash + // and not copied into RAM. + gc_collect_ptr((void *)hid_devices[device_idx].report_descriptor); + + // Collect all the OUT report buffers for this device. + for (size_t id_idx = 0; id_idx < hid_devices[device_idx].num_report_ids; id_idx++) { + gc_collect_ptr(hid_devices[id_idx].out_report_buffers[id_idx]); + } + } +} + +bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t **device_out, size_t *id_idx_out) { + for (uint8_t device_idx = 0; device_idx < num_hid_devices; device_idx++) { + usb_hid_device_obj_t *device = &hid_devices[device_idx]; + for (size_t id_idx = 0; id_idx < device->num_report_ids; id_idx++) { + if (device->report_ids[id_idx] == report_id) { + *device_out = device; + *id_idx_out = id_idx; + return true; } } } - - bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t **device_out, size_t *id_idx_out) { - for (uint8_t device_idx = 0; device_idx < num_hid_devices; device_idx++) { - usb_hid_device_obj_t *device = &hid_devices[device_idx]; - for (size_t id_idx = 0; id_idx < device->report_ids_count; id_idx++) { - if (device->report_ids[id_idx] == report_id) { - *device_out = device; - *id_idx_out = id_idx; - return true; - } - } - } - return false; - } + return false; +} // Invoked when GET HID REPORT DESCRIPTOR is received. // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete - uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf) { - return (uint8_t *)hid_report_descriptor_allocation->ptr; - } +uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf) { + return (uint8_t *)hid_report_descriptor_allocation->ptr; +}