Skip to content

Commit 24c6295

Browse files
committed
Boot protocol and feature reports: compiles; needs to be tested
1 parent 2025d0c commit 24c6295

File tree

7 files changed

+120
-25
lines changed

7 files changed

+120
-25
lines changed

ports/espressif/esp-idf

Submodule esp-idf updated 57 files

shared-bindings/usb_hid/__init__.c

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,23 +60,57 @@ STATIC mp_obj_t usb_hid_disable(void) {
6060
}
6161
MP_DEFINE_CONST_FUN_OBJ_0(usb_hid_disable_obj, usb_hid_disable);
6262

63-
//| def enable(devices: Optional[Sequence[Device]]) -> None:
63+
//| def enable(devices: Optional[Sequence[Device]], boot_device: int = 0) -> None:
6464
//| """Specify which USB HID devices that will be available.
6565
//| Can be called in ``boot.py``, before USB is connected.
6666
//|
6767
//| :param Sequence devices: `Device` objects.
6868
//| If `devices` is empty, HID is disabled. The order of the ``Devices``
6969
//| may matter to the host. For instance, for MacOS, put the mouse device
7070
//| before any Gamepad or Digitizer HID device or else it will not work.
71+
//| :param int boot_device: If non-zero, inform the host that support for a
72+
//| a boot HID device is available.
73+
//| If ``boot_device=1``, a boot keyboard is available.
74+
//| If ``boot_device=2``, a boot mouse is available. No other values are allowed.
75+
//| See below.
7176
//|
7277
//| If you enable too many devices at once, you will run out of USB endpoints.
7378
//| The number of available endpoints varies by microcontroller.
7479
//| CircuitPython will go into safe mode after running ``boot.py`` to inform you if
7580
//| not enough endpoints are available.
81+
//|
82+
//| **Boot Devices**
83+
//|
84+
//| Boot devices implement a fixed, predefined report descriptor, defined in
85+
//| https://www.usb.org/sites/default/files/hid1_12.pdf, Appendix B. A USB host
86+
//| can request to use the boot device if the USB device says it is available.
87+
//| Usually only a BIOS or other kind of boot-time, limited-functionality
88+
//| host needs boot keyboard support.
89+
//|
90+
//| For example, to make a boot keyboard available, you can use this code::
91+
//|
92+
//| usb_hid.enable((Device.KEYBOARD), boot_device=1)
93+
//|
94+
//| If the host requests the boot keyboard, the report descriptor provided by `Device.KEYBOARD`
95+
//| will be ignored, and the predefined report descriptor will be used.
96+
//| But if the host does not request the boot keyboard,
97+
//| the descriptor provided by `Device.KEYBOARD` will be used.
7698
//| """
7799
//| ...
78100
//|
79-
STATIC mp_obj_t usb_hid_enable(mp_obj_t devices) {
101+
STATIC mp_obj_t usb_hid_enable(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
102+
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
103+
104+
enum { ARG_devices, ARG_boot_device };
105+
static const mp_arg_t allowed_args[] = {
106+
{ MP_QSTR_devices, MP_ARG_REQUIRED | MP_ARG_OBJ },
107+
{ MP_QSTR_boot_device, MP_ARG_OBJ, {.u_int = 0} },
108+
};
109+
110+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
111+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
112+
113+
mp_obj_t devices = args[ARG_devices].u_obj;
80114
const mp_int_t len = mp_obj_get_int(mp_obj_len(devices));
81115
for (mp_int_t i = 0; i < len; i++) {
82116
mp_obj_t item = mp_obj_subscr(devices, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL);
@@ -85,21 +119,46 @@ STATIC mp_obj_t usb_hid_enable(mp_obj_t devices) {
85119
}
86120
}
87121

88-
if (!common_hal_usb_hid_enable(devices)) {
122+
uint8_t boot_device =
123+
(uint8_t)mp_arg_validate_int_range(args[ARG_boot_device].u_int, 0, 2, MP_QSTR_boot_device);
124+
125+
if (!common_hal_usb_hid_enable(devices, boot_device)) {
89126
mp_raise_RuntimeError(translate("Cannot change USB devices now"));
90127
}
91128

92129
return mp_const_none;
93130
}
94-
MP_DEFINE_CONST_FUN_OBJ_1(usb_hid_enable_obj, usb_hid_enable);
131+
MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_enable_obj, 2, usb_hid_enable);
132+
133+
//| def get_boot_device() -> int:
134+
//| """
135+
//| :return: the boot device requested by the host, if any.
136+
//| Returns 0 if the host did not request a boot device, or if `usb_hid.enable()`
137+
//| was called with `boot_device=0`, the default, which disables boot device support.
138+
//| If the host did request aboot device,
139+
//| returns the value of ``boot_device`` set in `usb_hid.enable()`:
140+
//| ``1`` for a boot keyboard, or ``2`` for boot mouse.
141+
//| Your device driver should check and act on this value if the keyboard or mouse
142+
//| device you specified provides reports that differ from the standard boot keyboard or mouse.
143+
//| However, the standard devices provided by CircuitPython, `Device.KEYBOARD` and `Device.MOUSE`,
144+
//| describe reports that match the boot device reports, so you don't need to check this
145+
//| if you are using those devices.
146+
//| :rtype int:
147+
//| """
148+
//|
149+
STATIC mp_obj_t usb_hid_get_boot_device(void) {
150+
return MP_OBJ_NEW_SMALL_INT(common_hal_usb_hid_get_boot_device());
151+
}
152+
MP_DEFINE_CONST_FUN_OBJ_0(usb_hid_get_boot_device_obj, usb_hid_get_boot_device);
95153

96154
// usb_hid.devices is set once the usb devices are determined, after boot.py runs.
97155
STATIC mp_map_elem_t usb_hid_module_globals_table[] = {
98-
{ MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid) },
99-
{ MP_ROM_QSTR(MP_QSTR_Device), MP_OBJ_FROM_PTR(&usb_hid_device_type) },
100-
{ MP_ROM_QSTR(MP_QSTR_devices), mp_const_none },
101-
{ MP_ROM_QSTR(MP_QSTR_disable), MP_OBJ_FROM_PTR(&usb_hid_disable_obj) },
102-
{ MP_ROM_QSTR(MP_QSTR_enable), MP_OBJ_FROM_PTR(&usb_hid_enable_obj) },
156+
{ MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid) },
157+
{ MP_ROM_QSTR(MP_QSTR_Device), MP_OBJ_FROM_PTR(&usb_hid_device_type) },
158+
{ MP_ROM_QSTR(MP_QSTR_devices), mp_const_none },
159+
{ MP_ROM_QSTR(MP_QSTR_disable), MP_OBJ_FROM_PTR(&usb_hid_disable_obj) },
160+
{ MP_ROM_QSTR(MP_QSTR_enable), MP_OBJ_FROM_PTR(&usb_hid_enable_obj) },
161+
{ MP_ROM_QSTR(MP_QSTR_get_boot_device), MP_OBJ_FROM_PTR(&usb_hid_get_boot_device_obj) },
103162
};
104163

105164
STATIC MP_DEFINE_MUTABLE_DICT(usb_hid_module_globals, usb_hid_module_globals_table);

shared-bindings/usb_hid/__init__.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ extern mp_obj_tuple_t common_hal_usb_hid_devices;
3636
void usb_hid_set_devices(mp_obj_t devices);
3737

3838
bool common_hal_usb_hid_disable(void);
39-
bool common_hal_usb_hid_enable(const mp_obj_t devices_seq);
39+
bool common_hal_usb_hid_enable(const mp_obj_t devices_seq, uint8_t boot_device);
40+
uint8_t common_hal_usb_hid_get_boot_device(void);
4041

4142
#endif // SHARED_BINDINGS_USB_HID_H

shared-module/usb_hid/Device.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *
244244
}
245245

246246
mp_obj_t common_hal_usb_hid_device_get_last_received_report(usb_hid_device_obj_t *self, uint8_t report_id) {
247-
// report_id has already been validated for this deveice.
247+
// report_id has already been validated for this device.
248248
size_t id_idx = get_report_id_idx(self, report_id);
249249
return mp_obj_new_bytes(self->out_report_buffers[id_idx], self->out_report_lengths[id_idx]);
250250
}
@@ -266,11 +266,11 @@ void usb_hid_device_create_report_buffers(usb_hid_device_obj_t *self) {
266266
}
267267

