mirror of
https://github.com/mist-devel/mist-firmware.git
synced 2026-03-10 12:18:39 +00:00
641 lines
20 KiB
C
641 lines
20 KiB
C
/*
|
|
This file is part of MiST-firmware
|
|
|
|
MiST-firmware is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
MiST-firmware is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "hardware.h"
|
|
#include "arch/barriers.h"
|
|
#include "usb/usb.h"
|
|
#include "usbdev.h"
|
|
#include "utils.h"
|
|
#include "debug.h"
|
|
|
|
#define WORD(a) (a)&0xff, ((a)>>8)&0xff
|
|
|
|
// Private members
|
|
unsigned int currentRcvBank;
|
|
|
|
static const char langDescriptor[] = {
|
|
/* Language descriptor */
|
|
0x04, // length of descriptor in bytes
|
|
0x03, // descriptor type
|
|
WORD(0x0409) // language index (0x0409 = US-English)
|
|
};
|
|
|
|
static const char devDescriptor[] = {
|
|
/* Device descriptor */
|
|
0x12, // bLength
|
|
0x01, // bDescriptorType
|
|
WORD(0x0200), // bcdUSBL
|
|
0xEF, // bDeviceClass: MISCELLANEOUS class code
|
|
0x02, // bDeviceSubclass: Common Class
|
|
0x01, // bDeviceProtocol: Interface Association Descriptor
|
|
EP0_SIZE, // bMaxPacketSize0
|
|
WORD(0x1c40), // idVendorL
|
|
WORD(0x0538), // idProductL
|
|
WORD(0x0001), // bcdDeviceL
|
|
0x01, // iManufacturer
|
|
0x02, // iProduct
|
|
0x00, // SerialNumber
|
|
0x01 // bNumConfigs
|
|
};
|
|
|
|
static const char cfgDescriptor[] = {
|
|
/* ============== CONFIGURATION 1 =========== */
|
|
/* Configuration 1 descriptor */
|
|
0x09, // CbLength
|
|
0x02, // CbDescriptorType
|
|
WORD(0x6A), // CwTotalLength 2 EP + Control
|
|
0x03, // CbNumInterfaces
|
|
0x01, // CbConfigurationValue
|
|
0x00, // CiConfiguration
|
|
0x80, // CbmAttributes 0xA0
|
|
200, // CMaxPower = 400mA
|
|
|
|
/* IAD */
|
|
0x08, // bLength
|
|
0x0B, // bDescriptorType IAD
|
|
0x00, // bFirstInterface
|
|
0x02, // bInterfaceCount
|
|
0x02, // bDeviceClass: CDC class code
|
|
0x02, // bDeviceSubclass: CDC class sub code
|
|
0x00, // bDeviceProtocol: CDC Device protocol
|
|
0x00, // iFunction
|
|
|
|
/********************************************************/
|
|
/* Communication Class Interface Descriptor Requirement */
|
|
/********************************************************/
|
|
0x09, // bLength
|
|
0x04, // bDescriptorType
|
|
0x00, // bInterfaceNumber
|
|
0x00, // bAlternateSetting
|
|
0x01, // bNumEndpoints
|
|
0x02, // bInterfaceClass
|
|
0x02, // bInterfaceSubclass
|
|
0x00, // bInterfaceProtocol
|
|
0x00, // iInterface
|
|
|
|
/* Header Functional Descriptor */
|
|
0x05, // bFunction Length
|
|
0x24, // bDescriptor type: CS_INTERFACE
|
|
0x00, // bDescriptor subtype: Header Func Desc
|
|
WORD(0x0110), // bcdCDC:1.1
|
|
|
|
/* ACM Functional Descriptor */
|
|
0x04, // bFunctionLength
|
|
0x24, // bDescriptor Type: CS_INTERFACE
|
|
0x02, // bDescriptor Subtype: ACM Func Desc
|
|
0x00, // bmCapabilities
|
|
|
|
/* Union Functional Descriptor */
|
|
0x05, // bFunctionLength
|
|
0x24, // bDescriptorType: CS_INTERFACE
|
|
0x06, // bDescriptor Subtype: Union Func Desc
|
|
0x00, // bMasterInterface: Communication Class Interface
|
|
0x01, // bSlaveInterface0: Data Class Interface
|
|
|
|
/* Call Management Functional Descriptor */
|
|
0x05, // bFunctionLength
|
|
0x24, // bDescriptor Type: CS_INTERFACE
|
|
0x01, // bDescriptor Subtype: Call Management Func Desc
|
|
0x00, // bmCapabilities: D1 + D0
|
|
0x01, // bDataInterface: Data Class Interface 1
|
|
|
|
/* Endpoint 3 descriptor */
|
|
0x07, // bLength
|
|
0x05, // bDescriptorType
|
|
0x83, // bEndpointAddress, Endpoint 03 - IN
|
|
0x03, // bmAttributes INT
|
|
WORD(0x08), // wMaxPacketSize
|
|
INTERVAL, // bInterval
|
|
|
|
/* Data Class Interface Descriptor Requirement */
|
|
0x09, // bLength
|
|
0x04, // bDescriptorType
|
|
0x01, // bInterfaceNumber
|
|
0x00, // bAlternateSetting
|
|
0x02, // bNumEndpoints
|
|
0x0A, // bInterfaceClass
|
|
0x00, // bInterfaceSubclass
|
|
0x00, // bInterfaceProtocol
|
|
0x06, // iInterface
|
|
|
|
/* First alternate setting */
|
|
/* Endpoint 1 descriptor */
|
|
0x07, // bLength
|
|
0x05, // bDescriptorType
|
|
0x81, // bEndpointAddress, Endpoint 01 - IN
|
|
0x02, // bmAttributes BULK
|
|
WORD(BULK_SIZE), // wMaxPacketSize
|
|
0x00, // bInterval
|
|
|
|
/* Endpoint 2 descriptor */
|
|
0x07, // bLength
|
|
0x05, // bDescriptorType
|
|
0x02, // bEndpointAddress, Endpoint 02 - OUT
|
|
0x02, // bmAttributes BULK
|
|
WORD(BULK_SIZE), // wMaxPacketSize
|
|
0x00, // bInterval
|
|
|
|
/* IAD */
|
|
0x08, // bLength
|
|
0x0B, // bDescriptorType IAD
|
|
0x02, // bFirstInterface
|
|
0x01, // bInterfaceCount
|
|
0x08, // bDeviceClass: Mass storage class
|
|
0x06, // bDeviceSubclass:
|
|
0x50, // bDeviceProtocol: Bulk-only transport
|
|
0x00, // iFunction
|
|
|
|
/*******************************************************/
|
|
/* Mass Storage Class Interface Descriptor Requirement */
|
|
/*******************************************************/
|
|
0x09, // bLength
|
|
0x04, // bDescriptorType
|
|
0x02, // bInterfaceNumber
|
|
0x00, // bAlternateSetting
|
|
0x02, // bNumEndpoints
|
|
0x08, // bInterfaceClass
|
|
0x06, // bInterfaceSubclass: SCSI transparent command set
|
|
0x50, // bInterfaceProtocol: USB Mass Storage Class Bulk-Only
|
|
0x00, // iInterface
|
|
|
|
/* Endpoint 4 descriptor - Bulk-in */
|
|
0x07, // bLength,
|
|
0x05, // bDescriptorType
|
|
0x84, // bEndpointAddress, Endpoint 04 - Bulk-in
|
|
0x02, // bmAttributes BULK
|
|
WORD(BULK_SIZE), // wMaxPacketSize
|
|
0x00, // bInterval
|
|
|
|
/* Endpoint 5 descriptor - Bulk-out */
|
|
0x07, // bLength,
|
|
0x05, // bDescriptorType
|
|
0x05, // bEndpointAddress, Endpoint 05 - Bulk-out
|
|
0x02, // bmAttributes BULK
|
|
WORD(BULK_SIZE), // wMaxPacketSize
|
|
0x00 // bInterval
|
|
};
|
|
|
|
static const char idProduct[] = {0x10, 0x03, 'S',0,'i',0,'D',0,'i',0,'1',0,'2',0,'8',0};
|
|
static const char idManufacturer[] = {0x28, 0x03, 'M',0,'a',0,'n',0,'u',0,'F',0,'e',0,'r',0,'H',0,'i',0,'/',0,'S',0,'l',0,'i',0,'n',0,'g',0,'s',0,'h',0,'o',0,'t',0};
|
|
|
|
/* CDC Class Specific Request Code */
|
|
#define USB_SET_LINE_CODING 0x20
|
|
#define USB_GET_LINE_CODING 0x21
|
|
#define USB_SET_CONTROL_LINE_STATE 0x22
|
|
|
|
/* Mass Storage Class Specific Request Code */
|
|
#define STORAGE_RESET 0xFF
|
|
#define STORAGE_GETMAXLUN 0xFE
|
|
|
|
typedef struct {
|
|
unsigned int dwDTERRate;
|
|
char bCharFormat;
|
|
char bParityType;
|
|
char bDataBits;
|
|
} CDC_LINE_CODING_t;
|
|
|
|
static CDC_LINE_CODING_t line = {
|
|
115200, // baudrate
|
|
0, // 1 Stop Bit
|
|
0, // None Parity
|
|
8}; // 8 Data bits
|
|
|
|
void usb_dump_regs() {
|
|
usb_debugf("DEVCTRL: %08x", USBHS->USBHS_DEVCTRL);
|
|
usb_debugf("DEVISR: %08x", USBHS->USBHS_DEVISR);
|
|
usb_debugf("DEVIMR: %08x", USBHS->USBHS_DEVIMR);
|
|
usb_debugf("DEVEPT: %08x", USBHS->USBHS_DEVEPT);
|
|
usb_debugf("DEVEPTCFG0: %08x", USBHS->USBHS_DEVEPTCFG[0]);
|
|
usb_debugf("DEVEPTISR0: %08x", USBHS->USBHS_DEVEPTISR[0]);
|
|
usb_debugf("DEVEPTIMR0: %08x", USBHS->USBHS_DEVEPTIMR[0]);
|
|
usb_debugf("DEVEPTCFG1: %08x", USBHS->USBHS_DEVEPTCFG[1]);
|
|
usb_debugf("DEVEPTISR1: %08x", USBHS->USBHS_DEVEPTISR[1]);
|
|
usb_debugf("DEVEPTIMR1: %08x", USBHS->USBHS_DEVEPTIMR[1]);
|
|
usb_debugf("DEVEPTCFG2: %08x", USBHS->USBHS_DEVEPTCFG[2]);
|
|
usb_debugf("DEVEPTISR2: %08x", USBHS->USBHS_DEVEPTISR[2]);
|
|
usb_debugf("DEVEPTIMR2: %08x", USBHS->USBHS_DEVEPTIMR[2]);
|
|
usb_debugf("DEVEPTCFG3: %08x", USBHS->USBHS_DEVEPTCFG[3]);
|
|
usb_debugf("DEVEPTISR3: %08x", USBHS->USBHS_DEVEPTISR[3]);
|
|
usb_debugf("DEVEPTIMR3: %08x", USBHS->USBHS_DEVEPTIMR[3]);
|
|
}
|
|
|
|
// Maximum transfer size on USB DMA
|
|
#define EPT_VIRTUAL_SIZE 0x8000
|
|
|
|
static void usb_dma_transfer(uint8_t ep, uint8_t* data, uint32_t size)
|
|
{
|
|
UsbhsDevDma* devdma = &USBHS->USBHS_DEVDMA[ep-1];
|
|
devdma->USBHS_DEVDMAADDRESS = (uint32_t) data;
|
|
devdma->USBHS_DEVDMASTATUS = devdma->USBHS_DEVDMASTATUS; // clear pending bits
|
|
devdma->USBHS_DEVDMACONTROL = 0;
|
|
devdma->USBHS_DEVDMACONTROL = USBHS_DEVDMACONTROL_BURST_LCK | USBHS_DEVDMACONTROL_CHANN_ENB | USBHS_DEVDMACONTROL_BUFF_LENGTH(size);
|
|
while (devdma->USBHS_DEVDMASTATUS & USBHS_DEVDMASTATUS_CHANN_ACT);
|
|
}
|
|
|
|
static void usb_write_fifo_buffer(uint8_t ep, uint8_t* data, uint32_t size)
|
|
{
|
|
if (ep) { // EP0 doesn't have DMA
|
|
usb_dma_transfer(ep, data, size);
|
|
} else {
|
|
volatile uint8_t *fifo = ((volatile uint8_t*)USBHS_RAM_ADDR) + EPT_VIRTUAL_SIZE * ep;
|
|
dsb(); isb();
|
|
for (; size; size--)
|
|
*(fifo++) = *(data++);
|
|
dsb(); isb();
|
|
}
|
|
}
|
|
|
|
static void usb_read_fifo_buffer(uint8_t ep, uint8_t* data, uint32_t size)
|
|
{
|
|
if (ep) { // EP0 doesn't have DMA
|
|
usb_dma_transfer(ep, data, size);
|
|
} else {
|
|
volatile uint8_t *fifo = ((volatile uint8_t*)USBHS_RAM_ADDR) + EPT_VIRTUAL_SIZE * ep;
|
|
dmb();
|
|
for (; size; size--)
|
|
*(data++) = *(fifo++);
|
|
}
|
|
}
|
|
|
|
static uint32_t usb_get_fifo_byte_cnt(uint8_t ep)
|
|
{
|
|
return ((USBHS->USBHS_DEVEPTISR[ep] & USBHS_DEVEPTISR_BYCT_Msk) >> USBHS_DEVEPTISR_BYCT_Pos);
|
|
}
|
|
|
|
typedef struct {
|
|
uint8_t state;
|
|
uint8_t *buf;
|
|
uint16_t size;
|
|
} usb_dev_ep_t;
|
|
|
|
static usb_dev_ep_t usb_ep0;
|
|
static uint8_t maxlun = 0;
|
|
static bool enumerated = false;
|
|
|
|
static void usb_irq_handler(void) {
|
|
uint32_t isr = USBHS->USBHS_DEVISR;
|
|
isr &= USBHS->USBHS_DEVIMR;
|
|
usb_debugf("usb_irq_handler isr=%08x", isr);
|
|
//usb_dump_regs();
|
|
|
|
if (isr & USBHS_DEVISR_EORST) {
|
|
usb_debugf("EORST");
|
|
usb_ep0.state = 0;
|
|
enumerated = false;
|
|
USBHS->USBHS_DEVICR = USBHS_DEVICR_EORSTC; // ack
|
|
USBHS->USBHS_DEVIER = USBHS_DEVIER_SUSPES; // enable suspend int
|
|
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_RXSTPES;
|
|
|
|
// setup endpoint 1 for IN requests
|
|
USBHS->USBHS_DEVEPTCFG[1] = USBHS_DEVEPTCFG_ALLOC | USBHS_DEVEPTCFG_EPBK_1_BANK |
|
|
BULK_SIZE_CONF | USBHS_DEVEPTCFG_EPDIR_IN | USBHS_DEVEPTCFG_EPTYPE_BLK | USBHS_DEVEPTCFG_AUTOSW;
|
|
|
|
// setup endpoint 2 for OUT requests
|
|
USBHS->USBHS_DEVEPTCFG[2] = USBHS_DEVEPTCFG_ALLOC | USBHS_DEVEPTCFG_EPBK_1_BANK |
|
|
BULK_SIZE_CONF | USBHS_DEVEPTCFG_EPDIR_OUT | USBHS_DEVEPTCFG_EPTYPE_BLK | USBHS_DEVEPTCFG_AUTOSW;
|
|
|
|
// setup endpoint 3 for INTRPT requests
|
|
USBHS->USBHS_DEVEPTCFG[3] = USBHS_DEVEPTCFG_ALLOC | USBHS_DEVEPTCFG_EPBK_1_BANK |
|
|
EP0_SIZE_CONF | USBHS_DEVEPTCFG_EPDIR_IN | USBHS_DEVEPTCFG_EPTYPE_INTRPT | USBHS_DEVEPTCFG_AUTOSW;
|
|
USBHS->USBHS_DEVEPTIER[3] = USBHS_DEVEPTIER_TXINES;
|
|
|
|
// setup endpoint 4 for IN requests
|
|
USBHS->USBHS_DEVEPTCFG[4] = USBHS_DEVEPTCFG_ALLOC | USBHS_DEVEPTCFG_EPBK_1_BANK |
|
|
BULK_SIZE_CONF | USBHS_DEVEPTCFG_EPDIR_IN | USBHS_DEVEPTCFG_EPTYPE_BLK | USBHS_DEVEPTCFG_AUTOSW;
|
|
|
|
// setup endpoint 5 for OUT requests
|
|
USBHS->USBHS_DEVEPTCFG[5] = USBHS_DEVEPTCFG_ALLOC | USBHS_DEVEPTCFG_EPBK_1_BANK |
|
|
BULK_SIZE_CONF | USBHS_DEVEPTCFG_EPDIR_OUT | USBHS_DEVEPTCFG_EPTYPE_BLK | USBHS_DEVEPTCFG_AUTOSW;
|
|
|
|
USBHS->USBHS_DEVEPT = USBHS_DEVEPT_EPEN0 | USBHS_DEVEPT_EPEN1 | USBHS_DEVEPT_EPEN2 | USBHS_DEVEPT_EPEN3 | USBHS_DEVEPT_EPEN4 | USBHS_DEVEPT_EPEN5;
|
|
USBHS->USBHS_DEVIER = USBHS_DEVIER_SUSPES | USBHS_DEVIER_WAKEUPES; // enable suspend int
|
|
} else if (isr & USBHS_DEVISR_WAKEUP) {
|
|
usb_debugf("WAKEUP");
|
|
USBHS->USBHS_CTRL &= ~USBHS_CTRL_FRZCLK;
|
|
USBHS->USBHS_DEVICR = USBHS_DEVICR_WAKEUPC; // ack
|
|
USBHS->USBHS_DEVIDR = USBHS_DEVIDR_WAKEUPEC; // disable wakeup int
|
|
USBHS->USBHS_DEVIER = USBHS_DEVIER_SUSPES; // enable suspend int
|
|
} else if (isr & USBHS_DEVISR_SUSP) {
|
|
usb_debugf("SUSPEND");
|
|
USBHS->USBHS_DEVICR = USBHS_DEVICR_SUSPC; // ack
|
|
USBHS->USBHS_DEVIDR = USBHS_DEVIDR_SUSPEC; // disable suspend int
|
|
USBHS->USBHS_DEVIER = USBHS_DEVIER_WAKEUPES; // enable wakeup int
|
|
USBHS->USBHS_CTRL |= USBHS_CTRL_FRZCLK;
|
|
} else if (isr & 0x1000) {
|
|
// ctrl endpoint
|
|
uint32_t ctrl_isr = USBHS->USBHS_DEVEPTISR[0];
|
|
uint32_t fifo_cnt;
|
|
uint8_t buf[64];
|
|
|
|
if (ctrl_isr & USBHS_DEVEPTISR_RXSTPI) {
|
|
usb_debugf("RXSTPI");
|
|
fifo_cnt = MIN(usb_get_fifo_byte_cnt(0), sizeof(buf));
|
|
usb_read_fifo_buffer(0, buf, fifo_cnt);
|
|
//hexdump(buf, fifo_cnt, 0);
|
|
if (fifo_cnt >= 8) {
|
|
uint16_t maxlen = buf[6] + (buf[7]<<8);
|
|
switch (buf[1]) { // bRequest
|
|
case USB_REQUEST_GET_DESCRIPTOR:
|
|
switch(buf[3]) {
|
|
case USB_DESCRIPTOR_DEVICE:
|
|
usb_debugf("send dev descriptor");
|
|
usb_ep0.buf = (char*)devDescriptor;
|
|
usb_ep0.size = MIN(sizeof(devDescriptor), maxlen);
|
|
usb_ep0.state = 1;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
case USB_DESCRIPTOR_CONFIGURATION:
|
|
usb_debugf("send conf descriptor");
|
|
usb_ep0.buf = (char*)cfgDescriptor;
|
|
usb_ep0.size = MIN(sizeof(cfgDescriptor), maxlen);
|
|
usb_ep0.state = 1;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
case USB_DESCRIPTOR_STRING:
|
|
switch (buf[2]) {
|
|
case 0:
|
|
usb_debugf("send lang descriptor");
|
|
usb_ep0.buf = (char*)langDescriptor;
|
|
usb_ep0.size = MIN(sizeof(langDescriptor), maxlen);
|
|
usb_ep0.state = 1;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
case 1:
|
|
usb_debugf("send manufacturer descriptor");
|
|
usb_ep0.buf = (char*)idManufacturer;
|
|
usb_ep0.size = MIN(sizeof(idManufacturer), maxlen);
|
|
usb_ep0.state = 1;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
case 2:
|
|
usb_debugf("send product descriptor");
|
|
usb_ep0.buf = (char*)idProduct;
|
|
usb_ep0.size = MIN(sizeof(idProduct), maxlen);
|
|
usb_ep0.state = 1;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case USB_REQUEST_SET_ADDRESS:
|
|
usb_debugf("set address to %d", buf[2]);
|
|
USBHS->USBHS_DEVCTRL |= USBHS_DEVCTRL_UADD(buf[2]);
|
|
usb_ep0.buf = 0;
|
|
usb_ep0.size = 0;
|
|
usb_ep0.state = 2;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
case USB_REQUEST_SET_CONFIGURATION:
|
|
usb_debugf("set config to %d", buf[2]);
|
|
usb_ep0.buf = 0;
|
|
usb_ep0.size = 0;
|
|
usb_ep0.state = 1;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
case USB_SET_LINE_CODING:
|
|
usb_debugf("set line coding");
|
|
usb_ep0.buf = (char*)&line;
|
|
usb_ep0.size = MIN(sizeof(CDC_LINE_CODING_t), maxlen);
|
|
usb_ep0.state = 4;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_RXOUTES;
|
|
break;
|
|
case USB_GET_LINE_CODING:
|
|
usb_debugf("get line coding");
|
|
usb_ep0.buf = (char*)devDescriptor;
|
|
usb_ep0.size = MIN(sizeof(CDC_LINE_CODING_t), maxlen);
|
|
usb_ep0.state = 1;
|
|
break;
|
|
case USB_SET_CONTROL_LINE_STATE:
|
|
usb_debugf("set ctrl line state");
|
|
usb_ep0.buf = 0;
|
|
usb_ep0.size = 0;
|
|
usb_ep0.state = 1;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
// handle Mass Storage class requests
|
|
case STORAGE_RESET:
|
|
usb_debugf("storage reset");
|
|
usb_ep0.buf = 0;
|
|
usb_ep0.size = 0;
|
|
usb_ep0.state = 1;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
case STORAGE_GETMAXLUN:
|
|
usb_debugf("getmaxlun");
|
|
usb_ep0.buf = &maxlun;
|
|
usb_ep0.size = sizeof(maxlun);
|
|
usb_ep0.state = 1;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_TXINES;
|
|
break;
|
|
default:
|
|
usb_debugf("unhandled: %02x", buf[1]);
|
|
//TODO: stall
|
|
}
|
|
}
|
|
USBHS->USBHS_DEVEPTICR[0] = USBHS_DEVEPTICR_RXSTPIC;
|
|
}
|
|
|
|
if ((ctrl_isr & USBHS_DEVEPTISR_RXOUTI) && (usb_ep0.state & 0x04)) {
|
|
uint32_t readbytes = MIN(usb_ep0.size, usb_get_fifo_byte_cnt(0));
|
|
usb_debugf("RXOUTI readbytes=%d",readbytes);
|
|
if (readbytes) {
|
|
usb_read_fifo_buffer(0, usb_ep0.buf, readbytes);
|
|
//hexdump(usb_ep[0].buf, readbytes, 0);
|
|
usb_ep0.buf += readbytes;
|
|
usb_ep0.size -= readbytes;
|
|
} else {
|
|
USBHS->USBHS_DEVEPTIDR[0] = USBHS_DEVEPTIDR_RXOUTEC;
|
|
usb_ep0.state = 0;
|
|
}
|
|
USBHS->USBHS_DEVEPTICR[0] = USBHS_DEVEPTICR_RXOUTIC; // ack
|
|
}
|
|
|
|
if ((ctrl_isr & USBHS_DEVEPTISR_TXINI) && (usb_ep0.state & 0x03)) {
|
|
uint8_t writebytes = MIN(usb_ep0.size, EP0_SIZE);
|
|
usb_debugf("TXINI writebytes=%d", writebytes);
|
|
|
|
if (writebytes) {
|
|
usb_write_fifo_buffer(0, usb_ep0.buf, writebytes);
|
|
usb_ep0.buf += writebytes;
|
|
usb_ep0.size -= writebytes;
|
|
}
|
|
USBHS->USBHS_DEVEPTICR[0] = USBHS_DEVEPTICR_TXINIC; // ack
|
|
|
|
if (usb_ep0.state == 2) {
|
|
WaitTimer(1);
|
|
USBHS->USBHS_DEVCTRL |= USBHS_DEVCTRL_ADDEN;
|
|
enumerated = true;
|
|
}
|
|
if (!writebytes) {
|
|
// end of transfer, disable interrupt
|
|
USBHS->USBHS_DEVEPTIDR[0] = USBHS_DEVEPTIDR_TXINEC;
|
|
usb_ep0.state = 0;
|
|
}
|
|
}
|
|
} else if (isr & 0x8000) {
|
|
// intrpt endpoint
|
|
uint32_t intrpt_isr = USBHS->USBHS_DEVEPTISR[3];
|
|
usb_debugf("INTRPT isr=%08x", intrpt_isr);
|
|
USBHS->USBHS_DEVEPTICR[3] = USBHS_DEVEPTICR_TXINIC; // ack
|
|
} else if (isr & 0xff) {
|
|
USBHS->USBHS_DEVICR = 0xff; // ack
|
|
}
|
|
}
|
|
|
|
void usb_dev_open(void) {
|
|
|
|
puts(__FUNCTION__);
|
|
|
|
usb_ep0.state = 0;
|
|
enumerated = false;
|
|
|
|
PMC->PMC_PCER1 = 1 << (ID_USBHS - 32);
|
|
|
|
// configure 48MHz USB PLL
|
|
PMC->PMC_USB = PMC_USB_USBDIV(PLLCLK / 48000000 - 1);
|
|
PMC->PMC_SCER = PMC_SCER_USBCLK;
|
|
|
|
#if USB_HS
|
|
// configure 480MHz UTMI PLL
|
|
UTMI->UTMI_CKTRIM = 0x1; // 16 MHZ UPLL source clock
|
|
PMC->CKGR_UCKR = CKGR_UCKR_UPLLEN | CKGR_UCKR_UPLLCOUNT(5);
|
|
while (!(PMC->PMC_SR & PMC_SR_LOCKU));
|
|
#endif
|
|
|
|
USBHS->USBHS_CTRL = USBHS_CTRL_UIMOD_DEVICE | USBHS_CTRL_USBE | USBHS_CTRL_VBUSHWC;
|
|
|
|
#if USB_HS
|
|
while (!(USBHS->USBHS_SR & USBHS_SR_CLKUSABLE));
|
|
#endif
|
|
|
|
// setup endpoint 0 for ctrl requests
|
|
USBHS->USBHS_DEVEPTCFG[0] = USBHS_DEVEPTCFG_ALLOC | USBHS_DEVEPTCFG_EPBK_1_BANK |
|
|
EP0_SIZE_CONF | USBHS_DEVEPTCFG_AUTOSW;
|
|
|
|
if (!(USBHS->USBHS_DEVEPTISR[0] & USBHS_DEVEPTISR_CFGOK)) {
|
|
usb_debugf("Error configuring endpoint 0");
|
|
return;
|
|
}
|
|
USBHS->USBHS_DEVEPT = USBHS_DEVEPT_EPEN0;
|
|
|
|
//usb_dump_regs();
|
|
|
|
// enable device interrupts
|
|
USBHS->USBHS_DEVIER = USBHS_DEVIER_PEP_0 | USBHS_DEVIER_PEP_1 | USBHS_DEVIER_PEP_2 | USBHS_DEVIER_PEP_3 | USBHS_DEVIER_PEP_4 | USBHS_DEVIER_PEP_5 |
|
|
USBHS_DEVIER_EORSTES;
|
|
USBHS->USBHS_DEVEPTIER[0] = USBHS_DEVEPTIER_RXSTPES;
|
|
// disable all host interrupts
|
|
USBHS->USBHS_HSTIER = 0;
|
|
USBHS->USBHS_HSTICR = 0xFF;
|
|
|
|
NVIC_SetVector(ID_USBHS, (uint32_t) &usb_irq_handler);
|
|
NVIC_EnableIRQ(ID_USBHS);
|
|
NVIC_SetPriority(ID_USBHS, 0xFFFFFFFF);
|
|
|
|
// activate USB
|
|
#if USB_HS
|
|
USBHS->USBHS_DEVCTRL = 0;
|
|
#else
|
|
USBHS->USBHS_DEVCTRL = USBHS_DEVCTRL_SPDCONF_LOW_POWER;
|
|
#endif
|
|
|
|
}
|
|
|
|
//*----------------------------------------------------------------------------
|
|
//* \fn usb_is_configured
|
|
//* \brief Test if the device is configured
|
|
//*----------------------------------------------------------------------------
|
|
static uint8_t usb_is_configured(void) {
|
|
return (!!(USBHS->USBHS_CTRL & USBHS_CTRL_USBE) && enumerated);
|
|
}
|
|
|
|
//*----------------------------------------------------------------------------
|
|
//* \fn usb_read
|
|
//* \brief Read available data from Endpoint OUT
|
|
//*----------------------------------------------------------------------------
|
|
uint16_t usb_read(uint8_t ep, char *pData, uint16_t length) {
|
|
uint16_t nbBytesRcv = 0;
|
|
|
|
if (usb_is_configured()) {
|
|
if (USBHS->USBHS_DEVEPTISR[ep] & USBHS_DEVEPTISR_RXOUTI) {
|
|
USBHS->USBHS_DEVEPTICR[ep] = USBHS_DEVEPTICR_RXOUTIC; // ack int
|
|
nbBytesRcv = usb_get_fifo_byte_cnt(ep);
|
|
//usb_debugf("received %d bytes on ep %d", nbBytesRcv, ep);
|
|
uint16_t read = nbBytesRcv;
|
|
while (read) {
|
|
uint16_t chunk = MIN(read, length);
|
|
usb_read_fifo_buffer(ep, pData, chunk);
|
|
read -= chunk;
|
|
pData += chunk;
|
|
}
|
|
USBHS->USBHS_DEVEPTIDR[ep] = USBHS_DEVEPTIDR_FIFOCONC; // ack fifo
|
|
}
|
|
}
|
|
return nbBytesRcv;
|
|
}
|
|
|
|
//*----------------------------------------------------------------------------
|
|
//* \fn usb_write
|
|
//* \brief Send through Endpoint IN
|
|
//*----------------------------------------------------------------------------
|
|
static uint16_t usb_write(uint8_t ep, const char *pData, uint16_t length) {
|
|
if(usb_is_configured()) {
|
|
while (length) {
|
|
uint16_t write = MIN(length, BULK_IN_SIZE);
|
|
while (!(USBHS->USBHS_DEVEPTISR[ep] & USBHS_DEVEPTISR_TXINI));
|
|
USBHS->USBHS_DEVEPTICR[ep] = USBHS_DEVEPTICR_TXINIC; // ack int
|
|
usb_write_fifo_buffer(ep, (char*)pData, write);
|
|
USBHS->USBHS_DEVEPTIDR[ep] = USBHS_DEVEPTIDR_FIFOCONC; // ack fifo
|
|
length -= write;
|
|
pData += write;
|
|
}
|
|
}
|
|
return length;
|
|
}
|
|
|
|
/////////////////
|
|
|
|
uint8_t usb_cdc_is_configured(void) {
|
|
return (usb_is_configured());
|
|
}
|
|
|
|
uint16_t usb_cdc_read(char *pData, uint16_t length) {
|
|
return usb_read(2, pData, length);
|
|
}
|
|
|
|
uint16_t usb_cdc_write(const char *pData, uint16_t length) {
|
|
return usb_write(1, pData, length);
|
|
}
|
|
|
|
uint8_t usb_storage_is_configured(void) {
|
|
return (usb_is_configured());
|
|
}
|
|
|
|
uint16_t usb_storage_read(char *pData, uint16_t length) {
|
|
return usb_read(5, pData, length);
|
|
}
|
|
|
|
uint16_t usb_storage_write(const char *pData, uint16_t length) {
|
|
return usb_write(4, pData, length);
|
|
}
|
|
|
|
void usb_dev_reconnect(void) {} |