diff --git a/epicardium/FreeRTOSConfig.h b/epicardium/FreeRTOSConfig.h index b92b12b9ec223da13082f23fcad849a1337ba316..f1d3aa46915e9c4373d281a1a4ab37707a918bf9 100644 --- a/epicardium/FreeRTOSConfig.h +++ b/epicardium/FreeRTOSConfig.h @@ -52,7 +52,7 @@ #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1 - +#define INCLUDE_xTimerPendFunctionCall 1 /* Allow static allocation of data structures */ #define configSUPPORT_STATIC_ALLOCATION 1 diff --git a/epicardium/cdcacm.c b/epicardium/cdcacm.c deleted file mode 100644 index 1cbbb154452a9ac96b9300bdded5071353121260..0000000000000000000000000000000000000000 --- a/epicardium/cdcacm.c +++ /dev/null @@ -1,382 +0,0 @@ -/******************************************************************************* - * 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 <stdio.h> -#include <stddef.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 "descriptors.h" - -#include "modules/modules.h" -#include "modules/log.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 echo_usb(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; - -int usb_startup_cb() -{ - const sys_cfg_usbhs_t sys_usbhs_cfg = NULL; - return SYS_USBHS_Init(&sys_usbhs_cfg); -} - -int usb_shutdown_cb() -{ - return SYS_USBHS_Shutdown(); -} - -/* 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 cdcacm_init(void) -{ - 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) { - LOG_ERR("cdcacm", "enum_init() failed"); - return -EIO; - } - - /* Register enumeration data */ - enum_register_descriptor( - ENUM_DESC_DEVICE, (uint8_t *)&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); - - /* 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) { - LOG_ERR("cdcacm", "acm_init() failed"); - return -EIO; - } - - /* 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); - - return 0; -} - -int cdcacm_num_read_avail(void) -{ - return acm_canread(); -} - -uint8_t cdcacm_read(void) -{ - while (acm_canread() <= 0) { - } - - 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) { - lockup_disable = 1; - LOG_ERR("cdcacm", "fifo lockup detected"); - } else if (ret != len) { - LOG_WARN( - "cdcacm", "write length mismatch, got %d", ret - ); - } - } -} - -/******************************************************************************/ -#if 0 -static void echo_usb(void) -{ - int chars; - uint8_t buffer[BUFFER_SIZE]; - - if ((chars = acm_canread()) > 0) { - - if (chars > BUFFER_SIZE) { - chars = BUFFER_SIZE; - } - - // Read the data from USB - if (acm_read(buffer, chars) != chars) { - printf("acm_read() failed\n"); - return; - } - - // Echo it back - if (acm_present()) { - if (acm_write(buffer, chars) != chars) { - printf("acm_write() failed\n"); - } - } - } -} -#endif -/******************************************************************************/ -static int setconfig_callback(usb_setup_pkt *sud, void *cbdata) -{ - /* Confirm the configuration value */ - if (sud->wValue == - config_descriptor.config_descriptor.bConfigurationValue) { - configured = 1; - MXC_SETBIT(&event_flags, EVENT_ENUM_COMP); - return acm_configure(&acm_cfg); /* Configure the device class */ - } 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 int event_callback(maxusb_event_t evt, void *data) -{ - /* Set event flag */ - MXC_SETBIT(&event_flags, 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(); - configured = 0; - enum_clearconfig(); - acm_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(); - enum_clearconfig(); - acm_deconfigure(); - configured = 0; - 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" - -void USB_IRQHandler(void) -{ - usb_event_handler(); - - if (serial_task_id != NULL) { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR( - serial_task_id, &xHigherPriorityTaskWoken - ); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - } -} - -/******************************************************************************/ -/* TODO: We probably need to fix something related to this */ -#if 0 -void SysTick_Handler(void) -{ - mxc_delay_handler(); -} -#endif /* 0 */ diff --git a/epicardium/cdcacm.h b/epicardium/cdcacm.h deleted file mode 100644 index 1c25d75027cebffd593f32364ebab7eec65fa7e6..0000000000000000000000000000000000000000 --- a/epicardium/cdcacm.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CDCACM_H -#define CDCACM_H -#include <stdint.h> - -int cdcacm_init(void); -int cdcacm_num_read_avail(void); -uint8_t cdcacm_read(void); -void cdcacm_write(uint8_t *data, int len); - -#endif diff --git a/epicardium/descriptors.h b/epicardium/descriptors.h deleted file mode 100644 index c081c4010e65c7c86980ad94a096a81febb69f61..0000000000000000000000000000000000000000 --- a/epicardium/descriptors.h +++ /dev/null @@ -1,227 +0,0 @@ -/******************************************************************************* - * 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. - * - * Description: Communications Device Class ACM (Serial Port) over USB - * $Id: descriptors.h 36682 2018-08-06 21:14:03Z michael.bayern $ - * - ******************************************************************************* - */ - -#ifndef _DESCRIPTORS_H_ -#define _DESCRIPTORS_H_ - -#include <stdint.h> -#include "usb.h" -#include "hid_kbd.h" - -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 */ -}; - -__attribute__((aligned(4))) -struct __attribute__((packed)) { - usb_configuration_descriptor_t config_descriptor; - usb_interface_descriptor_t comm_interface_descriptor; - 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_interface_descriptor_t data_interface_descriptor; - usb_endpoint_descriptor_t endpoint_descriptor_1; - usb_endpoint_descriptor_t endpoint_descriptor_2; -} config_descriptor = -{ - { - 0x09, /* bLength = 9 */ - 0x02, /* bDescriptorType = Config (2) */ - 0x0043, /* wTotalLength(L/H) */ - 0x02, /* bNumInterfaces */ - 0x01, /* bConfigValue */ - 0x00, /* iConfiguration */ - 0xE0, /* bmAttributes (self-powered, remote wakeup) */ - 0x01, /* MaxPower is 2ma (units are 2ma/bit) */ - }, - { /* First Interface Descriptor For Comm Class Interface */ - 0x09, /* bLength = 9 */ - 0x04, /* bDescriptorType = Interface (4) */ - 0x00, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x01, /* bNumEndpoints (one for OUT) */ - 0x02, /* bInterfaceClass = Communications Interface Class (2) */ - 0x02, /* bInterfaceSubClass = Abstract Control Model (2) */ - 0x01, /* bInterfaceProtocol = Common "AT" commands (1), no class specific protocol (0) */ - 0x00, /* iInterface */ - }, - { /* Header Functional Descriptor */ - 0x05, /* bFunctionalLength = 5 */ - 0x24, /* bDescriptorType */ - 0x00, /* bDescriptorSubtype */ - 0x10, 0x01, /* bcdCDC */ - }, - { /* 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 */ - 0x04, /* bFunctionalLength = 4 */ - 0x24, /* bDescriptorType */ - 0x02, /* bDescriptorSubtype */ - 0x02, /* bmCapabilities */ - }, - { /* Union Functional Descriptor */ - 0x05, /* bFunctionalLength = 5 */ - 0x24, /* bDescriptorType */ - 0x06, /* bDescriptorSubtype */ - 0x00, /* bmMasterInterface */ - 0x01, /* bmSlaveInterface0 */ - }, - { /* IN Endpoint 3 (Descriptor #1) */ - 0x07, /* bLength */ - 0x05, /* bDescriptorType (Endpoint) */ - 0x83, /* bEndpointAddress (EP3-IN) */ - 0x03, /* bmAttributes (interrupt) */ - 0x0040, /* wMaxPacketSize */ - 0xff, /* bInterval (milliseconds) */ - }, - { /* Second Interface Descriptor For Data Interface */ - 0x09, /* bLength */ - 0x04, /* bDescriptorType (Interface) */ - 0x01, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x02, /* bNumEndpoints */ - 0x0a, /* bInterfaceClass = Data Interface (10) */ - 0x00, /* bInterfaceSubClass = none (0) */ - 0x00, /* bInterfaceProtocol = No class specific protocol (0) */ - 0x00, /* biInterface = No Text String (0) */ - }, - { /* OUT Endpoint 1 (Descriptor #2) */ - 0x07, /* bLength */ - 0x05, /* bDescriptorType (Endpoint) */ - 0x01, /* bEndpointAddress (EP1-OUT) */ - 0x02, /* bmAttributes (bulk) */ - 0x0040, /* wMaxPacketSize */ - 0x00, /* bInterval (N/A) */ - }, - { /* IN Endpoint 2 (Descriptor #3) */ - 0x07, /* bLength */ - 0x05, /* bDescriptorType (Endpoint) */ - 0x82, /* bEndpointAddress (EP2-IN) */ - 0x02, /* bmAttributes (bulk) */ - 0x0040, /* wMaxPacketSize */ - 0x00 /* bInterval (N/A) */ - } -}; - -__attribute__((aligned(4))) -uint8_t lang_id_desc[] = { - 0x04, /* bLength */ - 0x03, /* bDescriptorType */ - 0x09, 0x04 /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */ -}; - -__attribute__((aligned(4))) -uint8_t mfg_id_desc[] = { - 0x22, /* bLength */ - 0x03, /* bDescriptorType */ - 'M', 0, - 'a', 0, - 'x', 0, - 'i', 0, - 'm', 0, - ' ', 0, - 'I', 0, - 'n', 0, - 't', 0, - 'e', 0, - 'g', 0, - 'r', 0, - 'a', 0, - 't', 0, - 'e', 0, - 'd', 0, -}; - -__attribute__((aligned(4))) -uint8_t prod_id_desc[] = { - 0x22, /* bLength */ - 0x03, /* bDescriptorType */ - 'M', 0, - 'A', 0, - 'X', 0, - '3', 0, - '2', 0, - '6', 0, - '6', 0, - '5', 0, - ' ', 0, - 'C', 0, - 'D', 0, - 'C', 0, - '-', 0, - 'A', 0, - 'C', 0, - 'M', 0, -}; - -/* Not currently used (see device descriptor), but could be enabled if desired */ -__attribute__((aligned(4))) -uint8_t serial_id_desc[] = { - 0x14, /* bLength */ - 0x03, /* bDescriptorType */ - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '1', 0 -}; - -#endif /* _DESCRIPTORS_H_ */ diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index eb34e17b9401b02993b11a857d084f3d19202d25..74b0c814653b0ad0193d2ffd11dc50f0d7fae8d7 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -131,6 +131,10 @@ typedef _Bool bool; #define API_MAX86150_GET_DATA 0x0101 #define API_MAX86150_SET_LED_AMPLITUDE 0x0102 +#define API_USB_SHUTDOWN 0x110 +#define API_USB_STORAGE 0x111 +#define API_USB_CDCACM 0x112 + /* clang-format on */ typedef uint32_t api_int_id_t; @@ -1700,5 +1704,20 @@ API(API_MAX30001_DISABLE, int epic_max30001_disable_sensor( void )); +/** + * De-initialize the currently configured USB device (if any) + * + */ +API(API_USB_SHUTDOWN, int epic_usb_shutdown(void)); +/** + * Configure the USB peripheral to export the internal FLASH + * as a Mass Storage device + */ +API(API_USB_STORAGE, int epic_usb_storage(void)); +/** + * Configure the USB peripheral to provide card10's stdin/stdout + * on a USB CDC-ACM device + */ +API(API_USB_CDCACM, int epic_usb_cdcacm(void)); #endif /* _EPICARDIUM_H */ diff --git a/epicardium/fs/filesystem_fat.c b/epicardium/fs/filesystem_fat.c index 7d95510360324ac5e105a53aa90a3a96ee0936c1..ccaec62157174cdfab14c957d157941b063acc64 100644 --- a/epicardium/fs/filesystem_fat.c +++ b/epicardium/fs/filesystem_fat.c @@ -16,6 +16,7 @@ #include <FreeRTOS.h> #include <semphr.h> +#include <timers.h> #include "fs/internal.h" #include "modules/filesystem.h" @@ -57,7 +58,7 @@ struct FatObject { struct EpicFileSystem { struct FatObject pool[EPIC_FAT_MAX_OPENED]; uint32_t generationCount; - bool initialized; + bool attached; FATFS FatFs; int lockCoreMask; }; @@ -102,6 +103,11 @@ static StaticSemaphore_t s_globalLockBuffer; static SemaphoreHandle_t s_globalLock = NULL; +static void cb_attachTimer(void *a, uint32_t b) +{ + fatfs_attach(); +} + void fatfs_init() { static volatile bool s_initCalled = false; @@ -117,6 +123,7 @@ void fatfs_init() #else s_globalLock = xSemaphoreCreateMutex(); #endif + s_globalFileSystem.generationCount = 1; fatfs_attach(); } @@ -137,11 +144,11 @@ int fatfs_attach() int rc = 0; if (globalLockAccquire()) { EpicFileSystem *fs = &s_globalFileSystem; - if (!fs->initialized) { + if (!fs->attached) { ff_res = f_mount(&fs->FatFs, "/", 0); if (ff_res == FR_OK) { - fs->initialized = true; - SSLOG_DEBUG("FatFs mounted\n"); + fs->attached = true; + SSLOG_INFO("attached\n"); } else { SSLOG_ERR( "f_mount error %s\n", @@ -158,25 +165,38 @@ int fatfs_attach() return rc; } +void fatfs_schedule_attach(void) +{ + //if we're running in thread context, cont't call the *FromISR version + if (xPortIsInsideInterrupt()) { + xTimerPendFunctionCallFromISR(cb_attachTimer, NULL, 0, NULL); + } else { + xTimerPendFunctionCall( + cb_attachTimer, NULL, 0, 1); //wait 1 tick + } +} + void fatfs_detach() { FRESULT ff_res; EpicFileSystem *fs; if (efs_lock_global(&fs) == 0) { - efs_close_all(fs, EPICARDIUM_COREMASK_BOTH); - - //unmount by passing NULL as fs object, will destroy our sync object via ff_del_syncobj - ff_res = f_mount(NULL, "/", 0); - if (ff_res != FR_OK) { - SSLOG_ERR( - "f_mount (unmount) error %s\n", - f_get_rc_string(ff_res) - ); - } + if (fs->attached) { + efs_close_all(fs, EPICARDIUM_COREMASK_BOTH); + + //unmount by passing NULL as fs object, will destroy our sync object via ff_del_syncobj + ff_res = f_mount(NULL, "/", 0); + if (ff_res != FR_OK) { + SSLOG_ERR( + "f_mount (unmount) error %s\n", + f_get_rc_string(ff_res) + ); + } - fs->initialized = false; - disk_deinitialize(); - SSLOG_INFO("detached\n"); + fs->attached = false; + disk_deinitialize(); + SSLOG_INFO("detached\n"); + } efs_unlock_global(fs); } } @@ -223,7 +243,7 @@ int efs_lock_global(EpicFileSystem **fs) if (!globalLockAccquire()) { return -EBUSY; } - if (!s_globalFileSystem.initialized) { + if (!s_globalFileSystem.attached) { globalLockRelease(); return -ENODEV; } diff --git a/epicardium/meson.build b/epicardium/meson.build index 1aa5556010941d2267f8b10aafcaf9ec7683c8a2..cf67023b9f7143336875d973108d8ee0a080ffd1 100644 --- a/epicardium/meson.build +++ b/epicardium/meson.build @@ -80,7 +80,9 @@ endif elf = executable( name + '.elf', - 'cdcacm.c', + 'usb/epc_usb.c', + 'usb/cdcacm.c', + 'usb/mass_storage.c', 'main.c', 'support.c', 'fs/filesystem_fat.c', diff --git a/epicardium/modules/filesystem.h b/epicardium/modules/filesystem.h index 720147ecd04a8f07755052d05adb90871b360bd7..db54a2895621d0e316dee83c362faac737b1a9e2 100644 --- a/epicardium/modules/filesystem.h +++ b/epicardium/modules/filesystem.h @@ -16,9 +16,19 @@ void fatfs_init(void); /** * initialize and mount the FLASH storage + * + * NOTE: not safe to be called from an ISR */ int fatfs_attach(void); + +/** + * asynchronously attach the FLASH storage + * + * safe to be called from an ISR + */ +void fatfs_schedule_attach(void); + /** close all opened FDs, sync and deinitialize FLASH layer */ void fatfs_detach(void); diff --git a/epicardium/modules/hardware.c b/epicardium/modules/hardware.c index cb983dae9ad8a696596c28f5317f7587574f1e7b..c944c6b9f140b5e6952c28996943bf19d1254332 100644 --- a/epicardium/modules/hardware.c +++ b/epicardium/modules/hardware.c @@ -2,7 +2,7 @@ #include "api/dispatcher.h" #include "api/interrupt-sender.h" -#include "cdcacm.h" +#include "usb/epc_usb.h" #include "modules/filesystem.h" #include "modules/log.h" #include "modules/modules.h" @@ -152,8 +152,8 @@ int hardware_early_init(void) /* * USB-Serial */ - if (cdcacm_init() < 0) { - LOG_ERR("init", "USB-Serial unavailable"); + if (epic_usb_cdcacm() < 0) { + LOG_ERR("startup", "USB-Serial unavailable"); } /* diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index 1aa7494ea0eff9003b48f0aa41a63257b1ab3833..693ca9a8f94a432ff64d770f91d9e9005c88d42a 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -21,4 +21,5 @@ module_sources = files( 'trng.c', 'vibra.c', 'watchdog.c', + 'usb.c' ) diff --git a/epicardium/modules/serial.c b/epicardium/modules/serial.c index b7be0594f3fcacbee9ac3ed7ab6ff0163c74f1a8..c5350b11fc45e8675f7d4f3ae87c37f20f526226 100644 --- a/epicardium/modules/serial.c +++ b/epicardium/modules/serial.c @@ -4,7 +4,7 @@ #include "modules/modules.h" #include "max32665.h" -#include "cdcacm.h" +#include "usb/cdcacm.h" #include "uart.h" #include "FreeRTOS.h" diff --git a/epicardium/modules/usb.c b/epicardium/modules/usb.c new file mode 100644 index 0000000000000000000000000000000000000000..e14032e4e3dccb91342630317786d8c35d363b3a --- /dev/null +++ b/epicardium/modules/usb.c @@ -0,0 +1,322 @@ +/** + * + * Implementation of public epic_usb_ interface + * + * Configuration and FLASH-specific implementation of USB mass storage device + * Configuration of USB CDCACM device + * + * USB descriptors for both are defined here. + * + */ + +#include "epicardium.h" + +#include "modules/filesystem.h" + +#include "usb/cdcacm.h" +#include "usb/mass_storage.h" +#include "usb/descriptors.h" +#include "usb/epc_usb.h" + +#include "modules/log.h" + +#include "mx25lba.h" +#include "msc.h" + +/* memory access callbacks for the mass storage (FLASH) device */ +static int mscmem_init(); +static uint32_t mscmem_size(void); +static int mscmem_read(uint32_t lba, uint8_t *buffer); +static int mscmem_write(uint32_t lba, uint8_t *buffer); +static int mscmem_start(); +static int mscmem_stop(); +static int mscmem_ready(); + +/* USB descriptors, definition at the end of this file */ +static usb_device_descriptor_t device_descriptor_cdcacm + __attribute__((aligned(4))); +static struct config_descriptor_cdcacm config_descriptor_cdcacm + __attribute__((aligned(4))); +static usb_device_descriptor_t device_descriptor_msc + __attribute__((aligned(4))); +static struct config_descriptor_msc config_descriptor_msc + __attribute__((aligned(4))); + +/* definitions of config structs */ +static const msc_mem_t s_mscCallbacks = { + mscmem_init, mscmem_start, mscmem_stop, mscmem_ready, + mscmem_size, mscmem_read, mscmem_write, +}; + +static struct esb_device_descriptors s_dd_cdcacm = { + .device = &device_descriptor_cdcacm, .cdcacm = &config_descriptor_cdcacm +}; + +static struct esb_device_descriptors s_dd_msc = { + .device = &device_descriptor_msc, .msc = &config_descriptor_msc +}; + +static struct esb_config s_cfg_cdcacm = { + .name = "cdcacm", + .init = esb_cdc_init, + .configure = esb_cdc_configure, + .deconfigure = esb_cdc_deconfigure, + .deinit = NULL, + + .descriptors = &s_dd_cdcacm, + .deviceData = NULL, +}; + +static struct esb_config s_cfg_msc = { + .name = "msc FLASH", + .init = esb_msc_init, + .configure = esb_msc_configure, + .deconfigure = esb_msc_deconfigure, + .deinit = NULL, + + .descriptors = &s_dd_msc, + .deviceData = (void *)&s_mscCallbacks, +}; + +/* function implementations */ + +static int dirty = 0; +static int mscmem_init() +{ + LOG_DEBUG("msc", "mem.init"); + fatfs_detach(); + return mx25_init(); +} + +static uint32_t mscmem_size(void) +{ + return mx25_size(); +} + +static int mscmem_read(uint32_t lba, uint8_t *buffer) +{ + return mx25_read(lba, buffer); +} + +static int mscmem_write(uint32_t lba, uint8_t *buffer) +{ + if (dirty == 0) { + //bootloader_dirty(); + } + dirty = 2; + return mx25_write(lba, buffer); +} + +static int mscmem_start() +{ + return mx25_start(); +} + +static int mscmem_stop() +{ + LOG_DEBUG("msc", "mem.stop"); + int ret = mx25_stop(); + fatfs_schedule_attach(); + return ret; +} + +static int mscmem_ready() +{ + if (dirty) { + dirty--; + if (dirty == 0) { + LOG_DEBUG("msc", "sync"); + mx25_sync(); + //bootloader_clean(); + } + } + return mx25_ready(); +} + +static bool s_fsDetached = false; +int epic_usb_shutdown(void) +{ + esb_deinit(); + if (s_fsDetached) { + fatfs_attach(); + } + return 0; +} + +int epic_usb_storage(void) +{ + esb_deinit(); + s_fsDetached = true; + return esb_init(&s_cfg_msc); +} + +int epic_usb_cdcacm(void) +{ + esb_deinit(); + if (s_fsDetached) { + fatfs_attach(); + } + return esb_init(&s_cfg_cdcacm); +} + +/* clang-format off */ +static usb_device_descriptor_t device_descriptor_cdcacm = { + .bLength = 0x12, + .bDescriptorType = DT_DEVICE, + .bcdUSB = 0x0110, + .bDeviceClass = CLS_COMM, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize = 0x40, + .idVendor = VNDR_MAXIM, + .idProduct = 0x003C, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x00, + .bNumConfigurations = 0x01 +}; + +static struct config_descriptor_cdcacm config_descriptor_cdcacm = { + .config = { + .bLength = 0x09, + .bDescriptorType = DT_CONFIG, + .wTotalLength = 0x0043, /* sizeof(struct config_descriptor_cdcacm) */ + .bNumInterfaces = 0x02, + .bConfigurationValue= 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0xE0, /* (self-powered, remote wakeup) */ + .bMaxPower = 0x01, /* MaxPower is 2ma (units are 2ma/bit) */ + }, + .comm_interface = { /* First Interface Descriptor For Comm Class Interface */ + .bLength = 0x09, + .bDescriptorType = DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x01, + .bInterfaceClass = CLS_COMM, + .bInterfaceSubClass = SCLS_ACM, + .bInterfaceProtocol = PROT_AT_CMDS, + .iInterface = 0x00, + }, + .header_functional = { + 0x05, /* bFunctionalLength = 5 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x00, /* bDescriptorSubtype */ + 0x10, 0x01, /* bcdCDC */ + }, + .call_management = { + 0x05, /* bFunctionalLength = 5 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x01, /* bDescriptorSubtype */ + 0x03, /* bmCapabilities = Device handles call management itself (0x01), management over data class (0x02) */ + 0x01, /* bmDataInterface */ + }, + .acm_functional = { + 0x04, /* bFunctionalLength = 4 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x02, /* bDescriptorSubtype */ + 0x02, /* bmCapabilities */ + }, + .union_functional = { + 0x05, /* bFunctionalLength = 5 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x06, /* bDescriptorSubtype */ + 0x00, /* bmMasterInterface */ + 0x01, /* bmSlaveInterface0 */ + }, + .endpoint_notify = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = 0x80 | ESB_ENDPOINT_CDCACM_NOTIFY, + .bmAttributes = ATTR_INTERRUPT, + .wMaxPacketSize = 0x0040, + .bInterval = 0xff, + }, + .data_interface = { + .bLength = 0x09, + .bDescriptorType = DT_INTERFACE, + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = CLS_DATA, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00, //not 1? + }, + .endpoint_out = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = ESB_ENDPOINT_CDCACM_OUT, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00, + }, + .endpoint_in = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = 0x80 | ESB_ENDPOINT_CDCACM_IN, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00 + } +}; + +static usb_device_descriptor_t device_descriptor_msc = { + .bLength = 0x12, + .bDescriptorType = DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = CLS_UNSPECIFIED, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize = 0x40, + .idVendor = VNDR_MAXIM, + .idProduct = 0x4402, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +static struct config_descriptor_msc config_descriptor_msc = +{ + .config = { + .bLength = 0x09, + .bDescriptorType = DT_CONFIG, + .wTotalLength = 0x0020, + .bNumInterfaces = 0x01, + .bConfigurationValue= 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0xC0, /* (self-powered, no remote wakeup) */ + .bMaxPower = 0x32, + }, + .msc_interface = { + .bLength = 0x09, + .bDescriptorType = DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = CLS_MASS_STOR, + .bInterfaceSubClass = SCLS_SCSI_CMDS, + .bInterfaceProtocol = PROT_BULK_TRANS, + .iInterface = 0x00, + }, + .endpoint_out = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = ESB_ENDPOINT_MSC_OUT, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00, + }, + .endpoint_in = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = 0x80 | ESB_ENDPOINT_MSC_IN, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00 + } +}; +/* clang-format on */ \ No newline at end of file diff --git a/epicardium/usb/cdcacm.c b/epicardium/usb/cdcacm.c new file mode 100644 index 0000000000000000000000000000000000000000..679a3a0562c78ba1418ff5d4a7b4e6d68f8072e9 --- /dev/null +++ b/epicardium/usb/cdcacm.c @@ -0,0 +1,126 @@ +/** + * + * epicardium-specific implemnetation of cdcacm device + * services CDCACM API + */ + +#include "usb/epc_usb.h" +#include "usb/cdcacm.h" +#include "usb/descriptors.h" + +#include <stdio.h> +#include <stddef.h> +#include <errno.h> + +#include "usb.h" +#include "usb_event.h" +#include "cdc_acm.h" + +#include "modules/log.h" + +#include "FreeRTOS.h" +#include "task.h" + +static inline struct config_descriptor_cdcacm * +descriptors(struct esb_config *self) +{ + return self->descriptors->cdcacm; +} + +/******************************************************************************/ +static volatile int usb_read_complete; // SWYM: only ever written to +static int cb_acm_read_ready(void) +{ + usb_read_complete = 1; + return 0; +} + +int esb_cdc_init(struct esb_config *self) +{ + LOG_DEBUG("cdcacm", "init"); + struct config_descriptor_cdcacm *dsc = descriptors(self); + usb_read_complete = 0; + acm_register_callback( + ACM_CB_READ_READY, + cb_acm_read_ready); //SWYM: actually not needed + return acm_init(&dsc->comm_interface); +} + +int esb_cdc_configure(struct esb_config *self) +{ + struct config_descriptor_cdcacm *dsc = descriptors(self); + + //acm_configure does not keep the amc_cfg_t pointer around + //so stack-local lifetime is fine + acm_cfg_t acm_cfg = { + .in_ep = dsc->endpoint_in.bEndpointAddress & 0x0f, + .in_maxpacket = MXC_USBHS_MAX_PACKET, + .out_ep = dsc->endpoint_out.bEndpointAddress, + .out_maxpacket = MXC_USBHS_MAX_PACKET, + .notify_ep = dsc->endpoint_notify.bEndpointAddress & 0x0f, + .notify_maxpacket = MXC_USBHS_MAX_PACKET, + }; + + LOG_DEBUG( + "cdcacm", + "configure, endpoints %d,%d,%d", + acm_cfg.in_ep, + acm_cfg.out_ep, + acm_cfg.notify_ep + ); + + return acm_configure(&acm_cfg); +} + +int esb_cdc_deconfigure(struct esb_config *self) +{ + LOG_DEBUG("cdcacm", "deconfigure"); + return acm_deconfigure(); +} + +int cdcacm_num_read_avail(void) +{ + return acm_canread(); +} + +uint8_t cdcacm_read(void) +{ + while (acm_canread() <= 0) { + } + + 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 + ); + } + } +} + +/******************************************************************************/ + +extern TaskHandle_t serial_task_id; +void USB_IRQHandler(void) +{ + usb_event_handler(); + + if (serial_task_id != NULL) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + vTaskNotifyGiveFromISR( + serial_task_id, &xHigherPriorityTaskWoken + ); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} diff --git a/epicardium/usb/cdcacm.h b/epicardium/usb/cdcacm.h new file mode 100644 index 0000000000000000000000000000000000000000..6683e05558aa06ffef793e3f5a8ab4fe03df1a35 --- /dev/null +++ b/epicardium/usb/cdcacm.h @@ -0,0 +1,15 @@ +#ifndef CDCACM_H +#define CDCACM_H + +#include "usb/epc_usb.h" + +// callbacks for esb_config +int esb_cdc_init(struct esb_config* self); +int esb_cdc_configure(struct esb_config* self); +int esb_cdc_deconfigure(struct esb_config* self); + +int cdcacm_num_read_avail(void); +uint8_t cdcacm_read(void); +void cdcacm_write(uint8_t *data, int len); + +#endif diff --git a/epicardium/usb/descriptors.h b/epicardium/usb/descriptors.h new file mode 100644 index 0000000000000000000000000000000000000000..2fb2cc452ffec6cfe35106c5e311df1fc09a67ac --- /dev/null +++ b/epicardium/usb/descriptors.h @@ -0,0 +1,83 @@ +#ifndef _DESCRIPTORS_H_ +#define _DESCRIPTORS_H_ + +#include <stdint.h> +#include "usb.h" + +#define ESB_ENDPOINT_MSC_IN 2 +#define ESB_ENDPOINT_MSC_OUT 1 +#define ESB_ENDPOINT_CDCACM_NOTIFY 3 +#define ESB_ENDPOINT_CDCACM_IN 2 +#define ESB_ENDPOINT_CDCACM_OUT 1 + +/* device types */ +#define DT_DEVICE 0x01 +#define DT_CONFIG 0x02 +#define DT_INTERFACE 0x04 +#define DT_ENDPOINT 0x05 +#define DT_FUNCTIONAL 0x24 + +/* interface classes */ +#define CLS_UNSPECIFIED 0x00 +#define CLS_COMM 0x02 +#define CLS_MASS_STOR 0x08 +#define CLS_DATA 0x0a + +/* sub-classes */ +#define SCLS_NONE 0x00 +#define SCLS_ACM 0x02 +#define SCLS_SCSI_CMDS 0x06 + +/* interface protocols */ +#define PROT_AT_CMDS 0x01 +#define PROT_BULK_TRANS 0x50 + +/* endpoint attributes */ +#define ATTR_BULK 0x02 +#define ATTR_INTERRUPT 0x03 + + +#define VNDR_MAXIM 0x0B6A + +#if defined(__GNUC__) +#define PACKED_STRUCT struct __attribute__((packed)) +#else +#define PACKED_STRUCT __packed struct +#endif + + + + +PACKED_STRUCT +config_descriptor_cdcacm { + usb_configuration_descriptor_t config; + usb_interface_descriptor_t comm_interface; + uint8_t header_functional[5]; + uint8_t call_management[5]; + uint8_t acm_functional[4]; + uint8_t union_functional[5]; + usb_endpoint_descriptor_t endpoint_notify; + usb_interface_descriptor_t data_interface; + usb_endpoint_descriptor_t endpoint_out; + usb_endpoint_descriptor_t endpoint_in; +}; + +PACKED_STRUCT +config_descriptor_msc { + usb_configuration_descriptor_t config; + usb_interface_descriptor_t msc_interface; + usb_endpoint_descriptor_t endpoint_out; + usb_endpoint_descriptor_t endpoint_in; +}; + +struct esb_device_descriptors { + usb_device_descriptor_t* device; + union { + usb_configuration_descriptor_t* config; + struct config_descriptor_cdcacm* cdcacm; + struct config_descriptor_msc* msc; + }; +}; + + +#endif /* _DESCRIPTORS_H_ */ diff --git a/epicardium/usb/epc_usb.c b/epicardium/usb/epc_usb.c new file mode 100644 index 0000000000000000000000000000000000000000..9c24ea141553bb9f5e1e8f9f1f268fbd4403a80a --- /dev/null +++ b/epicardium/usb/epc_usb.c @@ -0,0 +1,460 @@ +/** + * + * + * Core of the USB implementation: + * + * - device independent + * - handles setup of MAXUSB stack + * - handles events and state of the MAXUSB stack + * + * Also contains definitions of device-independent String descriptors + * + * + * + * + * + * swym's USB ramblings: + * + * 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 configured in + * descriptors.h several descriptors point to enumeration descriptors + * via iFooBar indices. 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 + * initialized. 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. + * + * + */ +#include "usb/epc_usb.h" + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +#include <FreeRTOS.h> +#include <task.h> +#include <timers.h> + +#include "mxc_config.h" +#include "mxc_sys.h" +#include "mxc_delay.h" + +// MAXUSB includes +#include "usb.h" +#include "usb_event.h" +#include "enumerate.h" +#include "msc.h" +#include "cdc_acm.h" + +#include "usb/descriptors.h" +#include "modules/log.h" +#include "modules/filesystem.h" + +//#define USE_REMOTE_WAKE_ENABLE + +/***** Function Prototypes *****/ +static int cb_usb_setconfig(usb_setup_pkt *sud, void *cbdata); +#ifdef USE_REMOTE_WAKE_ENABLE +static int cb_usb_setfeature(usb_setup_pkt *sud, void *cbdata); +static int cb_usb_clrfeature(usb_setup_pkt *sud, void *cbdata); +#endif +static int cb_usb_event(maxusb_event_t evt, void *data); +static int cb_usb_shutdown(); +static int cb_usb_init(); +static void usb_app_sleep(void); +static void usb_app_wakeup(void); +/* needed for usb_opts. mxc_delay() takes unsigned long, so can't use it directly */ +static void delay_us(unsigned int usec); +static void device_deinit(); +static bool device_deconfigure(); +static int device_configure(); + +volatile int suspended; +#ifdef USE_REMOTE_WAKE_ENABLE +int remote_wake_en; //SWYM: unused, only written to! +#endif + +static StaticTimer_t x; +static TimerHandle_t timerWakeup = NULL; +static void cb_timerReset(TimerHandle_t t); + +static struct esb_config s_device = { 0 }; + +__attribute__((aligned(4))) uint8_t lang_id_desc[] = { + 0x04, /* bLength */ + 0x03, /* bDescriptorType */ + 0x09, + 0x04 /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */ +}; + +__attribute__((aligned(4))) uint8_t mfg_id_desc[] = { + 0x22, /* bLength */ + 0x03, /* bDescriptorType */ + 'M', 0, 'a', 0, 'x', 0, 'i', 0, 'm', 0, ' ', 0, 'I', 0, 'n', 0, + 't', 0, 'e', 0, 'g', 0, 'r', 0, 'a', 0, 't', 0, 'e', 0, 'd', 0, +}; + +__attribute__((aligned(4))) uint8_t prod_id_desc[] = { + 0x24, /* bLength */ + 0x03, /* bDescriptorType */ + 'C', 0, 'A', 0, 'R', 0, 'D', 0, '1', 0, '0', 0, ' ', 0, 'U', 0, 'S', 0, + 'B', 0, ' ', 0, 'G', 0, 'A', 0, 'D', 0, 'G', 0, 'E', 0, 'T', 0, +}; + +/* Not currently used (see device descriptor), but could be enabled if desired */ +__attribute__((aligned(4))) +uint8_t serial_id_desc[] = { 0x14, /* bLength */ + 0x03, /* bDescriptorType */ + 'c', 0, 'a', 0, 'a', 0, 'a', 0, 'a', + 0, 'd', 0, '1', 0, '0', 0, '0', 0 }; + +int cb_usb_init() +{ + const sys_cfg_usbhs_t sys_usbhs_cfg = NULL; + LOG_DEBUG("usb", "init"); + return SYS_USBHS_Init(&sys_usbhs_cfg); +} + +volatile bool shutDownCompleted; +void do_usb_shutdown() +{ + LOG_DEBUG("usb", "shutting down..."); + shutDownCompleted = false; + usb_shutdown(); + for (int i = 0; i < 10000 && !shutDownCompleted; ++i) { + } +} +int cb_usb_shutdown() +{ + SYS_USBHS_Shutdown(); + SYS_Reset_Periph(SYS_RESET_USB); + device_deinit(); + LOG_DEBUG("usb", "shutdown complete"); + shutDownCompleted = true; + 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); +} + +static void device_deinit() +{ + esb_cfg_handler deinit = s_device.deinit; + s_device.deinit = NULL; + s_device.configure = NULL; + s_device.deconfigure = NULL; + if (deinit) { + LOG_DEBUG("usb", "deinit"); + deinit(&s_device); + } +} + +//de-configure last device, if any +static volatile bool s_configured = false; +static bool device_deconfigure() +{ + if (s_configured) { + s_configured = false; + if (s_device.deconfigure) { + LOG_DEBUG("usb", "deconfigure"); + enum_clearconfig(); + s_device.deconfigure(&s_device); + return true; + } + } + return false; +} + +static int device_configure() +{ + if (!s_configured && s_device.configure) { + s_configured = true; + LOG_DEBUG("usb", "configure"); + return s_device.configure(&s_device); + } + return 0; +} + +static volatile bool s_connected = false; +static void disconnect() +{ + if (s_connected) { + LOG_DEBUG("usb", "disconnect"); + s_connected = false; + usb_disconnect(); + // SYS_Reset_Periph(SYS_RESET_USB); + mxc_delay(10000); + } +} +static void connect() +{ + s_connected = true; + usb_connect(); +} + +static volatile bool s_initialized = false; +void esb_deinit(void) +{ + if (s_initialized) { + s_initialized = false; + disconnect(); + device_deconfigure(); + do_usb_shutdown(); + device_deinit(); + } +} +int esb_init(struct esb_config *cfg) +{ + esb_deinit(); + if (cfg == NULL) { + return 0; + } + if (timerWakeup == NULL) { + timerWakeup = xTimerCreateStatic( + "timerWakeup", /* name */ + pdMS_TO_TICKS(50), /* period/time */ + pdFALSE, /* auto reload */ + NULL, /* timer ID */ + cb_timerReset, + &x); + } + + maxusb_cfg_options_t usb_opts; + + /* Initialize state */ + suspended = 0; +#ifdef USE_REMOTE_WAKE_ENABLE + remote_wake_en = 0; +#endif + + /* 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("usb", "usb_init() failed"); + return -EIO; + } + + /* Initialize the enumeration module */ + if (enum_init() != 0) { + do_usb_shutdown(); + LOG_ERR("usb", "enum_init() failed"); + return -EIO; + } + + LOG_INFO("usb", "initializing device %s", cfg->name); + if (cfg->init(cfg)) { + enum_clearconfig(); + do_usb_shutdown(); + LOG_ERR("usb", "device init failed"); + return -EIO; + } + + s_initialized = true; + s_device = *cfg; + + enum_register_descriptor( + ENUM_DESC_DEVICE, (uint8_t *)cfg->descriptors->device, 0 + ); + enum_register_descriptor( + ENUM_DESC_CONFIG, (uint8_t *)cfg->descriptors->config, 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); + 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_setconfig, NULL); +#ifdef USE_REMOTE_WAKE_ENABLE + /* Handle feature set/clear */ + enum_register_callback(ENUM_SETFEATURE, cb_usb_setfeature, NULL); + enum_register_callback(ENUM_CLRFEATURE, cb_usb_clrfeature, NULL); +#endif + + /* Register callbacks */ + usb_event_enable(MAXUSB_EVENT_NOVBUS, cb_usb_event, NULL); + usb_event_enable(MAXUSB_EVENT_VBUS, cb_usb_event, NULL); + + /* Start with USB in low power mode */ + usb_app_sleep(); + /* TODO: Fix priority */ + NVIC_SetPriority(USB_IRQn, 6); + NVIC_EnableIRQ(USB_IRQn); + + return 0; +} + +static int cb_usb_setconfig(usb_setup_pkt *sud, void *cbdata) +{ + LOG_DEBUG("usb", "setconfig %d", sud->wValue); + if (sud->wValue == s_device.descriptors->config->bConfigurationValue) { + return device_configure(); + } else if (sud->wValue == 0) { + device_deconfigure(); + return 0; + } + return -1; +} + +#ifdef USE_REMOTE_WAKE_ENABLE +static int cb_usb_setfeature(usb_setup_pkt *sud, void *cbdata) +{ + if (sud->wValue == FEAT_REMOTE_WAKE) { + remote_wake_en = 1; + return 0; + } + return -1; +} + +static int cb_usb_clrfeature(usb_setup_pkt *sud, void *cbdata) +{ + if (sud->wValue == FEAT_REMOTE_WAKE) { + remote_wake_en = 0; + return 0; + } + return -1; +} +#endif + +/******************************************************************************/ +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 cb_timerReset(TimerHandle_t t) +{ + (void)t; + int s = usb_get_status(); + LOG_DEBUG("usb", "cb_timerReset %08x", s); + + LOG_DEBUG("usb", "SYS_USBHS_Shutdown"); + SYS_USBHS_Shutdown(); + LOG_DEBUG("usb", "SYS_Reset_Periph"); + SYS_Reset_Periph(SYS_RESET_USB); + + //copy-paste from esb_init(), need to refactor + maxusb_cfg_options_t usb_opts; + 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("usb", "usb_init() failed"); + return; + } + usb_event_enable(MAXUSB_EVENT_NOVBUS, cb_usb_event, NULL); + usb_event_enable(MAXUSB_EVENT_VBUS, cb_usb_event, NULL); +} + +static void scheduleReset() +{ + LOG_DEBUG("usb", "scheduleReset"); + xTimerChangePeriodFromISR(timerWakeup, pdMS_TO_TICKS(500), NULL); +} + +#if LOG_ENABLE_DEBUG +static const char *maxusb_event_string(maxusb_event_t evt) +{ + static const char *names[MAXUSB_NUM_EVENTS] = { + [MAXUSB_EVENT_DPACT] = "DPACT", + [MAXUSB_EVENT_RWUDN] = "RWUDN", + [MAXUSB_EVENT_BACT] = "BACT", + [MAXUSB_EVENT_BRST] = "BRST", + [MAXUSB_EVENT_SUSP] = "SUSP", + [MAXUSB_EVENT_NOVBUS] = "NOVBUS", + [MAXUSB_EVENT_VBUS] = "VBUS", + [MAXUSB_EVENT_BRSTDN] = "BRSTDN", + [MAXUSB_EVENT_SUDAV] = "SUDAV", + }; + return evt < MAXUSB_NUM_EVENTS ? names[evt] : "<INVALID>"; +} +#endif + +/******************************************************************************/ +static int cb_usb_event(maxusb_event_t evt, void *data) +{ + static int s_VBUS_SUSP_sequence = 0; + LOG_DEBUG("usb", "event %s (%d)\n", maxusb_event_string(evt), 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(); + device_deconfigure(); + usb_app_sleep(); + break; + case MAXUSB_EVENT_VBUS: + s_VBUS_SUSP_sequence = 1; + 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); + connect(); + usb_app_sleep(); + break; + case MAXUSB_EVENT_BRST: + usb_app_wakeup(); + device_deconfigure(); + suspended = 0; + break; + case MAXUSB_EVENT_SUSP: + if (!s_VBUS_SUSP_sequence) { + scheduleReset(); + } + s_VBUS_SUSP_sequence = 0; + //usb_app_sleep(); + break; + case MAXUSB_EVENT_DPACT: + usb_app_wakeup(); + break; + default: + break; + } + + return 0; +} diff --git a/epicardium/usb/epc_usb.h b/epicardium/usb/epc_usb.h new file mode 100644 index 0000000000000000000000000000000000000000..c6917bd328b270f240ab38a7c8e11f10aa4fea21 --- /dev/null +++ b/epicardium/usb/epc_usb.h @@ -0,0 +1,32 @@ +#ifndef EPICARDIUM_USB_EPC_USB_H_INCLUDED +#define EPICARDIUM_USB_EPC_USB_H_INCLUDED + +/** + * + * EPC - it's not just a Universal Serial Bus, + * it's an Epic Serial Bus! + */ + + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +struct esb_config; +typedef int (*esb_cfg_handler)(struct esb_config* self); + +struct esb_config { + const char* name; + esb_cfg_handler init; + esb_cfg_handler configure; + esb_cfg_handler deconfigure; + esb_cfg_handler deinit; + + struct esb_device_descriptors* descriptors; + void* deviceData; +}; + +int esb_init(struct esb_config* cfg); +void esb_deinit(void); + +#endif//EPICARDIUM_USB_EPC_USB_H_INCLUDED diff --git a/epicardium/usb/mass_storage.c b/epicardium/usb/mass_storage.c new file mode 100644 index 0000000000000000000000000000000000000000..b3c6c0f6cd04efb5cf02e8eb898b3eef7b02efe3 --- /dev/null +++ b/epicardium/usb/mass_storage.c @@ -0,0 +1,55 @@ +/** + * backend-independent implementation of the mass storage device + * services MSC API and esb_cfg API + */ + +#include "usb/mass_storage.h" +#include "usb/epc_usb.h" +#include "usb/descriptors.h" + +#include "modules/log.h" + +#include "msc.h" +#include "usb.h" +#include "max32665.h" + +#define VENDOR_STRING "CCC" +#define PRODUCT_STRING "card10" +#define VERSION_STRING "1.0" + +static inline struct config_descriptor_msc *descriptors(struct esb_config *self) +{ + return self->descriptors->msc; +} + +int esb_msc_configure(struct esb_config *self) +{ + LOG_DEBUG("msc", "configure"); + struct config_descriptor_msc *dsc = descriptors(self); + const msc_cfg_t msc_cfg = { + dsc->endpoint_out.bEndpointAddress, + MXC_USBHS_MAX_PACKET, /* OUT max packet size */ + dsc->endpoint_in.bEndpointAddress & 0x0f, + MXC_USBHS_MAX_PACKET, /* IN max packet size */ + }; + return msc_configure(&msc_cfg); +} + +int esb_msc_deconfigure(struct esb_config *self) +{ + LOG_DEBUG("msc", "deconfigure"); + (void)self; + return msc_deconfigure(); +} + +int esb_msc_init(struct esb_config *self) +{ + LOG_DEBUG("msc", "init"); + static const msc_idstrings_t ids = { VENDOR_STRING, + PRODUCT_STRING, + VERSION_STRING }; + + msc_mem_t *mem = self->deviceData; + struct config_descriptor_msc *dsc = descriptors(self); + return msc_init(&dsc->msc_interface, &ids, mem); +} diff --git a/epicardium/usb/mass_storage.h b/epicardium/usb/mass_storage.h new file mode 100644 index 0000000000000000000000000000000000000000..77c36bcecd433be56305a94a180945b6dba38dff --- /dev/null +++ b/epicardium/usb/mass_storage.h @@ -0,0 +1,10 @@ +#ifndef EPICARDIUM_USB_MSC_H_INCLUDED +#define EPICARDIUM_USB_MSC_H_INCLUDED + +#include "usb/epc_usb.h" + +int esb_msc_configure(struct esb_config* self); +int esb_msc_deconfigure(struct esb_config* self); +int esb_msc_init(struct esb_config* self); + +#endif//EPICARDIUM_USB_MSC_H_INCLUDED