268268

269-
// Callbacks invoked when we received Get_Report request through control endpoint
269+
// Callback invoked when we receive Get_Report request through control endpoint
270270
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) {
271271
(void)itf;
272-
// only support Input Report
273-
if (report_type != HID_REPORT_TYPE_INPUT) {
272+
// Support Input Report and Feature Report
273+
if (report_type != HID_REPORT_TYPE_INPUT && report_type != HID_REPORT_TYPE_FEATURE) {
274274
return 0;
275275
}
276276

@@ -289,14 +289,14 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t
289289
return 0;
290290
}
291291

292-
// Callbacks invoked when we received Set_Report request through control endpoint
292+
// Callback invoked when we receive Set_Report request through control endpoint
293293
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) {
294294
(void)itf;
295295
if (report_type == HID_REPORT_TYPE_INVALID) {
296296
report_id = buffer[0];
297297
buffer++;
298298
bufsize--;
299-
} else if (report_type != HID_REPORT_TYPE_OUTPUT) {
299+
} else if (report_type != HID_REPORT_TYPE_OUTPUT && report_type != HID_REPORT_TYPE_FEATURE) {
300300
return;
301301
}
302302

shared-module/usb_hid/__init__.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ static const uint8_t usb_hid_descriptor_template[] = {
4444
0x02, // 4 bNumEndpoints 2
4545
0x03, // 5 bInterfaceClass: HID
4646
0x00, // 6 bInterfaceSubClass: NOBOOT
47+
#define HID_DESCRIPTOR_SUBCLASS_INDEX (6)
4748
0x00, // 7 bInterfaceProtocol: NONE
49+
#define HID_DESCRIPTOR_INTERFACE_PROTOCOL_INDEX (7)
4850
0xFF, // 8 iInterface (String Index) [SET AT RUNTIME]
4951
#define HID_DESCRIPTOR_INTERFACE_STRING_INDEX (8)
5052

@@ -81,6 +83,14 @@ static usb_hid_device_obj_t hid_devices[MAX_HID_DEVICES];
8183
// If 0, USB HID is disabled.
8284
static mp_int_t num_hid_devices;
8385

86+
// Which boot device is available 0: no boot devices, 1: boot keyboard, 2: boot mouse.
87+
// This value is set by usb_hid.enable(), and used to build the HID interface descriptor.
88+
// The value is remembered here from boot.py to code.py.
89+
static uint8_t hid_boot_device;
90+
91+
// Whether a boot device was requested by a SET_PROTOCOL request from the host.
92+
static bool hid_boot_device_requested;
93+
8494
// This tuple is store in usb_hid.devices.
8595
static mp_obj_tuple_t *hid_devices_tuple;
8696

@@ -100,9 +110,19 @@ bool usb_hid_enabled(void) {
100110
return num_hid_devices > 0;
101111
}
102112

113+
uint8_t usb_hid_boot_device(void) {
114+
return hid_boot_device;
115+
}
116+
117+
// Returns 1 or 2 if host requested a boot device and boot protocol was enabled in the interface descriptor.
118+
uint8_t common_hal_usb_hid_get_boot_device(void) {
119+
return hid_boot_device_requested ? hid_boot_device : 0;
120+
}
121+
103122
void usb_hid_set_defaults(void) {
123+
hid_boot_device = 0;
104124
common_hal_usb_hid_enable(
105-
CIRCUITPY_USB_HID_ENABLED_DEFAULT ? &default_hid_devices_tuple : mp_const_empty_tuple);
125+
CIRCUITPY_USB_HID_ENABLED_DEFAULT ? &default_hid_devices_tuple : mp_const_empty_tuple, 0);
106126
}
107127

108128
// This is the interface descriptor, not the report descriptor.
@@ -113,12 +133,17 @@ size_t usb_hid_descriptor_length(void) {
113133
static const char usb_hid_interface_name[] = USB_INTERFACE_NAME " HID";
114134

115135
// This is the interface descriptor, not the report descriptor.
116-
size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string, uint16_t report_descriptor_length) {
136+
size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string, uint16_t report_descriptor_length, uint8_t boot_device) {
117137
memcpy(descriptor_buf, usb_hid_descriptor_template, sizeof(usb_hid_descriptor_template));
118138

119139
descriptor_buf[HID_DESCRIPTOR_INTERFACE_INDEX] = descriptor_counts->current_interface;
120140
descriptor_counts->current_interface++;
121141

142+
if (boot_device > 0) {
143+
descriptor_buf[HID_DESCRIPTOR_SUBCLASS_INDEX] = 1; // BOOT protocol (device) available.
144+
descriptor_buf[HID_DESCRIPTOR_INTERFACE_PROTOCOL_INDEX] = boot_device; // 1: keyboard, 2: mouse
145+
}
146+
122147
usb_add_interface_string(*current_interface_string, usb_hid_interface_name);
123148
descriptor_buf[HID_DESCRIPTOR_INTERFACE_STRING_INDEX] = *current_interface_string;
124149
(*current_interface_string)++;
@@ -151,10 +176,10 @@ static void usb_hid_set_devices_from_hid_devices(void) {
151176
}
152177

153178
bool common_hal_usb_hid_disable(void) {
154-
return common_hal_usb_hid_enable(mp_const_empty_tuple);
179+
return common_hal_usb_hid_enable(mp_const_empty_tuple, 0);
155180
}
156181

157-
bool common_hal_usb_hid_enable(const mp_obj_t devices) {
182+
bool common_hal_usb_hid_enable(const mp_obj_t devices, uint8_t boot_device) {
158183
// We can't change the devices once we're connected.
159184
if (tud_connected()) {
160185
return false;
@@ -167,6 +192,8 @@ bool common_hal_usb_hid_enable(const mp_obj_t devices) {
167192

168193
num_hid_devices = num_devices;
169194

195+
hid_boot_device = boot_device;
196+
170197
// Remember the devices in static storage so they live across VMs.
171198
for (mp_int_t i = 0; i < num_hid_devices; i++) {
172199
// devices has already been validated to contain only usb_hid_device_obj_t objects.
@@ -182,6 +209,7 @@ bool common_hal_usb_hid_enable(const mp_obj_t devices) {
182209

183210
// Called when HID devices are ready to be used, when code.py or the REPL starts running.
184211
void usb_hid_setup_devices(void) {
212+
hid_boot_device_requested = false;
185213
usb_hid_set_devices_from_hid_devices();
186214

187215
// Create report buffers on the heap.
@@ -272,9 +300,15 @@ bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t *
272300
return false;
273301
}
274302

275-
// Invoked when GET HID REPORT DESCRIPTOR is received.
276-
// Application return pointer to descriptor
303+
// Callback invoked when we receive a GET HID REPORT DESCRIPTOR
304+
// Application returns pointer to descriptor
277305
// Descriptor contents must exist long enough for transfer to complete
278306
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf) {
279307
return (uint8_t *)hid_report_descriptor_allocation->ptr;
280308
}
309+
310+
// Callback invoked when we receive a SET_PROTOCOL request.
311+
// Protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
312+
void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) {
313+
hid_boot_device_requested = (protocol == HID_PROTOCOL_BOOT);
314+
}

shared-module/usb_hid/__init__.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@
3333
extern usb_hid_device_obj_t usb_hid_devices[];
3434

3535
bool usb_hid_enabled(void);
36+
uint8_t usb_hid_boot_device(void);
3637
void usb_hid_set_defaults(void);
3738

38-
size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string, uint16_t report_descriptor_length);
39+
size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string, uint16_t report_descriptor_length, uint8_t boot_device);
3940
size_t usb_hid_descriptor_length(void);
4041
size_t usb_hid_report_descriptor_length(void);
4142

supervisor/shared/usb/usb_desc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ static void usb_build_configuration_descriptor(void) {
223223
if (usb_hid_enabled()) {
224224
descriptor_buf_remaining += usb_hid_add_descriptor(
225225
descriptor_buf_remaining, &descriptor_counts, &current_interface_string,
226-
usb_hid_report_descriptor_length());
226+
usb_hid_report_descriptor_length(), usb_hid_boot_device());
227227
}
228228
#endif
229229

0 commit comments

Comments
 (0)