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:
parent
6af732d17a
commit
07427592a9
12 changed files with 987 additions and 610 deletions
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue