1
0
Fork 0
forked from len0rd/rockbox

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; uint8_t bDescriptorType;
uint16_t wString[]; /* UTF-16LE encoded */ uint16_t wString[]; /* UTF-16LE encoded */
} __attribute__ ((packed)); } __attribute__ ((packed,aligned(2)));
/* note that "string" zero is special, it holds language codes that /* note that "string" zero is special, it holds language codes that
* the device supports, not Unicode characters. * the device supports, not Unicode characters.

View file

@ -19,6 +19,22 @@
#ifndef USB_CORE_H #ifndef USB_CORE_H
#define 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" #include "usb_ch9.h"
#if defined(CPU_PP) #if defined(CPU_PP)
@ -26,14 +42,31 @@
#define USB_IRAM_SIZE ((size_t)0xc000) #define USB_IRAM_SIZE ((size_t)0xc000)
#endif #endif
enum {
USB_CORE_QUIT,
USB_CORE_TRANSFER_COMPLETION
};
/* endpoints */ /* endpoints */
enum { enum {
EP_CONTROL = 0, EP_CONTROL = 0,
EP_RX, #ifdef USB_STORAGE
EP_TX, EP_MASS_STORAGE,
NUM_ENDPOINTS #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 */ /* queue events */
#define USB_TRANSFER_COMPLETE 1 #define USB_TRANSFER_COMPLETE 1
@ -48,7 +81,7 @@ extern int usb_max_pkt_size;
void usb_core_init(void); void usb_core_init(void);
void usb_core_exit(void); void usb_core_exit(void);
void usb_core_control_request(struct usb_ctrlrequest* req); 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); void usb_core_bus_reset(void);
bool usb_core_data_connection(void); bool usb_core_data_connection(void);

View file

@ -24,16 +24,17 @@
void usb_drv_init(void); void usb_drv_init(void);
void usb_drv_exit(void); void usb_drv_exit(void);
void usb_drv_int(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(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); int usb_drv_recv(int endpoint, void* ptr, int length);
void usb_drv_ack(struct usb_ctrlrequest* req); void usb_drv_ack(struct usb_ctrlrequest* req);
void usb_drv_set_address(int address); void usb_drv_set_address(int address);
void usb_drv_reset_endpoint(int endpoint, bool send); void usb_drv_reset_endpoint(int endpoint, bool send);
void usb_drv_wait(int endpoint, bool send); void usb_drv_wait(int endpoint, bool send);
bool usb_drv_powered(void); 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); int usb_drv_port_speed(void);
void usb_drv_cancel_all_transfers(void);
#endif #endif

View file

@ -30,6 +30,10 @@
#define REG_ID (*(volatile unsigned int *)(USB_BASE+0x000)) #define REG_ID (*(volatile unsigned int *)(USB_BASE+0x000))
#define REG_HWGENERAL (*(volatile unsigned int *)(USB_BASE+0x004)) #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_CAPLENGTH (*(volatile unsigned char*)(USB_BASE+0x100))
#define REG_DCIVERSION (*(volatile unsigned int *)(USB_BASE+0x120)) #define REG_DCIVERSION (*(volatile unsigned int *)(USB_BASE+0x120))
#define REG_DCCPARAMS (*(volatile unsigned int *)(USB_BASE+0x124)) #define REG_DCCPARAMS (*(volatile unsigned int *)(USB_BASE+0x124))
@ -278,6 +282,10 @@
DTD_STATUS_DATA_BUFF_ERR | \ DTD_STATUS_DATA_BUFF_ERR | \
DTD_STATUS_TRANSACTION_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) */ /* manual: 32.13.2 Endpoint Transfer Descriptor (dTD) */
@ -294,7 +302,7 @@ struct transfer_descriptor {
unsigned int reserved; unsigned int reserved;
} __attribute__ ((packed)); } __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; static struct transfer_descriptor* td_array;
/* manual: 32.13.1 Endpoint Queue Head (dQH) */ /* 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) */ unsigned int curr_dtd_ptr; /* Current dTD Pointer(31-5) */
struct transfer_descriptor dtd; /* dTD overlay */ struct transfer_descriptor dtd; /* dTD overlay */
unsigned int setup_buffer[2]; /* Setup data 8 bytes */ 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)); } __attribute__((packed));
static struct queue_head _qh_array[NUM_ENDPOINTS*2] __attribute((aligned (2048))); static struct queue_head _qh_array[NUM_ENDPOINTS*2] __attribute((aligned (2048)));
static struct queue_head* qh_array; 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, 0x01, 0x010000,
0x02, 0x020000, 0x02, 0x020000,
0x04, 0x040000, 0x04, 0x040000,
0x08, 0x080000,
0x10, 0x100000,
}; };
static struct transfer_descriptor* first_td;
static struct transfer_descriptor* last_td;
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void transfer_completed(void); 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, static void prepare_td(struct transfer_descriptor* td,
struct transfer_descriptor* previous_td, struct transfer_descriptor* previous_td,
void *ptr, int len); void *ptr, int len,int pipe);
static void bus_reset(void); 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); static void init_endpoints(void);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -344,6 +358,7 @@ void usb_drv_init(void)
REG_USBCMD |= USBCMD_CTRL_RESET; REG_USBCMD |= USBCMD_CTRL_RESET;
while (REG_USBCMD & USBCMD_CTRL_RESET); while (REG_USBCMD & USBCMD_CTRL_RESET);
REG_USBMODE = USBMODE_CTRL_MODE_DEVICE; REG_USBMODE = USBMODE_CTRL_MODE_DEVICE;
/* Force device to full speed */ /* Force device to full speed */
@ -352,7 +367,7 @@ void usb_drv_init(void)
td_array = (struct transfer_descriptor*)UNCACHED_ADDR(&_td_array); td_array = (struct transfer_descriptor*)UNCACHED_ADDR(&_td_array);
qh_array = (struct queue_head*)UNCACHED_ADDR(&_qh_array); qh_array = (struct queue_head*)UNCACHED_ADDR(&_qh_array);
init_queue_heads(); init_control_queue_heads();
memset(td_array, 0, sizeof _td_array); memset(td_array, 0, sizeof _td_array);
REG_ENDPOINTLISTADDR = (unsigned int)qh_array; REG_ENDPOINTLISTADDR = (unsigned int)qh_array;
@ -372,6 +387,7 @@ void usb_drv_init(void)
/* go go go */ /* go go go */
REG_USBCMD |= USBCMD_RUN; REG_USBCMD |= USBCMD_RUN;
logf("usb_drv_init() finished"); logf("usb_drv_init() finished");
logf("usb id %x", REG_ID); logf("usb id %x", REG_ID);
logf("usb dciversion %x", REG_DCIVERSION); logf("usb dciversion %x", REG_DCIVERSION);
@ -407,15 +423,7 @@ void usb_drv_int(void)
/* a control packet? */ /* a control packet? */
if (REG_ENDPTSETUPSTAT & EPSETUP_STATUS_EP0) { if (REG_ENDPTSETUPSTAT & EPSETUP_STATUS_EP0) {
/* copy setup data from packet */ control_received();
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);
} }
if (REG_ENDPTCOMPLETE) 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); logf("%sstall %d", stall?"":"un", endpoint);
if (stall) { if(in) {
REG_ENDPTCTRL(endpoint) |= EPCTRL_RX_EP_STALL; if (stall) {
REG_ENDPTCTRL(endpoint) |= EPCTRL_TX_EP_STALL; REG_ENDPTCTRL(endpoint) |= EPCTRL_TX_EP_STALL;
}
else {
REG_ENDPTCTRL(endpoint) &= ~EPCTRL_TX_EP_STALL;
}
} }
else { else {
REG_ENDPTCTRL(endpoint) &= ~EPCTRL_RX_EP_STALL; if (stall) {
REG_ENDPTCTRL(endpoint) &= ~EPCTRL_TX_EP_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) 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) int usb_drv_recv(int endpoint, void* ptr, int length)
{ {
//logf("usbrecv(%x, %d)", ptr, 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) 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) void usb_drv_set_address(int address)
{ {
REG_DEVICEADDR = address << USBDEVICEADDRESS_BIT_POS; REG_DEVICEADDR = address << USBDEVICEADDRESS_BIT_POS;
init_bulk_queue_heads();
init_endpoints(); init_endpoints();
} }
@ -496,78 +528,43 @@ void usb_drv_reset_endpoint(int endpoint, bool send)
while (REG_ENDPTFLUSH & mask); 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 */ /* 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); int pipe = endpoint * 2 + (send ? 1 : 0);
unsigned int mask = pipe2mask[pipe]; unsigned int mask = pipe2mask[pipe];
last_td = 0;
struct queue_head* qh = &qh_array[pipe]; struct queue_head* qh = &qh_array[pipe];
static long last_tick; static long last_tick;
struct transfer_descriptor* new_td;
/* /*
if (send && endpoint > EP_CONTROL) { if (send && endpoint > EP_CONTROL) {
logf("usb: sent %d bytes", len); 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; new_td=&td_array[pipe];
first_td = new_td; prepare_td(new_td, 0, ptr, len,pipe);
} //logf("starting ep %d %s",endpoint,send?"send":"receive");
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);
last_td = new_td; qh->dtd.next_td_ptr = (unsigned int)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.size_ioc_sts &= ~(QH_STATUS_HALT | QH_STATUS_ACTIVE); qh->dtd.size_ioc_sts &= ~(QH_STATUS_HALT | QH_STATUS_ACTIVE);
REG_ENDPTPRIME |= mask; 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; last_tick = current_tick;
while ((REG_ENDPTPRIME & mask)) { while ((REG_ENDPTPRIME & mask)) {
if (REG_USBSTS & USBSTS_RESET) 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 ); logf("no prime! %d %d %x", endpoint, pipe, qh->dtd.size_ioc_sts & 0xff );
return -3; 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 */ /* wait for transfer to finish */
struct transfer_descriptor* current_td = first_td; struct queue_event ev;
queue_wait(&transfer_completion_queue[pipe], &ev);
while (!((unsigned int)current_td & DTD_NEXT_TERMINATE)) { if(qh->status!=0) {
while ((current_td->size_ioc_sts & 0xff) == DTD_STATUS_ACTIVE) { return -5;
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;
} }
//logf("all tds done"); //logf("all tds done");
} }
return 0; 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, static void prepare_td(struct transfer_descriptor* td,
struct transfer_descriptor* previous_td, struct transfer_descriptor* previous_td,
void *ptr, int len) void *ptr, int len,int pipe)
{ {
//logf("adding a td : %d",len); //logf("adding a td : %d",len);
memset(td, 0, sizeof(struct transfer_descriptor)); 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_ptr2 = ((unsigned int)ptr & 0xfffff000) + 0x2000;
td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000; td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000;
td->buff_ptr4 = ((unsigned int)ptr & 0xfffff000) + 0x4000; 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) { if (previous_td != 0) {
previous_td->next_td_ptr=(unsigned int)td; previous_td->next_td_ptr=(unsigned int)td;
previous_td->size_ioc_sts&=~DTD_IOC;// Only an interrupt on the last one
} }
} }
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) static void transfer_completed(void)
{ {
int i; int ep;
unsigned int mask = REG_ENDPTCOMPLETE; unsigned int mask = REG_ENDPTCOMPLETE;
REG_ENDPTCOMPLETE |= mask; 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++) { if(td->size_ioc_sts & DTD_STATUS_ACTIVE) {
int x; /* TODO this shouldn't happen, but...*/
for (x=0; x<2; x++) { break;
unsigned int status; }
int pipe = i * 2 + x; if((td->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS != 0 && dir==0) {
/* We got less data than we asked for. */
if (mask & pipe2mask[pipe]) }
usb_core_transfer_complete(i, x ? true : false); qh->length = (td->reserved & DTD_RESERVED_LENGTH_MASK) -
((td->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS);
status = usb_drv_get_last_transfer_status(); if(td->size_ioc_sts & DTD_ERROR_MASK) {
if ((mask & pipe2mask[pipe]) && logf("pipe %d err %x", pipe, td->size_ioc_sts & DTD_ERROR_MASK);
status & DTD_ERROR_MASK) { qh->status |= td->size_ioc_sts & DTD_ERROR_MASK;
logf("pipe %d err %x", pipe, status & 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"); logf("usb: short reset timeout");
} }
REG_ENDPTFLUSH = ~0; usb_drv_cancel_all_transfers();
//while (REG_ENDPTFLUSH);
if (!(REG_PORTSC1 & PORTSCX_PORT_RESET)) { if (!(REG_PORTSC1 & PORTSCX_PORT_RESET)) {
logf("usb: slow 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 */ /* 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 i;
int rx_packetsize;
if (usb_drv_port_speed()) {
rx_packetsize = 512;
tx_packetsize = 512;
}
else {
rx_packetsize = 16;
tx_packetsize = 16;
}
memset(qh_array, 0, sizeof _qh_array); memset(qh_array, 0, sizeof _qh_array);
/*** control ***/ /*** 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].max_pkt_length = 64 << QH_MAX_PKT_LEN_POS;
qh_array[EP_CONTROL+1].dtd.next_td_ptr = QH_NEXT_TERMINATE; 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 ***/ /*** bulk ***/
qh_array[EP_RX*2].max_pkt_length = rx_packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; for(i=1;i<NUM_ENDPOINTS;i++) {
qh_array[EP_RX*2].dtd.next_td_ptr = QH_NEXT_TERMINATE; qh_array[i*2].max_pkt_length = rx_packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL;
qh_array[EP_TX*2+1].max_pkt_length = tx_packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; qh_array[i*2].dtd.next_td_ptr = QH_NEXT_TERMINATE;
qh_array[EP_TX*2+1].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) static void init_endpoints(void)
{ {
int i;
/* bulk */ /* bulk */
REG_ENDPTCTRL(EP_RX) = for(i=1;i<NUM_ENDPOINTS;i++) {
EPCTRL_RX_DATA_TOGGLE_RST | EPCTRL_RX_ENABLE | REG_ENDPTCTRL(i) =
(EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT) | EPCTRL_RX_DATA_TOGGLE_RST | EPCTRL_RX_ENABLE |
(EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT); EPCTRL_TX_DATA_TOGGLE_RST | EPCTRL_TX_ENABLE |
(EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT) |
REG_ENDPTCTRL(EP_TX) = (EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT);
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 */ /* enable usb module */
outl(inl(0x7000002C) | 0x3000000, 0x7000002C); outl(inl(0x7000002C) | 0x3000000, 0x7000002C);
DEV_EN |= DEV_USB0; DEV_EN |= DEV_USB0;
DEV_EN |= DEV_USB1; DEV_EN |= DEV_USB1;
@ -46,6 +47,7 @@ void usb_init_device(void)
#if CONFIG_CPU == PP5020 #if CONFIG_CPU == PP5020
DEV_INIT2 |= INIT_USB; DEV_INIT2 |= INIT_USB;
#endif #endif
while ((inl(0x70000028) & 0x80) == 0); while ((inl(0x70000028) & 0x80) == 0);
outl(inl(0x70000028) | 0x2, 0x70000028); outl(inl(0x70000028) | 0x2, 0x70000028);
udelay(0x186A0); udelay(0x186A0);
@ -68,9 +70,8 @@ void usb_init_device(void)
void usb_enable(bool on) void usb_enable(bool on)
{ {
if (on) { if (on) {
#ifdef USE_ROCKBOX_USB
usb_core_init(); usb_core_init();
#else #if !defined(USE_ROCKBOX_USB)
/* until we have native mass-storage mode, we want to reboot on /* until we have native mass-storage mode, we want to reboot on
usb host connect */ usb host connect */
#if defined(IRIVER_H10) || defined (IRIVER_H10_5GB) #if defined(IRIVER_H10) || defined (IRIVER_H10_5GB)

View file

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

View file

@ -21,6 +21,6 @@
void usb_benchmark_init(void); void usb_benchmark_init(void);
void usb_benchmark_control_request(struct usb_ctrlrequest* req); 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 #endif

View file

@ -23,44 +23,42 @@
//#define LOGF_ENABLE //#define LOGF_ENABLE
#include "logf.h" #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_ch9.h"
#include "usb_drv.h" #include "usb_drv.h"
#include "usb_core.h" #include "usb_core.h"
#define USB_THREAD
#if defined(USB_STORAGE) #if defined(USB_STORAGE)
#include "usb_storage.h" #include "usb_storage.h"
#define USB_THREAD #endif
#elif defined(USB_SERIAL)
#define USB_THREAD #if defined(USB_SERIAL)
#include "usb_serial.h" #include "usb_serial.h"
#elif defined(USB_BENCHMARK) #endif
#if defined(USB_BENCHMARK)
#include "usb_benchmark.h" #include "usb_benchmark.h"
#endif #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: */ /* USB protocol descriptors: */
#define USB_SC_SCSI 0x06 /* Transparent */ #define USB_SC_SCSI 0x06 /* Transparent */
#define USB_PROT_BULK 0x50 /* bulk only */ #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), .bLength = sizeof(struct usb_device_descriptor),
.bDescriptorType = USB_DT_DEVICE, .bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200, /* USB version 2.0 */ .bcdUSB = 0x0200,
.bDeviceClass = USB_CLASS_PER_INTERFACE, .bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0, .bDeviceSubClass = 0,
.bDeviceProtocol = 0, .bDeviceProtocol = 0,
@ -74,17 +72,34 @@ static const struct usb_device_descriptor device_descriptor = {
.bNumConfigurations = 1 .bNumConfigurations = 1
}; };
static const struct { static struct {
struct usb_config_descriptor config_descriptor; struct usb_config_descriptor config_descriptor;
struct usb_interface_descriptor interface_descriptor; #ifdef USB_CHARGING_ONLY
struct usb_endpoint_descriptor ep1_in_descriptor; struct usb_interface_descriptor charging_interface_descriptor;
struct usb_endpoint_descriptor ep1_out_descriptor; struct usb_endpoint_descriptor charging_ep_in_descriptor;
} config_data_fs = 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), .bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DT_CONFIG, .bDescriptorType = USB_DT_CONFIG,
.wTotalLength = sizeof config_data_fs, .wTotalLength = sizeof _config_data,
.bNumInterfaces = 1, .bNumInterfaces = 1,
.bConfigurationValue = 1, .bConfigurationValue = 1,
.iConfiguration = 0, .iConfiguration = 0,
@ -105,23 +120,23 @@ static const struct {
.bInterfaceProtocol = 0, .bInterfaceProtocol = 0,
.iInterface = 5 .iInterface = 5
}, },
/* TODO: try with zero endpoints */
{ {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN, .bEndpointAddress = EP_CHARGING_ONLY | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512, .wMaxPacketSize = 16,
.bInterval = 0 .bInterval = 0
}, },
{ {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT, .bEndpointAddress = EP_CHARGING_ONLY | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512, .wMaxPacketSize = 16,
.bInterval = 0 .bInterval = 0
} },
#endif #endif
#ifdef USB_STORAGE #ifdef USB_STORAGE
@ -141,7 +156,7 @@ static const struct {
{ {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN, .bEndpointAddress = EP_MASS_STORAGE | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 16, .wMaxPacketSize = 16,
.bInterval = 0 .bInterval = 0
@ -149,11 +164,11 @@ static const struct {
{ {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT, .bEndpointAddress = EP_MASS_STORAGE | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 16, .wMaxPacketSize = 16,
.bInterval = 0 .bInterval = 0
} },
#endif #endif
#ifdef USB_SERIAL #ifdef USB_SERIAL
@ -173,19 +188,19 @@ static const struct {
{ {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN, .bEndpointAddress = EP_SERIAL | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64, .wMaxPacketSize = 16,
.bInterval = 0 .bInterval = 0
}, },
{ {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT, .bEndpointAddress = EP_SERIAL | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64, .wMaxPacketSize = 16,
.bInterval = 0 .bInterval = 0
} },
#endif #endif
#ifdef USB_BENCHMARK #ifdef USB_BENCHMARK
@ -205,160 +220,19 @@ static const struct {
{ {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT, .bEndpointAddress = EP_BENCHMARK | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64, .wMaxPacketSize = 16,
.bInterval = 0 .bInterval = 0
}, },
{ {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | USB_DIR_IN, .bEndpointAddress = EP_BENCHMARK | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64, .wMaxPacketSize = 16,
.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,
.bInterval = 0 .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 #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'} {'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 =
{
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 = static struct usb_string_descriptor usb_string_iSerial =
{ {
34, 34,
USB_DT_STRING, 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'}
}; };
#endif
/* Generic for all targets */ /* Generic for all targets */
@ -443,10 +326,25 @@ static long usbcore_stack[DEFAULT_STACK_SIZE];
static void usb_core_thread(void); static void usb_core_thread(void);
#endif #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 #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', static short hex[16] = {'0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F'}; '8','9','A','B','C','D','E','F'};
@ -471,7 +369,23 @@ void set_serial_descriptor(void)
x >>= 4; 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 #endif
@ -480,9 +394,8 @@ void usb_core_init(void)
if (initialized) if (initialized)
return; return;
#ifdef IPOD_ARCH config_data = (void*)UNCACHED_ADDR(&_config_data);
set_serial_descriptor(); response_data = (void*)UNCACHED_ADDR(&__response_data);
#endif
queue_init(&usbcore_queue, false); queue_init(&usbcore_queue, false);
usb_drv_init(); usb_drv_init();
@ -514,10 +427,11 @@ void usb_core_exit(void)
{ {
if (initialized) { if (initialized) {
usb_drv_exit(); usb_drv_exit();
queue_delete(&usbcore_queue);
#ifdef USB_THREAD #ifdef USB_THREAD
remove_thread(usbcore_thread); queue_post(&usbcore_queue, USB_CORE_QUIT, 0);
thread_wait(usbcore_thread);
#endif #endif
queue_delete(&usbcore_queue);
} }
data_connection = false; data_connection = false;
initialized = false; initialized = false;
@ -532,25 +446,52 @@ bool usb_core_data_connection(void)
#ifdef USB_THREAD #ifdef USB_THREAD
void usb_core_thread(void) void usb_core_thread(void)
{ {
#if defined(IPOD_ARCH) || defined(HAVE_AS3514)
set_serial_descriptor();
#endif
while (1) { while (1) {
struct queue_event ev; struct queue_event ev;
queue_wait(&usbcore_queue, &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 #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 #endif
#ifdef USB_SERIAL #ifdef USB_SERIAL
usb_serial_transfer_complete(ev.id); case EP_SERIAL:
usb_serial_transfer_complete(event->in,event->status,event->length);
break;
#endif #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 #endif
/* called by usb_drv_int() */ static void usb_core_control_request_handler(struct usb_ctrlrequest* req)
void usb_core_control_request(struct usb_ctrlrequest* req)
{ {
/* note: interrupt context */ /* note: interrupt context */
data_connection = true; data_connection = true;
@ -565,29 +506,31 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
switch (req->bRequest) { switch (req->bRequest) {
case USB_REQ_SET_CONFIGURATION: case USB_REQ_SET_CONFIGURATION:
logf("usb_core: SET_CONFIG"); logf("usb_core: SET_CONFIG");
usb_drv_cancel_all_transfers();
if (req->wValue){
usb_state = CONFIGURED;
#ifdef USB_STORAGE #ifdef USB_STORAGE
usb_storage_control_request(req); usb_storage_control_request(req);
#endif #endif
#ifdef USB_SERIAL #ifdef USB_SERIAL
usb_serial_control_request(req); usb_serial_control_request(req);
#endif #endif
ack_control(req); }
if (req->wValue) else {
usb_state = CONFIGURED;
else
usb_state = ADDRESS; usb_state = ADDRESS;
}
ack_control(req);
break; break;
case USB_REQ_GET_CONFIGURATION: { case USB_REQ_GET_CONFIGURATION: {
static char confignum;
char* tmp = (void*)UNCACHED_ADDR(&confignum);
logf("usb_core: GET_CONFIG"); logf("usb_core: GET_CONFIG");
if (usb_state == ADDRESS) if (usb_state == ADDRESS)
*tmp = 0; response_data[0] = 0;
else else
*tmp = 1; response_data[0] = 1;
usb_drv_send(EP_CONTROL, tmp, 1); if(usb_drv_send(EP_CONTROL, response_data, 1)!= 0)
break;
ack_control(req); ack_control(req);
break; break;
} }
@ -597,29 +540,54 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
ack_control(req); ack_control(req);
break; 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: case USB_REQ_CLEAR_FEATURE:
logf("usb_core: CLEAR_FEATURE"); logf("usb_core: CLEAR_FEATURE");
if (req->wValue) if (req->wValue)
usb_drv_stall(req->wIndex, true); usb_drv_stall(req->wIndex & 0xf, false,(req->wIndex & 0x80) !=0);
else else
usb_drv_stall(req->wIndex, false); usb_drv_stall(req->wIndex & 0xf, false,(req->wIndex & 0x80) !=0);
ack_control(req); ack_control(req);
break; break;
case USB_REQ_SET_ADDRESS: case USB_REQ_SET_FEATURE:
usb_address = req->wValue; logf("usb_core: SET_FEATURE");
logf("usb_core: SET_ADR %d", usb_address); 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); 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_drv_set_address(usb_address);
usb_state = ADDRESS; usb_state = ADDRESS;
break; break;
}
case USB_REQ_GET_STATUS: { case USB_REQ_GET_STATUS: {
static char tmp[2] = {0,0}; response_data[0]= 0;
tmp[0] = 0; response_data[1]= 0;
tmp[1] = 0;
logf("usb_core: GET_STATUS"); 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); ack_control(req);
break; break;
} }
@ -637,18 +605,59 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
size = sizeof device_descriptor; size = sizeof device_descriptor;
break; break;
case USB_DT_CONFIG: case USB_DT_OTHER_SPEED_CONFIG:
if(usb_drv_port_speed()) case USB_DT_CONFIG: {
{ int max_packet_size;
ptr = &config_data_hs; int interface_number=0;
size = sizeof config_data_hs;
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 else {
{ if(usb_drv_port_speed()) {
ptr = &config_data_fs; max_packet_size=64;
size = sizeof config_data_fs; }
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; break;
}
case USB_DT_STRING: case USB_DT_STRING:
if ((unsigned)index < (sizeof(usb_strings)/sizeof(struct usb_string_descriptor*))) { 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 { else {
logf("bad string id %d", index); logf("bad string id %d", index);
usb_drv_stall(EP_CONTROL, true); usb_drv_stall(EP_CONTROL, true,true);
} }
break; break;
@ -668,13 +677,14 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
default: default:
logf("bad desc %d", req->wValue >> 8); logf("bad desc %d", req->wValue >> 8);
usb_drv_stall(EP_CONTROL, true); usb_drv_stall(EP_CONTROL, true,true);
break; break;
} }
if (ptr) { if (ptr) {
length = MIN(size, length); 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); ack_control(req);
break; break;
@ -693,7 +703,7 @@ void usb_core_control_request(struct usb_ctrlrequest* req)
{ {
/* nope. flag error */ /* nope. flag error */
logf("usb bad req %d", req->bRequest); logf("usb bad req %d", req->bRequest);
usb_drv_stall(EP_CONTROL, true); usb_drv_stall(EP_CONTROL, true,true);
ack_control(req); ack_control(req);
} }
break; break;
@ -709,9 +719,9 @@ void usb_core_bus_reset(void)
} }
/* called by usb_drv_transfer_completed() */ /* 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; (void)in;
#endif #endif
@ -720,25 +730,35 @@ void usb_core_transfer_complete(int endpoint, bool in)
/* already handled */ /* already handled */
break; 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: 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; 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) events[0].endpoint=0;
usb_drv_recv(EP_CONTROL, NULL, 0); events[0].in=0;
else events[0].data=(void *)req;
usb_drv_send(EP_CONTROL, NULL, 0); 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 //#define LOGF_ENABLE
#include "logf.h" #include "logf.h"
#ifdef USB_SERIAL
static unsigned char _transfer_buffer[16]; static unsigned char _transfer_buffer[16];
static unsigned char* transfer_buffer; static unsigned char* transfer_buffer;
@ -34,20 +36,26 @@ void usb_serial_init(void)
} }
/* called by usb_core_transfer_complete() */ /* 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) { int i;
case EP_RX: switch (in) {
case false:
logf("serial: %s", transfer_buffer); logf("serial: %s", transfer_buffer);
/* Data received. Send it back */
/* re-prime endpoint */ for(i=0;i<length;i++) {
usb_drv_recv(EP_RX, transfer_buffer, sizeof _transfer_buffer); if(transfer_buffer[i]>0x40 && transfer_buffer[i]<0x5b)
transfer_buffer[i]+=0x20;
/* echo back :) */ else if(transfer_buffer[i]>0x60 && transfer_buffer[i]<0x7b)
usb_drv_send(EP_TX, transfer_buffer, sizeof transfer_buffer); transfer_buffer[i]-=0x20;
}
usb_drv_send_nonblocking(EP_SERIAL, transfer_buffer, length);
break; 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; break;
} }
} }
@ -55,14 +63,12 @@ void usb_serial_transfer_complete(int endpoint)
/* called by usb_core_control_request() */ /* called by usb_core_control_request() */
bool usb_serial_control_request(struct usb_ctrlrequest* req) bool usb_serial_control_request(struct usb_ctrlrequest* req)
{ {
/* note: interrupt context */
bool handled = false; bool handled = false;
switch (req->bRequest) { switch (req->bRequest) {
case USB_REQ_SET_CONFIGURATION: case USB_REQ_SET_CONFIGURATION:
logf("serial: set config"); logf("serial: set config");
/* prime rx endpoint */ /* 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; handled = true;
break; break;
@ -72,3 +78,5 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req)
return handled; return handled;
} }
#endif /*USB_SERIAL*/

View file

@ -22,7 +22,7 @@
#include "usb_ch9.h" #include "usb_ch9.h"
void usb_serial_init(void); 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); bool usb_serial_control_request(struct usb_ctrlrequest* req);
#endif #endif

View file

@ -26,8 +26,22 @@
#include "hotswap.h" #include "hotswap.h"
#include "disk.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 #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 */ /* bulk-only class specific requests */
#define USB_BULK_RESET_REQUEST 0xff #define USB_BULK_RESET_REQUEST 0xff
#define USB_BULK_GET_MAX_LUN 0xfe #define USB_BULK_GET_MAX_LUN 0xfe
@ -40,7 +54,8 @@
#define SCSI_TEST_UNIT_READY 0x00 #define SCSI_TEST_UNIT_READY 0x00
#define SCSI_INQUIRY 0x12 #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_REQUEST_SENSE 0x03
#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1e #define SCSI_ALLOW_MEDIUM_REMOVAL 0x1e
#define SCSI_READ_CAPACITY 0x25 #define SCSI_READ_CAPACITY 0x25
@ -48,11 +63,23 @@
#define SCSI_READ_10 0x28 #define SCSI_READ_10 0x28
#define SCSI_WRITE_10 0x2a #define SCSI_WRITE_10 0x2a
#define SCSI_START_STOP_UNIT 0x1b #define SCSI_START_STOP_UNIT 0x1b
#define SCSI_REPORT_LUNS 0xa0
#define SCSI_STATUS_GOOD 0x00 #define SCSI_STATUS_GOOD 0x00
#define SCSI_STATUS_FAIL 0x01 #define SCSI_STATUS_FAIL 0x01
#define SCSI_STATUS_CHECK_CONDITION 0x02 #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 #define SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA 0x02000000
@ -69,6 +96,15 @@ struct inquiry_data {
unsigned char ProductRevisionLevel[4]; unsigned char ProductRevisionLevel[4];
} __attribute__ ((packed)); } __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 { struct sense_data {
unsigned char ResponseCode; unsigned char ResponseCode;
unsigned char Obsolete; unsigned char Obsolete;
@ -83,6 +119,21 @@ struct sense_data {
unsigned short SenseKeySpecific; unsigned short SenseKeySpecific;
} __attribute__ ((packed)); } __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 { struct command_block_wrapper {
unsigned int signature; unsigned int signature;
unsigned int tag; unsigned int tag;
@ -111,93 +162,195 @@ struct format_capacity {
unsigned int block_size; unsigned int block_size;
} __attribute__ ((packed)); } __attribute__ ((packed));
/* the ARC USB controller can at most buffer 16KB unaligned data */ static unsigned char _transfer_buffer[2*BUFFER_SIZE] __attribute((aligned (4096)));
static unsigned char _transfer_buffer[16384*8] __attribute((aligned (4096)));
static unsigned char* transfer_buffer; static unsigned char* transfer_buffer;
static struct inquiry_data _inquiry CACHEALIGN_ATTR;
static struct inquiry_data* inquiry; 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 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 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 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 { static struct {
unsigned int sector; unsigned int sector;
unsigned int count; unsigned int count;
unsigned int tag; unsigned int tag;
unsigned int lun; unsigned int lun;
unsigned char *data[2];
unsigned char data_select;
unsigned int last_result;
} current_cmd; } 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 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 identify2inquiry(int lun);
static void send_and_read_next(void);
static enum { static enum {
IDLE, WAITING_FOR_COMMAND,
SENDING, SENDING_BLOCKS,
RECEIVING SENDING_RESULT,
} state = IDLE; RECEIVING_BLOCKS,
SENDING_CSW
} state = WAITING_FOR_COMMAND;
/* called by usb_code_init() */ /* called by usb_code_init() */
void usb_storage_init(void) void usb_storage_init(void)
{ {
inquiry = (void*)UNCACHED_ADDR(&_inquiry);
transfer_buffer = (void*)UNCACHED_ADDR(&_transfer_buffer); transfer_buffer = (void*)UNCACHED_ADDR(&_transfer_buffer);
capacity_data = (void*)UNCACHED_ADDR(&_capacity_data); inquiry = (void*)UNCACHED_ADDR(&__inquiry);
format_capacity_data = (void*)UNCACHED_ADDR(&_format_capacity_data); capacity_data = (void*)UNCACHED_ADDR(&__capacity_data);
sense_data = (void*)UNCACHED_ADDR(&_sense_data); format_capacity_data = (void*)UNCACHED_ADDR(&__format_capacity_data);
state = IDLE; 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"); logf("usb_storage_init done");
} }
/* called by usb_core_transfer_complete() */ /* 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; struct command_block_wrapper* cbw = (void*)transfer_buffer;
switch (endpoint) { //logf("transfer result %X %d", status, length);
case EP_RX: switch(state) {
//logf("ums: %d bytes in", length); case RECEIVING_BLOCKS:
if(state == RECEIVING) if(in==true) {
{ logf("IN received in 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);
}
}
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; break;
case WAITING_FOR_COMMAND:
case EP_TX: if(in==true) {
//logf("ums: out complete"); logf("IN received in WAITING_FOR_COMMAND");
if(state != IDLE) }
{ //logf("command received");
/* re-prime endpoint. We only need room for commands */ handle_scsi(cbw);
state = IDLE; break;
usb_drv_recv(EP_RX, transfer_buffer, 1024); 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; break;
} }
} }
@ -205,15 +358,24 @@ void usb_storage_transfer_complete(int endpoint)
/* called by usb_core_control_request() */ /* called by usb_core_control_request() */
bool usb_storage_control_request(struct usb_ctrlrequest* req) bool usb_storage_control_request(struct usb_ctrlrequest* req)
{ {
/* note: interrupt context */
bool handled = false; bool handled = false;
switch (req->bRequest) { switch (req->bRequest) {
case USB_BULK_GET_MAX_LUN: { 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"); 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 */ usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */
handled = true; handled = true;
break; break;
@ -221,8 +383,8 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req)
case USB_BULK_RESET_REQUEST: case USB_BULK_RESET_REQUEST:
logf("ums: bulk reset"); logf("ums: bulk reset");
usb_drv_reset_endpoint(EP_RX, false); usb_drv_reset_endpoint(EP_MASS_STORAGE, false);
usb_drv_reset_endpoint(EP_TX, true); usb_drv_reset_endpoint(EP_MASS_STORAGE, true);
usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ usb_drv_send(EP_CONTROL, NULL, 0); /* ack */
handled = true; handled = true;
break; break;
@ -230,8 +392,8 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req)
case USB_REQ_SET_CONFIGURATION: case USB_REQ_SET_CONFIGURATION:
logf("ums: set config"); logf("ums: set config");
/* prime rx endpoint. We only need room for commands */ /* prime rx endpoint. We only need room for commands */
state = IDLE; state = WAITING_FOR_COMMAND;
usb_drv_recv(EP_RX, transfer_buffer, 1024); usb_drv_recv(EP_MASS_STORAGE, transfer_buffer, 1024);
handled = true; handled = true;
break; break;
} }
@ -239,6 +401,32 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req)
return handled; 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) 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. /* USB Mass Storage assumes LBA capability.
TODO: support 48-bit LBA */ TODO: support 48-bit LBA */
unsigned int sectors_per_transfer=0;
unsigned int length = cbw->data_transfer_length; unsigned int length = cbw->data_transfer_length;
unsigned int block_size; 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; unsigned char lun = cbw->lun;
#endif
unsigned int block_size_mult = 1; unsigned int block_size_mult = 1;
#ifdef HAVE_HOTSWAP #ifdef HAVE_HOTSWAP
tCardInfo* cinfo = card_get_info(lun); tCardInfo* cinfo = card_get_info(lun);
block_size = cinfo->blocksize; if(cinfo->initialized==1) {
if(cinfo->initialized==1) block_size = cinfo->blocksize;
{ block_count = cinfo->numblocks;
sectors_per_transfer=(sizeof _transfer_buffer/ block_size); }
else {
lun_present=false;
block_size = 0;
block_count = 0;
} }
#else #else
unsigned short* identify = ata_get_identify();
block_size = SECTOR_SIZE; block_size = SECTOR_SIZE;
sectors_per_transfer=(sizeof _transfer_buffer/ block_size); block_count = (identify[61] << 16 | identify[60]);
#endif #endif
#ifdef MAX_LOG_SECTOR_SIZE #ifdef MAX_LOG_SECTOR_SIZE
block_size_mult = disk_sector_multiplier; block_size_mult = disk_sector_multiplier;
#endif #endif
current_cmd.tag = cbw->tag;
current_cmd.lun = lun;
switch (cbw->command_block[0]) { switch (cbw->command_block[0]) {
case SCSI_TEST_UNIT_READY: case SCSI_TEST_UNIT_READY:
logf("scsi test_unit_ready %d",lun); logf("scsi test_unit_ready %d",lun);
#ifdef HAVE_HOTSWAP #ifdef HAVE_HOTSWAP
if(cinfo->initialized==1) if(cinfo->initialized==1)
send_csw(cbw->tag, SCSI_STATUS_GOOD); send_csw(SCSI_STATUS_GOOD);
else else {
send_csw(cbw->tag, SCSI_STATUS_FAIL); send_csw(SCSI_STATUS_FAIL);
cur_sense_data.sense_key=SENSE_NOT_READY;
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
}
#else #else
send_csw(cbw->tag, SCSI_STATUS_GOOD); send_csw(SCSI_STATUS_GOOD);
#endif #endif
break; 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: case SCSI_INQUIRY:
logf("scsi inquiry %d",lun); logf("scsi inquiry %d",lun);
identify2inquiry(lun); identify2inquiry(lun);
length = MIN(length, cbw->command_block[4]); length = MIN(length, cbw->command_block[4]);
usb_drv_send(EP_TX, inquiry, MIN(sizeof _inquiry, length)); send_command_result(inquiry, MIN(sizeof(struct inquiry_data), length));
send_csw(cbw->tag, SCSI_STATUS_GOOD);
break; break;
case SCSI_REQUEST_SENSE: { case SCSI_REQUEST_SENSE: {
sense_data->ResponseCode=0x70; sense_data->ResponseCode=0x70;/*current error*/
sense_data->filemark_eom_ili_sensekey=2; sense_data->filemark_eom_ili_sensekey=cur_sense_data.sense_key&0x0f;
sense_data->Information=2; sense_data->Information=cur_sense_data.information;
sense_data->AdditionalSenseLength=10; sense_data->AdditionalSenseLength=10;
sense_data->CommandSpecificInformation=0; sense_data->CommandSpecificInformation=0;
sense_data->AdditionalSenseCode=0x3a; sense_data->AdditionalSenseCode=cur_sense_data.asc;
sense_data->AdditionalSenseCodeQualifier=0; sense_data->AdditionalSenseCodeQualifier=0;
sense_data->FieldReplaceableUnitCode=0; sense_data->FieldReplaceableUnitCode=0;
sense_data->SKSV=0; sense_data->SKSV=0;
sense_data->SenseKeySpecific=0; sense_data->SenseKeySpecific=0;
logf("scsi request_sense %d",lun); logf("scsi request_sense %d",lun);
usb_drv_send(EP_TX, sense_data, send_command_result(sense_data, sizeof(struct sense_data));
sizeof(_sense_data));
send_csw(cbw->tag, SCSI_STATUS_GOOD);
break; break;
} }
case SCSI_MODE_SENSE: { case SCSI_MODE_SENSE_10: {
static unsigned char sense_data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /*unsigned char pc = (cbw->command_block[2] & 0xc0) >>6;*/
logf("scsi mode_sense %d",lun); unsigned char page_code = cbw->command_block[2] & 0x3f;
usb_drv_send(EP_TX, UNCACHED_ADDR(&sense_data), logf("scsi mode_sense_10 %d %X",lun,page_code);
MIN(sizeof sense_data, length)); switch(page_code) {
send_csw(cbw->tag, SCSI_STATUS_GOOD); 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; break;
} }
case SCSI_START_STOP_UNIT: case SCSI_START_STOP_UNIT:
logf("scsi start_stop unit %d",lun); logf("scsi start_stop unit %d",lun);
send_csw(cbw->tag, SCSI_STATUS_GOOD); send_csw(SCSI_STATUS_GOOD);
break; break;
case SCSI_ALLOW_MEDIUM_REMOVAL: case SCSI_ALLOW_MEDIUM_REMOVAL:
logf("scsi allow_medium_removal %d",lun); 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; break;
case SCSI_READ_FORMAT_CAPACITY: { case SCSI_READ_FORMAT_CAPACITY: {
logf("scsi read_format_capacity %d",lun); logf("scsi read_format_capacity %d",lun);
format_capacity_data->following_length=htobe32(8); format_capacity_data->following_length=htobe32(8);
#ifdef HAVE_HOTSWAP
/* Careful: "block count" actually means "number of last block" */ /* Careful: "block count" actually means "number of last block" */
if(cinfo->initialized==1) 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_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_size |= SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA; format_capacity_data->block_size |= SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA;
usb_drv_send(EP_TX, format_capacity_data, send_command_result(format_capacity_data,
MIN(sizeof _format_capacity_data, length)); MIN(sizeof(struct format_capacity), length));
send_csw(cbw->tag, SCSI_STATUS_GOOD);
break; break;
} }
case SCSI_READ_CAPACITY: { case SCSI_READ_CAPACITY: {
logf("scsi read_capacity %d",lun); logf("scsi read_capacity %d",lun);
#ifdef HAVE_HOTSWAP
/* Careful: "block count" actually means "number of last block" */ /* Careful: "block count" actually means "number of last block" */
if(cinfo->initialized==1) capacity_data->block_count = htobe32(block_count/block_size_mult - 1);
{ capacity_data->block_size = htobe32(block_size*block_size_mult);
capacity_data->block_count = htobe32(cinfo->numblocks - 1);
capacity_data->block_size = htobe32(cinfo->blocksize); send_command_result(capacity_data, MIN(sizeof(struct capacity), length));
}
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);
break; break;
} }
case SCSI_READ_10: 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 * current_cmd.sector = block_size_mult *
(cbw->command_block[2] << 24 | (cbw->command_block[2] << 24 |
cbw->command_block[3] << 16 | cbw->command_block[3] << 16 |
@ -389,32 +632,35 @@ static void handle_scsi(struct command_block_wrapper* cbw)
current_cmd.count = block_size_mult * current_cmd.count = block_size_mult *
(cbw->command_block[7] << 16 | (cbw->command_block[7] << 16 |
cbw->command_block[8]); 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.sector + current_cmd.count) * block_size_mult > block_count) {
if(current_cmd.count > sectors_per_transfer) send_csw(SCSI_STATUS_CHECK_CONDITION);
{ cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
current_cmd.count = sectors_per_transfer; cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
}
//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);
} }
else { else {
ata_read_sectors(IF_MV2(lun,) current_cmd.sector, /* TODO: any way to do this nonblocking ? */
current_cmd.count, transfer_buffer); current_cmd.last_result = ata_read_sectors(IF_MV2(current_cmd.lun,) current_cmd.sector,
usb_drv_send(EP_TX, transfer_buffer, MIN(BUFFER_SIZE/SECTOR_SIZE,current_cmd.count),
current_cmd.count*block_size); current_cmd.data[current_cmd.data_select]);
send_csw(current_cmd.tag, SCSI_STATUS_GOOD); send_and_read_next();
} }
break; break;
case SCSI_WRITE_10: 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 * current_cmd.sector = block_size_mult *
(cbw->command_block[2] << 24 | (cbw->command_block[2] << 24 |
cbw->command_block[3] << 16 | cbw->command_block[3] << 16 |
@ -423,53 +669,74 @@ static void handle_scsi(struct command_block_wrapper* cbw)
current_cmd.count = block_size_mult * current_cmd.count = block_size_mult *
(cbw->command_block[7] << 16 | (cbw->command_block[7] << 16 |
cbw->command_block[8]); cbw->command_block[8]);
current_cmd.tag = cbw->tag;
current_cmd.lun = cbw->lun;
/* expect data */ /* expect data */
if(current_cmd.count*block_size > sizeof(_transfer_buffer)) { if((current_cmd.sector + current_cmd.count) * block_size_mult > block_count) {
send_csw(current_cmd.tag, SCSI_STATUS_CHECK_CONDITION); send_csw(SCSI_STATUS_CHECK_CONDITION);
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
} }
else { else {
usb_drv_recv(EP_RX, transfer_buffer, receive_block_data(current_cmd.data[0],
current_cmd.count*block_size); MIN(BUFFER_SIZE,current_cmd.count*SECTOR_SIZE));
state = RECEIVING;
} }
break; break;
default: default:
logf("scsi unknown cmd %x",cbw->command_block[0x0]); logf("scsi unknown cmd %x",cbw->command_block[0x0]);
usb_drv_stall(EP_TX, true); usb_drv_stall(EP_MASS_STORAGE, true,true);
send_csw(current_cmd.tag, SCSI_STATUS_GOOD); send_csw(SCSI_STATUS_GOOD);
break; 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; usb_drv_send_nonblocking(EP_MASS_STORAGE, data,size);
struct command_status_wrapper* csw = UNCACHED_ADDR(&_csw); 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->signature = CSW_SIGNATURE;
csw->tag = tag; csw->tag = current_cmd.tag;
csw->data_residue = 0; csw->data_residue = 0;
csw->status = status; csw->status = status;
//logf("csw %x %x", csw->tag, csw->signature); usb_drv_send_nonblocking(EP_MASS_STORAGE, csw, sizeof(struct command_status_wrapper));
usb_drv_send(EP_TX, csw, sizeof _csw); 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 */ /* convert ATA IDENTIFY to SCSI INQUIRY */
static void identify2inquiry(int lun) static void identify2inquiry(int lun)
{ {
#ifdef HAVE_FLASH_STORAGE #ifdef HAVE_FLASH_STORAGE
if(lun==0) if(lun==0) {
{
memcpy(&inquiry->VendorId,"Rockbox ",8); memcpy(&inquiry->VendorId,"Rockbox ",8);
memcpy(&inquiry->ProductId,"Internal Storage",16); memcpy(&inquiry->ProductId,"Internal Storage",16);
memcpy(&inquiry->ProductRevisionLevel,"0.00",4); memcpy(&inquiry->ProductRevisionLevel,"0.00",4);
} }
else else {
{
memcpy(&inquiry->VendorId,"Rockbox ",8); memcpy(&inquiry->VendorId,"Rockbox ",8);
memcpy(&inquiry->ProductId,"SD Card Slot ",16); memcpy(&inquiry->ProductId,"SD Card Slot ",16);
memcpy(&inquiry->ProductRevisionLevel,"0.00",4); memcpy(&inquiry->ProductRevisionLevel,"0.00",4);
@ -480,7 +747,7 @@ static void identify2inquiry(int lun)
unsigned short* src; unsigned short* src;
unsigned short* identify = ata_get_identify(); unsigned short* identify = ata_get_identify();
(void)lun; (void)lun;
memset(inquiry, 0, sizeof _inquiry); memset(inquiry, 0, sizeof(struct inquiry_data));
if (identify[82] & 4) if (identify[82] & 4)
inquiry->DeviceTypeModifier = DEVICE_REMOVABLE; inquiry->DeviceTypeModifier = DEVICE_REMOVABLE;
@ -501,8 +768,8 @@ static void identify2inquiry(int lun)
inquiry->DeviceType = DIRECT_ACCESS_DEVICE; inquiry->DeviceType = DIRECT_ACCESS_DEVICE;
inquiry->AdditionalLength = 0x1f; inquiry->AdditionalLength = 0x1f;
inquiry->Versions = 3; /* ANSI SCSI level 2 */ inquiry->Versions = 4; /* SPC-2 */
inquiry->Format = 3; /* ANSI SCSI level 2 INQUIRY format */ inquiry->Format = 2; /* SPC-2/3 inquiry format */
#ifdef HAVE_HOTSWAP #ifdef HAVE_HOTSWAP
inquiry->DeviceTypeModifier = DEVICE_REMOVABLE; 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" #include "usb_ch9.h"
void usb_storage_init(void); void usb_storage_init(void);
void usb_storage_transfer(void* data); void usb_storage_transfer_complete(bool in,int state,int length);
void usb_storage_transfer_complete(int endpoint);
bool usb_storage_control_request(struct usb_ctrlrequest* req); bool usb_storage_control_request(struct usb_ctrlrequest* req);
#endif #endif