From 2b4c88d633eaa1f2ec1771d3547eef275d9b850c Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 23 Apr 2021 00:18:05 -0400 Subject: [PATCH] wip: partial HID, still needs rework --- locale/circuitpython.pot | 23 ++++- main.c | 4 + shared-bindings/usb_hid/Device.c | 90 +++++++++++++++++- shared-bindings/usb_hid/Device.h | 1 + shared-bindings/usb_hid/__init__.c | 38 +++++++- shared-module/storage/__init__.c | 8 +- shared-module/usb_hid/Device.c | 141 ++++++++++++++++++++++++++++- shared-module/usb_hid/Device.h | 23 ++--- shared-module/usb_hid/__init__.c | 35 ++++++- shared-module/usb_hid/__init__.h | 36 ++++++++ shared-module/usb_midi/__init__.c | 20 ++-- supervisor/shared/usb/usb.c | 5 + supervisor/shared/usb/usb_desc.c | 38 +++++--- supervisor/usb.h | 2 + 14 files changed, 407 insertions(+), 57 deletions(-) create mode 100644 shared-module/usb_hid/__init__.h diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 858152b52b..b8586ff136 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -87,6 +87,14 @@ msgstr "" msgid "%q list must be a list" msgstr "" +#: shared-bindings/usb_hid/Device.c +msgid "%q must be 1-255" +msgstr "" + +#: shared-bindings/usb_hid/Device.c +msgid "%q must be > 1-255" +msgstr "" + #: shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "" @@ -99,6 +107,14 @@ msgstr "" msgid "%q must be >= 1" msgstr "" +#: shared-bindings/usb_hid/Device.c +msgid "%q must be None or 1-255" +msgstr "" + +#: shared-bindings/usb_hid/Device.c +msgid "%q must be None or > 1-255" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "" @@ -600,7 +616,8 @@ msgstr "" msgid "Can't set CCCD on local Characteristic" msgstr "" -#: shared-bindings/usb_cdc/__init__.c shared-bindings/usb_midi/__init__.c +#: shared-bindings/usb_cdc/__init__.c shared-bindings/usb_hid/__init__.c +#: shared-bindings/usb_midi/__init__.c msgid "Cannot change USB devices now" msgstr "" @@ -3470,6 +3487,10 @@ msgstr "" msgid "no such attribute" msgstr "" +#: shared-bindings/usb_hid/__init__.c +msgid "non-Device in %q" +msgstr "" + #: ports/nrf/common-hal/_bleio/Connection.c msgid "non-UUID found in service_uuids_whitelist" msgstr "" diff --git a/main.c b/main.c index 9f83bb370c..4ec6b5ccb1 100755 --- a/main.c +++ b/main.c @@ -641,6 +641,10 @@ void gc_collect(void) { background_callback_gc_collect(); + #if CIRCUITPY_USB + usb_gc_collect(); + #endif + #if CIRCUITPY_ALARM common_hal_alarm_gc_collect(); #endif diff --git a/shared-bindings/usb_hid/Device.c b/shared-bindings/usb_hid/Device.c index cd4f3dd54b..8b205934e3 100644 --- a/shared-bindings/usb_hid/Device.c +++ b/shared-bindings/usb_hid/Device.c @@ -39,10 +39,88 @@ //| mouse.send_report()""" //| -//| def __init__(self) -> None: -//| """Not currently dynamically supported.""" +//| def __init__(self, *, descriptor: ReadableBuffer, usage_page: int, usage: int, in_report_length: int, out_report_length: Optional[int], , report_id_index: Optional[int]) -> None: +//| """Create a description of a USB HID device. To create an actual device, +//| pass a `Device` to `usb_hid.configure_usb()`. +//| +//| :param ReadableBuffer descriptor: The USB HID Report descriptor bytes. The descriptor is not +//| not verified for correctness; it is up to you to make sure it is not malformed. +//| :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 in_report_length: Size in bytes of the HID report sent to the host. +//| "In" is with respect to the host. +//| :param int out_report_length: Size in bytes of the HID report received from the host. +//| "Out" is with respect to the host. If no reports are expected, use ``None``. +//| :param int report_id_index: position of byte in descriptor that contains the Report ID. +//| A Report ID will be assigned when the device is created. If there is no +//| Report ID, use ``None``. //| ... //| +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) { + usb_hid_device_obj_t *self = m_new_obj(usb_hid_device_obj_t); + self->base.type = &usb_hid_device_type; + enum { ARG_descriptor, ARG_usage, ARG_usage_page, ARG_in_report_length, ARG_out_report_length, ARG_report_id_index }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_descriptor, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_usage_page, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_usage, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_in_report_length, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT } , + { MP_QSTR_out_report_length, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none } }, + { MP_QSTR_report_id_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(descriptor, &bufinfo, MP_BUFFER_READ); + + bytearray_obj_t descriptor = mp_obj_array_t new_bytearray(bufinfo.len, bufinfo.buf); + + const mp_int_t usage_page_arg = args[ARG_usage_page].u_int; + if (usage_page_arg <= 0 || usage_arg > 255) { + mp_raise_ValueError_varg(translate("%q must be 1-255"), MP_QSTR_usage_page); + } + const uint8_t usage_page = usage_page_arg; + + const mp_int_t usage_arg = args[ARG_usage].u_int; + if (usage_arg <= 0 || usage_arg > 255) { + mp_raise_ValueError_varg(translate("%q must be 1-255"), MP_QSTR_usage); + } + const uint8_t usage = usage_arg; + + const mp_int_t in_report_length_arg = args[ARG_in_report_length].u_int; + if (in_report_length_arg <= 0 || in_report_length_arg > 255) { + mp_raise_ValueError_varg(translate("%q must be > 1-255"), MP_QSTR_in_report_length); + } + const uint8_t in_report_length = in_report_length_arg; + + const mp_obj_t out_report_length_arg = args[ARG_out_report_length].u_obj; + if (out_report_length_arg == mp_const_none) { + self->out_report_length = 0; + } + else if (!MP_OBJ_IS_SMALL_INT(out_report_length_arg) || + MP_OBJ_SMALL_INT_VALUE(out_report_length_arg) <= 0 || + MP_OBJ_SMALL_INT_VALUE(out_report_length_arg) > 255) { + mp_raise_ValueError_varg(translate("%q must be None or > 1-255"), MP_QSTR_out_report_length); + } + uint8_t out_report_length = MP_OBJ_SMALL_INT_VALUE(out_report_length_arg); + + const mp_obj_t report_id_index_arg = args[ARG_report_id_index].u_obj; + if (report_id_index_arg == mp_const_none) { + self->report_id_index = 0; + } + else if (!MP_OBJ_IS_SMALL_INT(report_id_index_arg) || + MP_OBJ_SMALL_INT_VALUE(report_id_index_arg) <= 0 || + MP_OBJ_SMALL_INT_VALUE(report_id_index_arg) > 255 ) { + mp_raise_ValueError_varg(translate("%q must be None or 1-255"), MP_QSTR_report_id_index); + } + uint8_t report_id_index = MP_OBJ_SMALL_INT_VALUE(report_id_index_arg); + + common_hal_usb_hid_device_construct(self, descriptor, usage_page, usage, in_report_length, out_report_length, report_id_index); +} + + //| def send_report(self, buf: ReadableBuffer) -> None: //| """Send a HID report.""" //| ... @@ -116,8 +194,11 @@ const mp_obj_property_t usb_hid_device_usage_obj = { STATIC const mp_rom_map_elem_t usb_hid_device_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_send_report), MP_ROM_PTR(&usb_hid_device_send_report_obj) }, { MP_ROM_QSTR(MP_QSTR_last_received_report), MP_ROM_PTR(&usb_hid_device_last_received_report_obj) }, - { MP_ROM_QSTR(MP_QSTR_usage_page), MP_ROM_PTR(&usb_hid_device_usage_page_obj)}, - { MP_ROM_QSTR(MP_QSTR_usage), MP_ROM_PTR(&usb_hid_device_usage_obj)}, + { MP_ROM_QSTR(MP_QSTR_usage_page), MP_ROM_PTR(&usb_hid_device_usage_page_obj) }, + { MP_ROM_QSTR(MP_QSTR_usage), MP_ROM_PTR(&usb_hid_device_usage_obj) }, + { MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&usb_hid_device_keyboard_obj) }, + { MP_ROM_QSTR(MP_QSTR_MOUSE), MP_ROM_PTR(&usb_hid_device_mouse_obj) }, + { MP_ROM_QSTR(MP_QSTR_CONSUMER_CONTROL), MP_ROM_PTR(&usb_hid_device_consumer_control_obj) }, }; STATIC MP_DEFINE_CONST_DICT(usb_hid_device_locals_dict, usb_hid_device_locals_dict_table); @@ -125,5 +206,6 @@ STATIC MP_DEFINE_CONST_DICT(usb_hid_device_locals_dict, usb_hid_device_locals_di const mp_obj_type_t usb_hid_device_type = { { &mp_type_type }, .name = MP_QSTR_Device, + .make_new = usb_hid_device_make_new, .locals_dict = (mp_obj_t)&usb_hid_device_locals_dict, }; diff --git a/shared-bindings/usb_hid/Device.h b/shared-bindings/usb_hid/Device.h index d9918d4060..c9ca3e16f0 100644 --- a/shared-bindings/usb_hid/Device.h +++ b/shared-bindings/usb_hid/Device.h @@ -31,6 +31,7 @@ extern const mp_obj_type_t usb_hid_device_type; +void common_hal_usb_hid_device_construct(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 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); diff --git a/shared-bindings/usb_hid/__init__.c b/shared-bindings/usb_hid/__init__.c index 1a2f28d431..6558c98e64 100644 --- a/shared-bindings/usb_hid/__init__.c +++ b/shared-bindings/usb_hid/__init__.c @@ -39,10 +39,42 @@ //| devices: Tuple[Device, ...] //| """Tuple of all active HID device interfaces.""" //| + +//| def configure_usb(devices: Sequence[Device, ...]=) -> None: +//| """Configure the USB HID devices that will be available. +//| Can be called in ``boot.py``, before USB is connected. +//| +//| :param Sequence devices: `Device` objects. +//| If `devices` is empty, HID is disabled. The order of the ``Devices`` +//| may matter to the host. For instance, for MacOS, put the mouse device +//| before any Gamepad or Digitizer HID device. +//| ... +//| +STATIC mp_obj_t usb_hid_configure_usb(mp_obj_t devices) { + + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(devices, &iter_buf); + mp_obj_t device; + while ((device = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (!MP_OBJ_IS_TYPE(item, &usb_hid_device_type)) { + mp_raise_ValueError_varg(translate("non-Device in %q", MP_QSTR_devices)); + } + } + + if (!common_hal_usb_hid_configure_usb(descriptors)) { + mp_raise_RuntimeError(translate("Cannot change USB devices now")); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_hid_configure_usb_obj, usb_hid_configure_usb); + + STATIC const mp_rom_map_elem_t usb_hid_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid) }, - { MP_ROM_QSTR(MP_QSTR_devices), MP_ROM_PTR(&common_hal_usb_hid_devices) }, - { MP_ROM_QSTR(MP_QSTR_Device), MP_ROM_PTR(&usb_hid_device_type) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid) }, + { MP_ROM_QSTR(MP_QSTR_configure_usb), MP_OBJ_FROM_PTR(&usb_midi_configure_usb_obj) }, + { MP_ROM_QSTR(MP_QSTR_devices), MP_ROM_PTR(&common_hal_usb_hid_devices) }, + { MP_ROM_QSTR(MP_QSTR_Device), MP_ROM_PTR(&usb_hid_device_type) }, }; STATIC MP_DEFINE_CONST_DICT(usb_hid_module_globals, usb_hid_module_globals_table); diff --git a/shared-module/storage/__init__.c b/shared-module/storage/__init__.c index 368e30ac6d..8f361cb2d1 100644 --- a/shared-module/storage/__init__.c +++ b/shared-module/storage/__init__.c @@ -46,20 +46,20 @@ static const uint8_t storage_usb_msc_descriptor_template[] = { 0x09, // 0 bLength 0x04, // 1 bDescriptorType (Interface) 0xFF, // 2 bInterfaceNumber [SET AT RUNTIME] -#define MSC_INTERFACE_INDEX 2 +#define MSC_INTERFACE_INDEX (2) 0x00, // 3 bAlternateSetting 0x02, // 4 bNumEndpoints 2 0x08, // 5 bInterfaceClass: MSC 0x06, // 6 bInterfaceSubClass: TRANSPARENT 0x50, // 7 bInterfaceProtocol: BULK 0xFF, // 8 iInterface (String Index) [SET AT RUNTIME] -#define MSC_INTERFACE_STRING_INDEX 8 +#define MSC_INTERFACE_STRING_INDEX (8) // MSC Endpoint IN Descriptor 0x07, // 9 bLength 0x05, // 10 bDescriptorType (Endpoint) 0xFF, // 11 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number] -#define MSC_IN_ENDPOINT_INDEX 11 +#define MSC_IN_ENDPOINT_INDEX (11) 0x02, // 12 bmAttributes (Bulk) 0x40, 0x00, // 13,14 wMaxPacketSize 64 0x00, // 15 bInterval 0 (unit depends on device speed) @@ -68,7 +68,7 @@ static const uint8_t storage_usb_msc_descriptor_template[] = { 0x07, // 16 bLength 0x05, // 17 bDescriptorType (Endpoint) 0xFF, // 18 bEndpointAddress (OUT/H2D) [SET AT RUNTIME] -#define MSC_OUT_ENDPOINT_INDEX 18 +#define MSC_OUT_ENDPOINT_INDEX (18) 0x02, // 19 bmAttributes (Bulk) 0x40, 0x00, // 20,21 wMaxPacketSize 64 0x00, // 22 bInterval 0 (unit depends on device speed) diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c index 4f392847ff..d87fe3dff4 100644 --- a/shared-module/usb_hid/Device.c +++ b/shared-module/usb_hid/Device.c @@ -33,6 +33,137 @@ #include "supervisor/shared/tick.h" #include "tusb.h" +static const uint8_t keyboard_descriptor[] = { + 0x05, 0x01, // 0,1 Usage Page (Generic Desktop Ctrls) + 0x09, 0x06, // 2,3 Usage (Keyboard) + 0xA1, 0x01, // 4,5 Collection (Application) + 0x85, 0xFF, // 6,7 Report ID [SET AT RUNTIME] +#define KEYBOARD_REPORT_ID_INDEX (7) + 0x05, 0x07, // Usage Page (Kbrd/Keypad) + 0x19, 0xE0, // Usage Minimum (0xE0) + 0x29, 0xE7, // Usage Maximum (0xE7) + 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,No Wrap,Linear,Preferred State,No Null Position) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x19, 0x00, // Usage Minimum (0x00) + 0x29, 0xDD, // Usage Maximum (0xDD) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0xDD, // Logical Maximum (-35) + 0x75, 0x08, // Report Size (8) + 0x95, 0x06, // Report Count (6) + 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x08, // Usage Page (LEDs) + 0x19, 0x01, // Usage Minimum (Num Lock) + 0x29, 0x05, // Usage Maximum (Kana) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x05, // Report Count (5) + 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x95, 0x03, // Report Count (3) + 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, // End Collection + }, + +const usb_hid_device_obj_t usb_hid_device_keyboard_obj = { + .descriptor = keyboard_descriptor, + .descriptor_length = sizeof(keyboard_descriptor), + .usage_page = 0x01 + .usage = 0x06, + .in_report_length = 8, + .out_report_length = 1, + .report_id_index = KEYBOARD_REPORT_ID_INDEX, + +}; + +static const uint8_t mouse_descriptor[] = { + 0x05, 0x01, // 0,1 Usage Page (Generic Desktop Ctrls) + 0x09, 0x02, // 2,3 Usage (Mouse) + 0xA1, 0x01, // 4,5 Collection (Application) + 0x09, 0x01, // 6,7 Usage (Pointer) + 0xA1, 0x00, // 8,9 Collection (Physical) + 0x85, 0xFF, // 10, 11 Report ID [SET AT RUNTIME] +#define MOUSE_REPORT_ID_INDEX (11) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (0x01) + 0x29, 0x05, // Usage Maximum (0x05) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x95, 0x05, // Report Count (5) + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x01, // Report Count (1) + 0x75, 0x03, // Report Size (3) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x02, // Report Count (2) + 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0x09, 0x38, // Usage (Wheel) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection + 0xC0, // End Collection +}; + +const usb_hid_device_obj_t usb_hid_device_mouse_obj = { + .descriptor = mouse_descriptor, + .descriptor_length = sizeof(mouse_descriptor), + .usage_page = 0x01 + .usage = 0x02, + .in_report_length = 4, + .out_report_length = 0, + .descriptor = { + .report_id_index = MOUSE_REPORT_ID_INDEX, +}; + +static const uint8_t consumer_control_descriptor[] = { + 0x05, 0x0C, // 0,1 Usage Page (Consumer) + 0x09, 0x01, // 2,3 Usage (Consumer Control) + 0xA1, 0x01, // 4,5 Collection (Application) + 0x85, 0xFF, // 6,7 Report ID [SET AT RUNTIME] +#define CONSUMER_CONTROL_REPORT_ID_INDEX (7) + 0x75, 0x10, // Report Size (16) + 0x95, 0x01, // Report Count (1) + 0x15, 0x01, // Logical Minimum (1) + 0x26, 0x8C, 0x02, // Logical Maximum (652) + 0x19, 0x01, // Usage Minimum (Consumer Control) + 0x2A, 0x8C, 0x02, // Usage Maximum (AC Send) + 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection +}; + +const usb_hid_device_obj_t usb_hid_device_consumer_control_obj = { + .descriptor = consumer_control_descriptor, + .descriptor_length = sizeof(consumer_control_descriptor), + .usage_page = 0x0C + .usage = 0x01, + .in_report_length = 2, + .out_report_length = 0, + .report_id_index = CONSUMER_CONTROL_REPORT_ID_INDEX, +}; + + +void common_hal_usb_hid_device_construct(usb_hid_dev_obj_t *self, mp_obj_array_t *descriptor, uint8_t usage_page, uint8_t usage, uint8_t in_report_length, uint8_t out_report_length, uint8_t report_id_index) { + // report buffer pointers are NULL at start, and are created on demand. + self->descriptor = descriptor; + self->usage_page = usage_page; + self->usage = usage; + self->in_report_length = in_report_length; + self->out_report_length = out_report_length; + self->report_id_index = report_id_index; +} + uint8_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self) { return self->usage_page; } @@ -42,8 +173,8 @@ 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) { - if (len != self->report_length) { - mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."), self->report_length); + if (len != self->in_report_length) { + mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."), self->in_report_length); } // Wait until interface is ready, timeout = 2 seconds @@ -56,9 +187,9 @@ void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t * mp_raise_msg(&mp_type_OSError, translate("USB Busy")); } - memcpy(self->report_buffer, report, len); + memcpy(self->in_report_buffer, report, len); - if (!tud_hid_report(self->report_id, self->report_buffer, len)) { + if (!tud_hid_report(self->in_report_id, self->in_report_buffer, len)) { mp_raise_msg(&mp_type_OSError, translate("USB Error")); } } @@ -81,7 +212,7 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t } // fill buffer with current report - memcpy(buffer, get_hid_device(report_id)->report_buffer, reqlen); + memcpy(buffer, get_hid_device(report_id)->in_report_buffer, reqlen); return reqlen; } diff --git a/shared-module/usb_hid/Device.h b/shared-module/usb_hid/Device.h index bf4e9d9adb..5b2caf369f 100644 --- a/shared-module/usb_hid/Device.h +++ b/shared-module/usb_hid/Device.h @@ -32,26 +32,21 @@ #include "py/obj.h" -#ifdef __cplusplus -extern "C" { -#endif - typedef struct { mp_obj_base_t base; - uint8_t *report_buffer; - uint8_t report_id; - uint8_t report_length; + uint8_t *in_report_buffer; + uint8_t *out_report_buffer; + uint8_t *descriptor; + uint16_t descriptor_length; uint8_t usage_page; uint8_t usage; - uint8_t *out_report_buffer; + uint8_t report_id; + uint8_t in_report_length; uint8_t out_report_length; } usb_hid_device_obj_t; - -extern usb_hid_device_obj_t usb_hid_devices[]; - -#ifdef __cplusplus -} -#endif +extern usb_hid_device_obj_t usb_hid_device_keyboard_obj; +extern usb_hid_device_obj_t usb_hid_device_mouse_obj; +extern usb_hid_device_mouse_obj usb_hid_device_consumer_control_obj; #endif /* SHARED_MODULE_USB_HID_DEVICE_H */ diff --git a/shared-module/usb_hid/__init__.c b/shared-module/usb_hid/__init__.c index 5403b7fcef..f234709df6 100644 --- a/shared-module/usb_hid/__init__.c +++ b/shared-module/usb_hid/__init__.c @@ -32,12 +32,12 @@ static const uint8_t usb_hid_descriptor_template[] = { 0x01, // 4 bNumDescriptors 0x22, // 5 bDescriptorType[0] (HID) 0xFF, 0xFF, // 6,7 wDescriptorLength[0] [SET AT RUNTIME: lo, hi] -#define HID_DESCRIPTOR_LENGTH_INDEX 6 +#define HID_DESCRIPTOR_LENGTH_INDEX (6) 0x07, // 8 bLength 0x05, // 9 bDescriptorType (Endpoint) 0xFF, // 10 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | endpoint] -#define HID_IN_ENDPOINT_INDEX 10 +#define HID_IN_ENDPOINT_INDEX (10) 0x03, // 11 bmAttributes (Interrupt) 0x40, 0x00, // 12,13 wMaxPacketSize 64 0x08, // 14 bInterval 8 (unit depends on device speed) @@ -45,7 +45,7 @@ static const uint8_t usb_hid_descriptor_template[] = { 0x07, // 15 bLength 0x05, // 16 bDescriptorType (Endpoint) 0xFF, // 17 bEndpointAddress (OUT/H2D) [SET AT RUNTIME] -#define HID_OUT_ENDPOINT_INDEX 17 +#define HID_OUT_ENDPOINT_INDEX (17) 0x03, // 18 bmAttributes (Interrupt) 0x40, 0x00, // 19,20 wMaxPacketSize 64 0x08, // 21 bInterval 8 (unit depends on device speed) @@ -53,6 +53,7 @@ static const uint8_t usb_hid_descriptor_template[] = { // Is the HID device enabled? bool usb_hid_enabled; +mp_obj_t usb_hid_devices; size_t usb_hid_descriptor_length(void) { return sizeof(usb_hid_descriptor); @@ -72,3 +73,31 @@ size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, uint8_t *current_interfac return sizeof(usb_hid_descriptor_template); } + +void usb_hid_build_report_descriptor() { +} + +bool common_hal_usb_hid_configure_usb(mp_obj_t devices) { + // We can't change the devices once we're connected. + if (tud_connected()) { + return false; + } + + // Assume no devices to start. + usb_hid_enabled = false; + if (devices == mp_const_none) { + return true; + } +} + +void usb_hid_build_report + +void usb_hid_gc_collect(void) { + // Once tud_mounted() is true, we're done with the constructed descriptors. + if (tud_mounted()) { + // GC will pick up the inaccessible blocks. + usb_hid_devices_to_configure = NULL; + } else { + gc_collect_ptr(usb_hid_devices); + } +} diff --git a/shared-module/usb_hid/__init__.h b/shared-module/usb_hid/__init__.h new file mode 100644 index 0000000000..6eab014459 --- /dev/null +++ b/shared-module/usb_hid/__init__.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SHARED_MODULE_USB_HID___INIT___H +#define SHARED_MODULE_USB_HID___INIT___H + +extern bool usb_hid_enabled; +extern usb_hid_device_obj_t usb_hid_devices[]; + +void usb_hid_gc_collect(void); + + +#endif // SHARED_MODULE_USB_HID___INIT___H diff --git a/shared-module/usb_midi/__init__.c b/shared-module/usb_midi/__init__.c index 9fa95dbe22..d6abf7c9e5 100644 --- a/shared-module/usb_midi/__init__.c +++ b/shared-module/usb_midi/__init__.c @@ -43,14 +43,14 @@ static const uint8_t usb_midi_descriptor_template[] = { 0x09, // 0 bLength 0x04, // 1 bDescriptorType (Interface) 0xFF, // 2 bInterfaceNumber [SET AT RUNTIME] -#define MIDI_AUDIO_CONTROL_INTERFACE_NUMBER_INDEX 2 +#define MIDI_AUDIO_CONTROL_INTERFACE_NUMBER_INDEX (2) 0x00, // 3 bAlternateSetting 0x00, // 4 bNumEndpoints 0 0x01, // 5 bInterfaceClass (Audio) 0x01, // 6 bInterfaceSubClass (Audio Control) 0x00, // 7 bInterfaceProtocol 0xFF, // 8 iInterface (String Index) [SET AT RUNTIME] -#define MIDI_AUDIO_CONTROL_INTERFACE_STRING_INDEX 8 +#define MIDI_AUDIO_CONTROL_INTERFACE_STRING_INDEX (8) // Audio10 Control Interface Descriptor 0x09, // 9 bLength @@ -60,20 +60,20 @@ static const uint8_t usb_midi_descriptor_template[] = { 0x09, 0x00, // 14,15 wTotalLength 9 0x01, // 16 binCollection 0x01 0xFF, // 17 baInterfaceNr [SET AT RUNTIME: one-element list: same as 20] -#define MIDI_STREAMING_INTERFACE_NUMBER_INDEX_2 17 +#define MIDI_STREAMING_INTERFACE_NUMBER_INDEX_2 (17) // MIDI Streaming Interface Descriptor 0x09, // 18 bLength 0x04, // 19 bDescriptorType (Interface) 0xFF, // 20 bInterfaceNumber [SET AT RUNTIME] -#define MIDI_STREAMING_INTERFACE_NUMBER_INDEX 20 +#define MIDI_STREAMING_INTERFACE_NUMBER_INDEX (20) 0x00, // 21 bAlternateSetting 0x02, // 22 bNumEndpoints 2 0x01, // 23 bInterfaceClass (Audio) 0x03, // 24 bInterfaceSubClass (MIDI Streaming) 0x00, // 25 bInterfaceProtocol 0xFF, // 26 iInterface (String Index) [SET AT RUNTIME] -#define MIDI_STREAMING_INTERFACE_STRING_INDEX 26 +#define MIDI_STREAMING_INTERFACE_STRING_INDEX (26) // MIDI Header Descriptor 0x07, // 27 bLength @@ -89,7 +89,7 @@ static const uint8_t usb_midi_descriptor_template[] = { 0x01, // 37 bJackType: EMBEDDED 0x01, // 38 id (always 1) 0xFF, // 39 iJack (String Index) [SET AT RUNTIME] -#define MIDI_IN_JACK_STRING_INDEX 39 +#define MIDI_IN_JACK_STRING_INDEX (39) // MIDI External In Jack Descriptor 0x06, // 40 bLength @@ -109,7 +109,7 @@ static const uint8_t usb_midi_descriptor_template[] = { 0x02, // 52 BaSourceID(1) (always 2) 0x01, // 53 BaSourcePin(1) (always 1) 0xFF, // 54 iJack (String Index) [SET AT RUNTIME] -#define MIDI_OUT_JACK_STRING_INDEX 54 +#define MIDI_OUT_JACK_STRING_INDEX (54) // MIDI External Out Jack Descriptor 0x09, // 55 bLength @@ -126,9 +126,9 @@ static const uint8_t usb_midi_descriptor_template[] = { 0x07, // 64 bLength 0x05, // 65 bDescriptorType (EndPoint) 0xFF, // 66 bEndpointAddress (OUT/H2D) [SET AT RUNTIME] -#define MIDI_STREAMING_OUT_ENDPOINT_INDEX 66 +#define MIDI_STREAMING_OUT_ENDPOINT_INDEX (66) 0x02, // 67 bmAttributes (Bulk) - 0x40, 0x00, // 68,69 wMaxPacketSize 64 + 0x40, 0x00, // 68,69 wMaxPacketSize (64) 0x00, // 70 bInterval 0 (unit depends on device speed) // MIDI Data Endpoint Descriptor @@ -142,7 +142,7 @@ static const uint8_t usb_midi_descriptor_template[] = { 0x07, // 76 bLength 0x05, // 77 bDescriptorType: Endpoint 0xFF, // 78 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x8 | number] -#define MIDI_STREAMING_IN_ENDPOINT_INDEX 78 +#define MIDI_STREAMING_IN_ENDPOINT_INDEX (78) 0x02, // 79 bmAttributes (Bulk) 0x40, 0x00, // 8081 wMaxPacketSize 64 0x00, // 82 bInterval 0 (unit depends on device speed) diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 0605b3fc93..5ce5083b17 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -109,6 +109,11 @@ void usb_irq_handler(void) { usb_background_schedule(); } +void usb_gc_collect(void) { + usb_desc_gc_collect(); + usb_hid_gc_collect(); +} + // --------------------------------------------------------------------+ // tinyusb callbacks // --------------------------------------------------------------------+ diff --git a/supervisor/shared/usb/usb_desc.c b/supervisor/shared/usb/usb_desc.c index 516c9d5e8b..d77047c472 100644 --- a/supervisor/shared/usb/usb_desc.c +++ b/supervisor/shared/usb/usb_desc.c @@ -46,9 +46,9 @@ #include "genhdr/autogen_usb_descriptor.h" -// ******* TODO PROTECT AGAINST GC. static uint8_t *device_descriptor; static uint8_t *config_descriptor; +static uint8_t *hid_report_descriptor; // Table for collecting interface strings (interface names) as descriptor is built. #define MAX_INTERFACE_STRINGS 16 @@ -71,18 +71,18 @@ static const uint8_t device_descriptor_template[] = { 0x00, // 6 bDeviceProtocol 0x40, // 7 bMaxPacketSize0 64 0x9A, 0x23, // 8,9 idVendor [SET AT RUNTIME: lo,hi] -#define DEVICE_VID_LO_INDEX 8 -#define DEVICE_VID_HI_INDEX 9 +#define DEVICE_VID_LO_INDEX (8) +#define DEVICE_VID_HI_INDEX (9) 0x, 0xFF, // 10,11 idProduct [SET AT RUNTIME: lo,hi] -#define DEVICE PID_LO_INDEX 10 -#define DEVICE PID_HI_INDEX 11 +#define DEVICE PID_LO_INDEX (10) +#define DEVICE PID_HI_INDEX (11) 0x00, 0x01, // 12,13 bcdDevice 2.00 0x02, // 14 iManufacturer (String Index) [SET AT RUNTIME] -#define DEVICE_MANUFACTURER_STRING_INDEX 14 +#define DEVICE_MANUFACTURER_STRING_INDEX (14) 0x03, // 15 iProduct (String Index) [SET AT RUNTIME] -#define DEVICE_PRODUCT_STRING_INDEX 15 +#define DEVICE_PRODUCT_STRING_INDEX (15) 0x01, // 16 iSerialNumber (String Index) [SET AT RUNTIME] -#define DEVICE_SERIAL_NUMBER_STRING_INDEX 16 +#define DEVICE_SERIAL_NUMBER_STRING_INDEX (16) 0x01, // 17 bNumConfigurations 1 }; @@ -90,10 +90,10 @@ static const uint8_t configuration_descriptor_template[] = { 0x09, // 0 bLength 0x02, // 1 bDescriptorType (Configuration) 0xFF, 0xFF, // 2,3 wTotalLength [SET AT RUNTIME: lo, hi] -#define CONFIG_TOTAL_LENGTH_LO_INDEX 2 -#define CONFIG_TOTAL_LENGTH_HI_INDEX 3 +#define CONFIG_TOTAL_LENGTH_LO_INDEX (2) +#define CONFIG_TOTAL_LENGTH_HI_INDEX (3) 0xFF, // 4 bNumInterfaces [SET AT RUNTIME] -#define CONFIG_NUM_INTERFACES_INDEX 4 +#define CONFIG_NUM_INTERFACES_INDEX (4) 0x01, // 5 bConfigurationValue 0x00, // 6 iConfiguration (String Index) 0x80, // 7 bmAttributes @@ -254,7 +254,20 @@ void usb_add_interface_string(uint8_t interface_string_index, const char[] str) } -void usb_ +void usb_desc_gc_collect(void) { + // Once tud_mounted() is true, we're done with the constructed descriptors. + if (tud_mounted()) { + // GC will pick up the inaccessible blocks. + device_descriptor = NULL; + configuration_descriptor = NULL; + hid_report_descriptors = NULL; + } else { + gc_collect_ptr(device_descriptor); + gc_collect_ptr(configuration_descriptor); + gc_collect_ptr(hid_report_descriptors); // Collects children too. + } +} + // Invoked when GET DEVICE DESCRIPTOR is received. // Application return pointer to descriptor @@ -275,7 +288,6 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { // 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) { - (void)itf; return hid_report_descriptor; } #endif diff --git a/supervisor/usb.h b/supervisor/usb.h index c8a699065d..9e3e14c2fd 100644 --- a/supervisor/usb.h +++ b/supervisor/usb.h @@ -52,8 +52,10 @@ void post_usb_init(void); bool usb_enabled(void); void usb_init(void); void usb_disconnect(void); +void usb_gc_collect(void); void usb_add_interface_string(uint8_t interface_string_index, const char[] str); +void usb_desc_gc_collect(void); void usb_desc_init(void); void usb_build_device_descriptor(uint16_t vid, uint16_t pid, uint8_t *current_interface_string); void usb_build_configuration_descriptor(uint16_t total_length, uint8_t num_interfaces);