e4363c116c
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14761 a1c6a512-1295-4272-9138-f99709370657
358 lines
11 KiB
C
358 lines
11 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2007 by Christian Gmeiner
|
|
*
|
|
* 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 <string.h>
|
|
#include "usb_serial.h"
|
|
#include "usbstack/utils.h"
|
|
|
|
static struct usb_dcd_controller_ops* ops;
|
|
|
|
struct usb_device_driver usb_serial_driver = {
|
|
.name = "serial",
|
|
.bind = usb_serial_driver_bind,
|
|
.unbind = usb_serial_driver_unbind,
|
|
.request = usb_serial_driver_request,
|
|
.suspend = NULL,
|
|
.resume = NULL,
|
|
.speed = usb_serial_driver_speed,
|
|
};
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/* usb descriptors */
|
|
|
|
#define MANUFACTURER_STR_ID 1
|
|
#define PRODUCT_STR_ID 2
|
|
#define SERIAL_STR_ID 3
|
|
#define BULK_CONFIG_STR_ID 4
|
|
#define DATA_STR_ID 5
|
|
|
|
/* static strings, in UTF-8 */
|
|
static struct usb_string strings[] = {
|
|
{ MANUFACTURER_STR_ID, "RockBox" },
|
|
{ PRODUCT_STR_ID, "RockBox Serial Driver" },
|
|
{ SERIAL_STR_ID, "0" },
|
|
{ BULK_CONFIG_STR_ID, "Serial Bulk" },
|
|
{ DATA_STR_ID, "Serial Data" },
|
|
};
|
|
|
|
#define BULK_CONFIG_ID 1
|
|
|
|
static struct usb_device_descriptor serial_device_desc = {
|
|
.bLength = USB_DT_DEVICE_SIZE,
|
|
.bDescriptorType = USB_DT_DEVICE,
|
|
.bcdUSB = 0x0200,
|
|
.bDeviceClass = USB_CLASS_COMM,
|
|
.bDeviceSubClass = 0,
|
|
.bDeviceProtocol = 0,
|
|
.idVendor = 0x0525,
|
|
.idProduct = 0xa4a6,
|
|
.iManufacturer = MANUFACTURER_STR_ID,
|
|
.iProduct = PRODUCT_STR_ID,
|
|
.iSerialNumber = SERIAL_STR_ID,
|
|
.bNumConfigurations = 1,
|
|
};
|
|
|
|
static struct usb_config_descriptor serial_bulk_config_desc = {
|
|
.bLength = USB_DT_CONFIG_SIZE,
|
|
.bDescriptorType = USB_DT_CONFIG,
|
|
|
|
.bNumInterfaces = 1,
|
|
.bConfigurationValue = BULK_CONFIG_ID,
|
|
.iConfiguration = BULK_CONFIG_STR_ID,
|
|
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
|
|
.bMaxPower = 1,
|
|
};
|
|
|
|
static struct usb_interface_descriptor serial_bulk_interface_desc = {
|
|
.bLength = USB_DT_INTERFACE_SIZE,
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
.bInterfaceNumber = 0,
|
|
.bNumEndpoints = 2,
|
|
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
|
.bInterfaceSubClass = 0,
|
|
.bInterfaceProtocol = 0,
|
|
.iInterface = DATA_STR_ID,
|
|
};
|
|
|
|
static struct usb_endpoint_descriptor serial_fs_in_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_IN,
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
.wMaxPacketSize = 8,
|
|
};
|
|
|
|
static struct usb_endpoint_descriptor serial_fs_out_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_OUT,
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
.wMaxPacketSize = 8,
|
|
};
|
|
|
|
static struct usb_debug_descriptor serial_debug_desc = {
|
|
.bLength = sizeof(struct usb_debug_descriptor),
|
|
.bDescriptorType = USB_DT_DEBUG,
|
|
};
|
|
|
|
static struct usb_qualifier_descriptor serial_qualifier_desc = {
|
|
.bLength = sizeof(struct usb_qualifier_descriptor),
|
|
.bDescriptorType = USB_DT_DEVICE_QUALIFIER,
|
|
.bcdUSB = 0x0200,
|
|
.bDeviceClass = USB_CLASS_COMM,
|
|
.bNumConfigurations = 1,
|
|
};
|
|
|
|
struct usb_descriptor_header *serial_fs_function[] = {
|
|
(struct usb_descriptor_header *) &serial_bulk_interface_desc,
|
|
(struct usb_descriptor_header *) &serial_fs_in_desc,
|
|
(struct usb_descriptor_header *) &serial_fs_out_desc,
|
|
NULL,
|
|
};
|
|
|
|
/* USB 2.0 */
|
|
static struct usb_endpoint_descriptor serial_hs_in_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_IN,
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
.wMaxPacketSize = 512,
|
|
};
|
|
|
|
static struct usb_endpoint_descriptor serial_hs_out_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_IN,
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
.wMaxPacketSize = 512,
|
|
};
|
|
|
|
struct usb_descriptor_header *serial_hs_function[] = {
|
|
(struct usb_descriptor_header *) &serial_bulk_interface_desc,
|
|
(struct usb_descriptor_header *) &serial_hs_in_desc,
|
|
(struct usb_descriptor_header *) &serial_hs_out_desc,
|
|
NULL,
|
|
};
|
|
|
|
#define BUFFER_SIZE 100
|
|
uint8_t buf[BUFFER_SIZE];
|
|
|
|
struct usb_response res;
|
|
|
|
/* helper functions */
|
|
static int config_buf(uint8_t *buf, uint8_t type, unsigned index);
|
|
static int set_config(int config);
|
|
|
|
struct device {
|
|
struct usb_ep* in;
|
|
struct usb_ep* out;
|
|
uint32_t used_config;
|
|
struct usb_descriptor_header** descriptors;
|
|
};
|
|
|
|
static struct device dev;
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
void usb_serial_driver_init(void)
|
|
{
|
|
logf("usb serial: register");
|
|
usb_device_driver_register(&usb_serial_driver);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
int usb_serial_driver_bind(void* controler_ops)
|
|
{
|
|
logf("usb serial: bind");
|
|
ops = controler_ops;
|
|
|
|
/* serach and asign endpoints */
|
|
usb_ep_autoconfig_reset();
|
|
|
|
dev.in = usb_ep_autoconfig(&serial_fs_in_desc);
|
|
if (!dev.in) {
|
|
goto autoconf_fail;
|
|
}
|
|
dev.in->claimed = true;
|
|
logf("usb serial: in: %s", dev.in->name);
|
|
|
|
dev.out = usb_ep_autoconfig(&serial_fs_out_desc);
|
|
if (!dev.out) {
|
|
goto autoconf_fail;
|
|
}
|
|
dev.out->claimed = true;
|
|
logf("usb serial: out: %s", dev.out->name);
|
|
|
|
/* update device decsriptor */
|
|
serial_device_desc.bMaxPacketSize0 = ops->ep0->maxpacket;
|
|
|
|
/* update qualifie descriptor */
|
|
serial_qualifier_desc.bMaxPacketSize0 = ops->ep0->maxpacket;
|
|
|
|
/* update debug descriptor */
|
|
serial_debug_desc.bDebugInEndpoint = dev.in->ep_num;
|
|
serial_debug_desc.bDebugOutEndpoint = dev.out->ep_num;
|
|
|
|
/* update hs descriptors as we asume that endpoints
|
|
are the same for fs and hs */
|
|
serial_hs_in_desc.bEndpointAddress = serial_fs_in_desc.bEndpointAddress;
|
|
serial_hs_out_desc.bEndpointAddress = serial_fs_out_desc.bEndpointAddress;
|
|
|
|
return 0;
|
|
|
|
autoconf_fail:
|
|
logf("failed to find endpoints");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
void usb_serial_driver_unbind(void) {
|
|
|
|
/* disable endpoints... */
|
|
}
|
|
|
|
int usb_serial_driver_request(struct usb_ctrlrequest* request)
|
|
{
|
|
int ret = -EOPNOTSUPP;
|
|
logf("usb serial: request");
|
|
|
|
res.length = 0;
|
|
res.buf = NULL;
|
|
|
|
switch (request->bRequestType & USB_TYPE_MASK) {
|
|
case USB_TYPE_STANDARD:
|
|
|
|
switch (request->bRequest) {
|
|
case USB_REQ_GET_DESCRIPTOR:
|
|
|
|
switch (request->wValue >> 8) {
|
|
case USB_DT_DEVICE:
|
|
logf("usb serial: sending device desc");
|
|
ret = MIN(sizeof(struct usb_device_descriptor),
|
|
request->wLength);
|
|
res.buf = &serial_device_desc;
|
|
break;
|
|
|
|
case USB_DT_DEVICE_QUALIFIER:
|
|
logf("usb serial: sending qualifier dec");
|
|
ret = MIN(sizeof(struct usb_qualifier_descriptor),
|
|
request->wLength);
|
|
res.buf = &serial_qualifier_desc;
|
|
break;
|
|
|
|
case USB_DT_OTHER_SPEED_CONFIG:
|
|
case USB_DT_CONFIG:
|
|
logf("usb serial: sending config desc");
|
|
|
|
ret = config_buf(buf, request->wValue >> 8,
|
|
request->wValue & 0xff);
|
|
if (ret >= 0) {
|
|
logf("%d, vs %d", request->wLength, ret);
|
|
ret = MIN(request->wLength, (uint16_t)ret);
|
|
}
|
|
res.buf = buf;
|
|
break;
|
|
|
|
case USB_DT_DEBUG:
|
|
logf("usb serial: sending debug desc");
|
|
ret = MIN(sizeof(struct usb_debug_descriptor),
|
|
request->wLength);
|
|
res.buf = &serial_debug_desc;
|
|
break;
|
|
|
|
case USB_DT_STRING:
|
|
logf("usb serial: sending string desc");
|
|
ret = usb_stack_get_string(strings, request->wValue & 0xff,
|
|
buf);
|
|
ret = MIN(ret, request->wLength);
|
|
res.buf = buf;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case USB_REQ_SET_CONFIGURATION:
|
|
logf("usb serial: set configuration %d", request->wValue);
|
|
ret = set_config(request->wValue);
|
|
break;
|
|
|
|
case USB_REQ_GET_CONFIGURATION:
|
|
logf("usb serial: get configuration");
|
|
ret = 1;
|
|
res.buf = &dev.used_config;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret >= 0) {
|
|
res.length = ret;
|
|
ret = ops->send(NULL, &res);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void usb_serial_driver_speed(enum usb_device_speed speed)
|
|
{
|
|
switch (speed) {
|
|
case USB_SPEED_HIGH:
|
|
logf("usb serial: using highspeed");
|
|
dev.descriptors = serial_hs_function;
|
|
break;
|
|
default:
|
|
logf("usb serial: using fullspeed");
|
|
dev.descriptors = serial_fs_function;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/* helper functions */
|
|
|
|
static int config_buf(uint8_t *buf, uint8_t type, unsigned index)
|
|
{
|
|
int len;
|
|
|
|
/* TODO check index*/
|
|
(void)index;
|
|
|
|
len = usb_stack_configdesc(&serial_bulk_config_desc, buf, BUFFER_SIZE,
|
|
dev.descriptors);
|
|
if (len < 0) {
|
|
return len;
|
|
}
|
|
((struct usb_config_descriptor *)buf)->bDescriptorType = type;
|
|
return len;
|
|
}
|
|
|
|
static int set_config(int config)
|
|
{
|
|
/* TODO check config*/
|
|
|
|
/* enable endpoints */
|
|
logf("setup %s", dev.in->name);
|
|
ops->enable(dev.in, (struct usb_endpoint_descriptor*)dev.descriptors[1]);
|
|
logf("setup %s", dev.out->name);
|
|
ops->enable(dev.out, (struct usb_endpoint_descriptor*)dev.descriptors[2]);
|
|
|
|
/* store config */
|
|
logf("using config %d", config);
|
|
dev.used_config = config;
|
|
|
|
return 0;
|
|
}
|