Skip to content

Commit 3dc2b4c

Browse files
committed
at least original functionality with new API
1 parent 4f8ff12 commit 3dc2b4c

File tree

7 files changed

+148
-128
lines changed

7 files changed

+148
-128
lines changed

locale/circuitpython.pot

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ msgstr ""
9191
msgid "%q length must be %q"
9292
msgstr ""
9393

94+
#: shared-bindings/usb_hid/Device.c
95+
msgid "%q length must be >= 1"
96+
msgstr ""
97+
9498
#: shared-bindings/vectorio/Polygon.c
9599
msgid "%q list must be a list"
96100
msgstr ""
@@ -1226,7 +1230,7 @@ msgstr ""
12261230
msgid "Internal error #%d"
12271231
msgstr ""
12281232

1229-
#: shared-bindings/sdioio/SDCard.c
1233+
#: shared-bindings/sdioio/SDCard.c shared-module/usb_hid/Device.c
12301234
msgid "Invalid %q"
12311235
msgstr ""
12321236

shared-bindings/usb_hid/Device.c

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
//| class Device:
3232
//| """HID Device specification"""
3333
//|
34-
//| def __init__(self, *, descriptor: ReadableBuffer, usage_page: int, usage: int, in_report_lengths: Sequence[int], out_report_lengths: Sequence[int]) -> None:
34+
//| 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:
3535
//| """Create a description of a USB HID device. The actual device is created when you
3636
//| pass a `Device` to `usb_hid.enable()`.
3737
//|
@@ -40,6 +40,7 @@
4040
//| :param int usage_page: The Usage Page value from the descriptor. Must match what is in the descriptor.
4141
//| :param int usage: The Usage value from the descriptor. Must match what is in the descriptor.
4242
//| :param int report_ids: Sequence of report ids used by the descriptor.
43+
//| If the ``report_descriptor`` does not have a report ID, use 0.
4344
//| :param int in_report_lengths: Sequence of sizes in bytes of the HIDs report sent to the host.
4445
//| The sizes are in order of the ``report_ids``.
4546
//| "IN" is with respect to the host.
@@ -59,12 +60,12 @@
5960
//| MOUSE: Device
6061
//| """Standard mouse device supporting five mouse buttons, X and Y relative movements from -127 to 127
6162
//| in each report, and a relative mouse wheel change from -127 to 127 in each report.
62-
//| Uses Report ID 2 for its IN reports.
63+
//| Uses Report ID 2 for its IN report.
6364
//| """
6465
//|
6566
//| CONSUMER_CONTROL: Device
6667
//| """Consumer Control device supporting sent values from 1-652, with no rollover.
67-
//| Uses Report ID 3 for its IN reports."""
68+
//| Uses Report ID 3 for its IN report."""
6869
//|
6970

7071
STATIC mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
@@ -100,8 +101,12 @@ STATIC mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args
100101
mp_obj_t out_report_lengths = args[ARG_out_report_lengths].u_obj;
101102

102103
size_t report_ids_count = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(report_ids));
103-
if (MP_OBJ_SMALL_INT_VALUE(mp_obj_len(in_report_lengths)) != report_ids_count ||
104-
MP_OBJ_SMALL_INT_VALUE(mp_obj_len(out_report_lengths)) != report_ids_count) {
104+
if (report_ids_count < 1) {
105+
mp_raise_ValueError_varg(translate("%q length must be >= 1"), MP_QSTR_report_ids);
106+
}
107+
108+
if ((size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(in_report_lengths)) != report_ids_count ||
109+
(size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(out_report_lengths)) != report_ids_count) {
105110
mp_raise_ValueError_varg(translate("%q, %q, and %q must all be the same length"),
106111
MP_QSTR_report_ids, MP_QSTR_in_report_lengths, MP_QSTR_out_report_lengths);
107112
}
@@ -136,7 +141,9 @@ STATIC mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args
136141

137142

138143
//| def send_report(self, buf: ReadableBuffer, report_id: Optional[int] = None) -> None:
139-
//| """Send an HID report.
144+
//| """Send an HID report. If the device descriptor specifies zero or one report id's,
145+
//| you can supply `None` (the default) as the value of ``report_id``.
146+
//| Otherwise you must specify which report id to use when sending the report.
140147
//| """
141148
//| ...
142149
//|
@@ -155,22 +162,23 @@ STATIC mp_obj_t usb_hid_device_send_report(size_t n_args, const mp_obj_t *pos_ar
155162
mp_buffer_info_t bufinfo;
156163
mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ);
157164

158-
uint8_t report_id = 0;
165+
// -1 asks common_hal to determine the report id if possible.
166+
mp_int_t report_id_arg = -1;
159167
if (args[ARG_report_id].u_obj != mp_const_none) {
160-
const mp_int_t report_id_arg = mp_obj_int_get_checked(args[ARG_report_id].u_obj);
161-
report_id = mp_arg_validate_int_range(report_id_arg, 1, 255, MP_QSTR_report_id);
168+
report_id_arg = mp_obj_int_get_checked(args[ARG_report_id].u_obj);
162169
}
170+
const uint8_t report_id = common_hal_usb_hid_device_validate_report_id(self, report_id_arg);
163171

164172
common_hal_usb_hid_device_send_report(self, ((uint8_t *)bufinfo.buf), bufinfo.len, report_id);
165173
return mp_const_none;
166174
}
167175
MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_device_send_report_obj, 2, usb_hid_device_send_report);
168176

169177
//| def get_last_received_report(self, report_id: Optional[int] = None) -> bytes:
170-
//| """Get the last received HID OUT report for the given report ID.
171-
//| The report ID may be omitted if there is no report ID, or only one report ID.
172-
//| Return `None` if nothing received.
173-
//| """
178+
//| """Get the last received HID OUT report for the given report ID.
179+
//| The report ID may be omitted if there is no report ID, or only one report ID.
180+
//| Return `None` if nothing received.
181+
//| """
174182
//| ...
175183
//|
176184
STATIC mp_obj_t usb_hid_device_get_last_received_report(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
@@ -184,17 +192,18 @@ STATIC mp_obj_t usb_hid_device_get_last_received_report(size_t n_args, const mp_
184192
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
185193
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
186194

187-
uint8_t report_id = 0;
195+
mp_int_t report_id_arg = -1;
188196
if (args[ARG_report_id].u_obj != mp_const_none) {
189-
report_id = mp_obj_int_get_checked(args[ARG_report_id].u_obj);
197+
report_id_arg = mp_obj_int_get_checked(args[ARG_report_id].u_obj);
190198
}
199+
const uint8_t report_id = common_hal_usb_hid_device_validate_report_id(self, report_id_arg);
191200

192201
return common_hal_usb_hid_device_get_last_received_report(self, report_id);
193202
}
194203
MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_device_get_last_received_report_obj, 1, usb_hid_device_get_last_received_report);
195204

196205
//| last_received_report: bytes
197-
//| """The HID OUT report as a `bytes`. (read-only). `None` if nothing received.
206+
//| """The HID OUT report as a `bytes` (read-only). `None` if nothing received.
198207
//| Same as `get_last_received_report()` with no argument.
199208
//|
200209
//| Deprecated: will be removed in CircutPython 8.0.0. Use `get_last_received_report()` instead.
@@ -203,7 +212,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_device_get_last_received_report_obj, 1, usb_h
203212
STATIC mp_obj_t usb_hid_device_obj_get_last_received_report_property(mp_obj_t self_in) {
204213
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
205214

206-
return common_hal_usb_hid_device_get_last_received_report(self, 0);
215+
// Get the sole report_id, if there is one.
216+
const uint8_t report_id = common_hal_usb_hid_device_validate_report_id(self, -1);
217+
return common_hal_usb_hid_device_get_last_received_report(self, report_id);
207218
}
208219
MP_DEFINE_CONST_FUN_OBJ_1(usb_hid_device_get_last_received_report_property_obj, usb_hid_device_obj_get_last_received_report_property);
209220

@@ -215,7 +226,7 @@ const mp_obj_property_t usb_hid_device_last_received_report_obj = {
215226
};
216227

217228
//| usage_page: int
218-
//| """The usage page of the device as an `int`. Can be thought of a category. (read-only)"""
229+
//| """The device usage page identifier, which designates a category of device. (read-only)"""
219230
//|
220231
STATIC mp_obj_t usb_hid_device_obj_get_usage_page(mp_obj_t self_in) {
221232
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
@@ -231,7 +242,7 @@ const mp_obj_property_t usb_hid_device_usage_page_obj = {
231242
};
232243

233244
//| usage: int
234-
//| """The functionality of the device as an int. (read-only)
245+
//| """The device usage identifier, which designates a specific kind of device. (read-only)
235246
//|
236247
//| For example, Keyboard is 0x06 within the generic desktop usage page 0x01.
237248
//| Mouse is 0x02 within the same usage page."""

shared-bindings/usb_hid/Device.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@ void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *
3838
mp_obj_t common_hal_usb_hid_device_get_last_received_report(usb_hid_device_obj_t *self, uint8_t report_id);
3939
uint8_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self);
4040
uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self);
41-
bool common_hal_usb_hid_device_valid_report_id(usb_hid_device_obj_t *self, uint8_t report_id);
41+
uint8_t common_hal_usb_hid_device_validate_report_id(usb_hid_device_obj_t *self, mp_int_t report_id);
4242

4343
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_HID_DEVICE_H

