diff --git a/epicardium/cdcacm.c b/epicardium/cdcacm.c index 5a023fedee93fc0013ad83e0f762725426504b65..72b9ba6569d79bd3f6d23c580d6929605d6636c4 100644 --- a/epicardium/cdcacm.c +++ b/epicardium/cdcacm.c @@ -1,303 +1,48 @@ -/******************************************************************************* - * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. - * - * 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 MAXIM INTEGRATED 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. - * - * Except as contained in this notice, the name of Maxim Integrated - * Products, Inc. shall not be used except as stated in the Maxim Integrated - * Products, Inc. Branding Policy. - * - * The mere transfer of this software does not imply any licenses - * of trade secrets, proprietary technology, copyrights, patents, - * trademarks, maskwork rights, or any other form of intellectual - * property whatsoever. Maxim Integrated Products, Inc. retains all - * ownership rights. - * - * $Id: main.c 32120 2017-11-28 23:51:11Z lorne.smith $ - * - ******************************************************************************* - */ - -/** - * @file main.c - * @brief USB CDC-ACM example - * @details This project creates a virtual COM port, which loops back data sent to it. - * Load the project, connect a cable from the PC to the USB connector - * on the Evaluation Kit, and observe that the PC now recognizes a new COM port. - * A driver for the COM port, if needed, is located in the Driver/ subdirectory. - * - */ - #include "cdcacm.h" #include <stdio.h> #include <stddef.h> +#include <errno.h> -#include "mxc_config.h" -#include "mxc_sys.h" -#include "mxc_delay.h" -#include "board.h" -#include "led.h" #include "usb.h" #include "usb_event.h" -#include "enumerate.h" #include "cdc_acm.h" -#include "msc.h" -#include "descriptors.h" #include "modules/log.h" -#include "modules/filesystem.h" -#include <errno.h> - -/***** Definitions *****/ -#define EVENT_ENUM_COMP MAXUSB_NUM_EVENTS - -#define BUFFER_SIZE 64 - -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - -/***** Global Data *****/ -//SWYM: rename to CDC_xy or put into struct CDC_state -volatile int configured; //SWYM: actually unused... -volatile int suspended; -volatile unsigned int event_flags; -int remote_wake_en; - -/***** Function Prototypes *****/ -static int setconfig_callback(usb_setup_pkt *sud, void *cbdata); -static int setfeature_callback(usb_setup_pkt *sud, void *cbdata); -static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata); -static int event_callback(maxusb_event_t evt, void *data); -static void usb_app_sleep(void); -static void usb_app_wakeup(void); -static int usb_read_callback(void); -static void deconfigure(void); - -/***** File Scope Variables *****/ - -/* This EP assignment must match the Configuration Descriptor */ -static const acm_cfg_t acm_cfg = { - 1, /* EP OUT */ - MXC_USBHS_MAX_PACKET, /* OUT max packet size */ - 2, /* EP IN */ - MXC_USBHS_MAX_PACKET, /* IN max packet size */ - 3, /* EP Notify */ - MXC_USBHS_MAX_PACKET, /* Notify max packet size */ -}; - -static volatile int usb_read_complete; -static volatile bool initialized = false; -static const msc_cfg_t* s_msc_cfg = NULL; - - -int usb_startup_cb() -{ - const sys_cfg_usbhs_t sys_usbhs_cfg = NULL; - LOG_INFO("usb", "SYS_USBHS_Init"); - return SYS_USBHS_Init(&sys_usbhs_cfg); -} - -int usb_shutdown_cb() -{ - LOG_INFO("usb", "SYS_USBHS_Shutdown"); - SYS_USBHS_Shutdown(); - SYS_Reset_Periph(SYS_RESET_USB); - if (s_msc_cfg) { - LOG_INFO("usb", "re-attaching FLASH"); - fatfs_attach(); - s_msc_cfg = NULL; - } - return 0; -} - -/* User-supplied function to delay usec micro-seconds */ -void delay_us(unsigned int usec) -{ - /* mxc_delay() takes unsigned long, so can't use it directly */ - mxc_delay(usec); -} - -/******************************************************************************/ -int epc_usb_init(uint16_t interfaces) -{ - if (initialized) { - //NVIC_DisableIRQ(USB_IRQn); //yes? no? - - usb_disconnect(); - deconfigure(); - //usb_app_sleep(); - usb_shutdown(); - initialized = false; - } - if (interfaces == 0) { - return 0; - } - maxusb_cfg_options_t usb_opts; - - /* Initialize state */ - configured = 0; - suspended = 0; - event_flags = 0; - remote_wake_en = 0; - - /* Start out in full speed */ - usb_opts.enable_hs = 0; - usb_opts.delay_us = delay_us; /* Function which will be used for delays */ - usb_opts.init_callback = usb_startup_cb; - usb_opts.shutdown_callback = usb_shutdown_cb; - - /* Initialize the usb module */ - if (usb_init(&usb_opts) != 0) { - LOG_ERR("cdcacm", "usb_init() failed"); - return -EIO; - } - - /* Initialize the enumeration module */ - if (enum_init() != 0) { - usb_shutdown(); - LOG_ERR("cdcacm", "enum_init() failed"); - return -EIO; - } - - /* Register enumeration data */ - if (interfaces & USB_IFACE_STORAGE) { - config_descriptor.config_descriptor.wTotalLength = sizeof(config_descriptor); - config_descriptor.config_descriptor.bNumInterfaces = 3; - } else { - config_descriptor.config_descriptor.wTotalLength = offsetof(struct config_descriptor_s, msc_interface_descriptor); - config_descriptor.config_descriptor.bNumInterfaces = 2; - } - enum_register_descriptor(ENUM_DESC_DEVICE, (uint8_t*)&composite_device_descriptor, 0); - enum_register_descriptor(ENUM_DESC_CONFIG, (uint8_t*)&config_descriptor, 0); - enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0); - enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1); - enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2); - //used in composite but not in cdc (see iSerialNumber) - enum_register_descriptor(ENUM_DESC_STRING, serial_id_desc, 3); - enum_register_descriptor(ENUM_DESC_STRING, cdcacm_func_desc, 4); - enum_register_descriptor(ENUM_DESC_STRING, msc_func_desc, 5); - - /* Handle configuration */ - enum_register_callback(ENUM_SETCONFIG, setconfig_callback, NULL); - - /* Handle feature set/clear */ - enum_register_callback(ENUM_SETFEATURE, setfeature_callback, NULL); - enum_register_callback(ENUM_CLRFEATURE, clrfeature_callback, NULL); - /* Initialize the class driver */ - if (acm_init(&config_descriptor.comm_interface_descriptor) != 0) { - enum_clearconfig(); - usb_shutdown(); - LOG_ERR("cdcacm", "acm_init() failed"); - return -EIO; - } - - if (interfaces & USB_IFACE_STORAGE) { - /* Initialize the class driver */ - static const msc_idstrings_t ids = { - "CCC", /* Vendor string. Maximum of 8 bytes */ - "card10", /* Product string. Maximum of 16 bytes */ - "1.0" /* Version string. Maximum of 4 bytes */ - }; - - /* This EP assignment must match the Configuration Descriptor */ - static const msc_cfg_t msc_cfg = { - 4, /* EP OUT */ - MXC_USBHS_MAX_PACKET, /* OUT max packet size */ - 5, /* EP IN */ - MXC_USBHS_MAX_PACKET, /* IN max packet size */ - }; - s_msc_cfg = &msc_cfg; - LOG_INFO("usb", "detaching FLASH"); - fatfs_detach(); - - - /* Functions to control "disk" memory. See msc.h for definitions. */ - int mscmem_init(void); - int mscmem_start(void); - int mscmem_stop(void); - uint32_t mscmem_size(void); - int mscmem_read(uint32_t lba, uint8_t* buffer); - int mscmem_write(uint32_t lba, uint8_t* buffer); - int mscmem_ready(void); - - static const msc_mem_t mem = { - mscmem_init, mscmem_start, mscmem_stop, mscmem_ready, - mscmem_size, mscmem_read, mscmem_write, - }; - if (msc_init(&config_descriptor.msc_interface_descriptor, &ids, &mem) != 0) { - LOG_ERR("msc", "msc_init() failed\n"); - return -EIO; - } - } else { - s_msc_cfg = NULL; - } - - /* Register callbacks */ - usb_event_enable(MAXUSB_EVENT_NOVBUS, event_callback, NULL); - usb_event_enable(MAXUSB_EVENT_VBUS, event_callback, NULL); - acm_register_callback(ACM_CB_READ_READY, usb_read_callback); - usb_read_complete = 0; - - /* Start with USB in low power mode */ - usb_app_sleep(); - /* TODO: Fix priority */ - NVIC_SetPriority(USB_IRQn, 6); - NVIC_EnableIRQ(USB_IRQn); - - initialized = true; - - return 0; -} int cdcacm_num_read_avail(void) { - return acm_canread(); + return acm_canread(); } uint8_t cdcacm_read(void) { - while(acm_canread() <= 0) { - } + while (acm_canread() <= 0) { + } - uint8_t buf; - acm_read(&buf, 1); - return buf; + uint8_t buf; + acm_read(&buf, 1); + return buf; } void cdcacm_write(uint8_t *data, int len) { - static int lockup_disable = 0; - if (acm_present() && !lockup_disable) { - int ret = acm_write(data, len); - if (ret < 0) { - LOG_ERR("cdcacm", "fifo lockup detected"); - lockup_disable = 1; - } else if (ret != len) { - LOG_WARN("cdcacm", "write length mismatch, got %d", ret); - } - } + static int lockup_disable = 0; + if (acm_present() && !lockup_disable) { + int ret = acm_write(data, len); + if (ret < 0) { + LOG_ERR("cdcacm", "fifo lockup detected"); + lockup_disable = 1; + } else if (ret != len) { + LOG_WARN( + "cdcacm", "write length mismatch, got %d", ret + ); + } + } } - /******************************************************************************/ #if 0 static void echo_usb(void) @@ -328,131 +73,6 @@ static void echo_usb(void) #endif #include "mx25lba.h" - - -/******************************************************************************/ -static int setconfig_callback(usb_setup_pkt *sud, void *cbdata) -{ - LOG_INFO("usb", "setconfig %d \n", (int)sud->wValue); - /* Confirm the configuration value */ - if (sud->wValue == config_descriptor.config_descriptor.bConfigurationValue) { - configured = 1; - MXC_SETBIT(&event_flags, EVENT_ENUM_COMP); - int acm_rc = acm_configure(&acm_cfg); - if (acm_rc) { - LOG_ERR("usb", "acm_configure failed: %d\n", acm_rc); - } - int msc_rc = s_msc_cfg ? msc_configure(s_msc_cfg) : 0; - if (msc_rc) { - LOG_ERR("usb", "msc_configure failed: %d\n", msc_rc); - } - return acm_rc || msc_rc; - } else if (sud->wValue == 0) { - configured = 0; - return acm_deconfigure(); - } - - return -1; -} - -/******************************************************************************/ -static int setfeature_callback(usb_setup_pkt *sud, void *cbdata) -{ - if(sud->wValue == FEAT_REMOTE_WAKE) { - remote_wake_en = 1; - } else { - // Unknown callback - return -1; - } - - return 0; -} - -/******************************************************************************/ -static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata) -{ - if(sud->wValue == FEAT_REMOTE_WAKE) { - remote_wake_en = 0; - } else { - // Unknown callback - return -1; - } - - return 0; -} - -/******************************************************************************/ -static void usb_app_sleep(void) -{ - /* TODO: Place low-power code here */ - suspended = 1; -} - -/******************************************************************************/ -static void usb_app_wakeup(void) -{ - /* TODO: Place low-power code here */ - suspended = 0; -} - -static void deconfigure(void) { - enum_clearconfig(); - acm_deconfigure(); - if (s_msc_cfg) { - msc_deconfigure(); - } - configured = 0; -} -/******************************************************************************/ -static int event_callback(maxusb_event_t evt, void *data) -{ - /* Set event flag */ - MXC_SETBIT(&event_flags, evt); - - LOG_INFO("usb", "event %d\n", evt); - - switch (evt) { - case MAXUSB_EVENT_NOVBUS: - usb_event_disable(MAXUSB_EVENT_BRST); - usb_event_disable(MAXUSB_EVENT_SUSP); - usb_event_disable(MAXUSB_EVENT_DPACT); - usb_disconnect(); - deconfigure(); - usb_app_sleep(); - break; - case MAXUSB_EVENT_VBUS: - usb_event_clear(MAXUSB_EVENT_BRST); - usb_event_enable(MAXUSB_EVENT_BRST, event_callback, NULL); - usb_event_clear(MAXUSB_EVENT_SUSP); - usb_event_enable(MAXUSB_EVENT_SUSP, event_callback, NULL); - usb_connect(); - usb_app_sleep(); - break; - case MAXUSB_EVENT_BRST: - usb_app_wakeup(); - deconfigure(); - suspended = 0; - break; - case MAXUSB_EVENT_SUSP: - usb_app_sleep(); - break; - case MAXUSB_EVENT_DPACT: - usb_app_wakeup(); - break; - default: - break; - } - - return 0; -} - -/******************************************************************************/ -static int usb_read_callback(void) -{ - usb_read_complete = 1; - return 0; -} - /******************************************************************************/ #include "FreeRTOS.h" #include "task.h" @@ -460,13 +80,15 @@ static int usb_read_callback(void) extern TaskHandle_t serial_task_id; void USB_IRQHandler(void) { - usb_event_handler(); + usb_event_handler(); - if (serial_task_id != NULL) { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR(serial_task_id, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - } + if (serial_task_id != NULL) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + vTaskNotifyGiveFromISR( + serial_task_id, &xHigherPriorityTaskWoken + ); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } } /******************************************************************************/ @@ -493,6 +115,7 @@ uint32_t mscmem_size(void) /******************************************************************************/ int mscmem_read(uint32_t lba, uint8_t *buffer) { + LOG_INFO("msc", "mem.read %08lx", lba); return mx25_read(lba, buffer); } @@ -504,6 +127,7 @@ int mscmem_write(uint32_t lba, uint8_t *buffer) } dirty = 2; return mx25_write(lba, buffer); + } /******************************************************************************/ @@ -515,6 +139,7 @@ int mscmem_start() /******************************************************************************/ int mscmem_stop() { + LOG_INFO("msc", "mem.stop"); int ret = mx25_stop(); //bootloader_stop(); return ret; diff --git a/epicardium/descriptors.h b/epicardium/descriptors.h index 44ef924c8921dedb6003bda928a7b99c6b5776b3..344c29473fbc0a8855de87abf66c59fa2786adee 100644 --- a/epicardium/descriptors.h +++ b/epicardium/descriptors.h @@ -43,7 +43,6 @@ #define EPICARDIUM_USBSTORAGE -#ifdef EPICARDIUM_USBSTORAGE usb_device_descriptor_t __attribute__((aligned(4))) composite_device_descriptor = { .bLength = 0x12, .bDescriptorType = 0x01, /* Device */ @@ -60,47 +59,28 @@ usb_device_descriptor_t __attribute__((aligned(4))) composite_device_descriptor .iSerialNumber = 0x03, .bNumConfigurations = 0x01 }; -#else -usb_device_descriptor_t __attribute__((aligned(4))) device_descriptor = { - 0x12, /* bLength = 18 */ - 0x01, /* bDescriptorType = Device */ - 0x0110, /* bcdUSB USB spec rev (BCD) */ - 0x02, /* bDeviceClass = comm class (2) */ - 0x00, /* bDeviceSubClass */ - 0x00, /* bDeviceProtocol */ - 0x40, /* bMaxPacketSize0 is 64 bytes */ - 0x0B6A, /* idVendor (Maxim Integrated) */ - 0x003C, /* idProduct */ - 0x0100, /* bcdDevice */ - 0x01, /* iManufacturer Descriptor ID */ - 0x02, /* iProduct Descriptor ID */ - 0x00, /* iSerialNumber = (0) No string */ - 0x01 /* bNumConfigurations */ -}; - -#endif//EPICARDIUM_USBSTORAGE __attribute__((aligned(4))) struct config_descriptor_s { usb_configuration_descriptor_t config_descriptor; /* Interface #1 Comm Interface */ - usb_interface_descriptor_t comm_interface_descriptor; + usb_interface_descriptor_t if_comm; uint8_t header_functional_descriptor[5]; uint8_t call_management_descriptor[5]; uint8_t acm_functional_descriptor[4]; uint8_t union_functional_descriptor[5]; - usb_endpoint_descriptor_t endpoint_descriptor_3; + usb_endpoint_descriptor_t ep_comm_in; /* Interface #2 Data Interface */ - usb_interface_descriptor_t data_interface_descriptor; - usb_endpoint_descriptor_t endpoint_descriptor_1; - usb_endpoint_descriptor_t endpoint_descriptor_2; + usb_interface_descriptor_t if_data; + usb_endpoint_descriptor_t ep_data_out; + usb_endpoint_descriptor_t ep_data_in; /* Interface #3 Mass Storage Device */ - usb_interface_descriptor_t msc_interface_descriptor; - usb_endpoint_descriptor_t endpoint_descriptor_4; - usb_endpoint_descriptor_t endpoint_descriptor_5; + usb_interface_descriptor_t if_msc; + usb_endpoint_descriptor_t ep_msc_out; + usb_endpoint_descriptor_t ep_msc_in; } __attribute__((packed)) config_descriptor = { - { + .config_descriptor = { 0x09, /* bLength = 9 */ 0x02, /* bDescriptorType = Config (2) */ sizeof(config_descriptor), @@ -118,7 +98,7 @@ struct config_descriptor_s { 0xE0, /* bmAttributes (self-powered, remote wakeup) */ 0x01, /* MaxPower is 2ma (units are 2ma/bit) */ }, - { /* First Interface Descriptor For Comm Class Interface */ + .if_comm = { /* First Interface Descriptor For Comm Class Interface */ 0x09, /* bLength = 9 */ 0x04, /* bDescriptorType = Interface (4) */ 0x00, /* bInterfaceNumber */ @@ -129,33 +109,33 @@ struct config_descriptor_s { 0x01, /* bInterfaceProtocol = Common "AT" commands (1), no class specific protocol (0) */ 0x00, /* iInterface */ }, - { /* Header Functional Descriptor */ + .header_functional_descriptor = { /* Header Functional Descriptor */ 0x05, /* bFunctionalLength = 5 */ 0x24, /* bDescriptorType */ 0x00, /* bDescriptorSubtype */ 0x10, 0x01, /* bcdCDC */ }, - { /* Call Management Descriptor */ + .call_management_descriptor = { /* Call Management Descriptor */ 0x05, /* bFunctionalLength = 5 */ 0x24, /* bDescriptorType */ 0x01, /* bDescriptorSubtype */ 0x03, /* bmCapabilities = Device handles call management itself (0x01), management over data class (0x02) */ 0x01, /* bmDataInterface */ }, - { /* Abstract Control Management Functional Descriptor */ + .acm_functional_descriptor = { /* Abstract Control Management Functional Descriptor */ 0x04, /* bFunctionalLength = 4 */ 0x24, /* bDescriptorType */ 0x02, /* bDescriptorSubtype */ 0x02, /* bmCapabilities */ }, - { /* Union Functional Descriptor */ + .union_functional_descriptor = { /* Union Functional Descriptor */ 0x05, /* bFunctionalLength = 5 */ 0x24, /* bDescriptorType */ 0x06, /* bDescriptorSubtype */ 0x00, /* bmMasterInterface */ 0x01, /* bmSlaveInterface0 */ }, - { /* IN Endpoint 3 (Descriptor #1) */ + .ep_comm_in = { /* IN Endpoint 3 (Descriptor #1) */ 0x07, /* bLength */ 0x05, /* bDescriptorType (Endpoint) */ 0x83, /* bEndpointAddress (EP3-IN) */ @@ -163,7 +143,7 @@ struct config_descriptor_s { 0x0040, /* wMaxPacketSize */ 0xff, /* bInterval (milliseconds) */ }, - { /* Interface #2 Descriptor For Data Interface */ + .if_data = { /* Interface #2 Descriptor For Data Interface */ 0x09, /* bLength */ 0x04, /* bDescriptorType (Interface) */ 0x01, /* bInterfaceNumber */ @@ -174,7 +154,7 @@ struct config_descriptor_s { 0x00, /* bInterfaceProtocol = No class specific protocol (0) */ 0x00, /* biInterface = No Text String (0) (SWYM: biInterface? string? wat?)*/ }, - { /* OUT Endpoint 1 (Descriptor #2) */ + .ep_data_out = { /* OUT Endpoint 1 (Descriptor #2) */ 0x07, /* bLength */ 0x05, /* bDescriptorType (Endpoint) */ 0x01, /* bEndpointAddress (EP1-OUT) */ @@ -182,7 +162,7 @@ struct config_descriptor_s { 0x0040, /* wMaxPacketSize */ 0x00, /* bInterval (N/A) */ }, - { /* IN Endpoint 2 (Descriptor #3) */ + .ep_data_in = { /* IN Endpoint 2 (Descriptor #3) */ 0x07, /* bLength */ 0x05, /* bDescriptorType (Endpoint) */ 0x82, /* bEndpointAddress (EP2-IN) */ @@ -191,7 +171,7 @@ struct config_descriptor_s { 0x00 /* bInterval (N/A) */ }, /********** Interface #3 : Mass Storage Device **********/ - { /* Second Interface Descriptor For MSC Interface */ + .if_msc = { 0x09, /* bLength = 9 */ 0x04, /* bDescriptorType = Interface (4) */ 0x02, /* bInterfaceNumber */ @@ -202,7 +182,7 @@ struct config_descriptor_s { 0x50, /* bInterfaceProtocol = Bulk-Only Transport */ 0x05, /* iInterface */ }, - { /* OUT Endpoint 1 (Descriptor #4) */ + .ep_msc_out = { 0x07, /* bLength */ 0x05, /* bDescriptorType (Endpoint) */ 0x04, /* bEndpointAddress (EP4-OUT) */ @@ -210,7 +190,7 @@ struct config_descriptor_s { 0x0040, /* wMaxPacketSize */ 0x00, /* bInterval (N/A) */ }, - { /* IN Endpoint 2 (Descriptor #5) */ + .ep_msc_in = { 0x07, /* bLength */ 0x05, /* bDescriptorType (Endpoint) */ 0x85, /* bEndpointAddress (EP5-IN) */ diff --git a/epicardium/meson.build b/epicardium/meson.build index c5dc701d6164c79246e9a6aa8bc761c01626b32e..c8031c2f0ddb761766706527a3329d0510f3241a 100644 --- a/epicardium/meson.build +++ b/epicardium/meson.build @@ -71,6 +71,7 @@ subdir('l0der/') elf = executable( name + '.elf', 'cdcacm.c', + 'usb.c', 'main.c', 'support.c', module_sources, diff --git a/epicardium/usb.c b/epicardium/usb.c new file mode 100644 index 0000000000000000000000000000000000000000..3700c4f3831547e85a6d8f2e2b34b3d68c9d25ac --- /dev/null +++ b/epicardium/usb.c @@ -0,0 +1,426 @@ +/** + * + * + * setting up USB basically consists of two parts: + * + * - set up descriptors + * - initialize the corresponding MAXUSB stacks - acm, msc... + * + * at the moment, the descriptors are statically initialized in + * descriptors.h several descriptors point to enumeration descriptors + * via iFooBar indizes. Those enumeration descriptors are registered + * via enum_register_descriptor(type, desc, idx), where the idx passed + * corresponds to the iFooBar index mentioned above. + * + * There are several callbacks, some of which do not perform anything meaningful + * at the moment. For example usb_write_callback sets a global flag that is never + * read from. These will be removed later on. + * + * The initialization routines of acm & msc refer to the descriptors from which, + * among other things, they get their enpoint IDs. These are hard-coded right now + * but it would make sense to allocate these IDs at runtime. There is, AFAICT, no + * reason for these endpoint IDs to be 3, 1, 2 in the CDCACM case for example. + * + * Allocating those at runtime would also make the setup less rigid: we can have all + * combinations of {cdcacm, storage} interfaces without the awkward descriptor hackery + * in epc_usb_init. + * + * To generalize even further, the descriptors could be malloced. The total length of + * the descriptor structure can easily be derived from the number of interfaces to be + * configured. The memory allocated will then be split up (with alignment of the single + * descriptor structs taken care of) in a simple bump-allocator strategy on demand. + * + * + * MSC: + * + * atm it is not possible to detect umount from the host. One would expect + * msc_stop to be called due to a CMD_START_STOP handled by msc_cbwReceived + * but this does not seem to be the case. At least for Linux hosts. + * On wireshark, one can see that, upon umount, a + * USBMS packet "SCSI: prevent/allow removal" is sent. It looks as though this + * might reach the CMD_MEDIUM_REMOVAL case in msc_cbwReceived, in which the + * msc module does not invoke any callback / call mem.stop()... + * ...printf'ing from this case, it's clear that several CMD_MEDIUM_REMOVAL frames + * are received, even before mounting. + * ...alright, there's several pairs of prevent/allow coming in. There are no + * other parameters in the frame that seem to signify "I am actually accessing the FS" + * which makes sense, it's mass storage - it doesn't care if there is an FS or not. + * Some of these pre-mount prevent/allow pairs bracket mem.read() calls at different addresses. + * The only way a mount by an OS could be detected is: + * 1: check of prevent is asserted for a certain time + * 2: check if, in that time, an address range from 0x00..sizeof(FAT) has been read + * where 2 is probably not a solid indicator, since OSes besides Linux may (wrongfully?) + * not re-read the FAT after asserting prevent post-mount. + * + */ +#include <stdio.h> +#include <stddef.h> + +#include "cdcacm.h" +#include "mxc_config.h" +#include "mxc_sys.h" +#include "mxc_delay.h" +#include "usb.h" +#include "usb_event.h" +#include "enumerate.h" + +#include "descriptors.h" +#include "msc.h" +#include "cdc_acm.h" + +#include "modules/log.h" +#include "modules/filesystem.h" + + +/* instead of having three callbacks for setconfig, setfeature and clrfeature, we're + * using a single callback and pass one of three tags as cbdata parameter + * to enum_register_callback + */ + +enum usb_setup_callback_type { + USBSC_SetConfig, + USBSC_SetFeature, + USBSC_ClrFeature, +}; +struct usb_setup_callback_tag { + enum usb_setup_callback_type which; + //potentially needed callback data can be placed here like this: + // union { + // struct {} setconfig; + // struct {} setfeature; + // struct {} clrfeature; + // } data; +}; + + +/***** Function Prototypes *****/ +static int cb_usb_setup(usb_setup_pkt *sud, void *cbdata); +static int cb_usb_event(maxusb_event_t evt, void *data); +static int cb_usb_shutdown(); +static int cb_usb_init(); +static int cb_acm_read_ready(void); +static void usb_app_sleep(void); +static void usb_app_wakeup(void); +static int deconfigure(void); +/* needed for usb_opts. mxc_delay() takes unsigned long, so can't use it directly */ +static void delay_us(unsigned int usec); + + +static const struct usb_setup_callback_tag s_callbackData_SetConfig = { + .which = USBSC_SetConfig, +}; +static const struct usb_setup_callback_tag s_callbackData_SetFeature = { + .which = USBSC_SetFeature, +}; +static const struct usb_setup_callback_tag s_callbackData_ClrFeature = { + .which = USBSC_ClrFeature, +}; + +volatile int suspended; +int remote_wake_en; //SWYM: unused, only written to! +static volatile int usb_read_complete; // SWYM: only ever written to +static volatile bool initialized = false; +uint16_t configured; +uint16_t initialized; + + +int cb_usb_init() +{ + const sys_cfg_usbhs_t sys_usbhs_cfg = NULL; + LOG_INFO("usb", "SYS_USBHS_Init"); + return SYS_USBHS_Init(&sys_usbhs_cfg); +} + +int cb_usb_shutdown() +{ + LOG_INFO("usb", "SYS_USBHS_Shutdown"); + SYS_USBHS_Shutdown(); + SYS_Reset_Periph(SYS_RESET_USB); + if (configured & USB_IFACE_STORAGE) { + LOG_INFO("usb", "re-attaching FLASH"); + fatfs_attach(); + configured &= ~USB_IFACE_STORAGE + } + return 0; +} + +/* User-supplied function to delay usec micro-seconds */ +static void delay_us(unsigned int usec) +{ + /* mxc_delay() takes unsigned long, so can't use it directly */ + mxc_delay(usec); +} + +/******************************************************************************/ +int epc_usb_init(uint16_t interfaces) +{ + if (initialized) { + //NVIC_DisableIRQ(USB_IRQn); //yes? no? + + usb_disconnect(); + deconfigure(); + //usb_app_sleep(); + usb_shutdown(); + initialized = 0; + } + if (interfaces == 0) { + return 0; + } + maxusb_cfg_options_t usb_opts; + + /* Initialize state */ + configured = 0; + suspended = 0; + remote_wake_en = 0; + + /* Start out in full speed */ + usb_opts.enable_hs = 0; + usb_opts.delay_us = delay_us; + usb_opts.init_callback = cb_usb_init; + usb_opts.shutdown_callback = cb_usb_shutdown; + + /* Initialize the usb module */ + if (usb_init(&usb_opts) != 0) { + LOG_ERR("cdcacm", "usb_init() failed"); + return -EIO; + } + + /* Initialize the enumeration module */ + if (enum_init() != 0) { + usb_shutdown(); + LOG_ERR("cdcacm", "enum_init() failed"); + return -EIO; + } + + /* Register enumeration data */ + if (interfaces & USB_IFACE_STORAGE) { + config_descriptor.config_descriptor.wTotalLength = + sizeof(config_descriptor); + config_descriptor.config_descriptor.bNumInterfaces = 3; + } else { + config_descriptor.config_descriptor.wTotalLength = offsetof( + struct config_descriptor_s, if_msc + ); + config_descriptor.config_descriptor.bNumInterfaces = 2; + } + enum_register_descriptor( + ENUM_DESC_DEVICE, (uint8_t *)&composite_device_descriptor, 0 + ); + enum_register_descriptor( + ENUM_DESC_CONFIG, (uint8_t *)&config_descriptor, 0 + ); + enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0); + enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1); + enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2); + //used in composite but not in cdc (see iSerialNumber) + //TODO: what do we want our serial to be? + enum_register_descriptor(ENUM_DESC_STRING, serial_id_desc, 3); + enum_register_descriptor(ENUM_DESC_STRING, cdcacm_func_desc, 4); + enum_register_descriptor(ENUM_DESC_STRING, msc_func_desc, 5); + + /* Handle configuration */ + enum_register_callback(ENUM_SETCONFIG, cb_usb_setup, (void*)&s_callbackData_SetConfig); + + /* Handle feature set/clear */ + enum_register_callback(ENUM_SETFEATURE, cb_usb_setup, (void*)&s_callbackData_SetFeature); + enum_register_callback(ENUM_CLRFEATURE, cb_usb_setup, (void*)&s_callbackData_ClrFeature); + + if (interfaces & USB_IFACE_CDCACM) { + if (acm_init(&config_descriptor.if_comm) != 0) { + enum_clearconfig(); + usb_shutdown(); + LOG_ERR("cdcacm", "acm_init() failed"); + return -EIO; + } else { + configured |= USB_IFACE_CDCACM; + } + } + + if (interfaces & USB_IFACE_STORAGE) { + /* Initialize the class driver */ + static const msc_idstrings_t ids = { + "CCC", /* Vendor string. Maximum of 8 bytes */ + "card10", /* Product string. Maximum of 16 bytes */ + "1.0" /* Version string. Maximum of 4 bytes */ + }; + + configured |= USB_IFACE_STORAGE; + LOG_INFO("usb", "detaching FLASH"); + fatfs_detach(); //SWYM: move to mscmem_init + + /* Functions to control "disk" memory. See msc.h for definitions. */ + int mscmem_init(void); + int mscmem_start(void); + int mscmem_stop(void); + uint32_t mscmem_size(void); + int mscmem_read(uint32_t lba, uint8_t * buffer); + int mscmem_write(uint32_t lba, uint8_t * buffer); + int mscmem_ready(void); + + static const msc_mem_t mem = { + mscmem_init, mscmem_start, mscmem_stop, mscmem_ready, + mscmem_size, mscmem_read, mscmem_write, + }; + if (msc_init( + &config_descriptor.if_msc, + &ids, + &mem) != 0) { + LOG_ERR("msc", "msc_init() failed\n"); + return -EIO; + } + } + + /* Register callbacks */ + usb_event_enable(MAXUSB_EVENT_NOVBUS, cb_usb_event, NULL); + usb_event_enable(MAXUSB_EVENT_VBUS, cb_usb_event, NULL); + acm_register_callback(ACM_CB_READ_READY, cb_acm_read_ready); //SWYM: actually not needed + usb_read_complete = 0; + + /* Start with USB in low power mode */ + usb_app_sleep(); + /* TODO: Fix priority */ + NVIC_SetPriority(USB_IRQn, 6); + NVIC_EnableIRQ(USB_IRQn); + + initialized = true; + + return 0; +} + +static int cb_usb_setup(usb_setup_pkt *sud, void *cbdata) +{ + const struct usb_setup_callback_tag* self = (const struct usb_setup_callback_tag*)cbdata; + switch(self->which) { + case USBSC_SetConfig: + if (sud->wValue == + config_descriptor.config_descriptor.bConfigurationValue) + { + int acm_rc = 0; + if (configured & USB_IFACE_CDCACM) { + const acm_cfg_t acm_cfg = { + config_descriptor.ep_data_out.bEndpointAddress, + MXC_USBHS_MAX_PACKET, /* OUT max packet size */ + config_descriptor.ep_data_in.bEndpointAddress & 0x7, + MXC_USBHS_MAX_PACKET, /* IN max packet size */ + config_descriptor.ep_comm_in.bEndpointAddress & 0x7, + MXC_USBHS_MAX_PACKET, /* Notify max packet size */ + }; + + acm_configure(&acm_cfg); + if (acm_rc) { + LOG_ERR("usb", "acm_configure failed: %d\n", acm_rc); + } else { + initialized |= USB_IFACE_CDCACM; + } + } + int msc_rc = 0; + if (configured & USB_IFACE_STORAGE) { + const msc_cfg_t msc_cfg = { + config_descriptor.ep_msc_out.bEndpointAddress, + MXC_USBHS_MAX_PACKET, /* OUT max packet size */ + config_descriptor.ep_msc_out.bEndpointAddress & 0x7, + MXC_USBHS_MAX_PACKET, /* IN max packet size */ + }; + msc_rc = msc_configure(&msc_cfg); + if (msc_rc) { + LOG_ERR("usb", "msc_configure failed: %d\n", msc_rc); + } else { + initialized |= USB_IFACE_STORAGE; + } + } + return acm_rc || msc_rc; + } else if (sud->wValue == 0) { + return deconfigure(); + } + break; + case USBSC_SetFeature: + if (sud->wValue == FEAT_REMOTE_WAKE) { + remote_wake_en = 1; + return 0; + } + break; + case USBSC_ClrFeature: + if (sud->wValue == FEAT_REMOTE_WAKE) { + remote_wake_en = 0; + return 0; + } + break; + } + return -1; +} + +/******************************************************************************/ +static void usb_app_sleep(void) +{ + /* TODO: Place low-power code here */ + suspended = 1; +} + +/******************************************************************************/ +static void usb_app_wakeup(void) +{ + /* TODO: Place low-power code here */ + suspended = 0; +} + +static int deconfigure(void) +{ + enum_clearconfig(); + int rc_acm = 0; + int rc_msc = 0; + if (initialized & USB_IFACE_CDCACM) { + rc_acm = acm_deconfigure(); + } + if (initialized & USB_IFACE_STORAGE) { + rc_msc = msc_deconfigure(); + } + initialized = 0; + return rc_acm || rc_msc; +} +/******************************************************************************/ +static int cb_usb_event(maxusb_event_t evt, void *data) +{ + LOG_INFO("usb", "event %d\n", evt); + + switch (evt) { + case MAXUSB_EVENT_NOVBUS: + usb_event_disable(MAXUSB_EVENT_BRST); + usb_event_disable(MAXUSB_EVENT_SUSP); + usb_event_disable(MAXUSB_EVENT_DPACT); + usb_disconnect(); + deconfigure(); + usb_app_sleep(); + break; + case MAXUSB_EVENT_VBUS: + usb_event_clear(MAXUSB_EVENT_BRST); + usb_event_enable(MAXUSB_EVENT_BRST, cb_usb_event, NULL); + usb_event_clear(MAXUSB_EVENT_SUSP); + usb_event_enable(MAXUSB_EVENT_SUSP, cb_usb_event, NULL); + usb_connect(); + usb_app_sleep(); + break; + case MAXUSB_EVENT_BRST: + usb_app_wakeup(); + deconfigure(); + suspended = 0; + break; + case MAXUSB_EVENT_SUSP: + usb_app_sleep(); + break; + case MAXUSB_EVENT_DPACT: + usb_app_wakeup(); + break; + default: + break; + } + + return 0; +} + +/******************************************************************************/ +static int cb_acm_read_ready(void) +{ + usb_read_complete = 1; + return 0; +} + diff --git a/lib/sdk/Libraries/MAXUSB/src/devclass/msc.c b/lib/sdk/Libraries/MAXUSB/src/devclass/msc.c index 897cbf33eb12e15abfe154e54fa3bf87a239b332..df214a6ad0ae2b4d1f39c687f249c2814f08f499 100644 --- a/lib/sdk/Libraries/MAXUSB/src/devclass/msc.c +++ b/lib/sdk/Libraries/MAXUSB/src/devclass/msc.c @@ -40,6 +40,7 @@ #include "usb_event.h" #include "enumerate.h" #include "msc.h" +#include <stdio.h> /***** Definitions *****/ @@ -381,6 +382,7 @@ static void msc_cbwReceived(void* cbdata) msc_sendModeSense(); break; case CMD_MEDIUM_REMOVAL: + printf("CMD_MEDIUM_REMOVAL: %s\n", *(req_data + CBW_CB_IDX + 4) ? "prevent" : "allow"); /* This command only requires a CSW to be sent back. */ msc_sendCSW(); break; diff --git a/pycardium/modules/usb.c b/pycardium/modules/usb.c new file mode 100644 index 0000000000000000000000000000000000000000..1b178abc1d0af0609de450d1cfe473c02edd9af3 --- /dev/null +++ b/pycardium/modules/usb.c @@ -0,0 +1,44 @@ +#include "py/obj.h" +#include "py/runtime.h" +#include "py/builtin.h" + +#include "epicardium.h" + +static mp_obj_t mp_usb_shutdown() +{ + epic_usb_shutdown(); + return mp_const_none; +} + +static mp_obj_t mp_usb_storage() +{ + epic_usb_storage(); + return mp_const_none; +} + +static mp_obj_t mp_usb_cdcacm() +{ + epic_usb_cdcacm(); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(usb_shutdown_obj, mp_usb_shutdown); +static MP_DEFINE_CONST_FUN_OBJ_0(usb_storage_obj, mp_usb_storage); +static MP_DEFINE_CONST_FUN_OBJ_0(usb_cdcacm_obj, mp_usb_cdcacm); + +static const mp_rom_map_elem_t usb_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usb) }, + { MP_ROM_QSTR(MP_QSTR_shutdown), MP_ROM_PTR(&usb_shutdown_obj) }, + { MP_ROM_QSTR(MP_QSTR_storage), MP_ROM_PTR(&usb_storage_obj) }, + { MP_ROM_QSTR(MP_QSTR_cdcacm), MP_ROM_PTR(&usb_cdcacm_obj) }, +}; +static MP_DEFINE_CONST_DICT(usb_module_globals, usb_module_globals_table); + +// Define module object. +const mp_obj_module_t usb_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&usb_module_globals, +}; + +/* Register the module to make it available in Python */ +/* clang-format off */ +MP_REGISTER_MODULE(MP_QSTR_usb, usb_module, 1);