diff --git a/main.c b/main.c index 8c981aee18..0ad0506082 100644 --- a/main.c +++ b/main.c @@ -286,12 +286,12 @@ int __attribute__((used)) main(void) { #endif // Reset to remove any state that boot.py setup. It should only be used to - // change internal state thats not in the heap. + // change internal state that's not in the heap. reset_port(); reset_mp(); } - // Start serial after giving boot.py a chance to tweak behavior. + // Start serial and HID after giving boot.py a chance to tweak behavior. serial_init(); // Boot script is finished, so now go into REPL/main mode. diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 1bcf9c3501..16f789c523 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -72,6 +72,7 @@ BASE_CFLAGS = \ -Wnested-externs \ -Wunreachable-code \ -Wcast-align \ + -Wno-error=lto-type-mismatch \ -D__$(CHIP_VARIANT)__ \ -ffunction-sections \ -fdata-sections \ @@ -81,6 +82,7 @@ BASE_CFLAGS = \ -DCIRCUITPY_SAFE_RESTART_WORD=0xDEADBEEF \ --param max-inline-insns-single=500 + # NDEBUG disables assert() statements. This reduces code size pretty dramatically, per tannewt. ifeq ($(CHIP_FAMILY), samd21) @@ -95,8 +97,8 @@ endif ifeq ($(DEBUG), 1) # Turn on Python modules useful for debugging (e.g. uheap, ustack). CFLAGS += -ggdb - CFLAGS += -flto - ## CFLAGS += -fno-inline +## CFLAGS += -flto + CFLAGS += -fno-inline -fno-ipa-sra ifeq ($(CHIP_FAMILY), samd21) CFLAGS += -DENABLE_MICRO_TRACE_BUFFER endif @@ -192,6 +194,7 @@ SRC_ASF := \ hpl/systick/hpl_systick.c \ hpl/usb/hpl_usb.c \ usb/class/cdc/device/cdcdf_acm.c \ + usb/class/hid/device/hiddf_generic.c \ usb/class/msc/device/mscdf.c \ usb/device/usbdc.c \ usb/usb_protocol.c \ @@ -281,6 +284,8 @@ SRC_COMMON_HAL = \ pulseio/PulseIn.c \ pulseio/PulseOut.c \ pulseio/PWMOut.c \ + usb_hid/__init__.c \ + usb_hid/Device.c # audiobusio/__init__.c \ audiobusio/PDMIn.c \ audioio/__init__.c \ @@ -289,8 +294,6 @@ SRC_COMMON_HAL = \ nvm/ByteArray.c \ touchio/__init__.c \ touchio/TouchIn.c \ - usb_hid/__init__.c \ - usb_hid/Device.c ifeq ($(INTERNAL_LIBM),1) SRC_LIBM = $(addprefix lib/,\ @@ -378,14 +381,19 @@ $(BUILD)/firmware.uf2: $(BUILD)/firmware.bin $(ECHO) "Create $@" python2 $(TOP)/tools/uf2/utils/uf2conv.py -b $(BOOTLOADER_SIZE) -c -o $@ $^ -$(BUILD)/autogen_usb_descriptor.c: tools/gen_usb_descriptor.py Makefile +$(BUILD)/autogen_usb_descriptor.c $(BUILD)/genhdr/autogen_usb_descriptor.h: autogen_usb_descriptor.intermediate + +.INTERMEDIATE: autogen_usb_descriptor.intermediate + +autogen_usb_descriptor.intermediate: tools/gen_usb_descriptor.py Makefile install -d $(BUILD) python3 tools/gen_usb_descriptor.py \ --manufacturer $(USB_MANUFACTURER)\ --product $(USB_PRODUCT)\ --vid $(USB_VID)\ --pid $(USB_PID)\ - $@ + --output_c_file $(BUILD)/autogen_usb_descriptor.c\ + --output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h deploy: $(BUILD)/firmware.bin $(ECHO) "Writing $< to the board" diff --git a/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h b/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h index 2108077bca..d1bb42fe45 100644 --- a/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h +++ b/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h @@ -2,6 +2,62 @@ #ifndef HPL_USB_CONFIG_H #define HPL_USB_CONFIG_H +// CIRCUITPY: + +// Use 64-byte USB buffers for endpoint directions that are in use. They're set to 0 below otherwise. + +#include "genhdr/autogen_usb_descriptor.h" + +#if defined(USB_ENDPOINT_1_OUT_USED) && USB_ENDPOINT_1_OUT_USED +#define CONF_USB_EP1_CACHE 64 +#endif +#if defined(USB_ENDPOINT_1_IN_USED) && USB_ENDPOINT_1_IN_USED +#define CONF_USB_EP1_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_2_OUT_USED) && USB_ENDPOINT_2_OUT_USED +#define CONF_USB_EP2_CACHE 64 +#endif +#if defined(USB_ENDPOINT_2_IN_USED) && USB_ENDPOINT_2_IN_USED +#define CONF_USB_EP2_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_3_OUT_USED) && USB_ENDPOINT_3_OUT_USED +#define CONF_USB_EP3_CACHE 64 +#endif +#if defined(USB_ENDPOINT_3_IN_USED) && USB_ENDPOINT_3_IN_USED +#define CONF_USB_EP3_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_4_OUT_USED) && USB_ENDPOINT_4_OUT_USED +#define CONF_USB_EP4_CACHE 64 +#endif +#if defined(USB_ENDPOINT_4_IN_USED) && USB_ENDPOINT_4_IN_USED +#define CONF_USB_EP4_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_5_OUT_USED) && USB_ENDPOINT_5_OUT_USED +#define CONF_USB_EP5_CACHE 64 +#endif +#if defined(USB_ENDPOINT_5_IN_USED) && USB_ENDPOINT_5_IN_USED +#define CONF_USB_EP5_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_6_OUT_USED) && USB_ENDPOINT_6_OUT_USED +#define CONF_USB_EP6_CACHE 64 +#endif +#if defined(USB_ENDPOINT_6_IN_USED) && USB_ENDPOINT_6_IN_USED +#define CONF_USB_EP6_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_7_OUT_USED) && USB_ENDPOINT_7_OUT_USED +#define CONF_USB_EP7_CACHE 64 +#endif +#if defined(USB_ENDPOINT_7_IN_USED) && USB_ENDPOINT_7_IN_USED +#define CONF_USB_EP7_I_CACHE 64 +#endif + + // <<< Use Configuration Wizard in Context Menu >>> #define CONF_USB_N_0 0 @@ -28,6 +84,8 @@ // Max number of endpoints supported // Limits the number of endpoints (described by EP address) can be used in app. +// NOTE(tannewt): This not only limits the number of endpoints but also the +// addresses. In other words, even if you use endpoint 6 you need to set this to 11. // 1 (EP0 only) // 2 (EP0 + 1 endpoint) // 3 (EP0 + 2 endpoints) @@ -100,7 +158,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep1_cache #ifndef CONF_USB_EP1_CACHE -#define CONF_USB_EP1_CACHE 64 +#define CONF_USB_EP1_CACHE 0 #endif // Cache buffer size for EP1 IN @@ -138,7 +196,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep2_cache #ifndef CONF_USB_EP2_CACHE -#define CONF_USB_EP2_CACHE 64 +#define CONF_USB_EP2_CACHE 0 #endif // Cache buffer size for EP2 IN @@ -156,7 +214,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_ep2_I_CACHE #ifndef CONF_USB_EP2_I_CACHE -#define CONF_USB_EP2_I_CACHE 64 +#define CONF_USB_EP2_I_CACHE 0 #endif // @@ -194,7 +252,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_ep3_I_CACHE #ifndef CONF_USB_EP3_I_CACHE -#define CONF_USB_EP3_I_CACHE 64 +#define CONF_USB_EP3_I_CACHE 0 #endif // @@ -214,7 +272,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep4_cache #ifndef CONF_USB_EP4_CACHE -#define CONF_USB_EP4_CACHE 64 +#define CONF_USB_EP4_CACHE 0 #endif // Cache buffer size for EP4 IN diff --git a/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h b/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h index 92dfcaa9ba..d1bb42fe45 100644 --- a/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h +++ b/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h @@ -2,6 +2,62 @@ #ifndef HPL_USB_CONFIG_H #define HPL_USB_CONFIG_H +// CIRCUITPY: + +// Use 64-byte USB buffers for endpoint directions that are in use. They're set to 0 below otherwise. + +#include "genhdr/autogen_usb_descriptor.h" + +#if defined(USB_ENDPOINT_1_OUT_USED) && USB_ENDPOINT_1_OUT_USED +#define CONF_USB_EP1_CACHE 64 +#endif +#if defined(USB_ENDPOINT_1_IN_USED) && USB_ENDPOINT_1_IN_USED +#define CONF_USB_EP1_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_2_OUT_USED) && USB_ENDPOINT_2_OUT_USED +#define CONF_USB_EP2_CACHE 64 +#endif +#if defined(USB_ENDPOINT_2_IN_USED) && USB_ENDPOINT_2_IN_USED +#define CONF_USB_EP2_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_3_OUT_USED) && USB_ENDPOINT_3_OUT_USED +#define CONF_USB_EP3_CACHE 64 +#endif +#if defined(USB_ENDPOINT_3_IN_USED) && USB_ENDPOINT_3_IN_USED +#define CONF_USB_EP3_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_4_OUT_USED) && USB_ENDPOINT_4_OUT_USED +#define CONF_USB_EP4_CACHE 64 +#endif +#if defined(USB_ENDPOINT_4_IN_USED) && USB_ENDPOINT_4_IN_USED +#define CONF_USB_EP4_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_5_OUT_USED) && USB_ENDPOINT_5_OUT_USED +#define CONF_USB_EP5_CACHE 64 +#endif +#if defined(USB_ENDPOINT_5_IN_USED) && USB_ENDPOINT_5_IN_USED +#define CONF_USB_EP5_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_6_OUT_USED) && USB_ENDPOINT_6_OUT_USED +#define CONF_USB_EP6_CACHE 64 +#endif +#if defined(USB_ENDPOINT_6_IN_USED) && USB_ENDPOINT_6_IN_USED +#define CONF_USB_EP6_I_CACHE 64 +#endif + +#if defined(USB_ENDPOINT_7_OUT_USED) && USB_ENDPOINT_7_OUT_USED +#define CONF_USB_EP7_CACHE 64 +#endif +#if defined(USB_ENDPOINT_7_IN_USED) && USB_ENDPOINT_7_IN_USED +#define CONF_USB_EP7_I_CACHE 64 +#endif + + // <<< Use Configuration Wizard in Context Menu >>> #define CONF_USB_N_0 0 @@ -102,7 +158,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep1_cache #ifndef CONF_USB_EP1_CACHE -#define CONF_USB_EP1_CACHE 64 +#define CONF_USB_EP1_CACHE 0 #endif // Cache buffer size for EP1 IN @@ -140,7 +196,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep2_cache #ifndef CONF_USB_EP2_CACHE -#define CONF_USB_EP2_CACHE 64 +#define CONF_USB_EP2_CACHE 0 #endif // Cache buffer size for EP2 IN @@ -158,7 +214,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_ep2_I_CACHE #ifndef CONF_USB_EP2_I_CACHE -#define CONF_USB_EP2_I_CACHE 64 +#define CONF_USB_EP2_I_CACHE 0 #endif // @@ -178,7 +234,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep3_cache #ifndef CONF_USB_EP3_CACHE -#define CONF_USB_EP3_CACHE 64 +#define CONF_USB_EP3_CACHE 0 #endif // Cache buffer size for EP3 IN @@ -216,7 +272,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep4_cache #ifndef CONF_USB_EP4_CACHE -#define CONF_USB_EP4_CACHE 64 +#define CONF_USB_EP4_CACHE 0 #endif // Cache buffer size for EP4 IN @@ -254,7 +310,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep5_cache #ifndef CONF_USB_EP5_CACHE -#define CONF_USB_EP5_CACHE 64 +#define CONF_USB_EP5_CACHE 0 #endif // Cache buffer size for EP5 IN @@ -292,7 +348,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep6_cache #ifndef CONF_USB_EP6_CACHE -#define CONF_USB_EP6_CACHE 64 +#define CONF_USB_EP6_CACHE 0 #endif // Cache buffer size for EP6 IN @@ -310,7 +366,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_ep6_I_CACHE #ifndef CONF_USB_EP6_I_CACHE -#define CONF_USB_EP6_I_CACHE 64 +#define CONF_USB_EP6_I_CACHE 0 #endif // @@ -330,7 +386,7 @@ // <1024=> Cached by 1024 bytes buffer (interrupt or isochronous EP) // usb_arch_ep7_cache #ifndef CONF_USB_EP7_CACHE -#define CONF_USB_EP7_CACHE 64 +#define CONF_USB_EP7_CACHE 0 #endif // Cache buffer size for EP7 IN diff --git a/ports/atmel-samd/common-hal/usb_hid/Device.c b/ports/atmel-samd/common-hal/usb_hid/Device.c index 443a67f8ed..ae6985aa9c 100644 --- a/ports/atmel-samd/common-hal/usb_hid/Device.c +++ b/ports/atmel-samd/common-hal/usb_hid/Device.c @@ -26,60 +26,59 @@ #include -#include "py/nlr.h" -#include "common-hal/usb_hid/__init__.h" +#include "common-hal/usb_hid/Device.h" + +#include "py/runtime.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/usb_hid/Device.h" +#include "genhdr/autogen_usb_descriptor.h" -static void report_sent(udd_ep_status_t status, iram_size_t nb_sent, - udd_ep_id_t ep) { - UNUSED(status); - UNUSED(nb_sent); - for (uint8_t i = 0; i < 2; i++) { - if (ep == usb_hid_devices[i].endpoint) { - usb_hid_devices[i].transaction_ongoing = false; - return; +#include "tick.h" + +#include "usb/class/hid/device/hiddf_generic.h" + +static uint32_t usb_hid_send_report(usb_hid_device_obj_t *self, uint8_t* report, uint8_t len) { + + int32_t status; + + // Don't get stuck if USB fails in some way; timeout after a while. + uint64_t end_ticks = ticks_ms + 2000; + + while (ticks_ms < end_ticks) { + status = usb_d_ep_get_status(self->endpoint, NULL); + if (status == USB_BUSY) { + continue; } - } -} - -bool usb_hid_send_report(usb_hid_device_obj_t *self, uint8_t* report, uint8_t len) { - if (!self->enabled) { - return true; - } - // Wait for the previous transaction to finish. Shouldn't happen. - uint32_t timeout = 0xffff; - - while (self->transaction_ongoing && timeout > 0) { - timeout--; + if (status == USB_OK) { + break; + } + // Some error. Give up. + return status; } - if (self->transaction_ongoing) { - return false; + // Copy the data only when endpoint is ready to send. The previous + // buffer load gets zero'd out when transaction completes, so if + // you copy before it's ready, only zeros will get sent. + + // Prefix with a report id if one is supplied. + if (self->report_id > 0) { + self->report_buffer[0] = self->report_id; + memcpy(&(self->report_buffer[1]), report, len); + return hiddf_generic_write(self->report_buffer, len + 1); + } else { + memcpy(self->report_buffer, report, len); + return hiddf_generic_write(self->report_buffer, len); } - memcpy(self->report_buffer, report, len); - - // Disable interrupts to make sure we save the ongoing state before the - // report_sent interrupt. - common_hal_mcu_disable_interrupts(); - bool ok = udd_ep_run(self->endpoint, false, - self->report_buffer, self->report_length, report_sent); - self->transaction_ongoing = ok; - common_hal_mcu_enable_interrupts(); - return ok; } void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t* report, uint8_t len) { if (len != self->report_length) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "Buffer incorrect size. Should be %d bytes.", self->report_length)); + mp_raise_ValueError_varg("Buffer incorrect size. Should be %d bytes.", self->report_length); } - if (!self->enabled) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "USB Inactive")); - } - if (!usb_hid_send_report(self, report, len)) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "USB Busy")); + int32_t status = usb_hid_send_report(self, report, len); + if (status != ERR_NONE) { + mp_raise_msg(&mp_type_OSError, status == USB_BUSY ? "USB Busy" : "USB Error"); } } @@ -90,3 +89,17 @@ 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) { return self->usage; } + + +void usb_hid_init() { +} + +void usb_hid_reset() { + // We don't actually reset. We just set a report that is empty to prevent + // long keypresses and such. + uint8_t report[USB_HID_MAX_REPORT_LENGTH] = {0}; + + for (size_t i = 0; i < USB_HID_NUM_DEVICES; i++) { + usb_hid_send_report(&usb_hid_devices[i], report, usb_hid_devices[i].report_length); + } +} diff --git a/ports/atmel-samd/common-hal/usb_hid/Device.h b/ports/atmel-samd/common-hal/usb_hid/Device.h index 1b313f9ceb..f85522d657 100644 --- a/ports/atmel-samd/common-hal/usb_hid/Device.h +++ b/ports/atmel-samd/common-hal/usb_hid/Device.h @@ -30,8 +30,24 @@ #include #include -#include "common-hal/usb_hid/types.h" +#include "py/obj.h" -bool usb_hid_send_report(usb_hid_device_obj_t *self, uint8_t* report, uint8_t len); +#include "genhdr/autogen_usb_descriptor.h" + +typedef struct { + mp_obj_base_t base; + uint8_t* report_buffer; + uint8_t endpoint; + uint8_t report_id; // If non-zero, prefix report with given id. + uint8_t report_length; // Length not including Report ID. + uint8_t usage_page; + uint8_t usage; + +} usb_hid_device_obj_t; + +usb_hid_device_obj_t usb_hid_devices[USB_HID_NUM_DEVICES]; + +void usb_hid_init(void); +void usb_hid_reset(void); #endif // COMMON_HAL_USB_HID_DEVICE_H diff --git a/ports/atmel-samd/common-hal/usb_hid/__init__.c b/ports/atmel-samd/common-hal/usb_hid/__init__.c index 5fa704cf83..8cbccbb4c4 100644 --- a/ports/atmel-samd/common-hal/usb_hid/__init__.c +++ b/ports/atmel-samd/common-hal/usb_hid/__init__.c @@ -28,90 +28,67 @@ #include "py/mphal.h" #include "py/runtime.h" -#include "common-hal/usb_hid/__init__.h" #include "common-hal/usb_hid/Device.h" -#include "common-hal/usb_hid/types.h" -#include "shared-bindings/usb_hid/__init__.h" #include "shared-bindings/usb_hid/Device.h" -#define UDI_HID_MOUSE_REPORT_SIZE 4 -#define UDI_HID_KBD_REPORT_SIZE 8 +#include "genhdr/autogen_usb_descriptor.h" -uint8_t mouse_report_buffer[UDI_HID_MOUSE_REPORT_SIZE]; -uint8_t kbd_report_buffer[UDI_HID_KBD_REPORT_SIZE]; +// Buffers are report size + 1 to include the Report ID prefix byte if needed. +static uint8_t keyboard_report_buffer[USB_HID_REPORT_LENGTH_KEYBOARD + 1]; +static uint8_t mouse_report_buffer[USB_HID_REPORT_LENGTH_MOUSE + 1]; +static uint8_t consumer_report_buffer[USB_HID_REPORT_LENGTH_CONSUMER + 1]; +static uint8_t sys_control_report_buffer[USB_HID_REPORT_LENGTH_SYS_CONTROL + 1]; -usb_hid_device_obj_t usb_hid_devices[2] = { +usb_hid_device_obj_t usb_hid_devices[USB_HID_NUM_DEVICES] = { { - .endpoint = UDI_HID_MOUSE_EP_IN, - .report_length = UDI_HID_MOUSE_REPORT_SIZE, - .report_buffer = mouse_report_buffer, - .usage_page = 0x01, - .usage = 0x02, - .enabled = false, - .transaction_ongoing = false - }, - { - .endpoint = UDI_HID_KBD_EP_IN, - .report_length = UDI_HID_KBD_REPORT_SIZE, - .report_buffer = kbd_report_buffer, + .base = { .type = &usb_hid_device_type }, + .report_buffer = keyboard_report_buffer, + .endpoint = USB_HID_ENDPOINT_IN, + .report_id = USB_HID_REPORT_ID_KEYBOARD, + .report_length = USB_HID_REPORT_LENGTH_KEYBOARD, .usage_page = 0x01, .usage = 0x06, - .enabled = false, - .transaction_ongoing = false - } + }, + { + .base = { .type = &usb_hid_device_type }, + .report_buffer = mouse_report_buffer, + .endpoint = USB_HID_ENDPOINT_IN, + .report_id = USB_HID_REPORT_ID_MOUSE, + .report_length = USB_HID_REPORT_LENGTH_MOUSE, + .usage_page = 0x01, + .usage = 0x02, + }, + { + .base = { .type = &usb_hid_device_type }, + .report_buffer = consumer_report_buffer, + .endpoint = USB_HID_ENDPOINT_IN, + .report_id = USB_HID_REPORT_ID_CONSUMER, + .report_length = USB_HID_REPORT_LENGTH_CONSUMER, + .usage_page = 0x0C, + .usage = 0x01, + }, + { + .base = { .type = &usb_hid_device_type }, + .report_buffer = sys_control_report_buffer, + .endpoint = USB_HID_ENDPOINT_IN, + .report_id = USB_HID_REPORT_ID_SYS_CONTROL, + .report_length = USB_HID_REPORT_LENGTH_SYS_CONTROL, + .usage_page = 0x01, + .usage = 0x80, + }, }; -// TODO(tannewt): Make this a mp_obj_tuple_t when it is dynamically allocated. -// until then we hard code it to two entries so LTO is happy. -mp_obj_tuple2_t common_hal_usb_hid_devices = { + +mp_obj_tuple_t common_hal_usb_hid_devices = { .base = { .type = &mp_type_tuple, }, - .len = 2, + .len = USB_HID_NUM_DEVICES, .items = { (mp_obj_t) &usb_hid_devices[0], - (mp_obj_t) &usb_hid_devices[1] + (mp_obj_t) &usb_hid_devices[1], + (mp_obj_t) &usb_hid_devices[2], + (mp_obj_t) &usb_hid_devices[3], } }; - -void usb_hid_init() { - usb_hid_devices[0].base.type = &usb_hid_device_type; - usb_hid_devices[1].base.type = &usb_hid_device_type; -} - -void usb_hid_reset() { - // We don't actually reset. We just set a report that is empty to prevent - // long keypresses and such. - uint8_t report[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - - usb_hid_send_report(&usb_hid_devices[0], report, 4); - usb_hid_send_report(&usb_hid_devices[1], report, 8); -} - -bool mp_mouse_enable(void) -{ - usb_hid_devices[0].enabled = true; - return true; -} - -void mp_mouse_disable(void) -{ - usb_hid_devices[0].enabled = false; -} - -bool mp_keyboard_enable(void) -{ - usb_hid_devices[1].enabled = true; - return true; -} - -void mp_keyboard_disable(void) -{ - usb_hid_devices[1].enabled = false; -} - -void mp_keyboard_led(uint8_t leds) -{ - UNUSED(leds); -} diff --git a/ports/atmel-samd/common-hal/usb_hid/__init__.h b/ports/atmel-samd/common-hal/usb_hid/__init__.h deleted file mode 100644 index 21f629f137..0000000000 --- a/ports/atmel-samd/common-hal/usb_hid/__init__.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef COMMON_HAL_USB_HID_H -#define COMMON_HAL_USB_HID_H - -#include -#include - -#include "common-hal/usb_hid/types.h" - -usb_hid_device_obj_t usb_hid_devices[2]; - -void usb_hid_init(void); -void usb_hid_reset(void); - -#endif // COMMON_HAL_USB_HID_H diff --git a/ports/atmel-samd/common-hal/usb_hid/types.h b/ports/atmel-samd/common-hal/usb_hid/types.h deleted file mode 100644 index c276ebd0de..0000000000 --- a/ports/atmel-samd/common-hal/usb_hid/types.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef MICROPY_INCLUDED_COMMON_HAL_USB_HID_TYPES_H -#define MICROPY_INCLUDED_COMMON_HAL_USB_HID_TYPES_H - -#include "py/obj.h" - -typedef struct { - mp_obj_base_t base; - udd_ep_id_t endpoint; - volatile bool transaction_ongoing; - volatile bool enabled; - uint8_t report_length; - uint8_t* report_buffer; - uint8_t usage_page; - uint8_t usage; -} usb_hid_device_obj_t; - -#endif // MICROPY_INCLUDED_COMMON_HAL_USB_HID_TYPES_H diff --git a/ports/atmel-samd/mpconfigport.h b/ports/atmel-samd/mpconfigport.h index de8703943d..c66f0d0b32 100644 --- a/ports/atmel-samd/mpconfigport.h +++ b/ports/atmel-samd/mpconfigport.h @@ -215,7 +215,6 @@ extern const struct _mp_obj_module_t usb_hid_module; // Disabled for now. // { MP_OBJ_NEW_QSTR(MP_QSTR_touchio), (mp_obj_t)&touchio_module }, // { MP_OBJ_NEW_QSTR(MP_QSTR__stage), (mp_obj_t)&stage_module }, -// { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid),(mp_obj_t)&usb_hid_module }, #define MICROPY_PORT_BUILTIN_MODULES \ @@ -233,6 +232,7 @@ extern const struct _mp_obj_module_t usb_hid_module; { MP_OBJ_NEW_QSTR(MP_QSTR_supervisor), (mp_obj_t)&supervisor_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_math), (mp_obj_t)&math_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&time_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid),(mp_obj_t)&usb_hid_module }, \ EXTRA_BUILTIN_MODULES #define MICROPY_PORT_BUILTIN_DEBUG_MODULES \ diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index cb8ffac47e..23d9bc9098 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -49,6 +49,7 @@ #include "common-hal/pulseio/PulseIn.h" #include "common-hal/pulseio/PulseOut.h" #include "common-hal/pulseio/PWMOut.h" +#include "common-hal/usb_hid/Device.h" #include "shared_dma.h" #include "tick.h" @@ -229,9 +230,8 @@ void reset_port(void) { // gpio_set_pin_function(PIN_PB15, GPIO_PIN_FUNCTION_M); // GCLK1, D6 // #endif -// -// usb_hid_reset(); -// + usb_hid_reset(); + // #ifdef CALIBRATE_CRYSTALLESS // // If we are on USB lets double check our fine calibration for the clock and // // save the new value if its different enough. diff --git a/ports/atmel-samd/supervisor/serial.c b/ports/atmel-samd/supervisor/serial.c index 0410c74176..4917dfb5b2 100644 --- a/ports/atmel-samd/supervisor/serial.c +++ b/ports/atmel-samd/supervisor/serial.c @@ -28,8 +28,10 @@ #include "supervisor/serial.h" +#include "common-hal/usb_hid/Device.h" + #include "usb.h" -#include "tools/autogen_usb_descriptor.h" +#include "genhdr/autogen_usb_descriptor.h" // Serial number as hex characters. This writes directly to the USB // descriptor. @@ -57,6 +59,7 @@ void load_serial_number(void) { void serial_init(void) { load_serial_number(); init_usb(); + usb_hid_init(); } bool serial_connected(void) { diff --git a/ports/atmel-samd/tools/autogen_usb_descriptor.h b/ports/atmel-samd/tools/autogen_usb_descriptor.h deleted file mode 100644 index d9260681c3..0000000000 --- a/ports/atmel-samd/tools/autogen_usb_descriptor.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_TOOLS_AUTOGEN_USB_DESCRIPTOR_H -#define MICROPY_INCLUDED_ATMEL_SAMD_TOOLS_AUTOGEN_USB_DESCRIPTOR_H - -#include "usb/device/usbdc.h" - -struct usbd_descriptors descriptor_bounds; -uint8_t* serial_number; -uint8_t serial_number_length; - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_TOOLS_AUTOGEN_USB_DESCRIPTOR_H diff --git a/ports/atmel-samd/tools/gen_usb_descriptor.py b/ports/atmel-samd/tools/gen_usb_descriptor.py index 5456005236..4669110fb7 100644 --- a/ports/atmel-samd/tools/gen_usb_descriptor.py +++ b/ports/atmel-samd/tools/gen_usb_descriptor.py @@ -6,7 +6,7 @@ import sys # path hacking sys.path.append("../../tools/usb_descriptor") -from adafruit_usb_descriptor import cdc, standard, util +from adafruit_usb_descriptor import cdc, hid, msc, standard, util parser = argparse.ArgumentParser(description='Generate USB descriptors.') parser.add_argument('--manufacturer', type=str, @@ -19,131 +19,323 @@ parser.add_argument('--pid', type=lambda x: int(x, 16), help='product id') parser.add_argument('--serial_number_length', type=int, default=32, help='length needed for the serial number in digits') -parser.add_argument('output_file', type=argparse.FileType('w')) +parser.add_argument('--output_c_file', type=argparse.FileType('w'), required=True) +parser.add_argument('--output_h_file', type=argparse.FileType('w'), required=True) args = parser.parse_args() -langid = standard.StringDescriptor("\u0409") -manufacturer = standard.StringDescriptor(args.manufacturer) -product = standard.StringDescriptor(args.product) -serial_number = standard.StringDescriptor("serial number. you should fill in a unique serial number here."[:args.serial_number_length]) -strings = [langid, manufacturer, product, serial_number] +class StringIndex: + """Assign a monotonically increasing index to each unique string. Start with 0.""" + string_to_index = {} + strings = [] -# vid = 0x239A -# pid = 0x8021 + @classmethod + def index(cls, string): + if string in cls.string_to_index: + return cls.string_to_index[string] + else: + idx = len(cls.strings) + cls.string_to_index[string] = idx + cls.strings.append(string) + return idx + + @classmethod + def strings_in_order(cls): + return cls.strings + + + +# langid must be the 0th string descriptor +LANGID_INDEX = StringIndex.index("\u0409") +assert LANGID_INDEX == 0 +SERIAL_NUMBER_INDEX = StringIndex.index("S" * args.serial_number_length) device = standard.DeviceDescriptor( + description="top", idVendor=args.vid, idProduct=args.pid, - iManufacturer=strings.index(manufacturer), - iProduct=strings.index(product), - iSerialNumber=strings.index(serial_number)) + iManufacturer=StringIndex.index(args.manufacturer), + iProduct=StringIndex.index(args.product), + iSerialNumber=SERIAL_NUMBER_INDEX) -# Interface numbers are interface set local and endpoints are interface local -# until core.join_interfaces renumbers them. -cdc_interfaces = [ - standard.InterfaceDescriptor( - bInterfaceClass=0x2, # Communications Device Class - bInterfaceSubClass=0x02, # Abstract control model - bInterfaceProtocol=0x01, # Common AT Commands - subdescriptors=[ - # Working 2.x - # radix: hexadecimal - # 05 24 00 10 01 header - # 05 24 01 03 01 call manage - # 04 24 02 06 acm - # 05 24 06 00 01 union - cdc.Header(bcdCDC=0x0110), - cdc.CallManagement(bmCapabilities=0x03, bDataInterface=0x01), - cdc.AbstractControlManagement(bmCapabilities=0x02), - cdc.Union(bMasterInterface=0x00, - bSlaveInterface=[0x01]), - standard.EndpointDescriptor( - bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - wMaxPacketSize=0x8, - bInterval=10) - ] - ), - standard.InterfaceDescriptor( - bInterfaceClass=0x0a, - subdescriptors=[ - standard.EndpointDescriptor( - bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK), - standard.EndpointDescriptor( - bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK) - ] - ) -] +# Interface numbers are interface-set local and endpoints are interface local +# until util.join_interfaces renumbers them. + +cdc_union = cdc.Union( + description="CDC comm", + bMasterInterface=0x00, # Adjust this after interfaces are renumbered. + bSlaveInterface_list=[0x01]) # Adjust this after interfaces are renumbered. + +cdc_call_management = cdc.CallManagement( + description="CDC comm", + bmCapabilities=0x01, + bDataInterface=0x01) # Adjust this after interfaces are renumbered. + +cdc_comm_interface = standard.InterfaceDescriptor( + description="CDC comm", + bInterfaceClass=cdc.CDC_CLASS_COMM, # Communications Device Class + bInterfaceSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model + bInterfaceProtocol=cdc.CDC_PROTOCOL_NONE, + iInterface=StringIndex.index("CircuitPython CDC control"), + subdescriptors=[ + cdc.Header( + description="CDC comm", + bcdCDC=0x0110), + cdc_call_management, + cdc.AbstractControlManagement( + description="CDC comm", + bmCapabilities=0x02), + cdc_union, + standard.EndpointDescriptor( + description="CDC comm in", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, + wMaxPacketSize=0x0040, + bInterval=0x10) + ]) + +cdc_data_interface = standard.InterfaceDescriptor( + description="CDC data", + bInterfaceClass=cdc.CDC_CLASS_DATA, + iInterface=StringIndex.index("CircuitPython CDC data"), + subdescriptors=[ + standard.EndpointDescriptor( + description="CDC data out", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_OUT, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK), + standard.EndpointDescriptor( + description="CDC data in", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK), + ]) + +cdc_interfaces = [cdc_comm_interface, cdc_data_interface] msc_interfaces = [ standard.InterfaceDescriptor( - bInterfaceClass=0x08, - bInterfaceSubClass=0x06, - bInterfaceProtocol=0x50, + description="MSC", + bInterfaceClass=msc.MSC_CLASS, + bInterfaceSubClass=msc.MSC_SUBCLASS_TRANSPARENT, + bInterfaceProtocol=msc.MSC_PROTOCOL_BULK, + iInterface=StringIndex.index("CircuitPython Mass Storage"), subdescriptors=[ standard.EndpointDescriptor( + description="MSC in", bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, bmAttributes=standard.EndpointDescriptor.TYPE_BULK), standard.EndpointDescriptor( + description="MSC out", bEndpointAddress=0x1 | standard.EndpointDescriptor.DIRECTION_OUT, bmAttributes=standard.EndpointDescriptor.TYPE_BULK) ] ) ] -interfaces = util.join_interfaces(cdc_interfaces, msc_interfaces) +hid_report_descriptor = hid.ReportDescriptor.MOUSE_KEYBOARD_CONSUMER_SYS_CONTROL_REPORT +hid_report_ids = hid.ReportDescriptor.REPORT_IDS +hid_report_lengths = hid.ReportDescriptor.REPORT_LENGTHS +hid_max_report_length = max(hid_report_lengths.values()) -cdc_function = standard.InterfaceAssociationDescriptor( - bFirstInterface=interfaces.index(cdc_interfaces[0]), +# ASF4 expects keyboard and generic devices to have both in and out endpoints, +# and will fail (possibly silently) if both are not supplied. +hid_endpoint_in_descriptor = standard.EndpointDescriptor( + description="HID in", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, + bInterval=0x02) + +hid_endpoint_out_descriptor = standard.EndpointDescriptor( + description="HID out", + bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_OUT, + bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT) + +hid_interfaces = [ + standard.InterfaceDescriptor( + description="HID Multiple Devices", + bInterfaceClass=hid.HID_CLASS, + bInterfaceSubClass=hid.HID_SUBCLASS_NOBOOT, + bInterfaceProtocol=hid.HID_PROTOCOL_NONE, + iInterface=StringIndex.index("CircuitPython HID"), + subdescriptors=[ + hid.HIDDescriptor( + description="HID", + wDescriptorLength=len(bytes(hid_report_descriptor))), + hid_endpoint_in_descriptor, + hid_endpoint_out_descriptor, + ] + ), + ] + +# This will renumber the endpoints to make them unique across descriptors, +# and renumber the interfaces in order. But we still need to fix up certain +# interface cross-references. +interfaces = util.join_interfaces(cdc_interfaces, msc_interfaces, hid_interfaces) + +# Now adjust the CDC interface cross-references. + +cdc_union.bMasterInterface = cdc_comm_interface.bInterfaceNumber +cdc_union.bSlaveInterface_list = [cdc_data_interface.bInterfaceNumber] + +cdc_call_management.bDataInterface = cdc_data_interface.bInterfaceNumber + +cdc_iad = standard.InterfaceAssociationDescriptor( + description="CDC IAD", + bFirstInterface=cdc_comm_interface.bInterfaceNumber, bInterfaceCount=len(cdc_interfaces), bFunctionClass=0x2, # Communications Device Class bFunctionSubClass=0x2, # Abstract control model - bFunctionProtocol=0x1) # Common AT Commands + bFunctionProtocol=0x1) configuration = standard.ConfigurationDescriptor( + description="Composite configuration", wTotalLength=(standard.ConfigurationDescriptor.bLength + - cdc_function.bLength + + cdc_iad.bLength + sum([len(bytes(x)) for x in interfaces])), bNumInterfaces=len(interfaces)) -descriptor_list = [device, configuration, cdc_function] -descriptor_list.extend(interfaces) -descriptor_list.extend(strings) +descriptor_list = [] +descriptor_list.append(device) +descriptor_list.append(configuration) +descriptor_list.append(cdc_iad) +descriptor_list.extend(cdc_interfaces) +descriptor_list.extend(msc_interfaces) +# Put the CDC IAD just before the CDC interfaces. +# There appears to be a bug in the Windows composite USB driver that requests the +# HID report descriptor with the wrong interface number if the HID interface is not given +# first. However, it still fetches the descriptor anyway. We could reorder the interfaces but +# the Windows 7 Adafruit_usbser.inf file thinks CDC is at Interface 0, so we'll leave it +# there for backwards compatibility. +descriptor_list.extend(hid_interfaces) -output_file = args.output_file +string_descriptors = [standard.StringDescriptor(string) for string in StringIndex.strings_in_order()] +serial_number_descriptor = string_descriptors[SERIAL_NUMBER_INDEX] +descriptor_list.extend(string_descriptors) -output_file.write("#include \n\n") -output_file.write("#include \"tools/autogen_usb_descriptor.h\"\n\n") -output_file.write("uint8_t usb_descriptors[] = {\n") +c_file = args.output_c_file +h_file = args.output_h_file + + +c_file.write("""\ +#include + +#include "{H_FILE_NAME}" + +#include "usb/device/usbdc.h" + +""".format(H_FILE_NAME=h_file.name)) + +c_file.write("""\ +uint8_t usb_descriptors[] = { +""") + +# Write out all the regular descriptors as one long array (that's how ASF4 does it). descriptor_length = 0 serial_number_offset = None for descriptor in descriptor_list: - output_file.write("// " + str(descriptor) + "\n") + c_file.write("""\ +// {DESCRIPTION} : {CLASS} +""".format(DESCRIPTION=descriptor.description, + CLASS=descriptor.__class__)) + b = bytes(descriptor) i = 0 - if descriptor == serial_number: - # Add two for the length and descriptor type bytes. + + if descriptor == serial_number_descriptor: + # Add two for bLength and bDescriptorType. serial_number_offset = descriptor_length + 2 + # This prints each subdescriptor on a separate line. while i < len(b): length = b[i] for j in range(length): - output_file.write("0x{:02x}, ".format(b[i + j])) - output_file.write("\n") + c_file.write("0x{:02x}, ".format(b[i + j])) + c_file.write("\n") i += length descriptor_length += length - output_file.write("\n") -output_file.write("};\n\n") -output_file.write("struct usbd_descriptors descriptor_bounds = " + - "{usb_descriptors," + - " usb_descriptors + sizeof(usb_descriptors)};\n") -output_file.write("uint8_t* serial_number = usb_descriptors + " + - str(serial_number_offset) + ";\n") -output_file.write("uint8_t serial_number_length = " + - str(args.serial_number_length) + ";\n") -output_file.close() +c_file.write("""\ +}; +""") + +# Now we values we need for the .h file. +h_file.write("""\ +#ifndef MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H +#define MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H + +#define SERIAL_NUMBER_OFFSET {SERIAL_NUMBER_OFFSET} +#define SERIAL_NUMBER_LENGTH {SERIAL_NUMBER_LENGTH} +uint8_t* serial_number; + +uint8_t hid_report_descriptor[{HID_REPORT_DESCRIPTOR_LENGTH}]; +#define USB_HID_ENDPOINT_IN {HID_ENDPOINT_IN_ADDRESS} +#define USB_HID_ENDPOINT_OUT {HID_ENDPOINT_OUT_ADDRESS} + +""" +.format(SERIAL_NUMBER_OFFSET=serial_number_offset, + SERIAL_NUMBER_LENGTH=args.serial_number_length, + HID_REPORT_DESCRIPTOR_LENGTH=len(bytes(hid_report_descriptor)), + HID_ENDPOINT_IN_ADDRESS=hex(hid_endpoint_in_descriptor.bEndpointAddress), + HID_ENDPOINT_OUT_ADDRESS=hex(hid_endpoint_out_descriptor.bEndpointAddress))) + +# Write out #define's that declare which endpoints are in use. +# These provide information for declaring cache sizes and perhaps other things at compile time +for interface in interfaces: + for subdescriptor in interface.subdescriptors: + if isinstance(subdescriptor, standard.EndpointDescriptor): + endpoint_num = subdescriptor.bEndpointAddress & standard.EndpointDescriptor.NUMBER_MASK + endpoint_in = ((subdescriptor.bEndpointAddress & standard.EndpointDescriptor.DIRECTION_MASK) == + standard.EndpointDescriptor.DIRECTION_IN) + h_file.write("""\ +#define USB_ENDPOINT_{NUMBER}_{DIRECTION}_USED 1 +""".format(NUMBER=endpoint_num, + DIRECTION="IN" if endpoint_in else "OUT")) + +h_file.write("\n") + +# #define the report ID's used in the combined HID descriptor +for name, id in hid_report_ids.items(): + h_file.write("""\ +#define USB_HID_REPORT_ID_{NAME} {ID} +""".format(NAME=name, + ID = id)) + +h_file.write("\n") + +# #define the report sizes used in the combined HID descriptor +for name, length in hid_report_lengths.items(): + h_file.write("""\ +#define USB_HID_REPORT_LENGTH_{NAME} {LENGTH} +""".format(NAME=name, + LENGTH=length)) + +h_file.write("\n") + +h_file.write("""\ +#define USB_HID_NUM_DEVICES {NUM_DEVICES} +#define USB_HID_MAX_REPORT_LENGTH {MAX_LENGTH} +""".format(NUM_DEVICES=len(hid_report_lengths), + MAX_LENGTH=hid_max_report_length)) + + + +# Write out the report descriptor and info +c_file.write("""\ +uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{ +""".format(HID_DESCRIPTOR_LENGTH=len(bytes(hid_report_descriptor)))) + +for b in bytes(hid_report_descriptor): + c_file.write("0x{:02x}, ".format(b)) +c_file.write(""" +}; +""") + +c_file.write("""\ + +struct usbd_descriptors descriptor_bounds = {{usb_descriptors, usb_descriptors + sizeof(usb_descriptors)}}; +uint8_t* serial_number = usb_descriptors + {SERIAL_NUMBER_OFFSET}; +""".format(SERIAL_NUMBER_OFFSET=serial_number_offset)) + +h_file.write("""\ +#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H +""") diff --git a/ports/atmel-samd/usb.c b/ports/atmel-samd/usb.c index fd939e22df..7fff957c00 100644 --- a/ports/atmel-samd/usb.c +++ b/ports/atmel-samd/usb.c @@ -34,8 +34,6 @@ #include "hal/include/hal_gpio.h" #include "usb/class/cdc/device/cdcdf_acm.h" -// #include "hiddf_mouse.h" -// #include "hiddf_keyboard.h" #include "usb/class/hid/device/hiddf_generic.h" #include "usb/class/composite/device/composite_desc.h" #include "usb/class/msc/device/mscdf.h" @@ -44,12 +42,14 @@ #include "hpl/gclk/hpl_gclk_base.h" #include "lib/utils/interrupt_char.h" -#include "tools/autogen_usb_descriptor.h" +#include "genhdr/autogen_usb_descriptor.h" #include "reset.h" #include "usb_mass_storage.h" #include "supervisor/shared/autoreload.h" +extern struct usbd_descriptors descriptor_bounds; + // Store received characters on our own so that we can filter control characters // and act immediately on CTRL-C for example. @@ -199,14 +199,11 @@ void init_usb(void) { usbdc_init(ctrl_buffer); - /* usbdc_register_funcion inside */ + /* usbdc_register_function inside */ cdcdf_acm_init(); pending_read = false; mscdf_init(1); - // hiddf_mouse_init(); - // hiddf_keyboard_init(); - mscdf_register_callback(MSCDF_CB_INQUIRY_DISK, (FUNC_PTR)usb_msc_inquiry_info); mscdf_register_callback(MSCDF_CB_GET_DISK_CAPACITY, (FUNC_PTR)usb_msc_get_capacity); mscdf_register_callback(MSCDF_CB_START_READ_DISK, (FUNC_PTR)usb_msc_new_read); @@ -216,9 +213,13 @@ void init_usb(void) { mscdf_register_callback(MSCDF_CB_XFER_BLOCKS_DONE, (FUNC_PTR)usb_msc_xfer_done); mscdf_register_callback(MSCDF_CB_IS_WRITABLE, (FUNC_PTR)usb_msc_disk_is_writable); + hiddf_generic_init(hid_report_descriptor, sizeof(hid_report_descriptor)); + usbdc_start(&descriptor_bounds); usbdc_attach(); + + } static bool cdc_enabled(void) { diff --git a/py/objtuple.h b/py/objtuple.h index bcf0c9ed4f..7f20ab7b6f 100644 --- a/py/objtuple.h +++ b/py/objtuple.h @@ -34,13 +34,6 @@ typedef struct _mp_obj_tuple_t { mp_obj_t items[]; } mp_obj_tuple_t; -// TODO(tannewt): Remove this when we no longer hard code the usb hid tuple. -typedef struct _mp_obj_tuple2_t { - mp_obj_base_t base; - size_t len; - mp_obj_t items[2]; -} mp_obj_tuple2_t; - typedef struct _mp_rom_obj_tuple_t { mp_obj_base_t base; size_t len; diff --git a/shared-bindings/usb_hid/Device.h b/shared-bindings/usb_hid/Device.h index 33b9f1c994..2bc553c4a2 100644 --- a/shared-bindings/usb_hid/Device.h +++ b/shared-bindings/usb_hid/Device.h @@ -27,7 +27,7 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_USB_HID_DEVICE_H #define MICROPY_INCLUDED_SHARED_BINDINGS_USB_HID_DEVICE_H -#include "common-hal/usb_hid/types.h" +#include "common-hal/usb_hid/Device.h" const mp_obj_type_t usb_hid_device_type; diff --git a/shared-bindings/usb_hid/__init__.h b/shared-bindings/usb_hid/__init__.h index ff478d0836..3d56fbfd02 100644 --- a/shared-bindings/usb_hid/__init__.h +++ b/shared-bindings/usb_hid/__init__.h @@ -30,8 +30,6 @@ #include #include -// TODO(tannewt): Make this a mp_obj_tuple_t when it is dynamically allocated. -// until then we hard code it to two entries so LTO is happy. -extern mp_obj_tuple2_t common_hal_usb_hid_devices; +extern mp_obj_tuple_t common_hal_usb_hid_devices; #endif // SHARED_BINDINGS_USB_HID_H diff --git a/tools/usb_descriptor b/tools/usb_descriptor index a04341153b..0ccd1935d6 160000 --- a/tools/usb_descriptor +++ b/tools/usb_descriptor @@ -1 +1 @@ -Subproject commit a04341153b41b4728f9b42a77cbd51c495362287 +Subproject commit 0ccd1935d666bcffc3fd5941ba06bd9273d72e0c