Skip to content
Snippets Groups Projects
Commit 99298759 authored by rahix's avatar rahix
Browse files

Merge 'Epic Serial Bus' - USB Improvements

See merge request card10/firmware!107
parents ef12c8bb 9b0fd54f
No related branches found
No related tags found
No related merge requests found
Showing
with 1179 additions and 633 deletions
......@@ -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
......
/*******************************************************************************
* 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 */
/*******************************************************************************
* 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_ */
......@@ -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 */
......@@ -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;
}
......
......@@ -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',
......
......@@ -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);
......
......@@ -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");
}
/*
......
......@@ -21,4 +21,5 @@ module_sources = files(
'trng.c',
'vibra.c',
'watchdog.c',
'usb.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"
......
/**
*
* 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
/**
*
* 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);
}
}
#ifndef CDCACM_H
#define CDCACM_H
#include <stdint.h>
int cdcacm_init(void);
#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);
......
#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_ */
/**
*
*
* 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;
}
#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
/**
* 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);
}
#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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment