2014-05-26 16:47:47 +02:00
|
|
|
/*
|
|
|
|
* Implementation of generic HCD
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h> /* memcpy */
|
|
|
|
|
2014-05-26 16:47:56 +02:00
|
|
|
#include <minix/drivers.h> /* errno with sign */
|
|
|
|
#include <minix/usb.h> /* USB_TRANSFER_CTL... */
|
|
|
|
|
2014-05-26 16:47:47 +02:00
|
|
|
#include <usb/hcd_common.h>
|
|
|
|
#include <usb/hcd_ddekit.h>
|
|
|
|
#include <usb/hcd_interface.h>
|
|
|
|
#include <usb/usb_common.h>
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* Local declarations *
|
|
|
|
*===========================================================================*/
|
|
|
|
/* Thread to handle device logic */
|
|
|
|
static void hcd_device_thread(void *);
|
|
|
|
|
|
|
|
/* Procedure that locks device thread forever in case of error/completion */
|
|
|
|
static void hcd_device_finish(hcd_device_state *, const char *);
|
|
|
|
|
|
|
|
/* Typical USD device communication procedures */
|
|
|
|
static int hcd_enumerate(hcd_device_state *);
|
|
|
|
static int hcd_get_device_descriptor(hcd_device_state *);
|
|
|
|
static int hcd_set_address(hcd_device_state *, int);
|
|
|
|
static int hcd_get_descriptor_tree(hcd_device_state *);
|
|
|
|
static int hcd_set_configuration(hcd_device_state *, int);
|
|
|
|
static int hcd_handle_urb(hcd_device_state *);
|
2014-05-26 16:47:56 +02:00
|
|
|
static int hcd_control_urb(hcd_device_state *);
|
2014-05-30 14:58:16 +02:00
|
|
|
static int hcd_non_control_urb(hcd_device_state *, int);
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
/* For internal use by more general methods */
|
|
|
|
static int hcd_setup_packet(hcd_device_state *, hcd_ctrlrequest *);
|
2014-05-30 14:58:16 +02:00
|
|
|
static int hcd_data_transfer(hcd_device_state *, hcd_datarequest *);
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* Local definitions *
|
|
|
|
*===========================================================================*/
|
2014-05-26 16:47:56 +02:00
|
|
|
/* TODO: Only one device at a time
|
|
|
|
* If ever HUB functionality is added, one must remember that disconnecting
|
|
|
|
* HUB, means disconnecting every device attached to it, so data structure may
|
|
|
|
* have to be altered to allow that */
|
2014-05-26 16:47:47 +02:00
|
|
|
static hcd_device_state hcd_device[1];
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_handle_event *
|
|
|
|
*===========================================================================*/
|
|
|
|
void
|
|
|
|
hcd_handle_event(hcd_driver_state * driver)
|
|
|
|
{
|
|
|
|
hcd_device_state * this_device;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
/* TODO: Finding which hcd_device is in use should be performed here */
|
|
|
|
this_device = &(hcd_device[0]);
|
|
|
|
|
|
|
|
/* Sometimes interrupts occur in a weird order (EP after disconnect)
|
|
|
|
* This helps finding ordering errors in DEBUG */
|
|
|
|
USB_DBG("Event: 0x%02X, state: 0x%02X",
|
2014-05-26 16:47:56 +02:00
|
|
|
driver->current_event, this_device->state);
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
/* Set what was received for device thread to use */
|
|
|
|
this_device->driver = driver;
|
|
|
|
|
|
|
|
/* Handle event and forward control to device thread when required */
|
2014-05-26 16:47:56 +02:00
|
|
|
switch (driver->current_event) {
|
2014-05-26 16:47:47 +02:00
|
|
|
case HCD_EVENT_CONNECTED:
|
|
|
|
if (HCD_STATE_DISCONNECTED == this_device->state) {
|
|
|
|
if (EXIT_SUCCESS != hcd_connect_device(
|
|
|
|
this_device,
|
|
|
|
hcd_device_thread))
|
|
|
|
USB_MSG("Device creation failed");
|
|
|
|
} else
|
|
|
|
USB_MSG("Device not marked as 'disconnected' "
|
|
|
|
"for 'connection' event");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HCD_EVENT_DISCONNECTED:
|
|
|
|
if (HCD_STATE_DISCONNECTED != this_device->state) {
|
|
|
|
/* If connect callback was used before, call
|
|
|
|
* it's equivalent to signal disconnection */
|
|
|
|
if (HCD_STATE_CONNECTED == this_device->state)
|
|
|
|
hcd_disconnect_cb(this_device);
|
|
|
|
hcd_disconnect_device(this_device);
|
|
|
|
this_device->state = HCD_STATE_DISCONNECTED;
|
|
|
|
} else
|
|
|
|
USB_MSG("Device is marked as 'disconnected' "
|
|
|
|
"for 'disconnection' event");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HCD_EVENT_ENDPOINT:
|
2014-05-26 16:47:56 +02:00
|
|
|
case HCD_EVENT_URB:
|
2014-05-26 16:47:47 +02:00
|
|
|
/* Allow device thread to continue with it's logic */
|
|
|
|
if (HCD_STATE_DISCONNECTED != this_device->state)
|
|
|
|
hcd_device_continue(this_device);
|
|
|
|
else
|
|
|
|
USB_MSG("Device is marked as 'disconnected' "
|
|
|
|
"for 'EP' event");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
USB_ASSERT(0, "Illegal HCD event");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_device_thread *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void
|
|
|
|
hcd_device_thread(void * thread_args)
|
|
|
|
{
|
|
|
|
hcd_device_state * this_device;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
/* Retrieve structures from generic data */
|
|
|
|
this_device = (hcd_device_state *)thread_args;
|
|
|
|
|
|
|
|
/* Plugged in */
|
|
|
|
this_device->state = HCD_STATE_CONNECTION_PENDING;
|
|
|
|
|
|
|
|
/* Enumeration sequence */
|
|
|
|
if (EXIT_SUCCESS != hcd_enumerate(this_device))
|
|
|
|
hcd_device_finish(this_device, "USB device enumeration failed");
|
|
|
|
|
|
|
|
/* Tell everyone that device was connected */
|
|
|
|
hcd_connect_cb(this_device);
|
|
|
|
|
|
|
|
/* Fully configured */
|
|
|
|
this_device->state = HCD_STATE_CONNECTED;
|
|
|
|
|
|
|
|
USB_DBG("Waiting for URBs");
|
|
|
|
|
|
|
|
/* Start handling URB's */
|
|
|
|
for(;;) {
|
2014-05-26 16:47:56 +02:00
|
|
|
/* No URB's yet */
|
|
|
|
this_device->urb = NULL;
|
|
|
|
|
2014-05-26 16:47:47 +02:00
|
|
|
/* Block and wait for something like 'submit URB' */
|
2014-05-26 16:47:56 +02:00
|
|
|
hcd_device_wait(this_device, HCD_EVENT_URB, HCD_NO_ENDPOINT);
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
if (EXIT_SUCCESS != hcd_handle_urb(this_device))
|
|
|
|
hcd_device_finish(this_device, "URB handling failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finish device handling to avoid leaving thread */
|
|
|
|
hcd_device_finish(this_device, "USB device handling completed");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_device_finish *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void
|
|
|
|
hcd_device_finish(hcd_device_state * this_device, const char * finish_msg)
|
|
|
|
{
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
USB_MSG("USB device handling finished with message: '%s'", finish_msg);
|
|
|
|
|
|
|
|
/* Lock forever */
|
|
|
|
for (;;) {
|
2014-05-26 16:47:56 +02:00
|
|
|
hcd_device_wait(this_device, HCD_EVENT_URB, HCD_NO_ENDPOINT);
|
2014-05-26 16:47:47 +02:00
|
|
|
USB_MSG("Failed attempt to continue finished thread");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_enumerate *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
|
|
|
hcd_enumerate(hcd_device_state * this_device)
|
|
|
|
{
|
|
|
|
hcd_driver_state * d;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
d = this_device->driver;
|
|
|
|
|
|
|
|
/* First let driver reset device */
|
2014-05-26 16:47:56 +02:00
|
|
|
if (EXIT_SUCCESS != d->reset_device(d->private_data,
|
|
|
|
&(this_device->speed))) {
|
|
|
|
USB_MSG("Failed to reset device");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
/* Get device descriptor */
|
|
|
|
if (EXIT_SUCCESS != hcd_get_device_descriptor(this_device)) {
|
|
|
|
USB_MSG("Failed to get device descriptor");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: dynamic device address when more devices are available */
|
|
|
|
|
|
|
|
/* Set address */
|
|
|
|
if (EXIT_SUCCESS != hcd_set_address(this_device, HCD_ATTACHED_ADDR)) {
|
|
|
|
USB_MSG("Failed to set device address");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set parameters for further communication */
|
2014-05-26 16:47:56 +02:00
|
|
|
d->setup_device(d->private_data, HCD_DEFAULT_EP, this_device->address);
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
/* Get other descriptors */
|
|
|
|
if (EXIT_SUCCESS != hcd_get_descriptor_tree(this_device)) {
|
|
|
|
USB_MSG("Failed to get configuration descriptor tree");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: always first configuration */
|
|
|
|
/* Set configuration */
|
|
|
|
if (EXIT_SUCCESS != hcd_set_configuration(this_device, 0x01)) {
|
|
|
|
USB_MSG("Failed to set configuration");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
USB_DBG("Enumeration completed");
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_get_device_descriptor *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
|
|
|
hcd_get_device_descriptor(hcd_device_state * this_device)
|
|
|
|
{
|
|
|
|
hcd_ctrlrequest setup;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
/* TODO: magic numbers, no header for these */
|
|
|
|
|
|
|
|
/* Format setup packet */
|
|
|
|
setup.bRequestType = 0x80; /* IN */
|
|
|
|
setup.bRequest = 0x06; /* Get descriptor */
|
|
|
|
setup.wValue = 0x0100; /* Device */
|
|
|
|
setup.wIndex = 0x0000;
|
|
|
|
setup.wLength = sizeof(this_device->device_desc);
|
|
|
|
|
|
|
|
/* Handle formatted setup packet */
|
|
|
|
if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup)) {
|
|
|
|
USB_MSG("Handling setup packet failed");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Put what was read in device descriptor */
|
|
|
|
memcpy(&(this_device->device_desc), this_device->buffer,
|
|
|
|
sizeof(this_device->device_desc));
|
|
|
|
|
|
|
|
/* Remember max packet size from device descriptor */
|
|
|
|
this_device->max_packet_size = this_device->device_desc.bMaxPacketSize;
|
|
|
|
|
2014-05-26 16:47:56 +02:00
|
|
|
/* Dump device descriptor in debug mode */
|
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
hcd_device_descriptor * d;
|
|
|
|
d = &(this_device->device_desc);
|
|
|
|
|
|
|
|
USB_DBG("<<DEVICE>>");
|
|
|
|
USB_DBG("bLength %02X", d->bLength);
|
|
|
|
USB_DBG("bDescriptorType %02X", d->bDescriptorType);
|
|
|
|
USB_DBG("bcdUSB %04X", UGETW(d->bcdUSB));
|
|
|
|
USB_DBG("bDeviceClass %02X", d->bDeviceClass);
|
|
|
|
USB_DBG("bDeviceSubClass %02X", d->bDeviceSubClass);
|
|
|
|
USB_DBG("bDeviceProtocol %02X", d->bDeviceProtocol);
|
|
|
|
USB_DBG("bMaxPacketSize %02X", d->bMaxPacketSize);
|
|
|
|
USB_DBG("idVendor %04X", UGETW(d->idVendor));
|
|
|
|
USB_DBG("idProduct %04X", UGETW(d->idProduct));
|
|
|
|
USB_DBG("bcdDevice %04X", UGETW(d->bcdDevice));
|
|
|
|
USB_DBG("iManufacturer %02X", d->iManufacturer);
|
|
|
|
USB_DBG("iProduct %02X", d->iProduct);
|
|
|
|
USB_DBG("iSerialNumber %02X", d->iSerialNumber);
|
|
|
|
USB_DBG("bNumConfigurations %02X", d->bNumConfigurations);
|
|
|
|
}
|
|
|
|
#endif
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_set_address *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
|
|
|
hcd_set_address(hcd_device_state * this_device, int address)
|
|
|
|
{
|
|
|
|
hcd_ctrlrequest setup;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
USB_ASSERT((address > 0) && (address < 128), "Illegal address");
|
|
|
|
|
|
|
|
/* TODO: magic numbers, no header for these */
|
|
|
|
setup.bRequestType = 0x00; /* OUT */
|
|
|
|
setup.bRequest = 0x05; /* Set address */
|
|
|
|
setup.wValue = address;
|
|
|
|
setup.wIndex = 0x0000;
|
|
|
|
setup.wLength = 0x0000;
|
|
|
|
|
|
|
|
/* Handle formatted setup packet */
|
|
|
|
if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup)) {
|
|
|
|
USB_MSG("Handling setup packet failed");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2014-05-26 16:47:56 +02:00
|
|
|
/* Sleep 5msec to allow addressing */
|
|
|
|
hcd_os_nanosleep(HCD_NANOSLEEP_MSEC(5));
|
|
|
|
|
|
|
|
/* Remember what was assigned in hardware */
|
|
|
|
this_device->address = address;
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_get_descriptor_tree *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
|
|
|
hcd_get_descriptor_tree(hcd_device_state * this_device)
|
|
|
|
{
|
|
|
|
hcd_config_descriptor config_descriptor;
|
|
|
|
hcd_ctrlrequest setup;
|
|
|
|
int completed;
|
|
|
|
int total_length;
|
|
|
|
int buffer_length;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
/* First, ask only for configuration itself to get length info */
|
|
|
|
buffer_length = sizeof(config_descriptor);
|
|
|
|
completed = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
/* TODO: configuration 0 is hard-coded
|
|
|
|
* but others are rarely used anyway */
|
|
|
|
/* TODO: magic numbers, no header for these */
|
|
|
|
setup.bRequestType = 0x80; /* IN */
|
|
|
|
setup.bRequest = 0x06; /* Get descriptor */
|
|
|
|
setup.wValue = 0x0200; /* Configuration 0 */
|
|
|
|
setup.wIndex = 0x0000;
|
|
|
|
setup.wLength = buffer_length;
|
|
|
|
|
|
|
|
/* Handle formatted setup packet */
|
|
|
|
if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup)) {
|
|
|
|
USB_MSG("Handling setup packet failed");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we only asked for configuration itself
|
|
|
|
* then ask again for other descriptors */
|
|
|
|
if (sizeof(config_descriptor) == buffer_length) {
|
|
|
|
|
|
|
|
/* Put what was read in configuration descriptor */
|
|
|
|
memcpy(&config_descriptor, this_device->buffer,
|
|
|
|
sizeof(config_descriptor));
|
|
|
|
|
|
|
|
/* Continue only if there is more data */
|
|
|
|
total_length = config_descriptor.wTotalLength[0] +
|
|
|
|
(config_descriptor.wTotalLength[1] << 8);
|
|
|
|
|
|
|
|
if (total_length < (int)sizeof(config_descriptor)) {
|
|
|
|
/* This should never happen for a fine device */
|
|
|
|
USB_MSG("Illegal wTotalLength value");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
else if (sizeof(config_descriptor) == total_length) {
|
|
|
|
/* Nothing more was in descriptor anyway */
|
|
|
|
completed = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Read whatever is needed */
|
|
|
|
buffer_length = total_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* All data for given configuration was read */
|
|
|
|
completed = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (!completed);
|
|
|
|
|
|
|
|
/* Create tree based on received buffer */
|
|
|
|
if (EXIT_SUCCESS != hcd_buffer_to_tree(this_device->buffer,
|
|
|
|
this_device->data_len,
|
|
|
|
&(this_device->config_tree))) {
|
|
|
|
/* This should never happen for a fine device */
|
|
|
|
USB_MSG("Illegal descriptor values");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_set_configuration *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
|
|
|
hcd_set_configuration(hcd_device_state * this_device, int configuration)
|
|
|
|
{
|
|
|
|
hcd_ctrlrequest setup;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
/* TODO: magic numbers, no header for these */
|
|
|
|
setup.bRequestType = 0x00; /* OUT */
|
|
|
|
setup.bRequest = 0x09; /* Set configuration */
|
|
|
|
setup.wValue = configuration;
|
|
|
|
setup.wIndex = 0x0000;
|
|
|
|
setup.wLength = 0x0000;
|
|
|
|
|
|
|
|
/* Handle formatted setup packet */
|
|
|
|
if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup)) {
|
|
|
|
USB_MSG("Handling setup packet failed");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_handle_urb *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
|
|
|
hcd_handle_urb(hcd_device_state * this_device)
|
|
|
|
{
|
2014-05-26 16:47:56 +02:00
|
|
|
hcd_urb * urb;
|
|
|
|
int transfer_status;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
transfer_status = EXIT_FAILURE;
|
|
|
|
urb = this_device->urb;
|
|
|
|
|
|
|
|
USB_ASSERT(NULL != urb, "NULL URB given");
|
|
|
|
/* TODO: One device only */
|
|
|
|
USB_ASSERT((void *)this_device != (void *)urb->dev,
|
|
|
|
"Unknown device for URB");
|
|
|
|
|
|
|
|
switch (urb->type) {
|
|
|
|
|
|
|
|
case USB_TRANSFER_CTL:
|
|
|
|
transfer_status = hcd_control_urb(this_device);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USB_TRANSFER_BLK:
|
|
|
|
case USB_TRANSFER_INT:
|
2014-05-30 14:58:16 +02:00
|
|
|
transfer_status = hcd_non_control_urb(this_device,
|
|
|
|
urb->type);
|
2014-05-26 16:47:56 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case USB_TRANSFER_ISO:
|
2014-05-30 14:58:16 +02:00
|
|
|
/* TODO: ISO transfer */
|
2014-05-26 16:47:56 +02:00
|
|
|
USB_MSG("ISO transfer not supported");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
USB_MSG("Invalid transfer type 0x%X", urb->type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-05-30 14:58:16 +02:00
|
|
|
if (EXIT_SUCCESS != transfer_status)
|
|
|
|
USB_MSG("USB transfer failed");
|
|
|
|
|
|
|
|
/* Call completion regardless of status */
|
2014-05-26 16:47:56 +02:00
|
|
|
hcd_completion_cb(urb->priv);
|
|
|
|
|
2014-05-30 14:58:16 +02:00
|
|
|
/* TODO: Only critical failures should ever yield EXIT_FAILURE, so
|
|
|
|
* return is not bound to transfer_status for now, to let device
|
|
|
|
* driver act accordingly */
|
2014-05-26 16:47:56 +02:00
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_control_urb *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
|
|
|
hcd_control_urb(hcd_device_state * this_device)
|
|
|
|
{
|
|
|
|
hcd_urb * urb;
|
|
|
|
hcd_ctrlrequest setup;
|
|
|
|
|
2014-05-26 16:47:47 +02:00
|
|
|
DEBUG_DUMP;
|
|
|
|
|
2014-05-26 16:47:56 +02:00
|
|
|
urb = this_device->urb;
|
2014-05-26 16:47:47 +02:00
|
|
|
|
2014-05-26 16:47:56 +02:00
|
|
|
/* Assume bad values unless something different occurs later */
|
|
|
|
urb->status = EINVAL;
|
2014-05-26 16:47:47 +02:00
|
|
|
|
2014-05-26 16:47:56 +02:00
|
|
|
/* Must have setup packet */
|
|
|
|
if (NULL == urb->setup_packet) {
|
|
|
|
USB_MSG("No setup packet in URB, for control transfer");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Only EP0 can have control transfer */
|
|
|
|
if (0 != urb->endpoint) {
|
|
|
|
USB_MSG("Control transfer for non zero EP");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hold setup packet and analyze it */
|
|
|
|
memcpy(&setup, urb->setup_packet, sizeof(setup));
|
|
|
|
|
|
|
|
/* TODO: broken constants for urb->direction (USB_OUT...) */
|
|
|
|
if (((setup.bRequestType >> 7) & 0x01) != urb->direction) {
|
|
|
|
USB_MSG("URB Direction mismatch");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2014-05-26 16:47:47 +02:00
|
|
|
|
2014-05-26 16:47:56 +02:00
|
|
|
/* Send setup packet */
|
|
|
|
if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup)) {
|
|
|
|
USB_MSG("Sending URB setup packet, failed");
|
|
|
|
urb->status = EPIPE;
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
urb->status = EXIT_SUCCESS;
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-05-30 14:58:16 +02:00
|
|
|
* hcd_non_control_urb *
|
2014-05-26 16:47:56 +02:00
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
2014-05-30 14:58:16 +02:00
|
|
|
hcd_non_control_urb(hcd_device_state * this_device, int type)
|
2014-05-26 16:47:56 +02:00
|
|
|
{
|
|
|
|
hcd_endpoint * e;
|
2014-05-30 14:58:16 +02:00
|
|
|
hcd_datarequest request;
|
2014-05-26 16:47:56 +02:00
|
|
|
hcd_urb * urb;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
urb = this_device->urb;
|
|
|
|
|
|
|
|
/* Assume bad values unless something different occurs later */
|
|
|
|
urb->status = EINVAL;
|
|
|
|
|
|
|
|
if (NULL == urb->data) {
|
2014-05-30 14:58:16 +02:00
|
|
|
USB_MSG("No data packet in URB");
|
2014-05-26 16:47:56 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((UE_GET_ADDR(urb->endpoint) >= 16) ||
|
|
|
|
(UE_GET_ADDR(urb->endpoint) <= 0)) {
|
|
|
|
USB_MSG("Illegal EP number");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: broken USB_IN... constants */
|
|
|
|
if ((1 != urb->direction) && (0 != urb->direction)) {
|
|
|
|
USB_MSG("Illegal EP direction");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2014-05-30 14:58:16 +02:00
|
|
|
/* TODO: usb.h constants to type mapping */
|
|
|
|
switch (type) {
|
|
|
|
case USB_TRANSFER_BLK:
|
|
|
|
request.type = HCD_TRANSFER_BULK;
|
|
|
|
break;
|
|
|
|
case USB_TRANSFER_INT:
|
|
|
|
request.type = HCD_TRANSFER_INTERRUPT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* TODO: ISO transfer */
|
|
|
|
USB_MSG("Invalid transfer type");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2014-05-26 16:47:56 +02:00
|
|
|
/* TODO: Any additional checks? (sane size?) */
|
|
|
|
|
2014-05-30 14:58:16 +02:00
|
|
|
/* Assign to data request structure */
|
2014-05-26 16:47:56 +02:00
|
|
|
request.endpoint = urb->endpoint;
|
2014-05-30 14:58:16 +02:00
|
|
|
request.direction = urb->direction;
|
2014-05-26 16:47:56 +02:00
|
|
|
request.size = (int)urb->size;
|
|
|
|
request.data = urb->data;
|
2014-05-30 14:58:16 +02:00
|
|
|
request.interval = urb->interval;
|
2014-05-26 16:47:56 +02:00
|
|
|
|
|
|
|
/* Check if EP number is valid */
|
|
|
|
e = hcd_tree_find_ep(&(this_device->config_tree), request.endpoint);
|
|
|
|
|
|
|
|
if (NULL == e) {
|
|
|
|
USB_MSG("Invalid EP value");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: broken constants for urb->direction (USB_OUT...) */
|
|
|
|
/* Check if remembered direction matches */
|
|
|
|
if (((e->descriptor.bEndpointAddress >> 7) & 0x01) != urb->direction) {
|
|
|
|
USB_MSG("EP direction mismatch");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if remembered type matches */
|
2014-05-30 14:58:16 +02:00
|
|
|
if (UE_GET_XFERTYPE(e->descriptor.bmAttributes) != (int)request.type) {
|
2014-05-26 16:47:56 +02:00
|
|
|
USB_MSG("EP type mismatch");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assign to let know how much data can be transfered at a time */
|
|
|
|
request.max_packet_size = UGETW(e->descriptor.wMaxPacketSize);
|
|
|
|
|
|
|
|
/* Let know how to configure EP for speed */
|
|
|
|
request.speed = this_device->speed;
|
|
|
|
|
2014-05-30 14:58:16 +02:00
|
|
|
/* Start sending data */
|
|
|
|
if (EXIT_SUCCESS != hcd_data_transfer(this_device, &request)) {
|
|
|
|
USB_MSG("URB non-control transfer, failed");
|
2014-05-26 16:47:56 +02:00
|
|
|
urb->status = EPIPE;
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2014-05-30 14:58:16 +02:00
|
|
|
/* Transfer successfully completed */
|
2014-05-26 16:47:56 +02:00
|
|
|
urb->status = EXIT_SUCCESS;
|
2014-05-26 16:47:47 +02:00
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* hcd_setup_packet *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
|
|
|
hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup)
|
|
|
|
{
|
|
|
|
hcd_driver_state * d;
|
|
|
|
hcd_reg1 * current_byte;
|
|
|
|
int expected_len;
|
|
|
|
int received_len;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
/* Initially... */
|
|
|
|
d = this_device->driver;
|
|
|
|
expected_len = (int)setup->wLength;
|
|
|
|
current_byte = this_device->buffer;
|
|
|
|
|
|
|
|
/* Send setup packet */
|
|
|
|
d->setup_stage(d->private_data, setup);
|
|
|
|
|
|
|
|
/* Wait for response */
|
2014-05-26 16:47:56 +02:00
|
|
|
hcd_device_wait(this_device, HCD_EVENT_ENDPOINT, HCD_ENDPOINT_0);
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
/* Check response */
|
2014-05-26 16:47:56 +02:00
|
|
|
if (EXIT_SUCCESS != d->check_error(d->private_data,
|
|
|
|
HCD_TRANSFER_CONTROL,
|
|
|
|
HCD_DIRECTION_UNUSED))
|
2014-05-26 16:47:47 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
/* For data packets... */
|
|
|
|
if (expected_len > 0) {
|
|
|
|
|
|
|
|
/* TODO: magic number */
|
|
|
|
/* ...IN data packets */
|
|
|
|
if (setup->bRequestType & 0x80) {
|
|
|
|
|
|
|
|
/* What was received until now */
|
|
|
|
this_device->data_len = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
/* Try getting data */
|
|
|
|
d->in_data_stage(d->private_data);
|
|
|
|
|
|
|
|
/* Wait for response */
|
2014-05-26 16:47:56 +02:00
|
|
|
hcd_device_wait(this_device,
|
|
|
|
HCD_EVENT_ENDPOINT,
|
|
|
|
HCD_ENDPOINT_0);
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
/* Check response */
|
|
|
|
if (EXIT_SUCCESS != d->check_error(
|
2014-05-26 16:47:56 +02:00
|
|
|
d->private_data,
|
|
|
|
HCD_TRANSFER_CONTROL,
|
|
|
|
HCD_DIRECTION_UNUSED))
|
2014-05-26 16:47:47 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
/* Read data received as response */
|
|
|
|
received_len = d->read_data(d->private_data,
|
|
|
|
current_byte, 0);
|
|
|
|
|
|
|
|
/* Data reading should always yield positive
|
|
|
|
* results for proper setup packet */
|
|
|
|
if (received_len > 0) {
|
|
|
|
/* Try next packet */
|
|
|
|
this_device->data_len += received_len;
|
|
|
|
current_byte += received_len;
|
|
|
|
} else
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
} while (expected_len > this_device->data_len);
|
|
|
|
|
|
|
|
/* Should be exactly what we requested, no more */
|
|
|
|
if (this_device->data_len != expected_len) {
|
|
|
|
USB_MSG("Received more data than expected");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* TODO: unimplemented */
|
|
|
|
USB_MSG("Illegal non-zero length OUT setup packet");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Status stages */
|
|
|
|
if (setup->bRequestType & 0x80) {
|
|
|
|
|
|
|
|
/* Try confirming data receive */
|
|
|
|
d->out_status_stage(d->private_data);
|
|
|
|
|
|
|
|
/* Wait for response */
|
2014-05-26 16:47:56 +02:00
|
|
|
hcd_device_wait(this_device, HCD_EVENT_ENDPOINT,
|
|
|
|
HCD_ENDPOINT_0);
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
/* Check response */
|
2014-05-26 16:47:56 +02:00
|
|
|
if (EXIT_SUCCESS != d->check_error(d->private_data,
|
|
|
|
HCD_TRANSFER_CONTROL,
|
|
|
|
HCD_DIRECTION_UNUSED))
|
2014-05-26 16:47:47 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* Try getting status confirmation */
|
|
|
|
d->in_status_stage(d->private_data);
|
|
|
|
|
|
|
|
/* Wait for response */
|
2014-05-26 16:47:56 +02:00
|
|
|
hcd_device_wait(this_device, HCD_EVENT_ENDPOINT,
|
|
|
|
HCD_ENDPOINT_0);
|
2014-05-26 16:47:47 +02:00
|
|
|
|
|
|
|
/* Check response */
|
2014-05-26 16:47:56 +02:00
|
|
|
if (EXIT_SUCCESS != d->check_error(d->private_data,
|
|
|
|
HCD_TRANSFER_CONTROL,
|
|
|
|
HCD_DIRECTION_UNUSED))
|
2014-05-26 16:47:47 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
/* Read zero data from response to clear registers */
|
|
|
|
if (0 != d->read_data(d->private_data, NULL, 0))
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
2014-05-26 16:47:56 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-05-30 14:58:16 +02:00
|
|
|
* hcd_data_transfer *
|
2014-05-26 16:47:56 +02:00
|
|
|
*===========================================================================*/
|
|
|
|
static int
|
2014-05-30 14:58:16 +02:00
|
|
|
hcd_data_transfer(hcd_device_state * this_device, hcd_datarequest * request)
|
2014-05-26 16:47:56 +02:00
|
|
|
{
|
|
|
|
hcd_driver_state * d;
|
2014-05-30 14:58:16 +02:00
|
|
|
hcd_datarequest temp_req;
|
2014-05-26 16:47:56 +02:00
|
|
|
int transfer_len;
|
|
|
|
|
|
|
|
DEBUG_DUMP;
|
|
|
|
|
|
|
|
/* Initially... */
|
|
|
|
d = this_device->driver;
|
|
|
|
|
|
|
|
/* Set parameters for further communication */
|
|
|
|
d->setup_device(d->private_data,
|
|
|
|
request->endpoint,
|
|
|
|
this_device->address);
|
|
|
|
|
|
|
|
/* TODO: broken USB_IN... constants */
|
2014-05-30 14:58:16 +02:00
|
|
|
if (1 == request->direction) {
|
2014-05-26 16:47:56 +02:00
|
|
|
|
|
|
|
do {
|
2014-05-30 14:58:16 +02:00
|
|
|
/* Start actual data transfer */
|
|
|
|
d->rx_stage(d->private_data, request);
|
2014-05-26 16:47:56 +02:00
|
|
|
|
|
|
|
/* Wait for response */
|
|
|
|
hcd_device_wait(this_device, HCD_EVENT_ENDPOINT,
|
|
|
|
request->endpoint);
|
|
|
|
|
|
|
|
/* Check response */
|
|
|
|
if (EXIT_SUCCESS != d->check_error(d->private_data,
|
2014-05-30 14:58:16 +02:00
|
|
|
request->type,
|
2014-05-26 16:47:56 +02:00
|
|
|
HCD_DIRECTION_IN))
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
/* Read data received as response */
|
|
|
|
transfer_len = d->read_data(d->private_data,
|
|
|
|
(hcd_reg1 *)request->data,
|
|
|
|
request->endpoint);
|
|
|
|
|
|
|
|
request->size -= transfer_len;
|
|
|
|
request->data += transfer_len;
|
|
|
|
|
|
|
|
/* Total length shall not become negative */
|
|
|
|
USB_ASSERT(request->size >= 0,
|
|
|
|
"Invalid amount of data received");
|
|
|
|
|
2014-05-30 14:58:16 +02:00
|
|
|
#ifdef DEBUG
|
|
|
|
/* TODO: REMOVEME (dumping of data transfer) */
|
2014-05-26 16:47:56 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
USB_MSG("RECEIVED: %d", transfer_len);
|
|
|
|
for (i = 0; i < transfer_len; i++)
|
|
|
|
USB_MSG("%c",
|
|
|
|
(request->data-transfer_len)[i]);
|
|
|
|
}
|
2014-05-30 14:58:16 +02:00
|
|
|
#endif
|
2014-05-26 16:47:56 +02:00
|
|
|
|
|
|
|
} while (0 != request->size);
|
|
|
|
|
2014-05-30 14:58:16 +02:00
|
|
|
} else if (0 == request->direction) {
|
2014-05-26 16:47:56 +02:00
|
|
|
|
|
|
|
do {
|
|
|
|
temp_req = *request;
|
|
|
|
|
|
|
|
/* Decide transfer size */
|
|
|
|
if (temp_req.size > (int)temp_req.max_packet_size) {
|
|
|
|
temp_req.size = temp_req.max_packet_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
request->data += temp_req.size;
|
|
|
|
request->size -= temp_req.size;
|
|
|
|
|
|
|
|
/* Total length shall not become negative */
|
|
|
|
USB_ASSERT(request->size >= 0,
|
|
|
|
"Invalid amount of data received");
|
|
|
|
|
2014-05-30 14:58:16 +02:00
|
|
|
/* Start actual data transfer */
|
|
|
|
d->tx_stage(d->private_data, &temp_req);
|
2014-05-26 16:47:56 +02:00
|
|
|
|
|
|
|
/* Wait for response */
|
|
|
|
hcd_device_wait(this_device, HCD_EVENT_ENDPOINT,
|
|
|
|
request->endpoint);
|
|
|
|
|
|
|
|
/* Check response */
|
|
|
|
if (EXIT_SUCCESS != d->check_error(d->private_data,
|
2014-05-30 14:58:16 +02:00
|
|
|
request->type,
|
2014-05-26 16:47:56 +02:00
|
|
|
HCD_DIRECTION_OUT))
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
} while (0 != request->size);
|
|
|
|
|
|
|
|
} else
|
|
|
|
USB_ASSERT(0, "Invalid transfer direction");
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|