rockbox/firmware/usbstack/usb_core.c
Björn Stenberg 2f7cffa204 Major USB fixes by Frank Gevaerts. Still disabled in builds, #define USE_ROCKBOX_USB to test.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16279 a1c6a512-1295-4272-9138-f99709370657
2008-02-11 14:26:25 +00:00

744 lines
21 KiB
C
Raw Blame History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id: $
*
* Copyright (C) 2007 by Bj<42>rn Stenberg
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
#include "thread.h"
#include "kernel.h"
#include "string.h"
//#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"
#if defined(USB_STORAGE)
#include "usb_storage.h"
#define USB_THREAD
#elif defined(USB_SERIAL)
#define USB_THREAD
#include "usb_serial.h"
#elif defined(USB_BENCHMARK)
#include "usb_benchmark.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 = {
.bLength = sizeof(struct usb_device_descriptor),
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200, /* USB version 2.0 */
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = USB_VENDOR_ID,
.idProduct = USB_PRODUCT_ID,
.bcdDevice = 0x0100,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1
};
static const 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 =
{
{
.bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = sizeof config_data_fs,
.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
},
{
.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 = 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 = 16,
.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 = 64,
.bInterval = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_RX | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64,
.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 = 64,
.bInterval = 0
},
{
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_TX | 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,
.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
};
static const struct usb_qualifier_descriptor qualifier_descriptor =
{
.bLength = sizeof(struct usb_qualifier_descriptor),
.bDescriptorType = USB_DT_DEVICE_QUALIFIER,
.bcdUSB = 0x0200,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1
};
static struct usb_string_descriptor usb_string_iManufacturer =
{
24,
USB_DT_STRING,
{'R','o','c','k','b','o','x','.','o','r','g'}
};
static struct usb_string_descriptor usb_string_iProduct =
{
42,
USB_DT_STRING,
{'R','o','c','k','b','o','x',' ','m','e','d','i','a',' ','p','l','a','y','e','r'}
};
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'}
};
/* Generic for all targets */
/* this is stringid #0: languages supported */
static struct usb_string_descriptor lang_descriptor =
{
4,
USB_DT_STRING,
{0x0409} /* LANGID US English */
};
static struct usb_string_descriptor usb_string_usb_benchmark =
{
40,
USB_DT_STRING,
{'B','u','l','k',' ','t','e','s','t',' ','i','n','t','e','r','f','a','c','e'}
};
static struct usb_string_descriptor usb_string_charging_only =
{
28,
USB_DT_STRING,
{'C','h','a','r','g','i','n','g',' ','o','n','l','y'}
};
static struct usb_string_descriptor* usb_strings[] =
{
&lang_descriptor,
&usb_string_iManufacturer,
&usb_string_iProduct,
&usb_string_iSerial,
&usb_string_usb_benchmark,
&usb_string_charging_only
};
static int usb_address = 0;
static bool initialized = false;
static bool data_connection = false;
static struct event_queue usbcore_queue;
static enum { DEFAULT, ADDRESS, CONFIGURED } usb_state;
#ifdef USB_THREAD
static const char usbcore_thread_name[] = "usb_core";
static struct thread_entry* usbcore_thread;
static long usbcore_stack[DEFAULT_STACK_SIZE];
static void usb_core_thread(void);
#endif
static void ack_control(struct usb_ctrlrequest* req);
#ifdef IPOD_ARCH
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'};
#ifdef IPOD_VIDEO
uint32_t* serial = (uint32_t*)(0x20004034);
#else
uint32_t* serial = (uint32_t*)(0x20002034);
#endif
/* We need to convert from a little-endian 64-bit int
into a utf-16 string of hex characters */
short* p = &usb_string_iSerial.wString[15];
uint32_t x;
int i,j;
for (i = 0; i < 2; i++)
{
x = serial[i];
for (j=0;j<8;j++)
{
*p-- = hex[x & 0xf];
x >>= 4;
}
}
}
#endif
void usb_core_init(void)
{
if (initialized)
return;
#ifdef IPOD_ARCH
set_serial_descriptor();
#endif
queue_init(&usbcore_queue, false);
usb_drv_init();
#ifdef USB_STORAGE
usb_storage_init();
#endif
#ifdef USB_SERIAL
usb_serial_init();
#endif
#ifdef USB_THREAD
usbcore_thread =
create_thread(usb_core_thread, usbcore_stack, sizeof(usbcore_stack), 0,
usbcore_thread_name
IF_PRIO(, PRIORITY_SYSTEM)
IF_COP(, CPU));
#endif
#ifdef USB_BENCHMARK
usb_benchmark_init();
#endif
initialized = true;
usb_state = DEFAULT;
logf("usb_core_init() finished");
}
void usb_core_exit(void)
{
if (initialized) {
usb_drv_exit();
queue_delete(&usbcore_queue);
#ifdef USB_THREAD
remove_thread(usbcore_thread);
#endif
}
data_connection = false;
initialized = false;
logf("usb_core_exit() finished");
}
bool usb_core_data_connection(void)
{
return data_connection;
}
#ifdef USB_THREAD
void usb_core_thread(void)
{
while (1) {
struct queue_event ev;
queue_wait(&usbcore_queue, &ev);
#ifdef USB_STORAGE
usb_storage_transfer_complete(ev.id);
#endif
#ifdef USB_SERIAL
usb_serial_transfer_complete(ev.id);
#endif
}
}
#endif
/* called by usb_drv_int() */
void usb_core_control_request(struct usb_ctrlrequest* req)
{
/* note: interrupt context */
data_connection = true;
#ifdef USB_BENCHMARK
if ((req->bRequestType & 0x60) == USB_TYPE_VENDOR) {
usb_benchmark_control_request(req);
return;
}
#endif
switch (req->bRequest) {
case USB_REQ_SET_CONFIGURATION:
logf("usb_core: SET_CONFIG");
#ifdef USB_STORAGE
usb_storage_control_request(req);
#endif
#ifdef USB_SERIAL
usb_serial_control_request(req);
#endif
ack_control(req);
if (req->wValue)
usb_state = CONFIGURED;
else
usb_state = ADDRESS;
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;
else
*tmp = 1;
usb_drv_send(EP_CONTROL, tmp, 1);
ack_control(req);
break;
}
case USB_REQ_SET_INTERFACE:
logf("usb_core: SET_INTERFACE");
ack_control(req);
break;
case USB_REQ_CLEAR_FEATURE:
logf("usb_core: CLEAR_FEATURE");
if (req->wValue)
usb_drv_stall(req->wIndex, true);
else
usb_drv_stall(req->wIndex, false);
ack_control(req);
break;
case USB_REQ_SET_ADDRESS:
usb_address = req->wValue;
logf("usb_core: SET_ADR %d", usb_address);
ack_control(req);
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;
logf("usb_core: GET_STATUS");
usb_drv_send(EP_CONTROL, UNCACHED_ADDR(&tmp), 2);
ack_control(req);
break;
}
case USB_REQ_GET_DESCRIPTOR: {
int index = req->wValue & 0xff;
int length = req->wLength;
int size;
const void* ptr = NULL;
logf("usb_core: GET_DESC %d", req->wValue >> 8);
switch (req->wValue >> 8) { /* type */
case USB_DT_DEVICE:
ptr = &device_descriptor;
size = sizeof device_descriptor;
break;
case USB_DT_CONFIG:
if(usb_drv_port_speed())
{
ptr = &config_data_hs;
size = sizeof config_data_hs;
}
else
{
ptr = &config_data_fs;
size = sizeof config_data_fs;
}
break;
case USB_DT_STRING:
if ((unsigned)index < (sizeof(usb_strings)/sizeof(struct usb_string_descriptor*))) {
ptr = usb_strings[index];
size = usb_strings[index]->bLength;
}
else {
logf("bad string id %d", index);
usb_drv_stall(EP_CONTROL, true);
}
break;
case USB_DT_DEVICE_QUALIFIER:
ptr = &qualifier_descriptor;
size = sizeof qualifier_descriptor;
break;
default:
logf("bad desc %d", req->wValue >> 8);
usb_drv_stall(EP_CONTROL, true);
break;
}
if (ptr) {
length = MIN(size, length);
usb_drv_send(EP_CONTROL, (void*)UNCACHED_ADDR(ptr), length);
}
ack_control(req);
break;
} /* USB_REQ_GET_DESCRIPTOR */
default:
#ifdef USB_STORAGE
/* does usb_storage know this request? */
if (!usb_storage_control_request(req))
#endif
#ifdef USB_SERIAL
/* does usb_serial know this request? */
if (!usb_serial_control_request(req))
#endif
{
/* nope. flag error */
logf("usb bad req %d", req->bRequest);
usb_drv_stall(EP_CONTROL, true);
ack_control(req);
}
break;
}
}
/* called by usb_drv_int() */
void usb_core_bus_reset(void)
{
usb_address = 0;
data_connection = false;
usb_state = DEFAULT;
}
/* called by usb_drv_transfer_completed() */
void usb_core_transfer_complete(int endpoint, bool in)
{
#ifdef USB_CHARGING_ONLY
(void)in;
#endif
switch (endpoint) {
case EP_CONTROL:
/* 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:
break;
}
}
static void ack_control(struct usb_ctrlrequest* req)
{
if (req->bRequestType & 0x80)
usb_drv_recv(EP_CONTROL, NULL, 0);
else
usb_drv_send(EP_CONTROL, NULL, 0);
}