2018-07-31 05:42:04 -04:00
|
|
|
/*
|
|
|
|
* This file is part of the MicroPython project, http://micropython.org/
|
|
|
|
*
|
|
|
|
* The MIT License (MIT)
|
|
|
|
*
|
|
|
|
* Copyright (c) 2018 hathach 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.
|
|
|
|
*/
|
|
|
|
|
2021-04-26 23:54:01 -04:00
|
|
|
#include <string.h>
|
|
|
|
|
2021-04-27 14:37:36 -04:00
|
|
|
#include "tusb.h"
|
|
|
|
|
|
|
|
#include "py/gc.h"
|
2021-04-28 23:48:26 -04:00
|
|
|
#include "py/runtime.h"
|
2021-04-27 14:37:36 -04:00
|
|
|
#include "shared-bindings/usb_hid/__init__.h"
|
|
|
|
#include "shared-module/usb_hid/Device.h"
|
2021-04-26 23:54:01 -04:00
|
|
|
#include "supervisor/memory.h"
|
2021-04-27 14:37:36 -04:00
|
|
|
#include "supervisor/usb.h"
|
2021-04-26 23:54:01 -04:00
|
|
|
|
2021-04-21 23:25:36 -04:00
|
|
|
static const uint8_t usb_hid_descriptor_template[] = {
|
|
|
|
0x09, // 0 bLength
|
2021-04-27 14:37:36 -04:00
|
|
|
0x04, // 1 bDescriptorType (Interface)
|
|
|
|
0xFF, // 2 bInterfaceNumber 3
|
|
|
|
#define HID_DESCRIPTOR_INTERFACE_INDEX (2)
|
|
|
|
0x00, // 3 bAlternateSetting
|
|
|
|
0x02, // 4 bNumEndpoints 2
|
|
|
|
0x03, // 5 bInterfaceClass: HID
|
|
|
|
0x00, // 6 bInterfaceSubClass: NOBOOT
|
|
|
|
0x00, // 7 bInterfaceProtocol: NONE
|
|
|
|
0xFF, // 8 iInterface (String Index) [SET AT RUNTIME]
|
|
|
|
#define HID_DESCRIPTOR_INTERFACE_STRING_INDEX (8)
|
|
|
|
|
|
|
|
0x09, // 9 bLength
|
|
|
|
0x21, // 10 bDescriptorType (HID)
|
|
|
|
0x11, 0x01, // 11,12 bcdHID 1.11
|
|
|
|
0x00, // 13 bCountryCode
|
|
|
|
0x01, // 14 bNumDescriptors
|
|
|
|
0x22, // 15 bDescriptorType[0] (HID)
|
|
|
|
0xFF, 0xFF, // 16,17 wDescriptorLength[0] [SET AT RUNTIME: lo, hi]
|
|
|
|
#define HID_DESCRIPTOR_LENGTH_INDEX (16)
|
|
|
|
|
|
|
|
0x07, // 18 bLength
|
|
|
|
0x05, // 19 bDescriptorType (Endpoint)
|
|
|
|
0xFF, // 20 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | endpoint]
|
|
|
|
#define HID_IN_ENDPOINT_INDEX (20)
|
|
|
|
0x03, // 21 bmAttributes (Interrupt)
|
|
|
|
0x40, 0x00, // 22,23 wMaxPacketSize 64
|
|
|
|
0x08, // 24 bInterval 8 (unit depends on device speed)
|
|
|
|
|
|
|
|
0x07, // 25 bLength
|
|
|
|
0x05, // 26 bDescriptorType (Endpoint)
|
|
|
|
0xFF, // 27 bEndpointAddress (OUT/H2D) [SET AT RUNTIME]
|
2021-04-27 23:53:23 -04:00
|
|
|
#define HID_OUT_ENDPOINT_INDEX (27)
|
2021-04-27 14:37:36 -04:00
|
|
|
0x03, // 28 bmAttributes (Interrupt)
|
|
|
|
0x40, 0x00, // 29,30 wMaxPacketSize 64
|
|
|
|
0x08, // 31 bInterval 8 (unit depends on device speed)
|
2021-04-21 23:25:36 -04:00
|
|
|
};
|
|
|
|
|
2021-04-28 23:48:26 -04:00
|
|
|
#define MAX_HID_DEVICES 8
|
|
|
|
|
2021-04-27 14:37:36 -04:00
|
|
|
static supervisor_allocation *hid_report_descriptor_allocation;
|
2021-04-28 23:48:26 -04:00
|
|
|
static usb_hid_device_obj_t hid_devices[MAX_HID_DEVICES];
|
2021-05-03 22:22:29 -04:00
|
|
|
// If 0, USB HID is disabled.
|
2021-05-21 17:34:30 -04:00
|
|
|
static mp_int_t num_hid_devices;
|
2021-04-23 21:44:13 -04:00
|
|
|
|
2021-04-29 17:41:43 -04:00
|
|
|
// This tuple is store in usb_hid.devices.
|
|
|
|
static mp_obj_tuple_t *hid_devices_tuple;
|
|
|
|
|
2021-04-28 13:00:44 -04:00
|
|
|
static mp_obj_tuple_t default_hid_devices_tuple = {
|
|
|
|
.base = {
|
|
|
|
.type = &mp_type_tuple,
|
|
|
|
},
|
|
|
|
.len = 3,
|
|
|
|
.items = {
|
|
|
|
MP_OBJ_FROM_PTR(&usb_hid_device_keyboard_obj),
|
|
|
|
MP_OBJ_FROM_PTR(&usb_hid_device_mouse_obj),
|
|
|
|
MP_OBJ_FROM_PTR(&usb_hid_device_consumer_control_obj),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-04-28 23:48:26 -04:00
|
|
|
bool usb_hid_enabled(void) {
|
2021-05-21 17:34:30 -04:00
|
|
|
return num_hid_devices > 0;
|
2021-04-28 23:48:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void usb_hid_set_defaults(void) {
|
2021-05-03 22:22:29 -04:00
|
|
|
common_hal_usb_hid_enable(
|
|
|
|
CIRCUITPY_USB_HID_ENABLED_DEFAULT ? &default_hid_devices_tuple : mp_const_empty_tuple);
|
2021-04-28 13:00:44 -04:00
|
|
|
}
|
|
|
|
|
2021-04-23 21:44:13 -04:00
|
|
|
// This is the interface descriptor, not the report descriptor.
|
2021-04-21 23:25:36 -04:00
|
|
|
size_t usb_hid_descriptor_length(void) {
|
2021-04-26 23:54:01 -04:00
|
|
|
return sizeof(usb_hid_descriptor_template);
|
2021-04-21 23:25:36 -04:00
|
|
|
}
|
|
|
|
|
2021-04-30 10:40:12 -04:00
|
|
|
static const char usb_hid_interface_name[] = USB_INTERFACE_NAME " HID";
|
2021-04-21 23:25:36 -04:00
|
|
|
|
2021-04-25 10:23:59 -04:00
|
|
|
// This is the interface descriptor, not the report descriptor.
|
2021-05-13 21:49:04 -04:00
|
|
|
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) {
|
2021-04-21 23:25:36 -04:00
|
|
|
memcpy(descriptor_buf, usb_hid_descriptor_template, sizeof(usb_hid_descriptor_template));
|
|
|
|
|
2021-05-13 21:49:04 -04:00
|
|
|
descriptor_buf[HID_DESCRIPTOR_INTERFACE_INDEX] = descriptor_counts->current_interface;
|
|
|
|
descriptor_counts->current_interface++;
|
2021-04-27 14:37:36 -04:00
|
|
|
|
2021-05-09 00:42:45 -04:00
|
|
|
usb_add_interface_string(*current_interface_string, usb_hid_interface_name);
|
2021-04-27 14:37:36 -04:00
|
|
|
descriptor_buf[HID_DESCRIPTOR_INTERFACE_STRING_INDEX] = *current_interface_string;
|
|
|
|
(*current_interface_string)++;
|
|
|
|
|
2021-04-21 23:25:36 -04:00
|
|
|
descriptor_buf[HID_DESCRIPTOR_LENGTH_INDEX] = report_descriptor_length & 0xFF;
|
|
|
|
descriptor_buf[HID_DESCRIPTOR_LENGTH_INDEX + 1] = (report_descriptor_length >> 8);
|
|
|
|
|
2021-05-13 21:49:04 -04:00
|
|
|
descriptor_buf[HID_IN_ENDPOINT_INDEX] =
|
|
|
|
0x80 | (USB_HID_EP_NUM_IN ? USB_HID_EP_NUM_IN : descriptor_counts->current_endpoint);
|
|
|
|
descriptor_counts->num_in_endpoints++;
|
|
|
|
descriptor_buf[HID_OUT_ENDPOINT_INDEX] =
|
|
|
|
USB_HID_EP_NUM_OUT ? USB_HID_EP_NUM_OUT : descriptor_counts->current_endpoint;
|
|
|
|
descriptor_counts->num_out_endpoints++;
|
|
|
|
descriptor_counts->current_endpoint++;
|
2021-04-21 23:25:36 -04:00
|
|
|
|
|
|
|
return sizeof(usb_hid_descriptor_template);
|
|
|
|
}
|
2021-04-23 00:18:05 -04:00
|
|
|
|
2021-04-29 17:41:43 -04:00
|
|
|
// Make up a fresh tuple containing the device objects saved in the static
|
|
|
|
// devices table. Save the tuple in usb_hid.devices.
|
|
|
|
static void usb_hid_set_devices_from_hid_devices(void) {
|
2021-05-21 17:34:30 -04:00
|
|
|
mp_obj_t tuple_items[num_hid_devices];
|
|
|
|
for (mp_int_t i = 0; i < num_hid_devices; i++) {
|
2021-04-29 17:41:43 -04:00
|
|
|
tuple_items[i] = &hid_devices[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remember tuple for gc purposes.
|
2021-05-21 17:34:30 -04:00
|
|
|
hid_devices_tuple = mp_obj_new_tuple(num_hid_devices, tuple_items);
|
2021-04-29 17:41:43 -04:00
|
|
|
usb_hid_set_devices(hid_devices_tuple);
|
|
|
|
}
|
|
|
|
|
2021-05-03 22:22:29 -04:00
|
|
|
bool common_hal_usb_hid_disable(void) {
|
|
|
|
return common_hal_usb_hid_enable(mp_const_empty_tuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool common_hal_usb_hid_enable(const mp_obj_t devices) {
|
2021-04-23 00:18:05 -04:00
|
|
|
// We can't change the devices once we're connected.
|
|
|
|
if (tud_connected()) {
|
2021-04-25 23:17:41 -04:00
|
|
|
return false;
|
2021-04-23 00:18:05 -04:00
|
|
|
}
|
|
|
|
|
2021-05-21 17:29:21 -04:00
|
|
|
const mp_int_t num_devices = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(devices));
|
|
|
|
if (num_devices > MAX_HID_DEVICES) {
|
2021-04-28 23:48:26 -04:00
|
|
|
mp_raise_ValueError_varg(translate("No more than %d HID devices allowed"), MAX_HID_DEVICES);
|
|
|
|
}
|
|
|
|
|
2021-05-21 17:34:30 -04:00
|
|
|
num_hid_devices = num_devices;
|
2021-05-21 17:29:21 -04:00
|
|
|
|
2021-04-28 23:48:26 -04:00
|
|
|
// Remember the devices in static storage so they live across VMs.
|
2021-05-21 17:34:30 -04:00
|
|
|
for (mp_int_t i = 0; i < num_hid_devices; i++) {
|
2021-04-28 23:48:26 -04:00
|
|
|
// devices has already been validated to contain only usb_hid_device_obj_t objects.
|
|
|
|
usb_hid_device_obj_t *device =
|
|
|
|
MP_OBJ_TO_PTR(mp_obj_subscr(devices, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL));
|
|
|
|
memcpy(&hid_devices[i], device, sizeof(usb_hid_device_obj_t));
|
|
|
|
}
|
|
|
|
|
2021-04-29 17:41:43 -04:00
|
|
|
usb_hid_set_devices_from_hid_devices();
|
|
|
|
|
2021-04-25 23:17:41 -04:00
|
|
|
return true;
|
|
|
|
}
|
2021-04-23 00:18:05 -04:00
|
|
|
|
2021-04-29 17:41:43 -04:00
|
|
|
// Called when HID devices are ready to be used, when code.py or the REPL starts running.
|
2021-04-28 23:48:26 -04:00
|
|
|
void usb_hid_setup_devices(void) {
|
2021-04-29 17:41:43 -04:00
|
|
|
usb_hid_set_devices_from_hid_devices();
|
2021-04-23 21:44:13 -04:00
|
|
|
|
2021-04-30 10:40:12 -04:00
|
|
|
// Create report buffers on the heap.
|
2021-05-21 17:34:30 -04:00
|
|
|
for (mp_int_t i = 0; i < num_hid_devices; i++) {
|
2021-04-28 23:48:26 -04:00
|
|
|
usb_hid_device_create_report_buffers(&hid_devices[i]);
|
|
|
|
}
|
|
|
|
}
|
2021-04-23 21:44:13 -04:00
|
|
|
|
2021-04-28 23:48:26 -04:00
|
|
|
// Total length of the report descriptor, with all configured devices.
|
|
|
|
size_t usb_hid_report_descriptor_length(void) {
|
|
|
|
size_t total_hid_report_descriptor_length = 0;
|
2021-05-21 17:34:30 -04:00
|
|
|
for (mp_int_t i = 0; i < num_hid_devices; i++) {
|
2021-04-29 17:41:43 -04:00
|
|
|
total_hid_report_descriptor_length += hid_devices[i].report_descriptor_length;
|
2021-04-23 21:44:13 -04:00
|
|
|
}
|
|
|
|
|
2021-04-27 14:37:36 -04:00
|
|
|
// Don't need space for a report id if there's only one device.
|
2021-05-21 17:34:30 -04:00
|
|
|
if (num_hid_devices == 1) {
|
2021-04-27 14:37:36 -04:00
|
|
|
total_hid_report_descriptor_length -= 2;
|
2021-04-23 21:44:13 -04:00
|
|
|
}
|
2021-04-28 23:48:26 -04:00
|
|
|
return total_hid_report_descriptor_length;
|
|
|
|
}
|
2021-04-23 21:44:13 -04:00
|
|
|
|
2021-04-28 23:48:26 -04:00
|
|
|
// Build the combined HID report descriptor in the given space.
|
2021-04-30 10:40:12 -04:00
|
|
|
void usb_hid_build_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length) {
|
2021-05-03 22:22:29 -04:00
|
|
|
if (!usb_hid_enabled()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-04-28 23:48:26 -04:00
|
|
|
uint8_t *report_descriptor_start = report_descriptor_space;
|
2021-04-23 21:44:13 -04:00
|
|
|
|
2021-05-21 17:34:30 -04:00
|
|
|
for (mp_int_t i = 0; i < num_hid_devices; i++) {
|
2021-04-28 23:48:26 -04:00
|
|
|
usb_hid_device_obj_t *device = &hid_devices[i];
|
2021-04-23 21:44:13 -04:00
|
|
|
// Copy the report descriptor for this device.
|
2021-07-28 10:31:47 -04:00
|
|
|
if (num_hid_devices == 1 && device->num_report_ids == 1) {
|
|
|
|
// There's only one device, with one report id, so remove it.
|
2021-04-23 21:44:13 -04:00
|
|
|
// Copy the descriptor, but splice out the report id indicator and value (2 bytes).
|
2021-07-28 10:31:47 -04:00
|
|
|
size_t report_id_idx = 0;
|
|
|
|
for (report_id_idx = 0; report_id_idx < device->report_descriptor_length; report_id_idx++) {
|
|
|
|
if (report_descriptor_start[report_id_idx] == 0x85) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (report_id_idx < device->report_descriptor_length) {
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(report_descriptor_start, device->report_descriptor, device->report_id_index - 1);
|
|
|
|
report_descriptor_start += device->report_id_index - 1;
|
|
|
|
memcpy(report_descriptor_start, device->report_descriptor + device->report_id_index + 1,
|
|
|
|
device->report_descriptor_length - device->report_id_index - 1);
|
|
|
|
} else {
|
|
|
|
// Copy the whole descriptor and fill in the report id.
|
|
|
|
memcpy(report_descriptor_start, device->report_descriptor, device->report_descriptor_length);
|
|
|
|
report_descriptor_start[device->report_id_index] = i + 1;
|
|
|
|
|
|
|
|
// Remember the report id that was assigned.
|
|
|
|
device->report_id = i + 1;
|
|
|
|
|
|
|
|
// Advance to the next free chunk for the next report descriptor.x
|
|
|
|
report_descriptor_start += device->report_descriptor_length;
|
|
|
|
}
|
|
|
|
// Clear the heap pointer to the bytes of the descriptor.
|
|
|
|
// We don't need it any more and it will get lost when the heap goes away.
|
|
|
|
device->report_descriptor = NULL;
|
2021-04-23 21:44:13 -04:00
|
|
|
}
|
|
|
|
}
|
2021-04-23 00:18:05 -04:00
|
|
|
|
2021-04-28 23:48:26 -04:00
|
|
|
// Call this after the heap and VM are finished.
|
2021-07-28 10:31:47 -04:00
|
|
|
void usb_hid_save_report_descriptor(uint8_t *report_descriptor_space, size_t report_descriptor_length) {
|
|
|
|
if (!usb_hid_enabled()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate storage that persists across VMs to hold the combined report descriptor.
|
|
|
|
// and to remember the device details.
|
|
|
|
|
|
|
|
// Copy the descriptor from the temporary area to a supervisor storage allocation that
|
|
|
|
// will leave between VM instantiations.
|
|
|
|
hid_report_descriptor_allocation =
|
|
|
|
allocate_memory(align32_size(report_descriptor_length),
|
|
|
|
/*high_address*/ false, /*movable*/ false);
|
|
|
|
memcpy((uint8_t *)hid_report_descriptor_allocation->ptr, report_descriptor_space, report_descriptor_length);
|
2021-05-03 22:22:29 -04:00
|
|
|
}
|
|
|
|
|
2021-07-28 10:31:47 -04:00
|
|
|
void usb_hid_gc_collect(void) {
|
|
|
|
gc_collect_ptr(hid_devices_tuple);
|
2021-04-28 23:48:26 -04:00
|
|
|
|
2021-07-28 10:31:47 -04:00
|
|
|
// Mark possible heap pointers in the static device list as in use.
|
|
|
|
for (mp_int_t device_idx = 0; i < num_hid_devices; i++) {
|
2021-04-27 14:37:36 -04:00
|
|
|
|
2021-07-28 10:31:47 -04:00
|
|
|
// Cast away the const for .report_descriptor. It could be in flash or on the heap.
|
|
|
|
// Constant report descriptors must be const so that they are used directly from flash
|
|
|
|
// and not copied into RAM.
|
|
|
|
gc_collect_ptr((void *)hid_devices[device_idx].report_descriptor);
|
2021-04-29 17:41:43 -04:00
|
|
|
|
2021-07-28 10:31:47 -04:00
|
|
|
// Collect all the OUT report buffers for this device.
|
|
|
|
for (size_t id_idx = 0; id_idx < hid_devices[device_idx].report_ids_count; id_idx++) {
|
|
|
|
gc_collect_ptr(hid_devices[i].out_report_buffers[id_idx]);
|
|
|
|
}
|
|
|
|
}
|
2021-04-28 23:48:26 -04:00
|
|
|
}
|
2021-04-27 14:37:36 -04:00
|
|
|
|
2021-07-28 10:31:47 -04:00
|
|
|
bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t **device_out, size_t *id_idx_out) {
|
|
|
|
for (uint8_t device_idx = 0; device_idx < num_hid_devices; device_idx++) {
|
|
|
|
usb_hid_device_obj_t *device = &hid_devices[device_idx];
|
|
|
|
for (size_t id_idx = 0; id_idx < device->report_ids_count; id_idx++) {
|
|
|
|
if (device->report_ids[id_idx] == report_id) {
|
|
|
|
*device_out = device;
|
|
|
|
*id_idx_out = id_idx;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2021-04-27 14:37:36 -04:00
|
|
|
}
|
2021-07-28 10:31:47 -04:00
|
|
|
return false;
|
2021-04-27 14:37:36 -04:00
|
|
|
}
|
2021-04-28 23:48:26 -04:00
|
|
|
|
|
|
|
// Invoked when GET HID REPORT DESCRIPTOR is received.
|
|
|
|
// Application return pointer to descriptor
|
|
|
|
// Descriptor contents must exist long enough for transfer to complete
|
2021-07-28 10:31:47 -04:00
|
|
|
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf) {
|
|
|
|
return (uint8_t *)hid_report_descriptor_allocation->ptr;
|
|
|
|
}
|