shared-bindings/usb_hid/__init__.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(usb_hid_disable_obj, usb_hid_disable);
7171
//|
7272
//| If you enable too many devices at once, you will run out of USB endpoints.
7373
//| The number of available endpoints varies by microcontroller.
74-
//| CircuitPython will go into safe mode after running boot.py to inform you if
74+
//| CircuitPython will go into safe mode after running ``boot.py`` to inform you if
7575
//| not enough endpoints are available.
7676
//| """
7777
//| ...

shared-module/usb_hid/Device.c

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ const usb_hid_device_obj_t usb_hid_device_keyboard_obj = {
7979
.usage = 0x06,
8080
.num_report_ids = 1,
8181
.report_ids = { 0x01, },
82-
.in_report_lengths = { 8, 0, 0, 0, },
82+
.in_report_lengths = { 8, 0, 0, 0, 0, 0, },
8383
.out_report_lengths = { 1, },
8484
};
8585

@@ -129,7 +129,7 @@ const usb_hid_device_obj_t usb_hid_device_mouse_obj = {
129129
.usage = 0x02,
130130
.num_report_ids = 1,
131131
.report_ids = { 0x02, },
132-
.in_report_lengths = { 4, 0, 0, 0, },
132+
.in_report_lengths = { 4, 0, 0, 0, 0, 0, },
133133
.out_report_lengths = { 0, },
134134
};
135135

@@ -158,18 +158,30 @@ const usb_hid_device_obj_t usb_hid_device_consumer_control_obj = {
158158
.usage = 0x01,
159159
.num_report_ids = 1,
160160
.report_ids = { 0x03 },
161-
.in_report_lengths = { 2, 0, 0, 0 },
162-
.out_report_lengths = { 0 },
161+
.in_report_lengths = { 2, 0, 0, 0, 0, 0, },
162+
.out_report_lengths = { 0, },
163163
};
164164

165-
166-
bool common_hal_usb_hid_device_valid_report_id(usb_hid_device_obj_t *self, uint8_t report_id) {
165+
STATIC size_t get_report_id_idx(usb_hid_device_obj_t *self, size_t report_id) {
167166
for (size_t i = 0; i < self->num_report_ids; i++) {
168167
if (report_id == self->report_ids[i]) {
169-
return true;
168+
return i;
170169
}
171170
}
172-
return false;
171+
return MAX_REPORT_IDS_PER_DESCRIPTOR;
172+
}
173+
174+
// See if report_id is used by this device. If it is -1, then return the sole report id used by this device,
175+
// which might be 0 if no report_id was supplied.
176+
uint8_t common_hal_usb_hid_device_validate_report_id(usb_hid_device_obj_t *self, mp_int_t report_id_arg) {
177+
if (report_id_arg == -1 && self->num_report_ids == 1) {
178+
return self->report_ids[0];
179+
}
180+
if (!(report_id_arg >= 0 &&
181+
get_report_id_idx(self, (size_t)report_id_arg) < MAX_REPORT_IDS_PER_DESCRIPTOR)) {
182+
mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_report_id);
183+
}
184+
return (uint8_t)report_id_arg;
173185
}
174186

175187
void common_hal_usb_hid_device_construct(usb_hid_device_obj_t *self, mp_obj_t report_descriptor, uint8_t usage_page, uint8_t usage, size_t num_report_ids, uint8_t *report_ids, uint8_t *in_report_lengths, uint8_t *out_report_lengths) {
@@ -183,7 +195,7 @@ void common_hal_usb_hid_device_construct(usb_hid_device_obj_t *self, mp_obj_t re
183195
mp_get_buffer_raise(report_descriptor, &bufinfo, MP_BUFFER_READ);
184196
self->report_descriptor_length = bufinfo.len;
185197

186-
// Copy the raw the descriptor bytes into a heap obj. We don't keep the Python descriptor object.
198+
// Copy the raw descriptor bytes into a heap obj. We don't keep the Python descriptor object.
187199

188200
uint8_t *descriptor_bytes = gc_alloc(bufinfo.len, false, false);
189201
memcpy(descriptor_bytes, bufinfo.buf, bufinfo.len);
@@ -206,8 +218,12 @@ uint8_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self) {
206218
}
207219

208220
void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *report, uint8_t len, uint8_t report_id) {
209-
if (len != self->in_report_length) {
210-
mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."), self->in_report_length);
221+
// report_id and len have already been validated for this device.
222+
size_t id_idx = get_report_id_idx(self, report_id);
223+
224+
if (len != self->in_report_lengths[id_idx]) {
225+
mp_raise_ValueError_varg(translate("Buffer incorrect size. Should be %d bytes."),
226+
self->in_report_lengths[id_idx]);
211227
}
212228

213229
// Wait until interface is ready, timeout = 2 seconds
@@ -225,14 +241,25 @@ void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *
225241
}
226242
}
227243

