2007-11-22 20:51:00 +00:00
|
|
|
|
/***************************************************************************
|
|
|
|
|
* __________ __ ___.
|
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
|
* $Id: $
|
|
|
|
|
*
|
2007-11-30 00:13:00 +00:00
|
|
|
|
* Copyright (C) 2007 by Bj<EFBFBD>rn Stenberg
|
2007-11-22 20:51:00 +00:00
|
|
|
|
*
|
|
|
|
|
* 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"
|
|
|
|
|
|
2008-02-25 23:43:11 +00:00
|
|
|
|
#include "usb.h"
|
2007-11-22 20:51:00 +00:00
|
|
|
|
#include "usb_ch9.h"
|
|
|
|
|
#include "usb_drv.h"
|
|
|
|
|
#include "usb_core.h"
|
2008-03-06 21:25:09 +00:00
|
|
|
|
#include "usb_class_driver.h"
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
|
|
|
|
#if defined(USB_STORAGE)
|
|
|
|
|
#include "usb_storage.h"
|
2008-02-20 22:54:26 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(USB_SERIAL)
|
2007-11-30 00:13:00 +00:00
|
|
|
|
#include "usb_serial.h"
|
2008-02-20 22:54:26 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
/* TODO: Move target-specific stuff somewhere else (serial number reading) */
|
2008-02-20 22:54:26 +00:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_AS3514
|
|
|
|
|
#include "i2c-pp.h"
|
|
|
|
|
#include "as3514.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-02-22 23:14:24 +00:00
|
|
|
|
#if !defined(HAVE_AS3514) && !defined(IPOD_ARCH)
|
|
|
|
|
#include "ata.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
/* USB protocol descriptors: */
|
|
|
|
|
|
|
|
|
|
#define USB_SC_SCSI 0x06 /* Transparent */
|
|
|
|
|
#define USB_PROT_BULK 0x50 /* bulk only */
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
static const struct usb_device_descriptor __attribute__((aligned(2)))
|
|
|
|
|
device_descriptor=
|
|
|
|
|
{
|
2007-11-22 20:51:00 +00:00
|
|
|
|
.bLength = sizeof(struct usb_device_descriptor),
|
|
|
|
|
.bDescriptorType = USB_DT_DEVICE,
|
2008-02-24 13:45:36 +00:00
|
|
|
|
#ifdef USE_HIGH_SPEED
|
2008-02-20 22:54:26 +00:00
|
|
|
|
.bcdUSB = 0x0200,
|
2008-02-24 13:45:36 +00:00
|
|
|
|
#else
|
|
|
|
|
.bcdUSB = 0x0110,
|
|
|
|
|
#endif
|
2007-11-22 20:51:00 +00:00
|
|
|
|
.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,
|
2008-02-11 14:26:25 +00:00
|
|
|
|
.iSerialNumber = 3,
|
2007-11-22 20:51:00 +00:00
|
|
|
|
.bNumConfigurations = 1
|
2008-02-29 18:33:54 +00:00
|
|
|
|
} ;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
static struct usb_config_descriptor __attribute__((aligned(2)))
|
|
|
|
|
config_descriptor =
|
2008-02-11 14:26:25 +00:00
|
|
|
|
{
|
2008-02-22 20:38:31 +00:00
|
|
|
|
.bLength = sizeof(struct usb_config_descriptor),
|
|
|
|
|
.bDescriptorType = USB_DT_CONFIG,
|
|
|
|
|
.wTotalLength = 0, /* will be filled in later */
|
|
|
|
|
.bNumInterfaces = 1,
|
|
|
|
|
.bConfigurationValue = 1,
|
|
|
|
|
.iConfiguration = 0,
|
|
|
|
|
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
|
|
|
|
|
.bMaxPower = 250, /* 500mA in 2mA units */
|
|
|
|
|
};
|
2008-02-11 14:26:25 +00:00
|
|
|
|
|
|
|
|
|
#ifdef USB_CHARGING_ONLY
|
2008-02-22 20:38:31 +00:00
|
|
|
|
/* dummy interface for charging-only */
|
2008-03-06 21:25:09 +00:00
|
|
|
|
static struct usb_interface_descriptor __attribute__((aligned(2)))
|
|
|
|
|
charging_interface_descriptor =
|
2008-02-22 20:38:31 +00:00
|
|
|
|
{
|
|
|
|
|
.bLength = sizeof(struct usb_interface_descriptor),
|
|
|
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
|
|
|
.bInterfaceNumber = 0,
|
|
|
|
|
.bAlternateSetting = 0,
|
|
|
|
|
.bNumEndpoints = 0,
|
|
|
|
|
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
|
|
|
|
.bInterfaceSubClass = 0,
|
|
|
|
|
.bInterfaceProtocol = 0,
|
2008-03-03 17:59:58 +00:00
|
|
|
|
.iInterface = 4
|
2008-02-22 20:38:31 +00:00
|
|
|
|
};
|
2007-11-22 20:51:00 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
static const struct usb_qualifier_descriptor __attribute__((aligned(2)))
|
|
|
|
|
qualifier_descriptor =
|
2007-11-22 20:51:00 +00:00
|
|
|
|
{
|
|
|
|
|
.bLength = sizeof(struct usb_qualifier_descriptor),
|
|
|
|
|
.bDescriptorType = USB_DT_DEVICE_QUALIFIER,
|
|
|
|
|
.bcdUSB = 0x0200,
|
|
|
|
|
.bDeviceClass = 0,
|
|
|
|
|
.bDeviceSubClass = 0,
|
|
|
|
|
.bDeviceProtocol = 0,
|
|
|
|
|
.bMaxPacketSize0 = 64,
|
|
|
|
|
.bNumConfigurations = 1
|
|
|
|
|
};
|
|
|
|
|
|
2008-04-16 15:48:54 +00:00
|
|
|
|
static const struct usb_string_descriptor __attribute__((aligned(2)))
|
2008-03-06 21:25:09 +00:00
|
|
|
|
usb_string_iManufacturer =
|
2007-11-22 20:51:00 +00:00
|
|
|
|
{
|
2008-02-11 14:26:25 +00:00
|
|
|
|
24,
|
|
|
|
|
USB_DT_STRING,
|
|
|
|
|
{'R','o','c','k','b','o','x','.','o','r','g'}
|
2007-11-22 20:51:00 +00:00
|
|
|
|
};
|
|
|
|
|
|
2008-04-16 15:48:54 +00:00
|
|
|
|
static const struct usb_string_descriptor __attribute__((aligned(2)))
|
2008-03-06 21:25:09 +00:00
|
|
|
|
usb_string_iProduct =
|
2007-11-22 20:51:00 +00:00
|
|
|
|
{
|
2008-02-11 14:26:25 +00:00
|
|
|
|
42,
|
|
|
|
|
USB_DT_STRING,
|
2008-03-06 21:25:09 +00:00
|
|
|
|
{'R','o','c','k','b','o','x',' ',
|
|
|
|
|
'm','e','d','i','a',' ',
|
|
|
|
|
'p','l','a','y','e','r'}
|
2007-11-22 20:51:00 +00:00
|
|
|
|
};
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
static struct usb_string_descriptor __attribute__((aligned(2)))
|
|
|
|
|
usb_string_iSerial =
|
2007-11-22 20:51:00 +00:00
|
|
|
|
{
|
2008-03-04 12:08:00 +00:00
|
|
|
|
84,
|
2008-02-20 22:54:26 +00:00
|
|
|
|
USB_DT_STRING,
|
|
|
|
|
{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0',
|
2008-02-22 23:14:24 +00:00
|
|
|
|
'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0',
|
2008-03-04 12:08:00 +00:00
|
|
|
|
'0','0','0','0','0','0','0','0','0'}
|
2007-11-22 20:51:00 +00:00
|
|
|
|
};
|
2008-02-11 14:26:25 +00:00
|
|
|
|
|
|
|
|
|
/* Generic for all targets */
|
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
|
/* this is stringid #0: languages supported */
|
2008-04-16 15:48:54 +00:00
|
|
|
|
static const struct usb_string_descriptor __attribute__((aligned(2)))
|
2008-03-06 21:25:09 +00:00
|
|
|
|
lang_descriptor =
|
2007-11-22 20:51:00 +00:00
|
|
|
|
{
|
2008-02-11 14:26:25 +00:00
|
|
|
|
4,
|
2007-11-22 20:51:00 +00:00
|
|
|
|
USB_DT_STRING,
|
|
|
|
|
{0x0409} /* LANGID US English */
|
|
|
|
|
};
|
|
|
|
|
|
2008-04-16 15:48:54 +00:00
|
|
|
|
static const struct usb_string_descriptor __attribute__((aligned(2)))
|
2008-03-06 21:25:09 +00:00
|
|
|
|
usb_string_charging_only =
|
2008-02-11 14:26:25 +00:00
|
|
|
|
{
|
|
|
|
|
28,
|
|
|
|
|
USB_DT_STRING,
|
|
|
|
|
{'C','h','a','r','g','i','n','g',' ','o','n','l','y'}
|
|
|
|
|
};
|
|
|
|
|
|
2008-04-16 16:11:09 +00:00
|
|
|
|
static const struct usb_string_descriptor* const usb_strings[] =
|
2008-02-11 14:26:25 +00:00
|
|
|
|
{
|
|
|
|
|
&lang_descriptor,
|
|
|
|
|
&usb_string_iManufacturer,
|
|
|
|
|
&usb_string_iProduct,
|
|
|
|
|
&usb_string_iSerial,
|
|
|
|
|
&usb_string_charging_only
|
|
|
|
|
};
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
|
|
|
|
static int usb_address = 0;
|
|
|
|
|
static bool initialized = false;
|
2007-11-24 22:37:41 +00:00
|
|
|
|
static enum { DEFAULT, ADDRESS, CONFIGURED } usb_state;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
static int usb_core_num_interfaces;
|
|
|
|
|
|
2008-03-10 20:28:59 +00:00
|
|
|
|
static int usb_charging_get_config_descriptor(unsigned char *dest,int max_packet_size,
|
|
|
|
|
int interface_number,int endpoint);
|
2008-03-06 21:25:09 +00:00
|
|
|
|
|
2008-03-10 20:28:59 +00:00
|
|
|
|
static struct usb_class_driver drivers[USB_NUM_DRIVERS] =
|
2008-03-06 21:25:09 +00:00
|
|
|
|
{
|
2008-03-02 21:00:12 +00:00
|
|
|
|
#ifdef USB_STORAGE
|
2008-03-06 21:25:09 +00:00
|
|
|
|
[USB_DRIVER_MASS_STORAGE] = {
|
|
|
|
|
.enabled = false,
|
|
|
|
|
.needs_exclusive_ata = true,
|
|
|
|
|
.usb_endpoint = 0,
|
|
|
|
|
.usb_interface = 0,
|
|
|
|
|
.get_config_descriptor = usb_storage_get_config_descriptor,
|
|
|
|
|
.init_connection = usb_storage_init_connection,
|
|
|
|
|
.init = usb_storage_init,
|
|
|
|
|
.disconnect = NULL,
|
|
|
|
|
.transfer_complete = usb_storage_transfer_complete,
|
2008-03-10 20:55:24 +00:00
|
|
|
|
.control_request = usb_storage_control_request,
|
|
|
|
|
#ifdef HAVE_HOTSWAP
|
|
|
|
|
.notify_hotswap = usb_storage_notify_hotswap,
|
|
|
|
|
#endif
|
2008-03-06 21:25:09 +00:00
|
|
|
|
},
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef USB_SERIAL
|
|
|
|
|
[USB_DRIVER_SERIAL] = {
|
|
|
|
|
.enabled = false,
|
|
|
|
|
.needs_exclusive_ata = false,
|
|
|
|
|
.usb_endpoint = 0,
|
|
|
|
|
.usb_interface = 0,
|
|
|
|
|
.get_config_descriptor = usb_serial_get_config_descriptor,
|
|
|
|
|
.init_connection = usb_serial_init_connection,
|
|
|
|
|
.init = usb_serial_init,
|
|
|
|
|
.disconnect = usb_serial_disconnect,
|
|
|
|
|
.transfer_complete = usb_serial_transfer_complete,
|
2008-03-10 20:55:24 +00:00
|
|
|
|
.control_request = usb_serial_control_request,
|
|
|
|
|
#ifdef HAVE_HOTSWAP
|
|
|
|
|
.notify_hotswap = NULL,
|
|
|
|
|
#endif
|
2008-03-06 21:25:09 +00:00
|
|
|
|
},
|
2008-03-02 21:00:12 +00:00
|
|
|
|
#endif
|
|
|
|
|
#ifdef USB_CHARGING_ONLY
|
2008-03-06 21:25:09 +00:00
|
|
|
|
[USB_DRIVER_CHARGING_ONLY] = {
|
|
|
|
|
.enabled = false,
|
|
|
|
|
.needs_exclusive_ata = false,
|
|
|
|
|
.usb_endpoint = 0,
|
|
|
|
|
.usb_interface = 0,
|
|
|
|
|
.get_config_descriptor = usb_charging_get_config_descriptor,
|
|
|
|
|
.init_connection = NULL,
|
|
|
|
|
.init = NULL,
|
|
|
|
|
.disconnect = NULL,
|
|
|
|
|
.transfer_complete = NULL,
|
2008-03-10 20:55:24 +00:00
|
|
|
|
.control_request = NULL,
|
|
|
|
|
#ifdef HAVE_HOTSWAP
|
|
|
|
|
.notify_hotswap = NULL,
|
|
|
|
|
#endif
|
2008-03-06 21:25:09 +00:00
|
|
|
|
},
|
2008-03-02 21:00:12 +00:00
|
|
|
|
#endif
|
2008-03-06 21:25:09 +00:00
|
|
|
|
};
|
2008-02-25 23:43:11 +00:00
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
|
static void usb_core_control_request_handler(struct usb_ctrlrequest* req);
|
|
|
|
|
static int ack_control(struct usb_ctrlrequest* req);
|
|
|
|
|
|
2008-04-16 15:48:54 +00:00
|
|
|
|
static unsigned char response_data[256] NOCACHEBSS_ATTR;
|
2008-02-20 22:54:26 +00:00
|
|
|
|
|
2008-02-27 19:08:30 +00:00
|
|
|
|
static struct usb_transfer_completion_event_data events[NUM_ENDPOINTS];
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
2008-03-04 12:08:00 +00:00
|
|
|
|
static short hex[16] = {'0','1','2','3','4','5','6','7',
|
|
|
|
|
'8','9','A','B','C','D','E','F'};
|
2008-02-11 14:26:25 +00:00
|
|
|
|
#ifdef IPOD_ARCH
|
2008-02-20 22:54:26 +00:00
|
|
|
|
static void set_serial_descriptor(void)
|
2008-02-11 14:26:25 +00:00
|
|
|
|
{
|
|
|
|
|
#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 */
|
2008-03-04 12:08:00 +00:00
|
|
|
|
short* p = &usb_string_iSerial.wString[24];
|
2008-02-11 14:26:25 +00:00
|
|
|
|
uint32_t x;
|
|
|
|
|
int i,j;
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
for (i = 0; i < 2; i++) {
|
2008-02-11 14:26:25 +00:00
|
|
|
|
x = serial[i];
|
2008-03-06 21:25:09 +00:00
|
|
|
|
for (j=0;j<8;j++) {
|
2008-02-11 14:26:25 +00:00
|
|
|
|
*p-- = hex[x & 0xf];
|
|
|
|
|
x >>= 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-03-04 12:08:00 +00:00
|
|
|
|
usb_string_iSerial.bLength=52;
|
2008-02-20 22:54:26 +00:00
|
|
|
|
}
|
|
|
|
|
#elif defined(HAVE_AS3514)
|
|
|
|
|
static void set_serial_descriptor(void)
|
|
|
|
|
{
|
|
|
|
|
unsigned char serial[16];
|
2008-02-22 23:14:24 +00:00
|
|
|
|
/* Align 32 digits right in the 40-digit serial number */
|
2008-03-04 12:08:00 +00:00
|
|
|
|
short* p = &usb_string_iSerial.wString[1];
|
2008-02-20 22:54:26 +00:00
|
|
|
|
int i;
|
|
|
|
|
|
2008-04-15 21:33:32 +00:00
|
|
|
|
i2c_readbytes(AS3514_I2C_ADDR, AS3514_UID_0, 0x10, serial);
|
2008-03-06 21:25:09 +00:00
|
|
|
|
for (i = 0; i < 16; i++) {
|
2008-02-20 22:54:26 +00:00
|
|
|
|
*p++ = hex[(serial[i] >> 4) & 0xF];
|
|
|
|
|
*p++ = hex[(serial[i] >> 0) & 0xF];
|
|
|
|
|
}
|
2008-03-04 12:08:00 +00:00
|
|
|
|
usb_string_iSerial.bLength=68;
|
2008-02-22 23:14:24 +00:00
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
/* If we don't know the device serial number, use the one
|
|
|
|
|
* from the disk */
|
|
|
|
|
static void set_serial_descriptor(void)
|
|
|
|
|
{
|
2008-03-04 12:08:00 +00:00
|
|
|
|
short* p = &usb_string_iSerial.wString[1];
|
2008-02-22 23:14:24 +00:00
|
|
|
|
unsigned short* identify = ata_get_identify();
|
|
|
|
|
unsigned short x;
|
|
|
|
|
int i;
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
for (i = 10; i < 20; i++) {
|
2008-02-23 08:22:12 +00:00
|
|
|
|
x = identify[i];
|
2008-02-22 23:14:24 +00:00
|
|
|
|
*p++ = hex[(x >> 12) & 0xF];
|
|
|
|
|
*p++ = hex[(x >> 8) & 0xF];
|
2008-02-23 08:22:12 +00:00
|
|
|
|
*p++ = hex[(x >> 4) & 0xF];
|
|
|
|
|
*p++ = hex[(x >> 0) & 0xF];
|
2008-02-22 23:14:24 +00:00
|
|
|
|
}
|
2008-03-04 12:08:00 +00:00
|
|
|
|
usb_string_iSerial.bLength=84;
|
2008-02-11 14:26:25 +00:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
|
void usb_core_init(void)
|
|
|
|
|
{
|
2008-03-06 21:25:09 +00:00
|
|
|
|
int i;
|
2007-11-23 15:02:26 +00:00
|
|
|
|
if (initialized)
|
|
|
|
|
return;
|
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
|
usb_drv_init();
|
2008-02-27 19:08:30 +00:00
|
|
|
|
|
|
|
|
|
/* class driver init functions should be safe to call even if the driver
|
|
|
|
|
* won't be used. This simplifies other logic (i.e. we don't need to know
|
|
|
|
|
* yet which drivers will be enabled */
|
2008-03-06 21:25:09 +00:00
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled && drivers[i].init != NULL)
|
|
|
|
|
drivers[i].init();
|
|
|
|
|
}
|
2007-11-30 00:13:00 +00:00
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
|
initialized = true;
|
2007-11-24 22:37:41 +00:00
|
|
|
|
usb_state = DEFAULT;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
logf("usb_core_init() finished");
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-10 20:28:59 +00:00
|
|
|
|
static int usb_charging_get_config_descriptor(unsigned char *dest,int max_packet_size,
|
2008-03-06 21:25:09 +00:00
|
|
|
|
int interface_number,int endpoint)
|
|
|
|
|
{
|
|
|
|
|
(void) max_packet_size;
|
|
|
|
|
(void) endpoint;
|
|
|
|
|
charging_interface_descriptor.bInterfaceNumber=interface_number;
|
|
|
|
|
memcpy(dest,&charging_interface_descriptor,
|
|
|
|
|
sizeof(struct usb_interface_descriptor));
|
|
|
|
|
return sizeof(struct usb_interface_descriptor);
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
|
void usb_core_exit(void)
|
|
|
|
|
{
|
2008-03-06 21:25:09 +00:00
|
|
|
|
int i;
|
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled && drivers[i].disconnect != NULL)
|
|
|
|
|
drivers[i].disconnect ();
|
|
|
|
|
}
|
2008-03-02 20:45:33 +00:00
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
|
if (initialized) {
|
|
|
|
|
usb_drv_exit();
|
|
|
|
|
}
|
2007-11-24 22:37:41 +00:00
|
|
|
|
initialized = false;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
logf("usb_core_exit() finished");
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
void usb_core_handle_transfer_completion(
|
|
|
|
|
struct usb_transfer_completion_event_data* event)
|
2007-11-22 20:51:00 +00:00
|
|
|
|
{
|
2008-03-06 21:25:09 +00:00
|
|
|
|
int i;
|
2008-02-27 19:08:30 +00:00
|
|
|
|
switch(event->endpoint) {
|
|
|
|
|
case EP_CONTROL:
|
|
|
|
|
logf("ctrl handled %ld",current_tick);
|
2008-03-06 21:25:09 +00:00
|
|
|
|
usb_core_control_request_handler(
|
|
|
|
|
(struct usb_ctrlrequest*)event->data);
|
2008-02-27 19:08:30 +00:00
|
|
|
|
break;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
default:
|
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled &&
|
|
|
|
|
drivers[i].usb_endpoint == event->endpoint &&
|
|
|
|
|
drivers[i].transfer_complete != NULL)
|
|
|
|
|
{
|
|
|
|
|
drivers[i].transfer_complete(event->in,
|
|
|
|
|
event->status,event->length);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-02-27 19:08:30 +00:00
|
|
|
|
break;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
void usb_core_enable_driver(int driver,bool enabled)
|
2007-11-22 20:51:00 +00:00
|
|
|
|
{
|
2008-03-06 21:25:09 +00:00
|
|
|
|
drivers[driver].enabled = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool usb_core_driver_enabled(int driver)
|
|
|
|
|
{
|
|
|
|
|
return drivers[driver].enabled;
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-10 20:55:24 +00:00
|
|
|
|
#ifdef HAVE_HOTSWAP
|
|
|
|
|
void usb_core_hotswap_event(int volume,bool inserted)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled &&
|
|
|
|
|
drivers[i].notify_hotswap!=NULL)
|
|
|
|
|
{
|
|
|
|
|
drivers[i].notify_hotswap(volume,inserted);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
static void usb_core_set_serial_function_id(void)
|
|
|
|
|
{
|
|
|
|
|
int id = 0;
|
|
|
|
|
int i;
|
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled)
|
|
|
|
|
id |= 1<<i;
|
2008-02-27 19:08:30 +00:00
|
|
|
|
}
|
2008-03-06 21:25:09 +00:00
|
|
|
|
usb_string_iSerial.wString[0] = hex[id];
|
2008-02-27 19:08:30 +00:00
|
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
static void allocate_interfaces_and_endpoints(void)
|
2008-02-27 19:08:30 +00:00
|
|
|
|
{
|
2008-03-06 21:25:09 +00:00
|
|
|
|
int i;
|
|
|
|
|
int interface=0;
|
|
|
|
|
int endpoint=1;
|
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled) {
|
|
|
|
|
drivers[i].usb_endpoint = endpoint++;
|
|
|
|
|
drivers[i].usb_interface = interface++;
|
|
|
|
|
}
|
|
|
|
|
if(endpoint>NUM_ENDPOINTS) {
|
|
|
|
|
drivers[i].enabled = false;
|
2008-03-04 12:08:00 +00:00
|
|
|
|
}
|
2008-02-22 20:38:31 +00:00
|
|
|
|
}
|
2008-03-06 21:25:09 +00:00
|
|
|
|
usb_core_num_interfaces = interface;
|
|
|
|
|
}
|
2008-02-22 20:38:31 +00:00
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
static void usb_core_control_request_handler(struct usb_ctrlrequest* req)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
if(usb_state == DEFAULT) {
|
|
|
|
|
set_serial_descriptor();
|
|
|
|
|
usb_core_set_serial_function_id();
|
2007-11-30 00:13:00 +00:00
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
allocate_interfaces_and_endpoints();
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled &&
|
|
|
|
|
drivers[i].needs_exclusive_ata) {
|
|
|
|
|
usb_request_exclusive_ata();
|
2008-02-20 22:54:26 +00:00
|
|
|
|
break;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
}
|
2007-11-24 22:37:41 +00:00
|
|
|
|
}
|
2008-03-06 21:25:09 +00:00
|
|
|
|
}
|
2007-11-30 00:13:00 +00:00
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
switch(req->bRequestType & 0x1f) {
|
|
|
|
|
case 0: /* Device */
|
|
|
|
|
switch (req->bRequest) {
|
|
|
|
|
case USB_REQ_GET_CONFIGURATION: {
|
|
|
|
|
logf("usb_core: GET_CONFIG");
|
|
|
|
|
if (usb_state == ADDRESS)
|
|
|
|
|
response_data[0] = 0;
|
|
|
|
|
else
|
|
|
|
|
response_data[0] = 1;
|
|
|
|
|
if(usb_drv_send(EP_CONTROL, response_data, 1)!= 0)
|
|
|
|
|
break;
|
|
|
|
|
ack_control(req);
|
|
|
|
|
break;
|
|
|
|
|
case USB_REQ_SET_CONFIGURATION:
|
|
|
|
|
logf("usb_core: SET_CONFIG");
|
|
|
|
|
usb_drv_cancel_all_transfers();
|
|
|
|
|
if (req->wValue) {
|
|
|
|
|
usb_state = CONFIGURED;
|
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled &&
|
|
|
|
|
drivers[i].init_connection!=NULL)
|
|
|
|
|
{
|
|
|
|
|
drivers[i].init_connection(
|
|
|
|
|
drivers[i].usb_interface,
|
|
|
|
|
drivers[i].usb_endpoint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
usb_state = ADDRESS;
|
|
|
|
|
}
|
|
|
|
|
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_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(struct usb_device_descriptor);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case USB_DT_OTHER_SPEED_CONFIG:
|
|
|
|
|
case USB_DT_CONFIG: {
|
|
|
|
|
int max_packet_size;
|
|
|
|
|
|
|
|
|
|
if(req->wValue >> 8 == USB_DT_CONFIG) {
|
|
|
|
|
if(usb_drv_port_speed())
|
|
|
|
|
max_packet_size=512;
|
|
|
|
|
else
|
|
|
|
|
max_packet_size=64;
|
|
|
|
|
config_descriptor.bDescriptorType=USB_DT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if(usb_drv_port_speed())
|
|
|
|
|
max_packet_size=64;
|
|
|
|
|
else
|
|
|
|
|
max_packet_size=512;
|
|
|
|
|
config_descriptor.bDescriptorType =
|
|
|
|
|
USB_DT_OTHER_SPEED_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
size = sizeof(struct usb_config_descriptor);
|
|
|
|
|
|
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled &&
|
|
|
|
|
drivers[i].get_config_descriptor)
|
|
|
|
|
{
|
|
|
|
|
size+=drivers[i].get_config_descriptor(
|
|
|
|
|
&response_data[size],
|
|
|
|
|
max_packet_size,
|
|
|
|
|
drivers[i].usb_interface,
|
|
|
|
|
drivers[i].usb_endpoint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
config_descriptor.bNumInterfaces =
|
|
|
|
|
usb_core_num_interfaces;
|
|
|
|
|
config_descriptor.wTotalLength = size;
|
|
|
|
|
memcpy(&response_data[0],&config_descriptor,
|
|
|
|
|
sizeof(struct usb_config_descriptor));
|
|
|
|
|
|
|
|
|
|
ptr = response_data;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
case USB_DT_STRING:
|
|
|
|
|
logf("STRING %d",index);
|
|
|
|
|
if ((unsigned)index < (sizeof(usb_strings)/
|
|
|
|
|
sizeof(struct usb_string_descriptor*)))
|
|
|
|
|
{
|
|
|
|
|
size = usb_strings[index]->bLength;
|
|
|
|
|
memcpy(&response_data[0],usb_strings[index],
|
|
|
|
|
size);
|
|
|
|
|
ptr = response_data;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
logf("bad string id %d", index);
|
|
|
|
|
usb_drv_stall(EP_CONTROL, true,true);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case USB_DT_DEVICE_QUALIFIER:
|
|
|
|
|
ptr = &qualifier_descriptor;
|
|
|
|
|
size = sizeof (struct usb_qualifier_descriptor);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
logf("bad desc %d", req->wValue >> 8);
|
|
|
|
|
usb_drv_stall(EP_CONTROL, true,true);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
if (ptr) {
|
|
|
|
|
unsigned char *uncached = (void*)UNCACHED_ADDR(ptr);
|
|
|
|
|
length = MIN(size, length);
|
|
|
|
|
if(usb_drv_send(EP_CONTROL, uncached, length)!=0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ack_control(req);
|
|
|
|
|
break;
|
|
|
|
|
} /* USB_REQ_GET_DESCRIPTOR */
|
|
|
|
|
case USB_REQ_CLEAR_FEATURE:
|
|
|
|
|
break;
|
|
|
|
|
case USB_REQ_SET_FEATURE:
|
2008-02-26 18:02:26 +00:00
|
|
|
|
if(req->wValue == 2) { /* TEST_MODE */
|
|
|
|
|
int mode=req->wIndex>>8;
|
|
|
|
|
ack_control(req);
|
|
|
|
|
usb_drv_set_test_mode(mode);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
case USB_REQ_GET_STATUS:
|
|
|
|
|
response_data[0]= 0;
|
|
|
|
|
response_data[1]= 0;
|
|
|
|
|
if(usb_drv_send(EP_CONTROL, response_data, 2)!=0)
|
|
|
|
|
break;
|
2008-02-26 18:02:26 +00:00
|
|
|
|
ack_control(req);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2008-02-20 22:54:26 +00:00
|
|
|
|
break;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
case 1: /* Interface */
|
|
|
|
|
switch (req->bRequest) {
|
|
|
|
|
case USB_REQ_SET_INTERFACE:
|
|
|
|
|
logf("usb_core: SET_INTERFACE");
|
|
|
|
|
ack_control(req);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
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);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
break;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
case USB_REQ_CLEAR_FEATURE:
|
|
|
|
|
break;
|
|
|
|
|
case USB_REQ_SET_FEATURE:
|
|
|
|
|
break;
|
|
|
|
|
case USB_REQ_GET_STATUS:
|
|
|
|
|
response_data[0]= 0;
|
|
|
|
|
response_data[1]= 0;
|
|
|
|
|
if(usb_drv_send(EP_CONTROL, response_data, 2)!=0)
|
|
|
|
|
break;
|
|
|
|
|
ack_control(req);
|
|
|
|
|
break;
|
|
|
|
|
default: {
|
|
|
|
|
bool handled=false;
|
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled &&
|
|
|
|
|
drivers[i].control_request &&
|
|
|
|
|
drivers[i].usb_interface == (req->wIndex))
|
|
|
|
|
{
|
|
|
|
|
handled = drivers[i].control_request(req);
|
|
|
|
|
}
|
2008-02-20 22:54:26 +00:00
|
|
|
|
}
|
2008-03-06 21:25:09 +00:00
|
|
|
|
if(!handled) {
|
|
|
|
|
/* nope. flag error */
|
|
|
|
|
logf("usb bad req %d", req->bRequest);
|
2008-02-20 22:54:26 +00:00
|
|
|
|
usb_drv_stall(EP_CONTROL, true,true);
|
2008-03-06 21:25:09 +00:00
|
|
|
|
ack_control(req);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 2: /* Endpoint */
|
|
|
|
|
switch (req->bRequest) {
|
|
|
|
|
case USB_REQ_CLEAR_FEATURE:
|
|
|
|
|
if (req->wValue == 0 ) /* ENDPOINT_HALT */
|
|
|
|
|
usb_drv_stall(req->wIndex & 0xf, false,
|
|
|
|
|
(req->wIndex & 0x80) !=0);
|
|
|
|
|
ack_control(req);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
break;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
case USB_REQ_SET_FEATURE:
|
|
|
|
|
if (req->wValue == 0 ) /* ENDPOINT_HALT */
|
|
|
|
|
usb_drv_stall(req->wIndex & 0xf, true,
|
|
|
|
|
(req->wIndex & 0x80) !=0);
|
|
|
|
|
ack_control(req);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
break;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
case USB_REQ_GET_STATUS:
|
|
|
|
|
response_data[0]= 0;
|
|
|
|
|
response_data[1]= 0;
|
|
|
|
|
logf("usb_core: GET_STATUS");
|
|
|
|
|
if(req->wIndex>0)
|
|
|
|
|
response_data[0] = usb_drv_stalled(req->wIndex&0xf,
|
|
|
|
|
(req->wIndex&0x80)!=0);
|
|
|
|
|
if(usb_drv_send(EP_CONTROL, response_data, 2)!=0)
|
|
|
|
|
break;
|
|
|
|
|
ack_control(req);
|
|
|
|
|
break;
|
|
|
|
|
default: {
|
|
|
|
|
bool handled=false;
|
|
|
|
|
for(i=0;i<USB_NUM_DRIVERS;i++) {
|
|
|
|
|
if(drivers[i].enabled &&
|
|
|
|
|
drivers[i].control_request &&
|
|
|
|
|
drivers[i].usb_endpoint == (req->wIndex & 0xf))
|
|
|
|
|
{
|
|
|
|
|
handled = drivers[i].control_request(req);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(!handled) {
|
|
|
|
|
/* nope. flag error */
|
|
|
|
|
logf("usb bad req %d", req->bRequest);
|
|
|
|
|
usb_drv_stall(EP_CONTROL, true,true);
|
|
|
|
|
ack_control(req);
|
|
|
|
|
}
|
2008-02-20 22:54:26 +00:00
|
|
|
|
break;
|
2008-03-04 19:32:23 +00:00
|
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-02-27 19:08:30 +00:00
|
|
|
|
logf("control handled");
|
2007-11-22 20:51:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* called by usb_drv_int() */
|
|
|
|
|
void usb_core_bus_reset(void)
|
|
|
|
|
{
|
|
|
|
|
usb_address = 0;
|
2007-11-24 22:37:41 +00:00
|
|
|
|
usb_state = DEFAULT;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* called by usb_drv_transfer_completed() */
|
2008-02-20 22:54:26 +00:00
|
|
|
|
void usb_core_transfer_complete(int endpoint, bool in, int status,int length)
|
2007-11-22 20:51:00 +00:00
|
|
|
|
{
|
|
|
|
|
switch (endpoint) {
|
|
|
|
|
case EP_CONTROL:
|
|
|
|
|
/* already handled */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2008-02-20 22:54:26 +00:00
|
|
|
|
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 */
|
2008-02-27 19:08:30 +00:00
|
|
|
|
usb_signal_transfer_completion(&events[endpoint]);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
|
/* called by usb_drv_int() */
|
|
|
|
|
void usb_core_control_request(struct usb_ctrlrequest* req)
|
|
|
|
|
{
|
|
|
|
|
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);
|
2008-02-27 19:08:30 +00:00
|
|
|
|
usb_signal_transfer_completion(&events[0]);
|
2008-02-20 22:54:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ack_control(struct usb_ctrlrequest* req)
|
2007-11-22 20:51:00 +00:00
|
|
|
|
{
|
|
|
|
|
if (req->bRequestType & 0x80)
|
2008-02-20 22:54:26 +00:00
|
|
|
|
return usb_drv_recv(EP_CONTROL, NULL, 0);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
else
|
2008-02-20 22:54:26 +00:00
|
|
|
|
return usb_drv_send(EP_CONTROL, NULL, 0);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
}
|
|
|
|
|
|