Merge pull request #5151 from dhalbert/usb_hid-changes
Support multiple reports per device in usb_hid
This commit is contained in:
commit
5b0009cbc4
@ -91,6 +91,10 @@ msgstr ""
|
|||||||
msgid "%q length must be %q"
|
msgid "%q length must be %q"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-bindings/usb_hid/Device.c
|
||||||
|
msgid "%q length must be >= 1"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/vectorio/Polygon.c
|
#: shared-bindings/vectorio/Polygon.c
|
||||||
msgid "%q list must be a list"
|
msgid "%q list must be a list"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -103,14 +107,6 @@ msgstr ""
|
|||||||
msgid "%q must be %d-%d"
|
msgid "%q must be %d-%d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/usb_hid/Device.c
|
|
||||||
msgid "%q must be 0-255"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: shared-bindings/usb_hid/Device.c
|
|
||||||
msgid "%q must be 1-255"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: py/argcheck.c
|
#: py/argcheck.c
|
||||||
msgid "%q must be >= %d"
|
msgid "%q must be >= %d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -127,10 +123,6 @@ msgstr ""
|
|||||||
msgid "%q must be >= 1"
|
msgid "%q must be >= 1"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/usb_hid/Device.c
|
|
||||||
msgid "%q must be None or between 1 and len(report_descriptor)-1"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: py/argcheck.c
|
#: py/argcheck.c
|
||||||
msgid "%q must be a string"
|
msgid "%q must be a string"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -168,6 +160,10 @@ msgstr ""
|
|||||||
msgid "%q() takes %d positional arguments but %d were given"
|
msgid "%q() takes %d positional arguments but %d were given"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-bindings/usb_hid/Device.c
|
||||||
|
msgid "%q, %q, and %q must all be the same length"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c
|
#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s error 0x%x"
|
msgid "%s error 0x%x"
|
||||||
@ -1234,7 +1230,7 @@ msgstr ""
|
|||||||
msgid "Internal error #%d"
|
msgid "Internal error #%d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/sdioio/SDCard.c
|
#: shared-bindings/sdioio/SDCard.c shared-module/usb_hid/Device.c
|
||||||
msgid "Invalid %q"
|
msgid "Invalid %q"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1518,6 +1514,11 @@ msgstr ""
|
|||||||
msgid "Missing jmp_pin. Instruction %d jumps on pin"
|
msgid "Missing jmp_pin. Instruction %d jumps on pin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/usb_hid/Device.c
|
||||||
|
#, c-format
|
||||||
|
msgid "More than %d report ids not supported"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c
|
#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c
|
||||||
msgid "Must be a %q subclass."
|
msgid "Must be a %q subclass."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -14,6 +14,7 @@ CIRCUITPY_AUDIOIO = 1
|
|||||||
CIRCUITPY_AUDIOBUSIO = 1
|
CIRCUITPY_AUDIOBUSIO = 1
|
||||||
# Pins for I2SOut are not available.
|
# Pins for I2SOut are not available.
|
||||||
CIRCUITPY_AUDIOBUSIO_I2SOUT = 0
|
CIRCUITPY_AUDIOBUSIO_I2SOUT = 0
|
||||||
|
CIRCUITPY_BUSIO_SPI = 0
|
||||||
CIRCUITPY_PWMIO = 0
|
CIRCUITPY_PWMIO = 0
|
||||||
CIRCUITPY_ROTARYIO = 0
|
CIRCUITPY_ROTARYIO = 0
|
||||||
CIRCUITPY_RTC = 0
|
CIRCUITPY_RTC = 0
|
||||||
|
@ -10,4 +10,6 @@ INTERNAL_FLASH_FILESYSTEM = 1
|
|||||||
LONGINT_IMPL = NONE
|
LONGINT_IMPL = NONE
|
||||||
CIRCUITPY_FULL_BUILD = 0
|
CIRCUITPY_FULL_BUILD = 0
|
||||||
|
|
||||||
CIRCUITPY_GETPASS = 0
|
# There are many pin definitions on this board; it doesn't quite fit on very large translations.
|
||||||
|
# So remove what might be least likely module to be used.
|
||||||
|
CIRCUITPY_RAINBOWIO = 0
|
||||||
|
@ -197,7 +197,7 @@ mp_float_t mp_arg_validate_obj_float_non_negative(mp_obj_t float_in, mp_float_t
|
|||||||
|
|
||||||
size_t mp_arg_validate_length_with_name(mp_int_t i, size_t length, qstr arg_name, qstr length_name) {
|
size_t mp_arg_validate_length_with_name(mp_int_t i, size_t length, qstr arg_name, qstr length_name) {
|
||||||
if (i != (mp_int_t)length) {
|
if (i != (mp_int_t)length) {
|
||||||
mp_raise_ValueError_varg(translate("%q length must be %q"), MP_QSTR_pressed, MP_QSTR_num_keys);
|
mp_raise_ValueError_varg(translate("%q length must be %q"), arg_name, length_name);
|
||||||
}
|
}
|
||||||
return (size_t)i;
|
return (size_t)i;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
//| class Device:
|
//| class Device:
|
||||||
//| """HID Device specification"""
|
//| """HID Device specification"""
|
||||||
//|
|
//|
|
||||||
//| def __init__(self, *, descriptor: ReadableBuffer, usage_page: int, usage: int, in_report_length: int, out_report_length: int = 0, report_id_index: Optional[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
|
//| """Create a description of a USB HID device. The actual device is created when you
|
||||||
//| pass a `Device` to `usb_hid.enable()`.
|
//| pass a `Device` to `usb_hid.enable()`.
|
||||||
//|
|
//|
|
||||||
@ -39,38 +39,46 @@
|
|||||||
//| not verified for correctness; it is up to you to make sure it is not malformed.
|
//| 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_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 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.
|
//| :param int report_ids: Sequence of report ids used by the descriptor.
|
||||||
//| "In" is with respect to the host.
|
//| If the ``report_descriptor`` does not have a report ID, use 0.
|
||||||
//| :param int out_report_length: Size in bytes of the HID report received from the host.
|
//| :param int in_report_lengths: Sequence of sizes in bytes of the HIDs report sent to the host.
|
||||||
//| "Out" is with respect to the host. If no reports are expected, use 0.
|
//| The sizes are in order of the ``report_ids``.
|
||||||
//| :param int report_id_index: position of byte in descriptor that contains the Report ID.
|
//| "IN" is with respect to the host.
|
||||||
//| A Report ID will be assigned when the device is created. If there is no
|
//| :param int out_report_lengths: Size in bytes of the HID report received from the host.
|
||||||
//| Report ID, use ``None``.
|
//| The sizes are in order of the ``report_ids``.
|
||||||
|
//| "OUT" is with respect to the host.
|
||||||
|
//|
|
||||||
|
//| ``report_ids``, ``in_report_lengths``, and ``out_report_lengths`` must all be the same length.
|
||||||
//| """
|
//| """
|
||||||
//| ...
|
//| ...
|
||||||
//|
|
//|
|
||||||
//| KEYBOARD: Device
|
//| KEYBOARD: Device
|
||||||
//| """Standard keyboard device supporting keycodes 0x00-0xDD, modifiers 0xE-0xE7, and five LED indicators."""
|
//| """Standard keyboard device supporting keycodes 0x00-0xDD, modifiers 0xE-0xE7, and five LED indicators.
|
||||||
|
//| Uses Report ID 1 for its IN and OUT reports.
|
||||||
|
//| """
|
||||||
//|
|
//|
|
||||||
//| MOUSE: Device
|
//| MOUSE: Device
|
||||||
//| """Standard mouse device supporting five mouse buttons, X and Y relative movements from -127 to 127
|
//| """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."""
|
//| in each report, and a relative mouse wheel change from -127 to 127 in each report.
|
||||||
|
//| Uses Report ID 2 for its IN report.
|
||||||
|
//| """
|
||||||
//|
|
//|
|
||||||
//| CONSUMER_CONTROL: Device
|
//| CONSUMER_CONTROL: Device
|
||||||
//| """Consumer Control device supporting sent values from 1-652, with no rollover."""
|
//| """Consumer Control device supporting sent values from 1-652, with no rollover.
|
||||||
|
//| 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) {
|
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);
|
usb_hid_device_obj_t *self = m_new_obj(usb_hid_device_obj_t);
|
||||||
self->base.type = &usb_hid_device_type;
|
self->base.type = &usb_hid_device_type;
|
||||||
enum { ARG_report_descriptor, ARG_usage_page, ARG_usage, ARG_in_report_length, ARG_out_report_length, ARG_report_id_index };
|
enum { ARG_report_descriptor, ARG_usage_page, ARG_usage, ARG_report_ids, ARG_in_report_lengths, ARG_out_report_lengths };
|
||||||
static const mp_arg_t allowed_args[] = {
|
static const mp_arg_t allowed_args[] = {
|
||||||
{ MP_QSTR_report_descriptor, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
|
{ MP_QSTR_report_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_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_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_report_ids, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
{ MP_QSTR_out_report_length, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } },
|
{ MP_QSTR_in_report_lengths, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
{ MP_QSTR_report_id_index, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none } },
|
{ MP_QSTR_out_report_lengths, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
};
|
};
|
||||||
|
|
||||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||||
@ -81,82 +89,144 @@ STATIC mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args
|
|||||||
mp_obj_t descriptor = mp_obj_new_bytearray(descriptor_bufinfo.len, descriptor_bufinfo.buf);
|
mp_obj_t descriptor = mp_obj_new_bytearray(descriptor_bufinfo.len, descriptor_bufinfo.buf);
|
||||||
|
|
||||||
const mp_int_t usage_page_arg = args[ARG_usage_page].u_int;
|
const mp_int_t usage_page_arg = args[ARG_usage_page].u_int;
|
||||||
if (usage_page_arg <= 0 || usage_page_arg > 255) {
|
mp_arg_validate_int_range(usage_page_arg, 1, 255, MP_QSTR_usage_page);
|
||||||
mp_raise_ValueError_varg(translate("%q must be 1-255"), MP_QSTR_usage_page);
|
|
||||||
}
|
|
||||||
const uint8_t usage_page = usage_page_arg;
|
const uint8_t usage_page = usage_page_arg;
|
||||||
|
|
||||||
const mp_int_t usage_arg = args[ARG_usage].u_int;
|
const mp_int_t usage_arg = args[ARG_usage].u_int;
|
||||||
if (usage_arg <= 0 || usage_arg > 255) {
|
mp_arg_validate_int_range(usage_arg, 1, 255, MP_QSTR_usage_page);
|
||||||
mp_raise_ValueError_varg(translate("%q must be 1-255"), MP_QSTR_usage);
|
|
||||||
}
|
|
||||||
const uint8_t usage = usage_arg;
|
const uint8_t usage = usage_arg;
|
||||||
|
|
||||||
const mp_int_t in_report_length_arg = args[ARG_in_report_length].u_int;
|
mp_obj_t report_ids = args[ARG_report_ids].u_obj;
|
||||||
if (in_report_length_arg <= 0 || in_report_length_arg > 255) {
|
mp_obj_t in_report_lengths = args[ARG_in_report_lengths].u_obj;
|
||||||
mp_raise_ValueError_varg(translate("%q must be 1-255"), MP_QSTR_in_report_length);
|
mp_obj_t out_report_lengths = args[ARG_out_report_lengths].u_obj;
|
||||||
}
|
|
||||||
const uint8_t in_report_length = in_report_length_arg;
|
|
||||||
|
|
||||||
const mp_int_t out_report_length_arg = args[ARG_out_report_length].u_int;
|
size_t report_ids_count = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(report_ids));
|
||||||
if (out_report_length_arg < 0 || out_report_length_arg > 255) {
|
if (report_ids_count < 1) {
|
||||||
mp_raise_ValueError_varg(translate("%q must be 0-255"), MP_QSTR_out_report_length);
|
mp_raise_ValueError_varg(translate("%q length must be >= 1"), MP_QSTR_report_ids);
|
||||||
}
|
}
|
||||||
const uint8_t out_report_length = out_report_length_arg;
|
|
||||||
|
|
||||||
const mp_obj_t report_id_index_arg = args[ARG_report_id_index].u_obj;
|
if ((size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(in_report_lengths)) != report_ids_count ||
|
||||||
uint8_t report_id_index = 0;
|
(size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(out_report_lengths)) != report_ids_count) {
|
||||||
if (report_id_index_arg != mp_const_none) {
|
mp_raise_ValueError_varg(translate("%q, %q, and %q must all be the same length"),
|
||||||
const mp_int_t report_id_index_int = mp_obj_int_get_checked(report_id_index_arg);
|
MP_QSTR_report_ids, MP_QSTR_in_report_lengths, MP_QSTR_out_report_lengths);
|
||||||
if (report_id_index_int <= 0 || (uint32_t)report_id_index_int >= descriptor_bufinfo.len) {
|
}
|
||||||
mp_raise_ValueError_varg(translate("%q must be None or between 1 and len(report_descriptor)-1"),
|
|
||||||
MP_QSTR_report_id_index);
|
uint8_t report_ids_array[report_ids_count];
|
||||||
}
|
uint8_t in_report_lengths_array[report_ids_count];
|
||||||
report_id_index = report_id_index_int;
|
uint8_t out_report_lengths_array[report_ids_count];
|
||||||
|
|
||||||
|
// Validate the ids and lengths are all integers in range.
|
||||||
|
for (size_t i = 0; i < report_ids_count; i++) {
|
||||||
|
mp_obj_t i_obj = MP_OBJ_NEW_SMALL_INT(i);
|
||||||
|
|
||||||
|
report_ids_array[i] = (uint8_t)mp_arg_validate_int_range(
|
||||||
|
// It's not the actual argument that's out of range, but its elements.
|
||||||
|
// But the error message is close enough.
|
||||||
|
MP_OBJ_SMALL_INT_VALUE(mp_obj_subscr(report_ids, i_obj, MP_OBJ_SENTINEL)),
|
||||||
|
1, 255, MP_QSTR_report_ids);
|
||||||
|
|
||||||
|
in_report_lengths_array[i] = (uint8_t)mp_arg_validate_int_range(
|
||||||
|
MP_OBJ_SMALL_INT_VALUE(mp_obj_subscr(in_report_lengths_array, i_obj, MP_OBJ_SENTINEL)),
|
||||||
|
0, 255, MP_QSTR_in_report_lengths);
|
||||||
|
|
||||||
|
out_report_lengths_array[i] = (uint8_t)mp_arg_validate_int_range(
|
||||||
|
MP_OBJ_SMALL_INT_VALUE(mp_obj_subscr(out_report_lengths_array, i_obj, MP_OBJ_SENTINEL)),
|
||||||
|
0, 255, MP_QSTR_out_report_lengths);
|
||||||
}
|
}
|
||||||
|
|
||||||
common_hal_usb_hid_device_construct(
|
common_hal_usb_hid_device_construct(
|
||||||
self, descriptor, usage_page, usage, in_report_length, out_report_length, report_id_index);
|
self, descriptor, usage_page, usage, report_ids_count, report_ids_array, in_report_lengths_array, out_report_lengths_array);
|
||||||
return (mp_obj_t)self;
|
return (mp_obj_t)self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//| def send_report(self, buf: ReadableBuffer) -> None:
|
//| def send_report(self, buf: ReadableBuffer, report_id: Optional[int] = None) -> None:
|
||||||
//| """Send a 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.
|
||||||
|
//| """
|
||||||
//| ...
|
//| ...
|
||||||
//|
|
//|
|
||||||
STATIC mp_obj_t usb_hid_device_send_report(mp_obj_t self_in, mp_obj_t buffer) {
|
STATIC mp_obj_t usb_hid_device_send_report(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||||
|
|
||||||
|
enum { ARG_buf, ARG_report_id };
|
||||||
|
static const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_buf, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
|
{ MP_QSTR_report_id, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
mp_buffer_info_t bufinfo;
|
mp_buffer_info_t bufinfo;
|
||||||
mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_READ);
|
mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ);
|
||||||
|
|
||||||
common_hal_usb_hid_device_send_report(self, ((uint8_t *)bufinfo.buf), bufinfo.len);
|
// -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) {
|
||||||
|
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;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
MP_DEFINE_CONST_FUN_OBJ_2(usb_hid_device_send_report_obj, usb_hid_device_send_report);
|
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.
|
||||||
|
//| """
|
||||||
|
//| ...
|
||||||
|
//|
|
||||||
|
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) {
|
||||||
|
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||||
|
|
||||||
|
enum { ARG_report_id };
|
||||||
|
static const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_report_id, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
mp_int_t report_id_arg = -1;
|
||||||
|
if (args[ARG_report_id].u_obj != mp_const_none) {
|
||||||
|
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
|
//| 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.
|
||||||
//|
|
//|
|
||||||
STATIC mp_obj_t usb_hid_device_obj_get_last_received_report(mp_obj_t self_in) {
|
//| Deprecated: will be removed in CircutPython 8.0.0. Use `get_last_received_report()` instead.
|
||||||
|
//| """
|
||||||
|
//|
|
||||||
|
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);
|
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
if (self->out_report_buffer == 0) {
|
|
||||||
return mp_const_none;
|
// 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 mp_obj_new_bytes(self->out_report_buffer, self->out_report_length);
|
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_obj, usb_hid_device_obj_get_last_received_report);
|
MP_DEFINE_CONST_FUN_OBJ_1(usb_hid_device_get_last_received_report_property_obj, usb_hid_device_obj_get_last_received_report_property);
|
||||||
|
|
||||||
const mp_obj_property_t usb_hid_device_last_received_report_obj = {
|
const mp_obj_property_t usb_hid_device_last_received_report_obj = {
|
||||||
.base.type = &mp_type_property,
|
.base.type = &mp_type_property,
|
||||||
.proxy = {(mp_obj_t)&usb_hid_device_get_last_received_report_obj,
|
.proxy = {(mp_obj_t)&usb_hid_device_get_last_received_report_property_obj,
|
||||||
MP_ROM_NONE,
|
MP_ROM_NONE,
|
||||||
MP_ROM_NONE},
|
MP_ROM_NONE},
|
||||||
};
|
};
|
||||||
|
|
||||||
//| usage_page: int
|
//| 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) {
|
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);
|
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
@ -172,7 +242,7 @@ const mp_obj_property_t usb_hid_device_usage_page_obj = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//| usage: int
|
//| 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.
|
//| For example, Keyboard is 0x06 within the generic desktop usage page 0x01.
|
||||||
//| Mouse is 0x02 within the same usage page."""
|
//| Mouse is 0x02 within the same usage page."""
|
||||||
@ -192,13 +262,15 @@ const mp_obj_property_t usb_hid_device_usage_obj = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t usb_hid_device_locals_dict_table[] = {
|
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_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_get_last_received_report), MP_ROM_PTR(&usb_hid_device_get_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_last_received_report), MP_ROM_PTR(&usb_hid_device_last_received_report_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_KEYBOARD), MP_ROM_PTR(&usb_hid_device_keyboard_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_usage), MP_ROM_PTR(&usb_hid_device_usage_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) },
|
{ 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);
|
STATIC MP_DEFINE_CONST_DICT(usb_hid_device_locals_dict, usb_hid_device_locals_dict_table);
|
||||||
|
@ -33,9 +33,11 @@
|
|||||||
|
|
||||||
extern const mp_obj_type_t usb_hid_device_type;
|
extern const mp_obj_type_t usb_hid_device_type;
|
||||||
|
|
||||||
void common_hal_usb_hid_device_construct(usb_hid_device_obj_t *self, mp_obj_t descriptor, uint8_t usage_page, uint8_t usage, uint8_t in_report_length, uint8_t out_report_length, uint8_t report_id_index);
|
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 report_ids_count,uint8_t *report_ids, uint8_t *in_report_lengths, uint8_t *out_report_lengths);
|
||||||
void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *report, uint8_t len);
|
void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *report, uint8_t len, uint8_t report_id);
|
||||||
|
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_page(usb_hid_device_obj_t *self);
|
||||||
uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self);
|
uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self);
|
||||||
|
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
|
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_HID_DEVICE_H
|
||||||
|
@ -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.
|
//| If you enable too many devices at once, you will run out of USB endpoints.
|
||||||
//| The number of available endpoints varies by microcontroller.
|
//| 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.
|
//| not enough endpoints are available.
|
||||||
//| """
|
//| """
|
||||||
//| ...
|
//| ...
|
||||||
|
@ -36,38 +36,39 @@
|
|||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
|
||||||
static const uint8_t keyboard_report_descriptor[] = {
|
static const uint8_t keyboard_report_descriptor[] = {
|
||||||
0x05, 0x01, // 0,1 Usage Page (Generic Desktop Ctrls)
|
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||||
0x09, 0x06, // 2,3 Usage (Keyboard)
|
0x09, 0x06, // Usage (Keyboard)
|
||||||
0xA1, 0x01, // 4,5 Collection (Application)
|
0xA1, 0x01, // Collection (Application)
|
||||||
0x85, 0xFF, // 6,7 Report ID [SET AT RUNTIME]
|
0x85, 0x01, // Report ID (1)
|
||||||
#define KEYBOARD_REPORT_ID_INDEX (7)
|
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
0x19, 0xE0, // Usage Minimum (0xE0)
|
||||||
0x19, 0xE0, // Usage Minimum (0xE0)
|
0x29, 0xE7, // Usage Maximum (0xE7)
|
||||||
0x29, 0xE7, // Usage Maximum (0xE7)
|
0x15, 0x00, // Logical Minimum (0)
|
||||||
0x15, 0x00, // Logical Minimum (0)
|
0x25, 0x01, // Logical Maximum (1)
|
||||||
0x25, 0x01, // Logical Maximum (1)
|
0x75, 0x01, // Report Size (1)
|
||||||
0x75, 0x01, // Report Size (1)
|
0x95, 0x08, // Report Count (8)
|
||||||
0x95, 0x08, // Report Count (8)
|
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x95, 0x01, // Report Count (1)
|
||||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x75, 0x08, // Report Size (8)
|
||||||
0x19, 0x00, // Usage Minimum (0x00)
|
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x29, 0xDD, // Usage Maximum (0xDD)
|
0x95, 0x03, // Report Count (3)
|
||||||
0x15, 0x00, // Logical Minimum (0)
|
0x75, 0x01, // Report Size (1)
|
||||||
0x25, 0xDD, // Logical Maximum (-35)
|
0x05, 0x08, // Usage Page (LEDs)
|
||||||
0x75, 0x08, // Report Size (8)
|
0x19, 0x01, // Usage Minimum (Num Lock)
|
||||||
0x95, 0x06, // Report Count (6)
|
0x29, 0x05, // Usage Maximum (Kana)
|
||||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||||
0x05, 0x08, // Usage Page (LEDs)
|
0x95, 0x01, // Report Count (1)
|
||||||
0x19, 0x01, // Usage Minimum (Num Lock)
|
0x75, 0x05, // Report Size (5)
|
||||||
0x29, 0x05, // Usage Maximum (Kana)
|
0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||||
0x15, 0x00, // Logical Minimum (0)
|
0x95, 0x06, // Report Count (6)
|
||||||
0x25, 0x01, // Logical Maximum (1)
|
0x75, 0x08, // Report Size (8)
|
||||||
0x75, 0x01, // Report Size (1)
|
0x15, 0x00, // Logical Minimum (0)
|
||||||
0x95, 0x05, // Report Count (5)
|
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||||
0x95, 0x03, // Report Count (3)
|
0x19, 0x00, // Usage Minimum (0x00)
|
||||||
0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
|
||||||
0xC0, // End Collection
|
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_keyboard_obj = {
|
const usb_hid_device_obj_t usb_hid_device_keyboard_obj = {
|
||||||
@ -78,20 +79,19 @@ const usb_hid_device_obj_t usb_hid_device_keyboard_obj = {
|
|||||||
.report_descriptor_length = sizeof(keyboard_report_descriptor),
|
.report_descriptor_length = sizeof(keyboard_report_descriptor),
|
||||||
.usage_page = 0x01,
|
.usage_page = 0x01,
|
||||||
.usage = 0x06,
|
.usage = 0x06,
|
||||||
.in_report_length = 8,
|
.num_report_ids = 1,
|
||||||
.out_report_length = 1,
|
.report_ids = { 0x01, },
|
||||||
.report_id_index = KEYBOARD_REPORT_ID_INDEX,
|
.in_report_lengths = { 8, 0, 0, 0, 0, 0, },
|
||||||
|
.out_report_lengths = { 1, },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t mouse_report_descriptor[] = {
|
static const uint8_t mouse_report_descriptor[] = {
|
||||||
0x05, 0x01, // 0,1 Usage Page (Generic Desktop Ctrls)
|
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||||
0x09, 0x02, // 2,3 Usage (Mouse)
|
0x09, 0x02, // Usage (Mouse)
|
||||||
0xA1, 0x01, // 4,5 Collection (Application)
|
0xA1, 0x01, // Collection (Application)
|
||||||
0x09, 0x01, // 6,7 Usage (Pointer)
|
0x09, 0x01, // Usage (Pointer)
|
||||||
0xA1, 0x00, // 8,9 Collection (Physical)
|
0xA1, 0x00, // Collection (Physical)
|
||||||
0x85, 0xFF, // 10, 11 Report ID [SET AT RUNTIME]
|
0x85, 0x02, // 10, 11 Report ID (2)
|
||||||
#define MOUSE_REPORT_ID_INDEX (11)
|
|
||||||
0x05, 0x09, // Usage Page (Button)
|
0x05, 0x09, // Usage Page (Button)
|
||||||
0x19, 0x01, // Usage Minimum (0x01)
|
0x19, 0x01, // Usage Minimum (0x01)
|
||||||
0x29, 0x05, // Usage Maximum (0x05)
|
0x29, 0x05, // Usage Maximum (0x05)
|
||||||
@ -129,17 +129,17 @@ const usb_hid_device_obj_t usb_hid_device_mouse_obj = {
|
|||||||
.report_descriptor_length = sizeof(mouse_report_descriptor),
|
.report_descriptor_length = sizeof(mouse_report_descriptor),
|
||||||
.usage_page = 0x01,
|
.usage_page = 0x01,
|
||||||
.usage = 0x02,
|
.usage = 0x02,
|
||||||
.in_report_length = 4,
|
.num_report_ids = 1,
|
||||||
.out_report_length = 0,
|
.report_ids = { 0x02, },
|
||||||
.report_id_index = MOUSE_REPORT_ID_INDEX,
|
.in_report_lengths = { 4, 0, 0, 0, 0, 0, },
|
||||||
|
.out_report_lengths = { 0, },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t consumer_control_report_descriptor[] = {
|
static const uint8_t consumer_control_report_descriptor[] = {
|
||||||
0x05, 0x0C, // 0,1 Usage Page (Consumer)
|
0x05, 0x0C, // Usage Page (Consumer)
|
||||||
0x09, 0x01, // 2,3 Usage (Consumer Control)
|
0x09, 0x01, // Usage (Consumer Control)
|
||||||
0xA1, 0x01, // 4,5 Collection (Application)
|
0xA1, 0x01, // Collection (Application)
|
||||||
0x85, 0xFF, // 6,7 Report ID [SET AT RUNTIME]
|
0x85, 0x03, // Report ID (3)
|
||||||
#define CONSUMER_CONTROL_REPORT_ID_INDEX (7)
|
|
||||||
0x75, 0x10, // Report Size (16)
|
0x75, 0x10, // Report Size (16)
|
||||||
0x95, 0x01, // Report Count (1)
|
0x95, 0x01, // Report Count (1)
|
||||||
0x15, 0x01, // Logical Minimum (1)
|
0x15, 0x01, // Logical Minimum (1)
|
||||||
@ -158,20 +158,46 @@ const usb_hid_device_obj_t usb_hid_device_consumer_control_obj = {
|
|||||||
.report_descriptor_length = sizeof(consumer_control_report_descriptor),
|
.report_descriptor_length = sizeof(consumer_control_report_descriptor),
|
||||||
.usage_page = 0x0C,
|
.usage_page = 0x0C,
|
||||||
.usage = 0x01,
|
.usage = 0x01,
|
||||||
.in_report_length = 2,
|
.num_report_ids = 1,
|
||||||
.out_report_length = 0,
|
.report_ids = { 0x03 },
|
||||||
.report_id_index = CONSUMER_CONTROL_REPORT_ID_INDEX,
|
.in_report_lengths = { 2, 0, 0, 0, 0, 0, },
|
||||||
|
.out_report_lengths = { 0, },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
if (num_report_ids > MAX_REPORT_IDS_PER_DESCRIPTOR) {
|
||||||
|
mp_raise_ValueError_varg(translate("More than %d report ids not supported"),
|
||||||
|
MAX_REPORT_IDS_PER_DESCRIPTOR);
|
||||||
|
}
|
||||||
|
|
||||||
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, 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 when USB is initialized.
|
// report buffer pointers are NULL at start, and are created when USB is initialized.
|
||||||
|
|
||||||
mp_buffer_info_t bufinfo;
|
mp_buffer_info_t bufinfo;
|
||||||
mp_get_buffer_raise(report_descriptor, &bufinfo, MP_BUFFER_READ);
|
mp_get_buffer_raise(report_descriptor, &bufinfo, MP_BUFFER_READ);
|
||||||
self->report_descriptor_length = bufinfo.len;
|
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);
|
uint8_t *descriptor_bytes = gc_alloc(bufinfo.len, false, false);
|
||||||
memcpy(descriptor_bytes, bufinfo.buf, bufinfo.len);
|
memcpy(descriptor_bytes, bufinfo.buf, bufinfo.len);
|
||||||
@ -179,9 +205,10 @@ void common_hal_usb_hid_device_construct(usb_hid_device_obj_t *self, mp_obj_t re
|
|||||||
|
|
||||||
self->usage_page = usage_page;
|
self->usage_page = usage_page;
|
||||||
self->usage = usage;
|
self->usage = usage;
|
||||||
self->in_report_length = in_report_length;
|
self->num_report_ids = num_report_ids;
|
||||||
self->out_report_length = out_report_length;
|
memcpy(self->report_ids, report_ids, num_report_ids);
|
||||||
self->report_id_index = report_id_index;
|
memcpy(self->in_report_lengths, in_report_lengths, num_report_ids);
|
||||||
|
memcpy(self->out_report_lengths, out_report_lengths, num_report_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
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_page(usb_hid_device_obj_t *self) {
|
||||||
@ -192,9 +219,13 @@ uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self) {
|
|||||||
return self->usage;
|
return self->usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *report, uint8_t len) {
|
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) {
|
// report_id and len have already been validated for this device.
|
||||||
mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."), self->in_report_length);
|
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
|
// Wait until interface is ready, timeout = 2 seconds
|
||||||
@ -207,25 +238,35 @@ 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"));
|
mp_raise_msg(&mp_type_OSError, translate("USB busy"));
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(self->in_report_buffer, report, len);
|
if (!tud_hid_report(report_id, report, len)) {
|
||||||
|
|
||||||
if (!tud_hid_report(self->report_id, self->in_report_buffer, len)) {
|
|
||||||
mp_raise_msg(&mp_type_OSError, translate("USB error"));
|
mp_raise_msg(&mp_type_OSError, translate("USB error"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
void usb_hid_device_create_report_buffers(usb_hid_device_obj_t *self) {
|
||||||
if (self->in_report_length > 0) {
|
for (size_t i = 0; i < self->num_report_ids; i++) {
|
||||||
self->in_report_buffer = gc_alloc(self->in_report_length, false, true /*long-lived*/);
|
// 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().
|
||||||
if (self->out_report_length > 0) {
|
self->in_report_buffers[i] =
|
||||||
self->out_report_buffer = gc_alloc(self->out_report_length, false, true /*long-lived*/);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Callbacks invoked when receive Get_Report request through control endpoint
|
// Callbacks invoked when we received Get_Report request through control endpoint
|
||||||
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
||||||
(void)itf;
|
(void)itf;
|
||||||
// only support Input Report
|
// only support Input Report
|
||||||
@ -234,11 +275,21 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fill buffer with current report
|
// fill buffer with current report
|
||||||
memcpy(buffer, usb_hid_get_device_with_report_id(report_id)->in_report_buffer, reqlen);
|
|
||||||
return reqlen;
|
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)) {
|
||||||
|
// Make sure buffer exists before trying to copy into it.
|
||||||
|
if (hid_device->in_report_buffers[id_idx]) {
|
||||||
|
memcpy(buffer, hid_device->in_report_buffers[id_idx], reqlen);
|
||||||
|
return reqlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callbacks invoked when receive Set_Report request through control endpoint
|
// 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 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;
|
(void)itf;
|
||||||
if (report_type == HID_REPORT_TYPE_INVALID) {
|
if (report_type == HID_REPORT_TYPE_INVALID) {
|
||||||
@ -249,9 +300,15 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
usb_hid_device_obj_t *hid_device = usb_hid_get_device_with_report_id(report_id);
|
usb_hid_device_obj_t *hid_device;
|
||||||
|
size_t id_idx;
|
||||||
if (hid_device && hid_device->out_report_length >= bufsize) {
|
// Find device with this report id, and get the report id index.
|
||||||
memcpy(hid_device->out_report_buffer, buffer, bufsize);
|
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_buffers[id_idx] &&
|
||||||
|
hid_device->out_report_lengths[id_idx] >= bufsize) {
|
||||||
|
memcpy(hid_device->out_report_buffers[id_idx], buffer, bufsize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,19 +32,24 @@
|
|||||||
|
|
||||||
#include "py/obj.h"
|
#include "py/obj.h"
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
mp_obj_base_t base;
|
mp_obj_base_t base;
|
||||||
// Python buffer object whose contents are the descriptor.
|
// Python buffer object whose contents are the descriptor.
|
||||||
const uint8_t *report_descriptor;
|
const uint8_t *report_descriptor;
|
||||||
uint8_t *in_report_buffer;
|
uint8_t *in_report_buffers[MAX_REPORT_IDS_PER_DESCRIPTOR];
|
||||||
uint8_t *out_report_buffer;
|
uint8_t *out_report_buffers[MAX_REPORT_IDS_PER_DESCRIPTOR];
|
||||||
uint16_t report_id_index;
|
|
||||||
uint16_t report_descriptor_length;
|
uint16_t report_descriptor_length;
|
||||||
|
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 usage_page;
|
uint8_t usage_page;
|
||||||
uint8_t usage;
|
uint8_t usage;
|
||||||
uint8_t report_id;
|
uint8_t num_report_ids;
|
||||||
uint8_t in_report_length;
|
|
||||||
uint8_t out_report_length;
|
|
||||||
} usb_hid_device_obj_t;
|
} usb_hid_device_obj_t;
|
||||||
|
|
||||||
extern const usb_hid_device_obj_t usb_hid_device_keyboard_obj;
|
extern const usb_hid_device_obj_t usb_hid_device_keyboard_obj;
|
||||||
|
@ -197,10 +197,6 @@ size_t usb_hid_report_descriptor_length(void) {
|
|||||||
total_hid_report_descriptor_length += hid_devices[i].report_descriptor_length;
|
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;
|
return total_hid_report_descriptor_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,24 +211,11 @@ 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++) {
|
for (mp_int_t i = 0; i < num_hid_devices; i++) {
|
||||||
usb_hid_device_obj_t *device = &hid_devices[i];
|
usb_hid_device_obj_t *device = &hid_devices[i];
|
||||||
// Copy the report descriptor for this device.
|
// Copy the report descriptor for this device.
|
||||||
if (num_hid_devices == 1) {
|
memcpy(report_descriptor_start, device->report_descriptor, device->report_descriptor_length);
|
||||||
// There's only one device, so it shouldn't have a report ID.
|
|
||||||
// Copy the descriptor, but splice out the report id indicator and value (2 bytes).
|
|
||||||
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.
|
// Advance to the next free chunk for the next report descriptor.x
|
||||||
device->report_id = i + 1;
|
report_descriptor_start += device->report_descriptor_length;
|
||||||
|
|
||||||
// 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.
|
// 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.
|
// We don't need it any more and it will get lost when the heap goes away.
|
||||||
device->report_descriptor = NULL;
|
device->report_descriptor = NULL;
|
||||||
@ -260,24 +243,33 @@ void usb_hid_gc_collect(void) {
|
|||||||
gc_collect_ptr(hid_devices_tuple);
|
gc_collect_ptr(hid_devices_tuple);
|
||||||
|
|
||||||
// Mark possible heap pointers in the static device list as in use.
|
// Mark possible heap pointers in the static device list as in use.
|
||||||
for (mp_int_t i = 0; i < num_hid_devices; i++) {
|
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.
|
// 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
|
// Constant report descriptors must be const so that they are used directly from flash
|
||||||
// and not copied into RAM.
|
// and not copied into RAM.
|
||||||
gc_collect_ptr((void *)hid_devices[i].report_descriptor);
|
gc_collect_ptr((void *)hid_devices[device_idx].report_descriptor);
|
||||||
gc_collect_ptr(hid_devices[i].in_report_buffer);
|
|
||||||
gc_collect_ptr(hid_devices[i].out_report_buffer);
|
// Collect all the 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].in_report_buffers[id_idx]);
|
||||||
|
gc_collect_ptr(hid_devices[id_idx].out_report_buffers[id_idx]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usb_hid_device_obj_t *usb_hid_get_device_with_report_id(uint8_t report_id) {
|
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 i = 0; i < num_hid_devices; i++) {
|
for (uint8_t device_idx = 0; device_idx < num_hid_devices; device_idx++) {
|
||||||
usb_hid_device_obj_t *device = &hid_devices[i];
|
usb_hid_device_obj_t *device = &hid_devices[device_idx];
|
||||||
if (device->report_id == report_id) {
|
for (size_t id_idx = 0; id_idx < device->num_report_ids; id_idx++) {
|
||||||
return &hid_devices[i];
|
if (device->report_ids[id_idx] == report_id) {
|
||||||
|
*device_out = device;
|
||||||
|
*id_idx_out = id_idx;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoked when GET HID REPORT DESCRIPTOR is received.
|
// Invoked when GET HID REPORT DESCRIPTOR is received.
|
||||||
|
@ -44,7 +44,7 @@ size_t usb_hid_report_descriptor_length(void);
|
|||||||
void usb_hid_build_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length);
|
void usb_hid_build_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length);
|
||||||
void usb_hid_save_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length);
|
void usb_hid_save_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length);
|
||||||
|
|
||||||
usb_hid_device_obj_t *usb_hid_get_device_with_report_id(uint8_t report_id);
|
bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t **device_out, size_t *id_idx_out);
|
||||||
|
|
||||||
void usb_hid_gc_collect(void);
|
void usb_hid_gc_collect(void);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user