blob: f1892871b3c9cbe6768ed8b8875f4ef03665357a [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
/**
* @brief USB hub driver
* @see USB 2.0 spec, Chapter 11
*/
#include "usbhub.h"
#include <stdio.h>
#include <string.h>
#include <utils/util.h>
#include "../services.h"
#define HUBINT_RATE_MS 100
#define HUB_ENABLE_IRQS
/*** USB spec chapter 11 page 262 ***/
struct hub_desc {
/// Number of bytes in this descriptor, including this byte.
uint8_t bDescLength;
/// Descriptor Type, value: 0x29 for Hub Descriptor.
#define DESCRIPTOR_TYPE_HUB 0x29
uint8_t bDescriptorType;
/// Number of downstream ports that this hub supports.
uint8_t bNbrPorts;
/// Hub characteristics.
#define HUBCHAR_POWER_SWITCH_SINGLE BIT(0)
#define HUBCHAR_POWER_SWITCH_NONE BIT(1)
#define HUBCHAR_COMPOUND_DEVICE BIT(2)
#define HUBCHAR_OVER_CURRENT_SINGLE BIT(3)
#define HUBCHAR_OVER_CURRENT_NONE BIT(4)
uint16_t wHubCharacteristics;
/// Time (in 2ms intervals) until power is stable after child port power on
uint8_t bPwrOn2PwrGood;
/// Maximum current requirements of the hub controller
uint8_t bHubContrCurrent;
/* The size of the remaining fields depends on the number of ports
* Bit x will correspond to port x
* DeviceRemovable bitmap: 1-not removable.
* PortPwrCtrlMask bitmap: 1-requires manual power on (not ganged).
*/
uint8_t portcfg[64];
} __attribute__ ((packed));
/****************
*** FEATURES ***
****************/
static inline struct usbreq
__clear_port_feature_req(uint16_t port, uint16_t feature)
{
struct usbreq r = {
.bmRequestType = (USB_DIR_OUT | USB_TYPE_CLS | USB_RCPT_OTHER),
.bRequest = CLR_FEATURE,
.wValue = feature,
.wIndex = port,
.wLength = 0
};
return r;
}
static inline struct usbreq __clear_hub_feature_req(uint16_t feature)
{
struct usbreq r = {
.bmRequestType = (USB_DIR_OUT | USB_TYPE_CLS | USB_RCPT_DEVICE),
.bRequest = CLR_FEATURE,
.wValue = feature,
.wIndex = 0,
.wLength = 0
};
return r;
}
static inline struct usbreq
__set_port_feature_req(uint16_t port, uint16_t feature)
{
struct usbreq r = {
.bmRequestType = (USB_DIR_OUT | USB_TYPE_CLS | USB_RCPT_OTHER),
.bRequest = SET_FEATURE,
.wValue = feature,
.wIndex = port,
.wLength = 0
};
return r;
}
static inline struct usbreq __set_hub_feature_req(uint16_t feature)
{
struct usbreq r = {
.bmRequestType = (USB_DIR_OUT | USB_TYPE_CLS | USB_RCPT_DEVICE),
.bRequest = SET_FEATURE,
.wValue = feature,
.wIndex = 0,
.wLength = 0
};
return r;
}
static inline struct usbreq __get_port_status_req(uint16_t port)
{
struct usbreq r = {
.bmRequestType = (USB_DIR_IN | USB_TYPE_CLS | USB_RCPT_OTHER),
.bRequest = GET_STATUS,
.wValue = 0,
.wIndex = port,
.wLength = 4
};
return r;
}
static inline struct usbreq __get_hub_status_req(void)
{
struct usbreq r = {
.bmRequestType = (USB_DIR_IN | USB_TYPE_CLS | USB_RCPT_DEVICE),
.bRequest = GET_STATUS,
.wValue = 0,
.wIndex = 0,
.wLength = 4
};
return r;
}
static inline struct usbreq __get_hub_descriptor_req(void)
{
struct usbreq r = {
.bmRequestType = (USB_DIR_IN | USB_TYPE_CLS | USB_RCPT_DEVICE),
.bRequest = GET_DESCRIPTOR,
.wValue = DESCRIPTOR_TYPE_HUB << 8,
.wIndex = 0,
.wLength = sizeof(struct hub_desc)
};
return r;
}
static inline struct usbreq __set_hub_descriptor_req(void)
{
struct usbreq r = {
.bmRequestType = (USB_DIR_OUT | USB_TYPE_CLS | USB_RCPT_DEVICE),
.bRequest = SET_DESCRIPTOR,
.wValue = DESCRIPTOR_TYPE_HUB << 8,
.wIndex = 0,
.wLength = sizeof(struct hub_desc)
};
return r;
}
struct usb_hubem {
int hubem_nports;
int pwr_delay_ms;
int (*set_pf) (void *token, int port, enum port_feature feature);
int (*clr_pf) (void *token, int port, enum port_feature feature);
int (*get_pstat) (void *token, int port, struct port_status *ps);
void *token;
};
static void _handle_port_change(usb_hub_t h, int port)
{
struct xact xact[2];
struct usbreq *req;
struct port_status *sts;
uint16_t change, status;
int ret;
if (!h) {
ZF_LOGF("Invalid HUB\n");
}
if (!port) {
ZF_LOGD("Error: check hub status!\n");
return;
}
ZF_LOGD("Handle status change of port %d\n", port);
/* Get port status change */
xact[0].type = PID_SETUP;
xact[0].len = sizeof(struct usbreq);
xact[1].type = PID_IN;
xact[1].len = sizeof(struct port_status);
ret = usb_alloc_xact(h->udev->dman, xact, 2);
if (ret) {
ZF_LOGF("Out of DMA memory\n");
}
req = xact_get_vaddr(&xact[0]);
sts = xact_get_vaddr(&xact[1]);
*req = __get_port_status_req(port);
ret = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 2, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
/* Cache the port status, because we need to clear it right away. */
change = sts->wPortChange;
status = sts->wPortStatus;
ZF_LOGD("Status change (0x%x:0x%x) on port %d.\n",
change, status, port);
/* Attach and detach detect event */
if (change & BIT(PORT_CONNECTION)) {
/* Clear the port connection status */
*req = __clear_port_feature_req(port, C_PORT_CONNECTION);
ret = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 1, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
if (status & BIT(PORT_CONNECTION)) {
ZF_LOGD("Port %d connected\n", port);
/* Wait for the device to stabilize, USB spec 9.1.2 */
ps_mdelay(100);
/* Enable the connection by resetting the port */
*req = __set_port_feature_req(port, PORT_RESET);
ret = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 1, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
/*
* Wait for the hub to exit the resetting state, refer
* to USB spec 11.5.1.5
* We also need to re-read the port status, it's updated
* by the reset.
*/
*req = __get_port_status_req(port);
do {
ps_mdelay(10);
ret =
usbdev_schedule_xact(h->udev,
h->udev->ep_ctrl, xact,
2, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
status = sts->wPortStatus;
} while (status & BIT(PORT_RESET));
/* Reset finished, clear reset status */
*req = __clear_port_feature_req(port, C_PORT_RESET);
ret = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 1, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
/* Create the new device */
enum usb_speed speed;
struct usb_dev *new_dev = NULL;
usb_hub_t new_hub = NULL;
if (status & BIT(PORT_HIGH_SPEED)) {
speed = USBSPEED_HIGH;
} else if (status & BIT(PORT_LOW_SPEED)) {
speed = USBSPEED_LOW;
} else {
speed = USBSPEED_FULL;
}
ret = usb_new_device(h->udev, port, speed, &new_dev);
if (ret < 0) {
*req = __set_port_feature_req(port, PORT_RESET);
ret =
usbdev_schedule_xact(h->udev,
h->udev->ep_ctrl, xact,
1, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
if (new_dev) {
usbdev_disconnect(new_dev);
}
} else {
h->port[port - 1].udev = new_dev;
usb_hub_driver_bind(new_dev, &new_hub);
}
} else {
ZF_LOGD("Port %d disconnected\n", port);
*req = __set_port_feature_req(port, PORT_SUSPEND);
ret = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 1, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
if (h->port[port - 1].udev) {
usbdev_disconnect(h->port[port - 1].udev);
h->port[port - 1].udev = NULL;
}
}
}
/* Port enable */
if (change & BIT(PORT_ENABLE)) {
ZF_LOGD("Port %d enabled\n", port);
/* Clear the port connection status */
*req = __clear_port_feature_req(port, C_PORT_CONNECTION);
ret = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 1, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
}
/* Port suspend */
if (change & BIT(PORT_SUSPEND)) {
ZF_LOGD("Port %d suspended\n", port);
/* Clear suspend status */
*req = __clear_port_feature_req(port, C_PORT_SUSPEND);
ret = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 1, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
}
/* Port over-current */
if (change & BIT(PORT_OVER_CURRENT)) {
ZF_LOGD("Port %d over-current\n", port);
/* Clear over-current status */
*req = __clear_port_feature_req(port, C_PORT_OVER_CURRENT);
ret = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 1, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
}
/* Port reset */
if (change & BIT(PORT_RESET)) {
ZF_LOGD("Port %d reset\n", port);
/* Clear reset status */
*req = __clear_port_feature_req(port, C_PORT_RESET);
ret = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 1, NULL, NULL);
if (ret < 0) {
ZF_LOGF("Transaction error\n");
}
}
usb_destroy_xact(h->udev->dman, xact, 2);
}
static int
hub_irq_handler(void *token, enum usb_xact_status stat, int bytes_remaining)
{
usb_hub_t h = (usb_hub_t) token;
int i, j;
int handled = 0;
uint8_t *intbm;
int len = h->int_xact.len - bytes_remaining;
/* Check the status */
if (stat != XACTSTAT_SUCCESS) {
ZF_LOGD("Received unsuccessful IRQ\n");
return 1;
}
ZF_LOGD("Handling IRQ\n");
intbm = h->intbm;
if (intbm != xact_get_vaddr(&h->int_xact)) {
ZF_LOGF("Invalid bitmap\n");
}
for (i = 0; i < len; i++) {
/* Check if any bits have changed */
if (intbm[i] == 0) {
continue;
}
/* Scan bitfield */
for (j = 0; j < 8; j++) {
if ((1 << j) & intbm[i]) {
int port = i * 8 + j;
_handle_port_change(h, port);
handled++;
}
}
intbm[i] = 0;
}
if (!handled) {
ZF_LOGD("Spurious IRQ\n");
}
usbdev_schedule_xact(h->udev, h->udev->ep[0],
&h->int_xact, 1, &hub_irq_handler, h);
return 0;
}
static int hub_config_cb(void *token, int cfg, int iface, struct anon_desc *d)
{
struct endpoint_desc *e;
usb_hub_t hub = (usb_hub_t)token;
if (!hub) {
ZF_LOGF("Invalid token\n");
}
if (d) {
switch (d->bDescriptorType) {
case ENDPOINT:
e = (struct endpoint_desc*)d;
/* We just take the first endpoint */
hub->int_ep = e->bEndpointAddress & 0xf;
hub->int_max_pkt = e->wMaxPacketSize;
hub->int_rate_ms = e->bInterval * 2;
hub->ifno = iface;
hub->cfgno = cfg;
break;
default:
/* Don't care */
break;
}
return 0;
} else {
return 0;
}
}
int usb_hub_driver_bind(usb_dev_t *udev, usb_hub_t *hub)
{
usb_hub_t h;
struct usbreq *req;
struct hub_desc *hdesc;
struct xact xact[2];
int err;
int i;
/* Check the class */
if (usbdev_get_class(udev) != USB_CLASS_HUB) {
return -1;
}
/* Allocate memory */
h = (usb_hub_t) usb_malloc(sizeof(*h));
if (h == NULL) {
return -2;
}
memset(h, 0, sizeof(*h));
h->udev = udev;
udev->dev_data = (struct udev_priv *)h;
/* Get hub descriptor for nports and power delay */
ZF_LOGD("Get hub descriptor\n");
xact[0].type = PID_SETUP;
xact[0].len = sizeof(*req);
xact[1].type = PID_IN;
xact[1].len = sizeof(*hdesc);
err = usb_alloc_xact(udev->dman, xact, 2);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
req = xact_get_vaddr(&xact[0]);
*req = __get_hub_descriptor_req();
err = usbdev_schedule_xact(udev, h->udev->ep_ctrl, xact, 2, NULL, NULL);
if (err < 0) {
usb_destroy_xact(udev->dman, xact, 2);
usb_free(h);
h = NULL;
return -1;
}
hdesc = xact_get_vaddr(&xact[1]);
h->nports = hdesc->bNbrPorts;
h->power_good_delay_ms = hdesc->bPwrOn2PwrGood * 2;
usb_destroy_xact(udev->dman, xact, 2);
h->port = (struct usb_hub_port *)usb_malloc(sizeof(*h->port) * h->nports);
if (!h->port) {
ZF_LOGF("Out of memory\n");
}
memset(h->port, 0, sizeof(*h->port) * h->nports);
ZF_LOGD("Parsing config\n");
h->int_ep = -1;
err = usbdev_parse_config(h->udev, &hub_config_cb, h);
if (err || h->int_ep == -1) {
usb_free(h);
h = NULL;
return -1;
}
ZF_LOGD("Configure HUB\n");
xact[0].type = PID_SETUP;
xact[0].len = sizeof(*req);
err = usb_alloc_xact(h->udev->dman, xact, 1);
if (err) {
ZF_LOGE("Out of DMA memory\n");
return -1;
}
req = xact_get_vaddr(&xact[0]);
*req = __set_configuration_req(h->cfgno);
err = usbdev_schedule_xact(udev, h->udev->ep_ctrl, xact, 1, NULL, NULL);
if (err < 0) {
usb_destroy_xact(udev->dman, xact, 1);
usb_free(h);
h = NULL;
return -1;
}
usb_destroy_xact(udev->dman, xact, 1);
/* Power up ports */
xact[0].type = PID_SETUP;
xact[0].len = sizeof(*req);
usb_alloc_xact(h->udev->dman, xact, 1);
req = xact_get_vaddr(&xact[0]);
for (i = 1; i <= h->nports; i++) {
ZF_LOGD("Power on port %d\n", i);
*req = __set_port_feature_req(i, PORT_POWER);
err = usbdev_schedule_xact(h->udev, h->udev->ep_ctrl,
xact, 1, NULL, NULL);
if (err < 0) {
ZF_LOGF("Transaction error\n");
}
}
ps_mdelay(h->power_good_delay_ms);
usb_destroy_xact(udev->dman, xact, 1);
#if !defined(HUB_ENABLE_IRQS)
/* Setup ports */
for (i = 1; i <= h->nports; i++) {
_handle_port_change(h, i);
}
#endif
#if defined(HUB_ENABLE_IRQS)
h->int_xact.type = PID_IN;
/*
* USB 2.0 spec[11.12.4] says the packet size should be (nport + 7)/8, but
* some hubs are known to send more data, which would cause a "babble". So
* we use maximum packet size instead, short packet does no harm.
*/
h->int_xact.len = h->int_max_pkt;
err = usb_alloc_xact(udev->dman, &h->int_xact, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
h->intbm = xact_get_vaddr(&h->int_xact);
ZF_LOGD("Registering for INT\n");
/* FIXME: Search for the right ep */
usbdev_schedule_xact(udev, udev->ep[0],
&h->int_xact, 1, &hub_irq_handler, h);
#else
h->intbm = NULL;
h->int_xact.vaddr = NULL;
h->int_xact.paddr = 0;
h->int_xact.len = 0;
(void)hub_irq_handler;
#endif
*hub = h;
return 0;
}
/*********************
*** Hub emulation ***
*********************/
static struct device_desc _hub_device_desc = {
.bLength = sizeof(struct device_desc),
.bDescriptorType = DEVICE,
.bcdUSB = 0x200,
.bDeviceClass = USB_CLASS_HUB,
.bDeviceSubClass = 0,
.bDeviceProtocol = 2,
.bMaxPacketSize0 = 64,
.idVendor = 0xFEED,
.idProduct = 0xBEEF,
.bcdDevice = 1234,
.iManufacturer = 0,
.iProduct = 0,
.iSerialNumber = 0,
.bNumConfigurations = 1
};
static struct iface_desc _hub_iface_desc = {
.bLength = sizeof(_hub_iface_desc),
.bDescriptorType = INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = 9,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 1,
.iInterface = 0
};
static struct endpoint_desc _hub_endpoint_desc = {
.bLength = sizeof(_hub_endpoint_desc),
.bDescriptorType = ENDPOINT,
.bEndpointAddress = 0x81,
.bmAttributes = 0x3,
.wMaxPacketSize = 0x1,
.bInterval = 0xc
};
static struct config_desc _hub_config_desc = {
.bLength = sizeof(_hub_config_desc),
.bDescriptorType = CONFIGURATION,
.wTotalLength = sizeof(_hub_config_desc) +
sizeof(_hub_iface_desc) + sizeof(_hub_endpoint_desc),
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfigurationIndex = 0,
.bmAttributes = (1 << 7),
.bMaxPower = 100 /*mA */ / 2
};
static struct hub_desc _hub_hub_desc = {
.bDescLength = 0x8,
.bDescriptorType = DESCRIPTOR_TYPE_HUB,
.bNbrPorts = 2,
.wHubCharacteristics = 0,
.bPwrOn2PwrGood = 0xff,
.bHubContrCurrent = 0,
.portcfg = {0}
};
static int
hubem_get_descriptor(usb_hubem_t dev, struct usbreq *req, void *buf, int len)
{
int act_len = 0;
int dtype = req->wValue >> 8;
switch (dtype) {
case DEVICE:{
struct device_desc *ret = (struct device_desc *)buf;
ZF_LOGD("Get device descriptor\n");
act_len = MIN(len, sizeof(*ret));
memcpy(ret, &_hub_device_desc, act_len);
return act_len;}
case DESCRIPTOR_TYPE_HUB:{
struct hub_desc *ret = (struct hub_desc *)buf;
int nregs = (dev->hubem_nports + 7) / 8;
int i;
ZF_LOGD("Get hub type descriptor\n");
_hub_hub_desc.bNbrPorts = dev->hubem_nports;
_hub_hub_desc.bPwrOn2PwrGood = dev->pwr_delay_ms / 2;
_hub_hub_desc.bDescLength = 7 + nregs * 2;
for (i = 0; i < nregs; i++) {
_hub_hub_desc.portcfg[i] = 0;
_hub_hub_desc.portcfg[i + nregs] = 0;
}
act_len = MIN(_hub_hub_desc.bDescLength, len);
memcpy(ret, &_hub_hub_desc, act_len);
return act_len;}
case CONFIGURATION:{
int cp_len;
int pos = 0;
int act_len;
ZF_LOGD("Get configuration descriptor\n");
act_len = MIN(_hub_config_desc.wTotalLength, len);
/* Copy the config */
cp_len = MIN(act_len - pos, _hub_config_desc.bLength);
memcpy(buf + pos, &_hub_config_desc, cp_len);
pos += cp_len;
/* Copy the iface */
cp_len = MIN(act_len - pos, _hub_iface_desc.bLength);
memcpy(buf + pos, &_hub_iface_desc, cp_len);
pos += cp_len;
/* copy the endpoint */
_hub_endpoint_desc.wMaxPacketSize =
(dev->hubem_nports + 7) / 8;
cp_len = MIN(act_len - pos, _hub_endpoint_desc.bLength);
memcpy(buf + pos, &_hub_endpoint_desc, cp_len);
pos += cp_len;
if (pos != act_len) {
ZF_LOGF("Invalid descriptor\n");
}
return act_len;}
case INTERFACE:{
int act_len;
ZF_LOGD("Get interface descriptor\n");
act_len = MIN(_hub_iface_desc.bLength, len);
memcpy(buf, &_hub_iface_desc, act_len);
return act_len;}
case ENDPOINT:{
int act_len;
ZF_LOGD("Get endpoint descriptor\n");
act_len = MIN(_hub_endpoint_desc.bLength, len);
memcpy(buf, &_hub_endpoint_desc, act_len);
return act_len;}
case STRING:
case DEVICE_QUALIFIER:
case OTHER_SPEED_CONFIGURATION:
case INTERFACE_POWER:
default:
ZF_LOGD("Descriptor 0x%x not supported\n", dtype);
return -1;
}
}
static int hubem_feature(usb_hubem_t dev, struct usbreq *req)
{
int f = req->wValue;
int p = req->wIndex;
void *t = dev->token;
int ret;
switch (req->bRequest) {
case SET_FEATURE:
ZF_LOGD("Set feature %d -> port %d\n", f, p);
return dev->set_pf(t, p, f);
case CLR_FEATURE:
ZF_LOGD("Clear feature %d -> port %d\n", f, p);
return dev->clr_pf(t, p, f);
default:
printf("Unsupported feature: %d\n", f);
return -1;
}
return ret;
}
static int
hubem_get_status(usb_hubem_t dev, struct usbreq *req, void *buf, int len)
{
int port = req->wIndex;
if (port == 0) {
/* Device status: self powered | remote wakeup */
uint16_t stat = 0;
int act_len;
ZF_LOGD("Get Status: Device status\n");
act_len = MIN(len, sizeof(stat));
memcpy(buf, &stat, act_len);
return act_len;
} else if (port <= dev->hubem_nports) {
/* Port status */
struct port_status *psts = (struct port_status *)buf;
int act_len = MIN(len, sizeof(*psts));
if (len < sizeof(*psts)) {
ZF_LOGF("Invalid port status\n");
}
if (dev->get_pstat(dev->token, port, psts)) {
ZF_LOGD
("Get Status: Failed to read status for port %d\n",
port);
return -1;
} else {
ZF_LOGD("Get Status: Success s0x%x c0x%0x on port %d\n",
psts->wPortStatus, psts->wPortChange, port);
return act_len;
}
} else {
ZF_LOGD("Get Status: Invalid port (%d/%d)\n", port,
dev->hubem_nports);
return -1;
}
}
int
hubem_process_xact(usb_hubem_t dev, struct xact *xact, int nxact,
usb_cb_t cb, void *t)
{
struct usbreq *req;
void *buf;
int buf_len;
int i;
int err;
for (err = 0, i = 0; !err && i < nxact; i++) {
if (xact[i].type != PID_SETUP) {
continue;
}
req = xact_get_vaddr(&xact[i]);
if (xact[i].len < sizeof(*req)) {
ZF_LOGF("Buffer too small\n");
}
if (i + 1 < nxact && xact[i + 1].type != PID_SETUP) {
buf = xact_get_vaddr(&xact[i + 1]);
buf_len = xact[i + 1].len;
} else {
buf = NULL;
buf_len = 0;
}
switch (req->bRequest) {
case GET_STATUS:
return hubem_get_status(dev, req, buf, buf_len);
case GET_DESCRIPTOR:
return hubem_get_descriptor(dev, req, buf, buf_len);
case SET_CONFIGURATION:
ZF_LOGD("Unhandled transaction: SET_CONFIGURATION\n");
break;
case SET_INTERFACE:
ZF_LOGD("Unhandled transaction: SET_INTERFACE\n");
break;
case SET_ADDRESS:
ZF_LOGD("Unhandled transaction: SET_ADDRESS\n");
break;
case CLR_FEATURE:
case SET_FEATURE:
err = hubem_feature(dev, req);
break;
default:
ZF_LOGE("Request code %d not supported\n",
req->bRequest);
}
}
if (cb) {
if (err >= 0) {
cb(t, XACTSTAT_SUCCESS, err);
} else {
cb(t, XACTSTAT_ERROR, err);
}
}
return err;
}
int
usb_hubem_driver_init(void *token, int nports, int pwr_delay_ms,
int (*set_pf) (void *token, int port,
enum port_feature feature),
int (*clr_pf) (void *token, int port,
enum port_feature feature),
int (*get_pstat) (void *token, int port,
struct port_status *ps),
usb_hubem_t *hub)
{
usb_hubem_t h;
h = (usb_hubem_t) usb_malloc(sizeof(*h));
if (h == NULL) {
ZF_LOGE("Out of memory\n");
return -1;
}
h->token = token;
h->hubem_nports = nports;
h->pwr_delay_ms = pwr_delay_ms;
h->set_pf = set_pf;
h->clr_pf = clr_pf;
h->get_pstat = get_pstat;
*hub = h;
return 0;
}