Major USB stack improvements. It now works at nearly the maximum speed for a full speed connection, and does seem stable.

Still not enabled by default, #define USE_ROCKBOX_USB is still required to enable it.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16360 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Frank Gevaerts 2008-02-20 22:54:26 +00:00
parent 6af732d17a
commit 07427592a9
12 changed files with 987 additions and 610 deletions

View File

@ -245,7 +245,7 @@ struct usb_string_descriptor {
uint8_t bDescriptorType;
uint16_t wString[]; /* UTF-16LE encoded */
} __attribute__ ((packed));
} __attribute__ ((packed,aligned(2)));
/* note that "string" zero is special, it holds language codes that
* the device supports, not Unicode characters.

View File

@ -19,6 +19,22 @@
#ifndef USB_CORE_H
#define USB_CORE_H
#ifndef BOOTLOADER
#define USB_THREAD
#ifdef USE_ROCKBOX_USB
//#define USB_SERIAL
//#define USB_BENCHMARK
#define USB_STORAGE
#else
#define USB_CHARGING_ONLY
#endif /* USE_ROCKBOX_USB */
#else
#define USB_CHARGING_ONLY
#endif /* BOOTLOADER */
#include "usb_ch9.h"
#if defined(CPU_PP)
@ -26,14 +42,31 @@
#define USB_IRAM_SIZE ((size_t)0xc000)
#endif
enum {
USB_CORE_QUIT,
USB_CORE_TRANSFER_COMPLETION
};
/* endpoints */
enum {
EP_CONTROL = 0,
EP_RX,
EP_TX,
NUM_ENDPOINTS
EP_CONTROL = 0,
#ifdef USB_STORAGE
EP_MASS_STORAGE,
#endif
#ifdef USB_SERIAL
EP_SERIAL,
#endif
#ifdef USB_CHARGING_ONLY
EP_CHARGING_ONLY,
#endif
#ifdef USB_BENCHMARK
EP_BENCHMARK,
#endif
NUM_ENDPOINTS
};
/* queue events */
#define USB_TRANSFER_COMPLETE 1
@ -48,7 +81,7 @@ extern int usb_max_pkt_size;
void usb_core_init(void);
void usb_core_exit(void);
void usb_core_control_request(struct usb_ctrlrequest* req);
void usb_core_transfer_complete(int endpoint, bool in);
void usb_core_transfer_complete(int endpoint, bool in, int status, int length);
void usb_core_bus_reset(void);
bool usb_core_data_connection(void);

View File

@ -24,16 +24,17 @@
void usb_drv_init(void);
void usb_drv_exit(void);
void usb_drv_int(void);
void usb_drv_stall(int endpoint, bool stall);
void usb_drv_stall(int endpoint, bool stall,bool in);
bool usb_drv_stalled(int endpoint,bool in);
int usb_drv_send(int endpoint, void* ptr, int length);
int usb_drv_send_nonblocking(int endpoint, void* ptr, int length);
int usb_drv_recv(int endpoint, void* ptr, int length);
void usb_drv_ack(struct usb_ctrlrequest* req);
void usb_drv_set_address(int address);
void usb_drv_reset_endpoint(int endpoint, bool send);
void usb_drv_wait(int endpoint, bool send);
bool usb_drv_powered(void);
int usb_drv_get_last_transfer_status(void);
int usb_drv_get_last_transfer_length(void);
int usb_drv_port_speed(void);
void usb_drv_cancel_all_transfers(void);
#endif

View File