244+
mp_obj_t common_hal_usb_hid_device_get_last_received_report(usb_hid_device_obj_t *self, uint8_t report_id) {
245+
// report_id has already been validated for this deveice.
246+
size_t id_idx = get_report_id_idx(self, report_id);
247+
return mp_obj_new_bytes(self->out_report_buffers[id_idx], self->out_report_lengths[id_idx]);
248+
}
228249

229250
void usb_hid_device_create_report_buffers(usb_hid_device_obj_t *self) {
230-
for (size_t i = 0; i < self->num_report_ids; count++) {
231-
if (self->out_report_length > 0) {
232-
self->out_report_buffers[i] = self->out_report_lengths[i] > 0
233-
? gc_alloc(self->out_report_length, false, true /*long-lived*/)
234-
: NULL;
235-
}
251+
for (size_t i = 0; i < self->num_report_ids; i++) {
252+
// The IN buffers are used only for tud_hid_get_report_cb(),
253+
// which is an unusual case. Normally we can just pass the data directly with tud_hid_report().
254+
self->in_report_buffers[i] =
255+
self->in_report_lengths[i] > 0
256+
? gc_alloc(self->in_report_lengths[i], false, true /*long-lived*/)
257+
: NULL;
258+
259+
self->out_report_buffers[i] =
260+
self->out_report_lengths[i] > 0
261+
? gc_alloc(self->out_report_lengths[i], false, true /*long-lived*/)
262+
: NULL;
236263
}
237264
}
238265

@@ -254,25 +281,27 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t
254281
memcpy(buffer, hid_device->in_report_buffers[id_idx], reqlen);
255282
return reqlen;
256283
}
284+
return 0;
285+
}
257286

258287
// Callbacks invoked when we received Set_Report request through control endpoint
259-
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) {
260-
(void)itf;
261-
if (report_type == HID_REPORT_TYPE_INVALID) {
262-
report_id = buffer[0];
263-
buffer++;
264-
bufsize--;
265-
} else if (report_type != HID_REPORT_TYPE_OUTPUT) {
266-
return;
267-
}
288+
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) {
289+
(void)itf;
290+
if (report_type == HID_REPORT_TYPE_INVALID) {
291+
report_id = buffer[0];
292+
buffer++;
293+
bufsize--;
294+
} else if (report_type != HID_REPORT_TYPE_OUTPUT) {
295+
return;
296+
}
268297

269-
usb_hid_device_obj_t *hid_device;
270-
size_t id_idx;
271-
// Find device with this report id, and get the report id index.
272-
if (usb_hid_get_device_with_report_id(report_id, &hid_device, &id_idx)) {
273-
// If a report of the correct size has been read, save it in the proper OUT report buffer.
274-
if (hid_device && hid_device->out_report_lengths[id_idx] >= bufsize) {
275-
memcpy(hid_device->out_report_buffers[id_idx], buffer, bufsize);
276-
}
298+
usb_hid_device_obj_t *hid_device;
299+
size_t id_idx;
300+
// Find device with this report id, and get the report id index.
301+
if (usb_hid_get_device_with_report_id(report_id, &hid_device, &id_idx)) {
302+
// If a report of the correct size has been read, save it in the proper OUT report buffer.
303+
if (hid_device && hid_device->out_report_lengths[id_idx] >= bufsize) {
304+
memcpy(hid_device->out_report_buffers[id_idx], buffer, bufsize);
277305
}
278306
}
307+
}

shared-module/usb_hid/Device.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232

3333
#include "py/obj.h"
3434

35-
// The most complicated one currently know of is the head and eye tracker, which requires 5:
35+
// The most complicated device currently known of is the head and eye tracker, which requires 5
36+
// report ids.
3637
// https://usb.org/sites/default/files/hutrr74_-_usage_page_for_head_and_eye_trackers_0.pdf
3738
#define MAX_REPORT_IDS_PER_DESCRIPTOR (6)
3839

@@ -43,6 +44,7 @@ typedef struct {
4344
uint8_t report_ids[MAX_REPORT_IDS_PER_DESCRIPTOR];
4445
uint8_t in_report_lengths[MAX_REPORT_IDS_PER_DESCRIPTOR];
4546
uint8_t out_report_lengths[MAX_REPORT_IDS_PER_DESCRIPTOR];
47+
uint8_t *in_report_buffers[MAX_REPORT_IDS_PER_DESCRIPTOR];
4648
uint8_t *out_report_buffers[MAX_REPORT_IDS_PER_DESCRIPTOR];
4749
uint16_t report_descriptor_length;
4850
uint8_t usage_page;

0 commit comments

Comments
 (0)