@ -30,6 +30,10 @@
#define REG_ID (*(volatile unsigned int *)(USB_BASE+0x000))
#define REG_HWGENERAL (*(volatile unsigned int *)(USB_BASE+0x004))
#define REG_HWHOST (*(volatile unsigned int *)(USB_BASE+0x008))
#define REG_HWDEVICE (*(volatile unsigned int *)(USB_BASE+0x00c))
#define REG_TXBUF (*(volatile unsigned int *)(USB_BASE+0x010))
#define REG_RXBUF (*(volatile unsigned int *)(USB_BASE+0x014))
#define REG_CAPLENGTH (*(volatile unsigned char*)(USB_BASE+0x100))
#define REG_DCIVERSION (*(volatile unsigned int *)(USB_BASE+0x120))
#define REG_DCCPARAMS (*(volatile unsigned int *)(USB_BASE+0x124))
@ -278,6 +282,10 @@
DTD_STATUS_DATA_BUFF_ERR | \
DTD_STATUS_TRANSACTION_ERR)
#define DTD_RESERVED_LENGTH_MASK 0x0001ffff
#define DTD_RESERVED_IN_USE 0x80000000
#define DTD_RESERVED_PIPE_MASK 0x0ff00000
#define DTD_RESERVED_PIPE_OFFSET 20
/*-------------------------------------------------------------------------*/
/* manual: 32.13.2 Endpoint Transfer Descriptor (dTD) */
@ -294,7 +302,7 @@ struct transfer_descriptor {
unsigned int reserved;
} __attribute__ ((packed));
static struct transfer_descriptor _td_array[32] __attribute((aligned (32)));
static struct transfer_descriptor _td_array[NUM_ENDPOINTS*2] __attribute((aligned (32)));
static struct transfer_descriptor* td_array;
/* manual: 32.13.1 Endpoint Queue Head (dQH) */
@ -304,30 +312,36 @@ struct queue_head {
unsigned int curr_dtd_ptr; /* Current dTD Pointer(31-5) */
struct transfer_descriptor dtd; /* dTD overlay */
unsigned int setup_buffer[2]; /* Setup data 8 bytes */
unsigned int reserved[4];
unsigned int reserved; /* for software use, pointer to the first TD */
unsigned int status; /* for software use, status of chain in progress */
unsigned int length; /* for software use, transfered bytes of chain in progress */
unsigned int wait; /* for softwate use, indicates if the transfer is blocking */
} __attribute__((packed));
static struct queue_head _qh_array[NUM_ENDPOINTS*2] __attribute((aligned (2048)));
static struct queue_head* qh_array;
static struct event_queue transfer_completion_queue[NUM_ENDPOINTS*2];
static const unsigned int pipe2mask[NUM_ENDPOINTS*2] = {
static const unsigned int pipe2mask[] = {
0x01, 0x010000,
0x02, 0x020000,
0x04, 0x040000,
0x08, 0x080000,
0x10, 0x100000,
};
static struct transfer_descriptor* first_td;
static struct transfer_descriptor* last_td;
/*-------------------------------------------------------------------------*/
static void transfer_completed(void);
static int prime_transfer(int endpoint, void* ptr, int len, bool send);
static void control_received(void);
static int prime_transfer(int endpoint, void* ptr,
int len, bool send, bool wait);
static void prepare_td(struct transfer_descriptor* td,
struct transfer_descriptor* previous_td,
void *ptr, int len);
void *ptr, int len,int pipe);
static void bus_reset(void);
static void init_queue_heads(void);
static void init_control_queue_heads(void);
static void init_bulk_queue_heads(void);
static void init_endpoints(void);
/*-------------------------------------------------------------------------*/
@ -344,6 +358,7 @@ void usb_drv_init(void)
REG_USBCMD |= USBCMD_CTRL_RESET;
while (REG_USBCMD & USBCMD_CTRL_RESET);
REG_USBMODE = USBMODE_CTRL_MODE_DEVICE;
/* Force device to full speed */
@ -352,7 +367,7 @@ void usb_drv_init(void)
td_array = (struct transfer_descriptor*)UNCACHED_ADDR(&_td_array);
qh_array = (struct queue_head*)UNCACHED_ADDR(&_qh_array);
init_queue_heads();
init_control_queue_heads();
memset(td_array, 0, sizeof _td_array);
REG_ENDPOINTLISTADDR = (unsigned int)qh_array;
@ -372,6 +387,7 @@ void usb_drv_init(void)
/* go go go */
REG_USBCMD |= USBCMD_RUN;
logf("usb_drv_init() finished");
logf("usb id %x", REG_ID);
logf("usb dciversion %x", REG_DCIVERSION);
@ -407,15 +423,7 @@ void usb_drv_int(void)
/* a control packet? */
if (REG_ENDPTSETUPSTAT & EPSETUP_STATUS_EP0) {
/* copy setup data from packet */
unsigned int tmp[2];
tmp[0] = qh_array[0].setup_buffer[0];
tmp[1] = qh_array[0].setup_buffer[1];
/* acknowledge packet recieved */
REG_ENDPTSETUPSTAT |= EPSETUP_STATUS_EP0;
usb_core_control_request((struct usb_ctrlrequest*)tmp);
control_received();
}
if (REG_ENDPTCOMPLETE)
@ -441,29 +449,52 @@ void usb_drv_int(void)
}
}
void usb_drv_stall(int endpoint, bool stall)
bool usb_drv_stalled(int endpoint,bool in)
{
if(in) {
return ((REG_ENDPTCTRL(endpoint) & EPCTRL_TX_EP_STALL)!=0);
}
else {
return ((REG_ENDPTCTRL(endpoint) & EPCTRL_RX_EP_STALL)!=0);
}
}
void usb_drv_stall(int endpoint, bool stall,bool in)
{
logf("%sstall %d", stall?"":"un", endpoint);
if (stall) {
REG_ENDPTCTRL(endpoint) |= EPCTRL_RX_EP_STALL;
REG_ENDPTCTRL(endpoint) |= EPCTRL_TX_EP_STALL;
if(in) {
if (stall) {
REG_ENDPTCTRL(endpoint) |= EPCTRL_TX_EP_STALL;
}
else {
REG_ENDPTCTRL(endpoint) &= ~EPCTRL_TX_EP_STALL;
}
}
else {
REG_ENDPTCTRL(endpoint) &= ~EPCTRL_RX_EP_STALL;
REG_ENDPTCTRL(endpoint) &= ~EPCTRL_TX_EP_STALL;
if (stall) {
REG_ENDPTCTRL(endpoint) |= EPCTRL_RX_EP_STALL;
}
else {
REG_ENDPTCTRL(endpoint) &= ~EPCTRL_RX_EP_STALL;
}
}
}
int usb_drv_send_nonblocking(int endpoint, void* ptr, int length)
{
return prime_transfer(endpoint, ptr, length, true, false);
}
int usb_drv_send(int endpoint, void* ptr, int length)
{
return prime_transfer(endpoint, ptr, length, true);
return prime_transfer(endpoint, ptr, length, true, true);
}
int usb_drv_recv(int endpoint, void* ptr, int length)
{
//logf("usbrecv(%x, %d)", ptr, length);
return prime_transfer(endpoint, ptr, length, false);
return prime_transfer(endpoint, ptr, length, false, false);
}
void usb_drv_wait(int endpoint, bool send)
@ -485,6 +516,7 @@ int usb_drv_port_speed(void)
void usb_drv_set_address(int address)
{
REG_DEVICEADDR = address << USBDEVICEADDRESS_BIT_POS;
init_bulk_queue_heads();
init_endpoints();
}
@ -496,78 +528,43 @@ void usb_drv_reset_endpoint(int endpoint, bool send)
while (REG_ENDPTFLUSH & mask);
}
int usb_drv_get_last_transfer_length(void)
{
struct transfer_descriptor* current_td = first_td;
int length = 0;
while (!((unsigned int)current_td & DTD_NEXT_TERMINATE)) {
if ((current_td->size_ioc_sts & 0xff) != 0)
return -1;
length += current_td->reserved -
((current_td->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS);
current_td = (struct transfer_descriptor*)current_td->next_td_ptr;
}
return length;
}
int usb_drv_get_last_transfer_status(void)
{
struct transfer_descriptor* current_td = first_td;
while (!((unsigned int)current_td & DTD_NEXT_TERMINATE)) {
if ((current_td->size_ioc_sts & 0xff) != 0)
return current_td->size_ioc_sts & 0xff;
current_td = (struct transfer_descriptor*)current_td->next_td_ptr;
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* manual: 32.14.5.2 */
static int prime_transfer(int endpoint, void* ptr, int len, bool send)
static int prime_transfer(int endpoint, void* ptr, int len, bool send, bool wait)
{
int pipe = endpoint * 2 + (send ? 1 : 0);
unsigned int mask = pipe2mask[pipe];
last_td = 0;
struct queue_head* qh = &qh_array[pipe];
static long last_tick;
struct transfer_descriptor* new_td;
/*
if (send && endpoint > EP_CONTROL) {
logf("usb: sent %d bytes", len);
}
*/
qh->status = 0;
qh->length = 0;
qh->wait = wait;
if (len==0) {
struct transfer_descriptor* new_td = &td_array[0];
prepare_td(new_td, 0, ptr, 0);
last_td = new_td;
first_td = new_td;
}
else {
int td_idx = 0;
while (len > 0) {
int current_transfer_length = MIN(16384,len);
struct transfer_descriptor* new_td = &td_array[td_idx];
prepare_td(new_td, last_td, ptr, current_transfer_length);
new_td=&td_array[pipe];
prepare_td(new_td, 0, ptr, len,pipe);
//logf("starting ep %d %s",endpoint,send?"send":"receive");
last_td = new_td;
len -= current_transfer_length;
td_idx++;
ptr += current_transfer_length;
}
first_td = &td_array[0];
}
qh->dtd.next_td_ptr = (unsigned int)first_td;
qh->dtd.next_td_ptr = (unsigned int)new_td;
qh->dtd.size_ioc_sts &= ~(QH_STATUS_HALT | QH_STATUS_ACTIVE);
REG_ENDPTPRIME |= mask;
if(endpoint == EP_CONTROL && (REG_ENDPTSETUPSTAT & EPSETUP_STATUS_EP0)) {
/* 32.14.3.2.2 */
logf("new setup arrived");
return -4;
}
last_tick = current_tick;
while ((REG_ENDPTPRIME & mask)) {
if (REG_USBSTS & USBSTS_RESET)
@ -583,38 +580,43 @@ static int prime_transfer(int endpoint, void* ptr, int len, bool send)
logf("no prime! %d %d %x", endpoint, pipe, qh->dtd.size_ioc_sts & 0xff );
return -3;
}
if(endpoint == EP_CONTROL && (REG_ENDPTSETUPSTAT & EPSETUP_STATUS_EP0)) {
/* 32.14.3.2.2 */
logf("new setup arrived");
return -4;
}
if (send) {
if (wait) {
/* wait for transfer to finish */
struct transfer_descriptor* current_td = first_td;
while (!((unsigned int)current_td & DTD_NEXT_TERMINATE)) {
while ((current_td->size_ioc_sts & 0xff) == DTD_STATUS_ACTIVE) {
if (REG_ENDPTCOMPLETE & mask)
REG_ENDPTCOMPLETE |= mask;
/* let the host handle timeouts */
if (REG_USBSTS & USBSTS_RESET) {
logf("td interrupted by reset");
return -4;
}
}
if ((current_td->size_ioc_sts & 0xff) != 0) {
logf("td failed with error %X",(current_td->size_ioc_sts & 0xff));
return -6;
}
//logf("td finished : %X",current_td->size_ioc_sts & 0xff);
current_td=(struct transfer_descriptor*)current_td->next_td_ptr;
struct queue_event ev;
queue_wait(&transfer_completion_queue[pipe], &ev);
if(qh->status!=0) {
return -5;
}
//logf("all tds done");
}
return 0;
}
void usb_drv_cancel_all_transfers(void)
{
int i;
REG_ENDPTFLUSH = ~0;
while (REG_ENDPTFLUSH);
memset(td_array, 0, sizeof _td_array);
for(i=0;i<NUM_ENDPOINTS*2;i++) {
if(qh_array[i].wait) {
qh_array[i].wait=0;
qh_array[i].status=DTD_STATUS_HALTED;
queue_post(&transfer_completion_queue[i],0, 0);
}
}
}
static void prepare_td(struct transfer_descriptor* td,
struct transfer_descriptor* previous_td,
void *ptr, int len)
void *ptr, int len,int pipe)
{
//logf("adding a td : %d",len);
memset(td, 0, sizeof(struct transfer_descriptor));
@ -626,35 +628,72 @@ static void prepare_td(struct transfer_descriptor* td,
td->buff_ptr2 = ((unsigned int)ptr & 0xfffff000) + 0x2000;
td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000;
td->buff_ptr4 = ((unsigned int)ptr & 0xfffff000) + 0x4000;
td->reserved = len;
td->reserved |= DTD_RESERVED_LENGTH_MASK & len;
td->reserved |= DTD_RESERVED_IN_USE;
td->reserved |= (pipe << DTD_RESERVED_PIPE_OFFSET);
if (previous_td != 0) {
previous_td->next_td_ptr=(unsigned int)td;
previous_td->size_ioc_sts&=~DTD_IOC;// Only an interrupt on the last one
previous_td->next_td_ptr=(unsigned int)td;
}
}
static void control_received(void)
{
int i;
logf("control stuff");
/* copy setup data from packet */
static unsigned int tmp[2];
tmp[0] = qh_array[0].setup_buffer[0];
tmp[1] = qh_array[0].setup_buffer[1];
/* acknowledge packet recieved */
REG_ENDPTSETUPSTAT |= EPSETUP_STATUS_EP0;
/* Stop pending interrupt transfers */
for(i=0;i<2;i++) {
if(qh_array[i].wait) {
qh_array[i].wait=0;
qh_array[i].status=DTD_STATUS_HALTED;
queue_post(&transfer_completion_queue[i],0, 0);
}
}
usb_core_control_request((struct usb_ctrlrequest*)tmp);
}
static void transfer_completed(void)
{
int i;
int ep;
unsigned int mask = REG_ENDPTCOMPLETE;
REG_ENDPTCOMPLETE |= mask;
//logf("usb comp %x", mask);
for (ep=0; ep<NUM_ENDPOINTS; ep++) {
int dir;
for (dir=0; dir<2; dir++) {
int pipe = ep * 2 + dir;
if (mask & pipe2mask[pipe]) {
struct queue_head* qh = &qh_array[pipe];
struct transfer_descriptor *td = &td_array[pipe];
for (i=0; i<NUM_ENDPOINTS; i++) {
int x;
for (x=0; x<2; x++) {
unsigned int status;
int pipe = i * 2 + x;
if (mask & pipe2mask[pipe])
usb_core_transfer_complete(i, x ? true : false);
status = usb_drv_get_last_transfer_status();
if ((mask & pipe2mask[pipe]) &&
status & DTD_ERROR_MASK) {
logf("pipe %d err %x", pipe, status & DTD_ERROR_MASK);
if(td->size_ioc_sts & DTD_STATUS_ACTIVE) {
/* TODO this shouldn't happen, but...*/
break;
}
if((td->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS != 0 && dir==0) {
/* We got less data than we asked for. */
}
qh->length = (td->reserved & DTD_RESERVED_LENGTH_MASK) -
((td->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS);
if(td->size_ioc_sts & DTD_ERROR_MASK) {
logf("pipe %d err %x", pipe, td->size_ioc_sts & DTD_ERROR_MASK);
qh->status |= td->size_ioc_sts & DTD_ERROR_MASK;
/* TODO we need to handle this somehow. Flush the endpoint ? */
}
if(qh->wait) {
qh->wait=0;
queue_post(&transfer_completion_queue[pipe],0, 0);
}
usb_core_transfer_complete(ep, dir, qh->status, qh->length);
}
}
}
@ -685,36 +724,17 @@ static void bus_reset(void)
logf("usb: short reset timeout");
}
REG_ENDPTFLUSH = ~0;
//while (REG_ENDPTFLUSH);
usb_drv_cancel_all_transfers();
if (!(REG_PORTSC1 & PORTSCX_PORT_RESET)) {
logf("usb: slow reset!");
}
logf("PTS : %X",(REG_PORTSC1 & 0xC0000000)>>30);
logf("STS : %X",(REG_PORTSC1 & 0x20000000)>>29);
logf("PTW : %X",(REG_PORTSC1 & 0x10000000)>>28);
logf("PSPD : %X",(REG_PORTSC1 & 0x0C000000)>>26);
logf("PFSC : %X",(REG_PORTSC1 & 0x01000000)>>24);
logf("PTC : %X",(REG_PORTSC1 & 0x000F0000)>>16);
logf("PO : %X",(REG_PORTSC1 & 0x00002000)>>13);
}
/* manual: 32.14.4.1 Queue Head Initialization */
static void init_queue_heads(void)
static void init_control_queue_heads(void)
{
int tx_packetsize;
int rx_packetsize;
if (usb_drv_port_speed()) {
rx_packetsize = 512;
tx_packetsize = 512;
}
else {
rx_packetsize = 16;
tx_packetsize = 16;
}
int i;
memset(qh_array, 0, sizeof _qh_array);
/*** control ***/
@ -723,23 +743,47 @@ static void init_queue_heads(void)
qh_array[EP_CONTROL+1].max_pkt_length = 64 << QH_MAX_PKT_LEN_POS;
qh_array[EP_CONTROL+1].dtd.next_td_ptr = QH_NEXT_TERMINATE;
for(i=0;i<2;i++) {
queue_init(&transfer_completion_queue[i], false);
}
}
/* manual: 32.14.4.1 Queue Head Initialization */
static void init_bulk_queue_heads(void)
{
int tx_packetsize;
int rx_packetsize;
int i;
if (usb_drv_port_speed()) {
rx_packetsize = 512;
tx_packetsize = 512;
}
else {
rx_packetsize = 64;
tx_packetsize = 64;
}
/*** bulk ***/
qh_array[EP_RX*2].max_pkt_length = rx_packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL;
qh_array[EP_RX*2].dtd.next_td_ptr = QH_NEXT_TERMINATE;
qh_array[EP_TX*2+1].max_pkt_length = tx_packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL;
qh_array[EP_TX*2+1].dtd.next_td_ptr = QH_NEXT_TERMINATE;
for(i=1;i<NUM_ENDPOINTS;i++) {
qh_array[i*2].max_pkt_length = rx_packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL;
qh_array[i*2].dtd.next_td_ptr = QH_NEXT_TERMINATE;
qh_array[i*2+1].max_pkt_length = tx_packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL;
qh_array[i*2+1].dtd.next_td_ptr = QH_NEXT_TERMINATE;
}
for(i=2;i<NUM_ENDPOINTS*2;i++) {
queue_init(&transfer_completion_queue[i], false);
}
}
static void init_endpoints(void)
{
int i;
/* bulk */
REG_ENDPTCTRL(EP_RX) =
EPCTRL_RX_DATA_TOGGLE_RST | EPCTRL_RX_ENABLE |
(EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT) |
(EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT);
REG_ENDPTCTRL(EP_TX) =
EPCTRL_TX_DATA_TOGGLE_RST | EPCTRL_TX_ENABLE |
(EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT) |
(EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT);
for(i=1;i<NUM_ENDPOINTS;i++) {
REG_ENDPTCTRL(i) =
EPCTRL_RX_DATA_TOGGLE_RST | EPCTRL_RX_ENABLE |
EPCTRL_TX_DATA_TOGGLE_RST | EPCTRL_TX_ENABLE |
(EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT) |
(EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT);
}
}

View File

@ -34,6 +34,7 @@ void usb_init_device(void)
{
/* enable usb module */
outl(inl(0x7000002C) | 0x3000000, 0x7000002C);
DEV_EN |= DEV_USB0;
DEV_EN |= DEV_USB1;
@ -46,6 +47,7 @@ void usb_init_device(void)
#if CONFIG_CPU == PP5020
DEV_INIT2 |= INIT_USB;
#endif
while ((inl(0x70000028) & 0x80) == 0);
outl(inl(0x70000028) | 0x2, 0x70000028);
udelay(0x186A0);
@ -68,9 +70,8 @@ void usb_init_device(void)
void usb_enable(bool on)
{
if (on) {
#ifdef USE_ROCKBOX_USB
usb_core_init();
#else
#if !defined(USE_ROCKBOX_USB)
/* until we have native mass-storage mode, we want to reboot on
usb host connect */
#if defined(IRIVER_H10) || defined (IRIVER_H10_5GB)

View File

@ -22,6 +22,8 @@
//#define LOGF_ENABLE
#include "logf.h"
#ifdef USB_BENCHMARK
static int current_length;
static unsigned char _input_buffer[16384];
@ -56,8 +58,8 @@ void usb_benchmark_control_request(struct usb_ctrlrequest* req)
logf("bench: read %d", current_length);
todo = MIN(usb_max_pkt_size, current_length);
state = SENDING;
usb_drv_reset_endpoint(EP_TX, true);
usb_drv_send(EP_TX, &input_buffer, todo);
usb_drv_reset_endpoint(EP_BENCHMARK, true);
usb_drv_send(EP_BENCHMARK, &input_buffer, todo);
current_length -= todo;
break;
@ -66,13 +68,13 @@ void usb_benchmark_control_request(struct usb_ctrlrequest* req)
current_length = req->wValue * req->wIndex;
logf("bench: write %d", current_length);
state = RECEIVING;
usb_drv_reset_endpoint(EP_RX, false);
usb_drv_recv(EP_RX, &input_buffer, sizeof _input_buffer);
usb_drv_reset_endpoint(EP_BENCHMARK, false);
usb_drv_recv(EP_BENCHMARK, &input_buffer, sizeof _input_buffer);
break;
}
}
void usb_benchmark_transfer_complete(int endpoint, bool in)
void usb_benchmark_transfer_complete(bool in)
{
(void)in;
@ -87,26 +89,26 @@ void usb_benchmark_transfer_complete(int endpoint, bool in)
{
case SENDING: {
int todo = MIN(usb_max_pkt_size, current_length);
if (endpoint == EP_RX) {
if (in == false) {
logf("unexpected ep_rx");
break;
}
logf("bench: %d more tx", current_length);
usb_drv_send(EP_TX, &input_buffer, todo);
usb_drv_send(EP_BENCHMARK, &input_buffer, todo);
current_length -= todo;
input_buffer[0]++;
break;
}
case RECEIVING:
if (endpoint == EP_TX) {
if (in == true) {
logf("unexpected ep_tx");
break;
}
/* re-prime endpoint */
usb_drv_recv(EP_RX, &input_buffer, sizeof _input_buffer);
usb_drv_recv(EP_BENCHMARK, &input_buffer, sizeof _input_buffer);
input_buffer[0]++;
break;
@ -123,3 +125,4 @@ static void ack_control(struct usb_ctrlrequest* req)
else
usb_drv_send(EP_CONTROL, NULL, 0);
}
#endif /*USB_BENCHMARK*/

View File

@ -21,6 +21,6 @@
void usb_benchmark_init(void);
void usb_benchmark_control_request(struct usb_ctrlrequest* req);
void usb_benchmark_transfer_complete(int endpoint, bool in);
void usb_benchmark_transfer_complete(bool in);
#endif

View File

@ -23,44 +23,42 @@
//#define LOGF_ENABLE
#include "logf.h"
#ifndef BOOTLOADER
//#define USB_SERIAL
//#define USB_BENCHMARK
#ifdef USE_ROCKBOX_USB
#define USB_STORAGE
#else
#define USB_CHARGING_ONLY
#endif /* USE_ROCKBOX_USB */
#else
#define USB_CHARGING_ONLY
#endif
#include "usb_ch9.h"
#include "usb_drv.h"
#include "usb_core.h"
#define USB_THREAD
#if defined(USB_STORAGE)
#include "usb_storage.h"
#define USB_THREAD
#elif defined(USB_SERIAL)
#define USB_THREAD
#endif
#if defined(USB_SERIAL)
#include "usb_serial.h"
#elif defined(USB_BENCHMARK)
#endif
#if defined(USB_BENCHMARK)
#include "usb_benchmark.h"
#endif
/* TODO: Move this target-specific stuff somewhere else (serial number reading) */
#ifdef HAVE_AS3514
#include "i2c-pp.h"
#include "as3514.h"
#endif
/*-------------------------------------------------------------------------*/
/* USB protocol descriptors: */
#define USB_SC_SCSI 0x06 /* Transparent */
#define USB_PROT_BULK 0x50 /* bulk only */
int usb_max_pkt_size = 512;
static const struct usb_device_descriptor device_descriptor = {
static const struct usb_device_descriptor device_descriptor= {
.bLength = sizeof(struct usb_device_descriptor),
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200, /* USB version 2.0 */
.bcdUSB = 0x0200,
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
@ -74,17 +72,34 @@ static const struct usb_device_descriptor device_descriptor = {
.bNumConfigurations = 1
};
static const struct {
static struct {
struct usb_config_descriptor config_descriptor;
struct usb_interface_descriptor interface_descriptor;
struct usb_endpoint_descriptor ep1_in_descriptor;
struct usb_endpoint_descriptor ep1_out_descriptor;
} config_data_fs =
#ifdef USB_CHARGING_ONLY
struct usb_interface_descriptor charging_interface_descriptor;
struct usb_endpoint_descriptor charging_ep_in_descriptor;
struct usb_endpoint_descriptor charging_ep_out_descriptor;
#endif
#ifdef USB_STORAGE
struct usb_interface_descriptor mass_storage_interface_descriptor;
struct usb_endpoint_descriptor mass_storage_ep_in_descriptor;
struct usb_endpoint_descriptor mass_storage_ep_out_descriptor;
#endif
#ifdef USB_SERIAL
struct usb_interface_descriptor serial_interface_descriptor;
struct usb_endpoint_descriptor serial_ep_in_descriptor;
struct usb_endpoint_descriptor serial_ep_out_descriptor;
#endif
#ifdef USB_BENCHMARK
struct usb_interface_descriptor benchmark_interface_descriptor;
struct usb_endpoint_descriptor benchmark_ep_in_descriptor;
struct usb_endpoint_descriptor benchmark_ep_out_descriptor;
#endif
} __attribute__((packed)) *config_data, _config_data =
{
{
.bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = sizeof config_data_fs,
.wTotalLength = sizeof _config_data,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
@ -105,23 +120,23 @@ static const struct {
.bInterfaceProtocol = 0,
.iInterface = 5
},
/* TODO: try with zero endpoints */
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN,
.bEndpointAddress = EP_CHARGING_ONLY | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.wMaxPacketSize = 16,
.bInterval = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT,
.bEndpointAddress = EP_CHARGING_ONLY | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.wMaxPacketSize = 16,
.bInterval = 0
}
},
#endif
#ifdef USB_STORAGE
@ -141,7 +156,7 @@ static const struct {
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN,
.bEndpointAddress = EP_MASS_STORAGE | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 16,
.bInterval = 0
@ -149,11 +164,11 @@ static const struct {
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT,
.bEndpointAddress = EP_MASS_STORAGE | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 16,
.bInterval = 0
}
},
#endif
#ifdef USB_SERIAL
@ -173,19 +188,19 @@ static const struct {
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN,
.bEndpointAddress = EP_SERIAL | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64,
.wMaxPacketSize = 16,
.bInterval = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT,
.bEndpointAddress = EP_SERIAL | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64,
.wMaxPacketSize = 16,
.bInterval = 0
}
},
#endif
#ifdef USB_BENCHMARK
@ -205,160 +220,19 @@ static const struct {
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT,
.bEndpointAddress = EP_BENCHMARK | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64,
.wMaxPacketSize = 16,
.bInterval = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN,
.bEndpointAddress = EP_BENCHMARK | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64,
.bInterval = 0
}
#endif
},
config_data_hs =
{
{
.bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = sizeof config_data_hs,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = 250, /* 500mA in 2mA units */
},
#ifdef USB_CHARGING_ONLY
/* dummy interface for charging-only */
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 5
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.wMaxPacketSize = 16,
.bInterval = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0
}
#endif
#ifdef USB_STORAGE
/* storage interface */
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
.bInterfaceSubClass = USB_SC_SCSI,
.bInterfaceProtocol = USB_PROT_BULK,
.iInterface = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0
}
#endif
#ifdef USB_SERIAL
/* serial interface */
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0
}
#endif
#ifdef USB_BENCHMARK
/* bulk test interface */
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 255,
.bInterfaceProtocol = 255,
.iInterface = 4
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
.bInterval = 0
}
#endif
};
@ -388,13 +262,22 @@ static struct usb_string_descriptor usb_string_iProduct =
{'R','o','c','k','b','o','x',' ','m','e','d','i','a',' ','p','l','a','y','e','r'}
};
#if defined(HAVE_AS3514)
static struct usb_string_descriptor usb_string_iSerial =
{
34,
USB_DT_STRING,
{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}
66,
USB_DT_STRING,
{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0',
'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}
};
#else
static struct usb_string_descriptor usb_string_iSerial =
{
34,
USB_DT_STRING,
{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}
};
#endif
/* Generic for all targets */
@ -443,10 +326,25 @@ static long usbcore_stack[DEFAULT_STACK_SIZE];
static void usb_core_thread(void);
#endif
static void ack_control(struct usb_ctrlrequest* req);
static void usb_core_control_request_handler(struct usb_ctrlrequest* req);
static int ack_control(struct usb_ctrlrequest* req);
static unsigned char *response_data;
static unsigned char __response_data[CACHEALIGN_UP(2)] CACHEALIGN_ATTR;
struct usb_core_event
{
unsigned char endpoint;
bool in;
int status;
int length;
void* data;
};
static struct usb_core_event events[NUM_ENDPOINTS];
#ifdef IPOD_ARCH
void set_serial_descriptor(void)
static void set_serial_descriptor(void)
{
static short hex[16] = {'0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F'};
@ -471,7 +369,23 @@ void set_serial_descriptor(void)
x >>= 4;
}
}
}
#elif defined(HAVE_AS3514)
static void set_serial_descriptor(void)
{
static short hex[16] = {'0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F'};
unsigned char serial[16];
short* p = usb_string_iSerial.wString;
int i;
i2c_readbytes(AS3514_I2C_ADDR, 0x30, 0x10, serial);
for (i = 0; i < 16; i++)
{
*p++ = hex[(serial[i] >> 4) & 0xF];
*p++ = hex[(serial[i] >> 0) & 0xF];
}
}
#endif
@ -480,9 +394,8 @@ void usb_core_init(void)
if (initialized)
return;
#ifdef IPOD_ARCH
set_serial_descriptor();
#endif
config_data = (void*)UNCACHED_ADDR(&_config_data);
response_data = (void*)UNCACHED_ADDR(&__response_data);
queue_init(&usbcore_queue, false);
usb_drv_init();
@ -514,10 +427,11 @@ void usb_core_exit(void)
{
if (initialized) {
usb_drv_exit();
queue_delete(&usbcore_queue);
#ifdef USB_THREAD
remove_thread(usbcore_thread);
queue_post(&usbcore_queue, USB_CORE_QUIT, 0);
thread_wait(usbcore_thread);
#endif
queue_delete(&usbcore_queue);
}
data_connection = false;
initialized = false;
@ -532,25 +446,52 @@ bool usb_core_data_connection(void)
#ifdef USB_THREAD
void usb_core_thread(void)
{
#if defined(IPOD_ARCH) || defined(HAVE_AS3514)
set_serial_descriptor();
#endif
while (1) {
struct queue_event ev;
queue_wait(&usbcore_queue, &ev);
if (ev.id == USB_CORE_QUIT) {
cancel_cpu_boost();
return;
}
if (ev.id == USB_CORE_TRANSFER_COMPLETION) {
struct usb_core_event* event = (struct usb_core_event*)ev.data;
switch(event->endpoint) {
case EP_CONTROL:
logf("ctrl handled %ld",current_tick);
usb_core_control_request_handler((struct usb_ctrlrequest*)event->data);
break;
#ifdef USB_STORAGE
usb_storage_transfer_complete(ev.id);
case EP_MASS_STORAGE:
usb_storage_transfer_complete(event->in,event->status,event->length);
break;
#endif
#ifdef USB_SERIAL
usb_serial_transfer_complete(ev.id);
case EP_SERIAL:
usb_serial_transfer_complete(event->in,event->status,event->length);
break;
#endif
#ifdef USB_BENCHMARK
case EP_BENCHMARK:
usb_benchmark_transfer_complete(event->in);
break;
#endif
#ifdef USB_CHARGING_ONLY
case EP_CHARGING_ONLY:
break;
#endif
}
}
}
}
#endif
/* called by usb_drv_int() */
void usb_core_control_request(struct usb_ctrlrequest* req)
static void usb_core_control_request_handler(struct usb_ctrlrequest* req)
{
/* note: interrupt context */
data_connection = true;
@ -565,29 +506,31 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
switch (req->bRequest) {
case USB_REQ_SET_CONFIGURATION:
logf("usb_core: SET_CONFIG");
usb_drv_cancel_all_transfers();
if (req->wValue){
usb_state = CONFIGURED;
#ifdef USB_STORAGE
usb_storage_control_request(req);
usb_storage_control_request(req);
#endif
#ifdef USB_SERIAL
usb_serial_control_request(req);
usb_serial_control_request(req);
#endif
ack_control(req);
if (req->wValue)
usb_state = CONFIGURED;
else
}
else {
usb_state = ADDRESS;
}
ack_control(req);
break;
case USB_REQ_GET_CONFIGURATION: {
static char confignum;
char* tmp = (void*)UNCACHED_ADDR(&confignum);
logf("usb_core: GET_CONFIG");
if (usb_state == ADDRESS)
*tmp = 0;
response_data[0] = 0;
else
*tmp = 1;
usb_drv_send(EP_CONTROL, tmp, 1);
response_data[0] = 1;
if(usb_drv_send(EP_CONTROL, response_data, 1)!= 0)
break;
ack_control(req);
break;
}
@ -597,29 +540,54 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
ack_control(req);
break;
case USB_REQ_GET_INTERFACE:
logf("usb_core: GET_INTERFACE");
response_data[0] = 0;
if(usb_drv_send(EP_CONTROL, response_data, 1)!=0)
break;
ack_control(req);
break;
case USB_REQ_CLEAR_FEATURE:
logf("usb_core: CLEAR_FEATURE");
if (req->wValue)
usb_drv_stall(req->wIndex, true);
usb_drv_stall(req->wIndex & 0xf, false,(req->wIndex & 0x80) !=0);
else
usb_drv_stall(req->wIndex, false);
usb_drv_stall(req->wIndex & 0xf, false,(req->wIndex & 0x80) !=0);
ack_control(req);
break;
case USB_REQ_SET_ADDRESS:
usb_address = req->wValue;
logf("usb_core: SET_ADR %d", usb_address);
case USB_REQ_SET_FEATURE:
logf("usb_core: SET_FEATURE");
if (req->wValue)
usb_drv_stall(req->wIndex & 0xf, true,(req->wIndex & 0x80) !=0);
else
usb_drv_stall(req->wIndex & 0xf, false,(req->wIndex & 0x80) !=0);
ack_control(req);
break;
case USB_REQ_SET_ADDRESS: {
unsigned char address = req->wValue;
logf("usb_core: SET_ADR %d", address);
if(ack_control(req)!=0)
break;
usb_drv_cancel_all_transfers();
usb_address = address;
usb_drv_set_address(usb_address);
usb_state = ADDRESS;
break;
}
case USB_REQ_GET_STATUS: {
static char tmp[2] = {0,0};
tmp[0] = 0;
tmp[1] = 0;
response_data[0]= 0;
response_data[1]= 0;
logf("usb_core: GET_STATUS");
usb_drv_send(EP_CONTROL, UNCACHED_ADDR(&tmp), 2);
if(req->wIndex>0) {
if(usb_drv_stalled(req->wIndex&0xf,(req->wIndex&0x80)!=0))
response_data[0] = 1;
}
logf("usb_core: %X %X",response_data[0],response_data[1]);
if(usb_drv_send(EP_CONTROL, response_data, 2)!=0)
break;
ack_control(req);
break;
}
@ -637,18 +605,59 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
size = sizeof device_descriptor;
break;
case USB_DT_CONFIG:
if(usb_drv_port_speed())
{
ptr = &config_data_hs;
size = sizeof config_data_hs;
case USB_DT_OTHER_SPEED_CONFIG:
case USB_DT_CONFIG: {
int max_packet_size;
int interface_number=0;
if(req->wValue >> 8 == USB_DT_CONFIG) {
if(usb_drv_port_speed()) {
max_packet_size=512;
}
else {
max_packet_size=64;
}
config_data->config_descriptor.bDescriptorType=USB_DT_CONFIG;
}
else
{
ptr = &config_data_fs;
size = sizeof config_data_fs;
else {
if(usb_drv_port_speed()) {
max_packet_size=64;
}
else {
max_packet_size=512;
}
config_data->config_descriptor.bDescriptorType=USB_DT_OTHER_SPEED_CONFIG;
}
#ifdef USB_CHARGING_ONLY
memcpy(&config_data->charging_ep_in_descriptor.wMaxPacketSize,&max_packet_size,sizeof(unsigned short));
memcpy(&config_data->charging_ep_out_descriptor.wMaxPacketSize,&max_packet_size,sizeof(unsigned short));
config_data->charging_interface_descriptor.bInterfaceNumber=interface_number;
interface_number++;
#endif
#ifdef USB_STORAGE
memcpy(&config_data->mass_storage_ep_in_descriptor.wMaxPacketSize,&max_packet_size,sizeof(unsigned short));
memcpy(&config_data->mass_storage_ep_out_descriptor.wMaxPacketSize,&max_packet_size,sizeof(unsigned short));
config_data->mass_storage_interface_descriptor.bInterfaceNumber=interface_number;
interface_number++;
#endif
#ifdef USB_SERIAL
memcpy(&config_data->serial_ep_in_descriptor.wMaxPacketSize,&max_packet_size,sizeof(unsigned short));
memcpy(&config_data->serial_ep_out_descriptor.wMaxPacketSize,&max_packet_size,sizeof(unsigned short));
config_data->serial_interface_descriptor.bInterfaceNumber=interface_number;
interface_number++;
#endif
#ifdef USB_BENCHMARK
memcpy(&config_data->benchmark_ep_in_descriptor.wMaxPacketSize,&max_packet_size,sizeof(unsigned short));
memcpy(&config_data->benchmark_ep_out_descriptor.wMaxPacketSize,&max_packet_size,sizeof(unsigned short));
config_data.benchmark_interface_descriptor.bInterfaceNumber=interface_number;
interface_number++;
#endif
config_data->config_descriptor.bNumInterfaces=interface_number;
ptr = config_data;
size = sizeof _config_data;
break;
}
case USB_DT_STRING:
if ((unsigned)index < (sizeof(usb_strings)/sizeof(struct usb_string_descriptor*))) {
@ -657,7 +666,7 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
}
else {
logf("bad string id %d", index);
usb_drv_stall(EP_CONTROL, true);
usb_drv_stall(EP_CONTROL, true,true);
}
break;
@ -668,13 +677,14 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
default:
logf("bad desc %d", req->wValue >> 8);
usb_drv_stall(EP_CONTROL, true);
usb_drv_stall(EP_CONTROL, true,true);
break;
}
if (ptr) {
length = MIN(size, length);
usb_drv_send(EP_CONTROL, (void*)UNCACHED_ADDR(ptr), length);
if(usb_drv_send(EP_CONTROL, (void*)UNCACHED_ADDR(ptr), length)!=0)
break;
}
ack_control(req);
break;
@ -693,7 +703,7 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
{
/* nope. flag error */
logf("usb bad req %d", req->bRequest);
usb_drv_stall(EP_CONTROL, true);
usb_drv_stall(EP_CONTROL, true,true);
ack_control(req);
}
break;
@ -709,9 +719,9 @@ void usb_core_bus_reset(void)
}
/* called by usb_drv_transfer_completed() */
void usb_core_transfer_complete(int endpoint, bool in)
void usb_core_transfer_complete(int endpoint, bool in, int status,int length)
{
#ifdef USB_CHARGING_ONLY
#if defined(USB_CHARGING_ONLY) || defined(USB_STORAGE)
(void)in;
#endif
@ -720,25 +730,35 @@ void usb_core_transfer_complete(int endpoint, bool in)
/* already handled */
break;
case EP_RX:
case EP_TX:
#if defined(USB_BENCHMARK)
usb_benchmark_transfer_complete(endpoint, in);
#elif defined(USB_STORAGE) || defined(USB_SERIAL)
queue_post(&usbcore_queue, endpoint, 0);
#endif
break;
default:
events[endpoint].endpoint=endpoint;
events[endpoint].in=in;
events[endpoint].data=0;
events[endpoint].status=status;
events[endpoint].length=length;
/* All other endoints. Let the thread deal with it */
queue_post(&usbcore_queue, USB_CORE_TRANSFER_COMPLETION, (intptr_t)&events[endpoint]);
break;
}
}
static void ack_control(struct usb_ctrlrequest* req)
/* called by usb_drv_int() */
void usb_core_control_request(struct usb_ctrlrequest* req)
{
if (req->bRequestType & 0x80)
usb_drv_recv(EP_CONTROL, NULL, 0);
else
usb_drv_send(EP_CONTROL, NULL, 0);
events[0].endpoint=0;
events[0].in=0;
events[0].data=(void *)req;
events[0].status=0;
events[0].length=0;
logf("ctrl received %ld",current_tick);
queue_post(&usbcore_queue, USB_CORE_TRANSFER_COMPLETION,(intptr_t)&events[0]);
}
static int ack_control(struct usb_ctrlrequest* req)
{
if (req->bRequestType & 0x80)
return usb_drv_recv(EP_CONTROL, NULL, 0);
else
return usb_drv_send(EP_CONTROL, NULL, 0);
}

View File

@ -23,6 +23,8 @@
//#define LOGF_ENABLE
#include "logf.h"
#ifdef USB_SERIAL
static unsigned char _transfer_buffer[16];
static unsigned char* transfer_buffer;
@ -34,20 +36,26 @@ void usb_serial_init(void)
}
/* called by usb_core_transfer_complete() */
void usb_serial_transfer_complete(int endpoint)
void usb_serial_transfer_complete(bool in, int status, int length)
{
switch (endpoint) {
case EP_RX:
int i;
switch (in) {
case false:
logf("serial: %s", transfer_buffer);
/* re-prime endpoint */
usb_drv_recv(EP_RX, transfer_buffer, sizeof _transfer_buffer);
/* echo back :) */
usb_drv_send(EP_TX, transfer_buffer, sizeof transfer_buffer);
/* Data received. Send it back */
for(i=0;i<length;i++) {
if(transfer_buffer[i]>0x40 && transfer_buffer[i]<0x5b)
transfer_buffer[i]+=0x20;
else if(transfer_buffer[i]>0x60 && transfer_buffer[i]<0x7b)
transfer_buffer[i]-=0x20;
}
usb_drv_send_nonblocking(EP_SERIAL, transfer_buffer, length);
break;
case EP_TX:
case true:
/* Data sent out (maybe correctly, but we don't actually care.
* Re-prime read endpoint */
usb_drv_recv(EP_SERIAL, transfer_buffer, sizeof _transfer_buffer);
break;
}
}
@ -55,14 +63,12 @@ void usb_serial_transfer_complete(int endpoint)
/* called by usb_core_control_request() */
bool usb_serial_control_request(struct usb_ctrlrequest* req)
{
/* note: interrupt context */
bool handled = false;
switch (req->bRequest) {
case USB_REQ_SET_CONFIGURATION:
logf("serial: set config");
/* prime rx endpoint */
usb_drv_recv(EP_RX, transfer_buffer, sizeof _transfer_buffer);
usb_drv_recv(EP_SERIAL, transfer_buffer, sizeof _transfer_buffer);
handled = true;
break;
@ -72,3 +78,5 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req)
return handled;
}
#endif /*USB_SERIAL*/

View File

@ -22,7 +22,7 @@
#include "usb_ch9.h"
void usb_serial_init(void);
void usb_serial_transfer_complete(int endpoint);
void usb_serial_transfer_complete(bool in, int status, int length);
bool usb_serial_control_request(struct usb_ctrlrequest* req);
#endif

View File

@ -26,8 +26,22 @@
#include "hotswap.h"
#include "disk.h"
#ifdef USB_STORAGE
/* Enable the following define to export only the SD card slot. This
* is useful for USBCV MSC tests, as those are destructive.
* This won't work right if the device doesn't have a card slot.
*/
//#define ONLY_EXPOSE_CARD_SLOT
#define SECTOR_SIZE 512
/* We can currently use up to 20k buffer size. More than that requires
* transfer chaining in the driver. Tests on sansa c200 show that the 16k
* limitation causes no more than 2% slowdown.
*/
#define BUFFER_SIZE 16384
/* bulk-only class specific requests */
#define USB_BULK_RESET_REQUEST 0xff
#define USB_BULK_GET_MAX_LUN 0xfe
@ -40,7 +54,8 @@
#define SCSI_TEST_UNIT_READY 0x00
#define SCSI_INQUIRY 0x12
#define SCSI_MODE_SENSE 0x1a
#define SCSI_MODE_SENSE_6 0x1a
#define SCSI_MODE_SENSE_10 0x5a
#define SCSI_REQUEST_SENSE 0x03
#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1e
#define SCSI_READ_CAPACITY 0x25
@ -48,11 +63,23 @@
#define SCSI_READ_10 0x28
#define SCSI_WRITE_10 0x2a
#define SCSI_START_STOP_UNIT 0x1b
#define SCSI_REPORT_LUNS 0xa0
#define SCSI_STATUS_GOOD 0x00
#define SCSI_STATUS_FAIL 0x01
#define SCSI_STATUS_CHECK_CONDITION 0x02
#define SENSE_NOT_READY 0x02
#define SENSE_MEDIUM_ERROR 0x03
#define SENSE_ILLEGAL_REQUEST 0x05
#define SENSE_UNIT_ATTENTION 0x06
#define ASC_MEDIUM_NOT_PRESENT 0x3a
#define ASC_INVALID_FIELD_IN_CBD 0x24
#define ASC_LBA_OUT_OF_RANGE 0x21
#define ASC_WRITE_ERROR 0x0C
#define ASC_READ_ERROR 0x11
#define SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA 0x02000000
@ -69,6 +96,15 @@ struct inquiry_data {
unsigned char ProductRevisionLevel[4];
} __attribute__ ((packed));
struct report_lun_data {
unsigned int lun_list_length;
unsigned int reserved1;
unsigned char lun0[8];
#ifdef HAVE_HOTSWAP
unsigned char lun1[8];
#endif
} __attribute__ ((packed));
struct sense_data {
unsigned char ResponseCode;
unsigned char Obsolete;
@ -83,6 +119,21 @@ struct sense_data {
unsigned short SenseKeySpecific;
} __attribute__ ((packed));
struct mode_sense_header_10 {
unsigned short mode_data_length;
unsigned char medium_type;
unsigned char device_specific;
unsigned char reserved1[2];
unsigned short block_descriptor_length;
} __attribute__ ((packed));
struct mode_sense_header_6 {
unsigned char mode_data_length;
unsigned char medium_type;
unsigned char device_specific;
unsigned char block_descriptor_length;
} __attribute__ ((packed));
struct command_block_wrapper {
unsigned int signature;
unsigned int tag;
@ -111,93 +162,195 @@ struct format_capacity {
unsigned int block_size;
} __attribute__ ((packed));
/* the ARC USB controller can at most buffer 16KB unaligned data */
static unsigned char _transfer_buffer[16384*8] __attribute((aligned (4096)));
static unsigned char _transfer_buffer[2*BUFFER_SIZE] __attribute((aligned (4096)));
static unsigned char* transfer_buffer;
static struct inquiry_data _inquiry CACHEALIGN_ATTR;
static struct inquiry_data* inquiry;
static struct capacity _capacity_data CACHEALIGN_ATTR;
static unsigned char __inquiry[CACHEALIGN_UP(sizeof(struct inquiry_data))] CACHEALIGN_ATTR;
static struct capacity* capacity_data;
static struct format_capacity _format_capacity_data CACHEALIGN_ATTR;
static unsigned char __capacity_data[CACHEALIGN_UP(sizeof(struct capacity))] CACHEALIGN_ATTR;
static struct format_capacity* format_capacity_data;
static struct sense_data _sense_data CACHEALIGN_ATTR;
static unsigned char __format_capacity_data[CACHEALIGN_UP(sizeof(struct format_capacity))] CACHEALIGN_ATTR;
static struct sense_data *sense_data;
static unsigned char __sense_data[CACHEALIGN_UP(sizeof(struct sense_data))] CACHEALIGN_ATTR;
static struct mode_sense_header_6 *mode_sense_data_6;
static unsigned char __mode_sense_data_6[CACHEALIGN_UP(sizeof(struct mode_sense_header_6))] CACHEALIGN_ATTR;
static struct mode_sense_header_10 *mode_sense_data_10;
static unsigned char __mode_sense_data_10[CACHEALIGN_UP(sizeof(struct mode_sense_header_10))] CACHEALIGN_ATTR;
static struct report_lun_data *lun_data;
static unsigned char __lun_data[CACHEALIGN_UP(sizeof(struct report_lun_data))] CACHEALIGN_ATTR;
static struct command_status_wrapper* csw;
static unsigned char __csw[CACHEALIGN_UP(sizeof(struct command_status_wrapper))] CACHEALIGN_ATTR;
static char *max_lun;
static unsigned char __max_lun[CACHEALIGN_UP(1)] CACHEALIGN_ATTR;
static struct {
unsigned int sector;
unsigned int count;
unsigned int tag;
unsigned int lun;
unsigned char *data[2];
unsigned char data_select;
unsigned int last_result;
} current_cmd;
static struct {
unsigned char sense_key;
unsigned char information;
unsigned char asc;
} cur_sense_data;
static void handle_scsi(struct command_block_wrapper* cbw);
static void send_csw(unsigned int tag, int status);
static void send_csw(int status);
static void send_command_result(void *data,int size);
static void send_block_data(void *data,int size);
static void receive_block_data(void *data,int size);
static void identify2inquiry(int lun);
static void send_and_read_next(void);
static enum {
IDLE,
SENDING,
RECEIVING
} state = IDLE;
WAITING_FOR_COMMAND,
SENDING_BLOCKS,
SENDING_RESULT,
RECEIVING_BLOCKS,
SENDING_CSW
} state = WAITING_FOR_COMMAND;
/* called by usb_code_init() */
void usb_storage_init(void)
{
inquiry = (void*)UNCACHED_ADDR(&_inquiry);
transfer_buffer = (void*)UNCACHED_ADDR(&_transfer_buffer);
capacity_data = (void*)UNCACHED_ADDR(&_capacity_data);
format_capacity_data = (void*)UNCACHED_ADDR(&_format_capacity_data);
sense_data = (void*)UNCACHED_ADDR(&_sense_data);
state = IDLE;
inquiry = (void*)UNCACHED_ADDR(&__inquiry);
capacity_data = (void*)UNCACHED_ADDR(&__capacity_data);
format_capacity_data = (void*)UNCACHED_ADDR(&__format_capacity_data);
sense_data = (void*)UNCACHED_ADDR(&__sense_data);
mode_sense_data_6 = (void*)UNCACHED_ADDR(&__mode_sense_data_6);
mode_sense_data_10 = (void*)UNCACHED_ADDR(&__mode_sense_data_10);
lun_data = (void*)UNCACHED_ADDR(&__lun_data);
max_lun = (void*)UNCACHED_ADDR(&__max_lun);
csw = (void*)UNCACHED_ADDR(&__csw);
logf("usb_storage_init done");
}
/* called by usb_core_transfer_complete() */
void usb_storage_transfer_complete(int endpoint)
void usb_storage_transfer_complete(bool in,int status,int length)
{
struct command_block_wrapper* cbw = (void*)transfer_buffer;
switch (endpoint) {
case EP_RX:
//logf("ums: %d bytes in", length);
if(state == RECEIVING)
{
int receive_count=usb_drv_get_last_transfer_length();
logf("scsi write %d %d", current_cmd.sector, current_cmd.count);
if(usb_drv_get_last_transfer_status()==0)
{
if((unsigned int)receive_count!=(SECTOR_SIZE*current_cmd.count))
{
logf("%d >= %d",SECTOR_SIZE*current_cmd.count,receive_count);
}
ata_write_sectors(IF_MV2(current_cmd.lun,)
current_cmd.sector, current_cmd.count,
transfer_buffer);
send_csw(current_cmd.tag, SCSI_STATUS_GOOD);
}
else
{
logf("Transfer failed %X",usb_drv_get_last_transfer_status());
send_csw(current_cmd.tag, SCSI_STATUS_CHECK_CONDITION);
}
//logf("transfer result %X %d", status, length);
switch(state) {
case RECEIVING_BLOCKS:
if(in==true) {
logf("IN received in RECEIVING");
}
else
{
state = SENDING;
handle_scsi(cbw);
logf("scsi write %d %d", current_cmd.sector, current_cmd.count);
if(status==0) {
if((unsigned int)length!=(SECTOR_SIZE*current_cmd.count)
&& (unsigned int)length!=BUFFER_SIZE) {
logf("unexpected length :%d",length);
}
unsigned int next_sector = current_cmd.sector + (BUFFER_SIZE/SECTOR_SIZE);
unsigned int next_count = current_cmd.count - MIN(current_cmd.count,BUFFER_SIZE/SECTOR_SIZE);
if(next_count!=0) {
/* Ask the host to send more, to the other buffer */
receive_block_data(current_cmd.data[!current_cmd.data_select],
MIN(BUFFER_SIZE,next_count*SECTOR_SIZE));
}
/* Now write the data that just came in, while the host is sending the next bit */
int result = ata_write_sectors(IF_MV2(current_cmd.lun,)
current_cmd.sector, MIN(BUFFER_SIZE/SECTOR_SIZE,current_cmd.count),
current_cmd.data[current_cmd.data_select]);
if(result != 0) {
send_csw(SCSI_STATUS_CHECK_CONDITION);
cur_sense_data.sense_key=SENSE_MEDIUM_ERROR;
cur_sense_data.asc=ASC_WRITE_ERROR;
break;
}
if(next_count==0) {
send_csw(SCSI_STATUS_GOOD);
}
/* Switch buffers for the next one */
current_cmd.data_select=!current_cmd.data_select;
current_cmd.sector = next_sector;
current_cmd.count = next_count;
}
else {
logf("Transfer failed %X",status);
send_csw(SCSI_STATUS_CHECK_CONDITION);
/* TODO fill in cur_sense_data */
cur_sense_data.sense_key=0;
cur_sense_data.information=0;
cur_sense_data.asc=0;
}
break;
case EP_TX:
//logf("ums: out complete");
if(state != IDLE)
{
/* re-prime endpoint. We only need room for commands */
state = IDLE;
usb_drv_recv(EP_RX, transfer_buffer, 1024);
case WAITING_FOR_COMMAND:
if(in==true) {
logf("IN received in WAITING_FOR_COMMAND");
}
//logf("command received");
handle_scsi(cbw);
break;
case SENDING_CSW:
if(in==false) {
logf("OUT received in SENDING_CSW");
}
//logf("csw sent, now go back to idle");
state = WAITING_FOR_COMMAND;
usb_drv_recv(EP_MASS_STORAGE, transfer_buffer, 1024);
break;
case SENDING_RESULT:
if(in==false) {
logf("OUT received in SENDING");
}
if(status==0) {
//logf("data sent, now send csw");
send_csw(SCSI_STATUS_GOOD);
}
else {
logf("Transfer failed %X",status);
send_csw(SCSI_STATUS_CHECK_CONDITION);
/* TODO fill in cur_sense_data */
cur_sense_data.sense_key=0;
cur_sense_data.information=0;
cur_sense_data.asc=0;
}
break;
case SENDING_BLOCKS:
if(in==false) {
logf("OUT received in SENDING");
}
if(status==0) {
if(current_cmd.count==0) {
//logf("data sent, now send csw");
send_csw(SCSI_STATUS_GOOD);
}
else {
send_and_read_next();
}
}
else {
logf("Transfer failed %X",status);
send_csw(SCSI_STATUS_CHECK_CONDITION);
/* TODO fill in cur_sense_data */
cur_sense_data.sense_key=0;
cur_sense_data.information=0;
cur_sense_data.asc=0;
}
break;
}
}
@ -205,15 +358,24 @@ void usb_storage_transfer_complete(int endpoint)
/* called by usb_core_control_request() */
bool usb_storage_control_request(struct usb_ctrlrequest* req)
{
/* note: interrupt context */
bool handled = false;
switch (req->bRequest) {
case USB_BULK_GET_MAX_LUN: {
static char maxlun = NUM_VOLUMES - 1;
#ifdef ONLY_EXPOSE_CARD_SLOT
*max_lun = 0;
#else
*max_lun = NUM_VOLUMES - 1;
#endif
#ifdef HAVE_HOTSWAP
/* Workaround until we find out how to do removable devices properly */
tCardInfo* cinfo = card_get_info(1);
if(cinfo->initialized==0) {
*max_lun=0;
}
#endif
logf("ums: getmaxlun");
usb_drv_send(EP_CONTROL, UNCACHED_ADDR(&maxlun), 1);
usb_drv_send(EP_CONTROL, UNCACHED_ADDR(max_lun), 1);
usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */
handled = true;
break;
@ -221,8 +383,8 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req)
case USB_BULK_RESET_REQUEST:
logf("ums: bulk reset");
usb_drv_reset_endpoint(EP_RX, false);
usb_drv_reset_endpoint(EP_TX, true);
usb_drv_reset_endpoint(EP_MASS_STORAGE, false);
usb_drv_reset_endpoint(EP_MASS_STORAGE, true);
usb_drv_send(EP_CONTROL, NULL, 0); /* ack */
handled = true;
break;
@ -230,8 +392,8 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req)
case USB_REQ_SET_CONFIGURATION:
logf("ums: set config");
/* prime rx endpoint. We only need room for commands */
state = IDLE;
usb_drv_recv(EP_RX, transfer_buffer, 1024);
state = WAITING_FOR_COMMAND;
usb_drv_recv(EP_MASS_STORAGE, transfer_buffer, 1024);
handled = true;
break;
}
@ -239,6 +401,32 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req)
return handled;
}
static void send_and_read_next(void)
{
if(current_cmd.last_result!=0) {
/* The last read failed. */
send_csw(SCSI_STATUS_CHECK_CONDITION);
cur_sense_data.sense_key=SENSE_MEDIUM_ERROR;
cur_sense_data.asc=ASC_READ_ERROR;
return;
}
send_block_data(current_cmd.data[current_cmd.data_select],
MIN(BUFFER_SIZE,current_cmd.count*SECTOR_SIZE));
/* Switch buffers for the next one */
current_cmd.data_select=!current_cmd.data_select;
current_cmd.sector+=(BUFFER_SIZE/SECTOR_SIZE);
current_cmd.count-=MIN(current_cmd.count,BUFFER_SIZE/SECTOR_SIZE);
if(current_cmd.count!=0){
/* already read the next bit, so we can send it out immediately when the
* current transfer completes. */
current_cmd.last_result = ata_read_sectors(IF_MV2(current_cmd.lun,) current_cmd.sector,
MIN(BUFFER_SIZE/SECTOR_SIZE,current_cmd.count),
current_cmd.data[current_cmd.data_select]);
}
}
/****************************************************************************/
static void handle_scsi(struct command_block_wrapper* cbw)
@ -246,141 +434,196 @@ static void handle_scsi(struct command_block_wrapper* cbw)
/* USB Mass Storage assumes LBA capability.
TODO: support 48-bit LBA */
unsigned int sectors_per_transfer=0;
unsigned int length = cbw->data_transfer_length;
unsigned int block_size;
unsigned int block_count;
bool lun_present=true;
#ifdef ONLY_EXPOSE_CARD_SLOT
unsigned char lun = cbw->lun+1;
#else
unsigned char lun = cbw->lun;
#endif
unsigned int block_size_mult = 1;
#ifdef HAVE_HOTSWAP
tCardInfo* cinfo = card_get_info(lun);
block_size = cinfo->blocksize;
if(cinfo->initialized==1)
{
sectors_per_transfer=(sizeof _transfer_buffer/ block_size);
if(cinfo->initialized==1) {
block_size = cinfo->blocksize;
block_count = cinfo->numblocks;
}
else {
lun_present=false;
block_size = 0;
block_count = 0;
}
#else
unsigned short* identify = ata_get_identify();
block_size = SECTOR_SIZE;
sectors_per_transfer=(sizeof _transfer_buffer/ block_size);
block_count = (identify[61] << 16 | identify[60]);
#endif
#ifdef MAX_LOG_SECTOR_SIZE
block_size_mult = disk_sector_multiplier;
#endif
current_cmd.tag = cbw->tag;
current_cmd.lun = lun;
switch (cbw->command_block[0]) {
case SCSI_TEST_UNIT_READY:
logf("scsi test_unit_ready %d",lun);
#ifdef HAVE_HOTSWAP
if(cinfo->initialized==1)
send_csw(cbw->tag, SCSI_STATUS_GOOD);
else
send_csw(cbw->tag, SCSI_STATUS_FAIL);
send_csw(SCSI_STATUS_GOOD);
else {
send_csw(SCSI_STATUS_FAIL);
cur_sense_data.sense_key=SENSE_NOT_READY;
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
}
#else
send_csw(cbw->tag, SCSI_STATUS_GOOD);
send_csw(SCSI_STATUS_GOOD);
#endif
break;
case SCSI_REPORT_LUNS: {
logf("scsi inquiry %d",lun);
int allocation_length=0;
allocation_length|=(cbw->command_block[6]<<24);
allocation_length|=(cbw->command_block[7]<<16);
allocation_length|=(cbw->command_block[8]<<8);
allocation_length|=(cbw->command_block[9]);
memset(lun_data,0,sizeof(struct report_lun_data));
#ifdef HAVE_HOTSWAP
lun_data->lun_list_length=htobe32(16);
lun_data->lun1[1]=1;
#else
lun_data->lun_list_length=htobe32(8);
#endif
lun_data->lun0[1]=0;
send_command_result(lun_data, MIN(sizeof(struct report_lun_data), length));
break;
}
case SCSI_INQUIRY:
logf("scsi inquiry %d",lun);
identify2inquiry(lun);
length = MIN(length, cbw->command_block[4]);
usb_drv_send(EP_TX, inquiry, MIN(sizeof _inquiry, length));
send_csw(cbw->tag, SCSI_STATUS_GOOD);
send_command_result(inquiry, MIN(sizeof(struct inquiry_data), length));
break;
case SCSI_REQUEST_SENSE: {
sense_data->ResponseCode=0x70;
sense_data->filemark_eom_ili_sensekey=2;
sense_data->Information=2;
sense_data->ResponseCode=0x70;/*current error*/
sense_data->filemark_eom_ili_sensekey=cur_sense_data.sense_key&0x0f;
sense_data->Information=cur_sense_data.information;
sense_data->AdditionalSenseLength=10;
sense_data->CommandSpecificInformation=0;
sense_data->AdditionalSenseCode=0x3a;
sense_data->AdditionalSenseCode=cur_sense_data.asc;
sense_data->AdditionalSenseCodeQualifier=0;
sense_data->FieldReplaceableUnitCode=0;
sense_data->SKSV=0;
sense_data->SenseKeySpecific=0;
logf("scsi request_sense %d",lun);
usb_drv_send(EP_TX, sense_data,
sizeof(_sense_data));
send_csw(cbw->tag, SCSI_STATUS_GOOD);
send_command_result(sense_data, sizeof(struct sense_data));
break;
}
case SCSI_MODE_SENSE: {
static unsigned char sense_data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
logf("scsi mode_sense %d",lun);
usb_drv_send(EP_TX, UNCACHED_ADDR(&sense_data),
MIN(sizeof sense_data, length));
send_csw(cbw->tag, SCSI_STATUS_GOOD);
case SCSI_MODE_SENSE_10: {
/*unsigned char pc = (cbw->command_block[2] & 0xc0) >>6;*/
unsigned char page_code = cbw->command_block[2] & 0x3f;
logf("scsi mode_sense_10 %d %X",lun,page_code);
switch(page_code) {
case 0x3f:
default:
mode_sense_data_10->mode_data_length=0;
mode_sense_data_10->medium_type=0;
mode_sense_data_10->device_specific=0;
mode_sense_data_10->block_descriptor_length=0;
send_command_result(mode_sense_data_10,
MIN(sizeof(struct mode_sense_header_10), length));
break;
#if 0
default:
send_csw(SCSI_STATUS_CHECK_CONDITION);
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
cur_sense_data.asc=ASC_INVALID_FIELD_IN_CBD;
break;
#endif
}
break;
}
case SCSI_MODE_SENSE_6: {
/*unsigned char pc = (cbw->command_block[2] & 0xc0) >>6;*/
unsigned char page_code = cbw->command_block[2] & 0x3f;
logf("scsi mode_sense_6 %d %X",lun,page_code);
switch(page_code) {
case 0x3f:
default:
/* All supported pages Since we support only one this is easy*/
mode_sense_data_6->mode_data_length=0;
mode_sense_data_6->medium_type=0;
mode_sense_data_6->device_specific=0;
mode_sense_data_6->block_descriptor_length=0;
send_command_result(mode_sense_data_6,
MIN(sizeof(struct mode_sense_header_6), length));
break;
#if 0
default:
send_csw(SCSI_STATUS_CHECK_CONDITION);
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
cur_sense_data.asc=ASC_INVALID_FIELD_IN_CBD;
break;
#endif
}
break;
}
case SCSI_START_STOP_UNIT:
logf("scsi start_stop unit %d",lun);
send_csw(cbw->tag, SCSI_STATUS_GOOD);
send_csw(SCSI_STATUS_GOOD);
break;
case SCSI_ALLOW_MEDIUM_REMOVAL:
logf("scsi allow_medium_removal %d",lun);
send_csw(cbw->tag, SCSI_STATUS_GOOD);
/* TODO: use this to show the connect screen ? */
send_csw(SCSI_STATUS_GOOD);
break;
case SCSI_READ_FORMAT_CAPACITY: {
logf("scsi read_format_capacity %d",lun);
format_capacity_data->following_length=htobe32(8);
#ifdef HAVE_HOTSWAP
/* Careful: "block count" actually means "number of last block" */
if(cinfo->initialized==1)
{
format_capacity_data->block_count = htobe32(cinfo->numblocks - 1);
format_capacity_data->block_size = htobe32(cinfo->blocksize);
}
else
{
format_capacity_data->block_count = htobe32(0);
format_capacity_data->block_size = htobe32(0);
}
#else
unsigned short* identify = ata_get_identify();
/* Careful: "block count" actually means "number of last block" */
format_capacity_data->block_count = htobe32((identify[61] << 16 | identify[60]) / block_size_mult - 1);
format_capacity_data->block_size = htobe32(block_size * block_size_mult);
#endif
format_capacity_data->block_count = htobe32(block_count/block_size_mult - 1);
format_capacity_data->block_size = htobe32(block_size*block_size_mult);
format_capacity_data->block_size |= SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA;
usb_drv_send(EP_TX, format_capacity_data,
MIN(sizeof _format_capacity_data, length));
send_csw(cbw->tag, SCSI_STATUS_GOOD);
send_command_result(format_capacity_data,
MIN(sizeof(struct format_capacity), length));
break;
}
case SCSI_READ_CAPACITY: {
logf("scsi read_capacity %d",lun);
#ifdef HAVE_HOTSWAP
/* Careful: "block count" actually means "number of last block" */
if(cinfo->initialized==1)
{
capacity_data->block_count = htobe32(cinfo->numblocks - 1);
capacity_data->block_size = htobe32(cinfo->blocksize);
}
else
{
capacity_data->block_count = htobe32(0);
capacity_data->block_size = htobe32(0);
}
#else
unsigned short* identify = ata_get_identify();
/* Careful : "block count" actually means the number of the last block */
capacity_data->block_count = htobe32((identify[61] << 16 | identify[60]) / block_size_mult - 1);
capacity_data->block_size = htobe32(block_size * block_size_mult);
#endif
usb_drv_send(EP_TX, capacity_data,
MIN(sizeof _capacity_data, length));
send_csw(cbw->tag, SCSI_STATUS_GOOD);
capacity_data->block_count = htobe32(block_count/block_size_mult - 1);
capacity_data->block_size = htobe32(block_size*block_size_mult);
send_command_result(capacity_data, MIN(sizeof(struct capacity), length));
break;
}
case SCSI_READ_10:
logf("scsi read10 %d",lun);
if(! lun_present) {
send_csw(SCSI_STATUS_CHECK_CONDITION);
cur_sense_data.sense_key=SENSE_NOT_READY;
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
break;
}
trigger_cpu_boost();
current_cmd.data[0] = transfer_buffer;
current_cmd.data[1] = &transfer_buffer[BUFFER_SIZE];
current_cmd.data_select=0;
current_cmd.sector = block_size_mult *
(cbw->command_block[2] << 24 |
cbw->command_block[3] << 16 |
@ -389,32 +632,35 @@ static void handle_scsi(struct command_block_wrapper* cbw)
current_cmd.count = block_size_mult *
(cbw->command_block[7] << 16 |
cbw->command_block[8]);
current_cmd.tag = cbw->tag;
current_cmd.lun = cbw->lun;
//logf("scsi read %d %d", current_cmd.sector, current_cmd.count);
logf("scsi read %d %d", current_cmd.sector, current_cmd.count);
//logf("Asked for %d sectors",current_cmd.count);
if(current_cmd.count > sectors_per_transfer)
{
current_cmd.count = sectors_per_transfer;
}
//logf("Sending %d sectors",current_cmd.count);
if(current_cmd.count*block_size > sizeof(_transfer_buffer)) {
send_csw(current_cmd.tag, SCSI_STATUS_CHECK_CONDITION);
if((current_cmd.sector + current_cmd.count) * block_size_mult > block_count) {
send_csw(SCSI_STATUS_CHECK_CONDITION);
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
}
else {
ata_read_sectors(IF_MV2(lun,) current_cmd.sector,
current_cmd.count, transfer_buffer);
usb_drv_send(EP_TX, transfer_buffer,
current_cmd.count*block_size);
send_csw(current_cmd.tag, SCSI_STATUS_GOOD);
/* TODO: any way to do this nonblocking ? */
current_cmd.last_result = ata_read_sectors(IF_MV2(current_cmd.lun,) current_cmd.sector,
MIN(BUFFER_SIZE/SECTOR_SIZE,current_cmd.count),
current_cmd.data[current_cmd.data_select]);
send_and_read_next();
}
break;
case SCSI_WRITE_10:
//logf("scsi write10");
logf("scsi write10 %d",lun);
if(! lun_present) {
send_csw(SCSI_STATUS_CHECK_CONDITION);
cur_sense_data.sense_key=SENSE_NOT_READY;
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
break;
}
trigger_cpu_boost();
current_cmd.data[0] = transfer_buffer;
current_cmd.data[1] = &transfer_buffer[BUFFER_SIZE];
current_cmd.data_select=0;
current_cmd.sector = block_size_mult *
(cbw->command_block[2] << 24 |
cbw->command_block[3] << 16 |
@ -423,53 +669,74 @@ static void handle_scsi(struct command_block_wrapper* cbw)
current_cmd.count = block_size_mult *
(cbw->command_block[7] << 16 |
cbw->command_block[8]);
current_cmd.tag = cbw->tag;
current_cmd.lun = cbw->lun;
/* expect data */
if(current_cmd.count*block_size > sizeof(_transfer_buffer)) {
send_csw(current_cmd.tag, SCSI_STATUS_CHECK_CONDITION);
if((current_cmd.sector + current_cmd.count) * block_size_mult > block_count) {
send_csw(SCSI_STATUS_CHECK_CONDITION);
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
}
else {
usb_drv_recv(EP_RX, transfer_buffer,
current_cmd.count*block_size);
state = RECEIVING;
receive_block_data(current_cmd.data[0],
MIN(BUFFER_SIZE,current_cmd.count*SECTOR_SIZE));
}
break;
default:
logf("scsi unknown cmd %x",cbw->command_block[0x0]);
usb_drv_stall(EP_TX, true);
send_csw(current_cmd.tag, SCSI_STATUS_GOOD);
usb_drv_stall(EP_MASS_STORAGE, true,true);
send_csw(SCSI_STATUS_GOOD);
break;
}
}
static void send_csw(unsigned int tag, int status)
static void send_block_data(void *data,int size)
{
static struct command_status_wrapper _csw;
struct command_status_wrapper* csw = UNCACHED_ADDR(&_csw);
usb_drv_send_nonblocking(EP_MASS_STORAGE, data,size);
state = SENDING_BLOCKS;
}
static void send_command_result(void *data,int size)
{
usb_drv_send_nonblocking(EP_MASS_STORAGE, data,size);
state = SENDING_RESULT;
}
static void receive_block_data(void *data,int size)
{
usb_drv_recv(EP_MASS_STORAGE, data, size);
state = RECEIVING_BLOCKS;
}
static void send_csw(int status)
{
cancel_cpu_boost();
csw->signature = CSW_SIGNATURE;
csw->tag = tag;
csw->tag = current_cmd.tag;
csw->data_residue = 0;
csw->status = status;
//logf("csw %x %x", csw->tag, csw->signature);
usb_drv_send(EP_TX, csw, sizeof _csw);
usb_drv_send_nonblocking(EP_MASS_STORAGE, csw, sizeof(struct command_status_wrapper));
state = SENDING_CSW;
logf("CSW: %X",status);
if(status == SCSI_STATUS_GOOD) {
cur_sense_data.sense_key=0;
cur_sense_data.information=0;
cur_sense_data.asc=0;
}
}
/* convert ATA IDENTIFY to SCSI INQUIRY */
static void identify2inquiry(int lun)
{
#ifdef HAVE_FLASH_STORAGE
if(lun==0)
{
if(lun==0) {
memcpy(&inquiry->VendorId,"Rockbox ",8);
memcpy(&inquiry->ProductId,"Internal Storage",16);
memcpy(&inquiry->ProductRevisionLevel,"0.00",4);
}
else
{
else {
memcpy(&inquiry->VendorId,"Rockbox ",8);
memcpy(&inquiry->ProductId,"SD Card Slot ",16);
memcpy(&inquiry->ProductRevisionLevel,"0.00",4);
@ -480,7 +747,7 @@ static void identify2inquiry(int lun)
unsigned short* src;
unsigned short* identify = ata_get_identify();
(void)lun;
memset(inquiry, 0, sizeof _inquiry);
memset(inquiry, 0, sizeof(struct inquiry_data));
if (identify[82] & 4)
inquiry->DeviceTypeModifier = DEVICE_REMOVABLE;
@ -501,8 +768,8 @@ static void identify2inquiry(int lun)
inquiry->DeviceType = DIRECT_ACCESS_DEVICE;
inquiry->AdditionalLength = 0x1f;
inquiry->Versions = 3; /* ANSI SCSI level 2 */
inquiry->Format = 3; /* ANSI SCSI level 2 INQUIRY format */
inquiry->Versions = 4; /* SPC-2 */
inquiry->Format = 2; /* SPC-2/3 inquiry format */
#ifdef HAVE_HOTSWAP
inquiry->DeviceTypeModifier = DEVICE_REMOVABLE;
@ -510,3 +777,4 @@ static void identify2inquiry(int lun)
}
#endif /* USB_STORAGE */

View File

@ -22,8 +22,7 @@
#include "usb_ch9.h"
void usb_storage_init(void);
void usb_storage_transfer(void* data);
void usb_storage_transfer_complete(int endpoint);
void usb_storage_transfer_complete(bool in,int state,int length);
bool usb_storage_control_request(struct usb_ctrlrequest* req);
#endif