+ si.interface = iface;
+ si.altsetting = alt;
+ ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
+
+ dprintf("husb: ctrl set iface %d altset %d ret %d errno %d\n",
+ iface, alt, ret, errno);
+
+ if (ret < 0)
+ return ctrl_error();
+
+ usb_linux_update_endp_table(s);
+ return 0;
+}
+
+static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
+{
+ struct usbdevfs_urb *urb;
+ AsyncURB *aurb;
+ int ret, value, index;
+
+ /*
+ * Process certain standard device requests.
+ * These are infrequent and are processed synchronously.
+ */
+ value = le16_to_cpu(s->ctrl.req.wValue);
+ index = le16_to_cpu(s->ctrl.req.wIndex);
+
+ dprintf("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n",
+ s->ctrl.req.bRequestType, s->ctrl.req.bRequest, value, index,
+ s->ctrl.len);
+
+ if (s->ctrl.req.bRequestType == 0) {
+ switch (s->ctrl.req.bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ return usb_host_set_address(s, value);
+
+ case USB_REQ_SET_CONFIGURATION:
+ return usb_host_set_config(s, value & 0xff);
+ }
+ }
+
+ if (s->ctrl.req.bRequestType == 1 &&
+ s->ctrl.req.bRequest == USB_REQ_SET_INTERFACE)
+ return usb_host_set_interface(s, index, value);
+
+ /* The rest are asynchronous */
+
+ aurb = async_alloc();
+ aurb->hdev = s;
+ aurb->packet = p;
+
+ /*
+ * Setup ctrl transfer.
+ *
+ * s->ctrl is layed out such that data buffer immediately follows
+ * 'req' struct which is exactly what usbdevfs expects.
+ */
+ urb = &aurb->urb;
+
+ urb->type = USBDEVFS_URB_TYPE_CONTROL;
+ urb->endpoint = p->devep;
+
+ urb->buffer = &s->ctrl.req;
+ urb->buffer_length = 8 + s->ctrl.len;
+
+ urb->usercontext = s;
+
+ ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+ dprintf("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
+
+ if (ret < 0) {
+ dprintf("husb: submit failed. errno %d\n", errno);
+ async_free(aurb);
+
+ switch(errno) {
+ case ETIMEDOUT:
+ return USB_RET_NAK;
+ case EPIPE:
+ default:
+ return USB_RET_STALL;
+ }
+ }
+
+ usb_defer_packet(p, async_cancel, aurb);
+ return USB_RET_ASYNC;
+}
+
+static int do_token_setup(USBDevice *dev, USBPacket *p)
+{
+ USBHostDevice *s = (USBHostDevice *) dev;
+ int ret = 0;
+
+ if (p->len != 8)
+ return USB_RET_STALL;
+
+ memcpy(&s->ctrl.req, p->data, 8);
+ s->ctrl.len = le16_to_cpu(s->ctrl.req.wLength);
+ s->ctrl.offset = 0;
+ s->ctrl.state = CTRL_STATE_SETUP;
+
+ if (s->ctrl.req.bRequestType & USB_DIR_IN) {
+ ret = usb_host_handle_control(s, p);
+ if (ret < 0)
+ return ret;
+
+ if (ret < s->ctrl.len)
+ s->ctrl.len = ret;
+ s->ctrl.state = CTRL_STATE_DATA;
+ } else {
+ if (s->ctrl.len == 0)
+ s->ctrl.state = CTRL_STATE_ACK;
+ else
+ s->ctrl.state = CTRL_STATE_DATA;
+ }
+
+ return ret;
+}
+
+static int do_token_in(USBDevice *dev, USBPacket *p)
+{
+ USBHostDevice *s = (USBHostDevice *) dev;
+ int ret = 0;
+
+ if (p->devep != 0)
+ return usb_host_handle_data(s, p);
+
+ switch(s->ctrl.state) {
+ case CTRL_STATE_ACK:
+ if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
+ ret = usb_host_handle_control(s, p);
+ if (ret == USB_RET_ASYNC)
+ return USB_RET_ASYNC;
+
+ s->ctrl.state = CTRL_STATE_IDLE;
+ return ret > 0 ? 0 : ret;
+ }
+
+ return 0;
+
+ case CTRL_STATE_DATA:
+ if (s->ctrl.req.bRequestType & USB_DIR_IN) {
+ int len = s->ctrl.len - s->ctrl.offset;
+ if (len > p->len)
+ len = p->len;
+ memcpy(p->data, s->ctrl.buffer + s->ctrl.offset, len);
+ s->ctrl.offset += len;
+ if (s->ctrl.offset >= s->ctrl.len)
+ s->ctrl.state = CTRL_STATE_ACK;
+ return len;
+ }
+
+ s->ctrl.state = CTRL_STATE_IDLE;
+ return USB_RET_STALL;
+
+ default:
+ return USB_RET_STALL;
+ }
+}
+
+static int do_token_out(USBDevice *dev, USBPacket *p)
+{
+ USBHostDevice *s = (USBHostDevice *) dev;
+
+ if (p->devep != 0)
+ return usb_host_handle_data(s, p);
+
+ switch(s->ctrl.state) {
+ case CTRL_STATE_ACK:
+ if (s->ctrl.req.bRequestType & USB_DIR_IN) {
+ s->ctrl.state = CTRL_STATE_IDLE;
+ /* transfer OK */
+ } else {
+ /* ignore additional output */
+ }
+ return 0;
+
+ case CTRL_STATE_DATA:
+ if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
+ int len = s->ctrl.len - s->ctrl.offset;
+ if (len > p->len)
+ len = p->len;
+ memcpy(s->ctrl.buffer + s->ctrl.offset, p->data, len);
+ s->ctrl.offset += len;
+ if (s->ctrl.offset >= s->ctrl.len)
+ s->ctrl.state = CTRL_STATE_ACK;
+ return len;
+ }
+
+ s->ctrl.state = CTRL_STATE_IDLE;
+ return USB_RET_STALL;
+
+ default:
+ return USB_RET_STALL;
+ }
+}
+
+/*
+ * Packet handler.
+ * Called by the HC (host controller).
+ *
+ * Returns length of the transaction or one of the USB_RET_XXX codes.
+ */
+static int usb_host_handle_packet(USBDevice *s, USBPacket *p)
+{
+ switch(p->pid) {
+ case USB_MSG_ATTACH:
+ s->state = USB_STATE_ATTACHED;
+ return 0;
+
+ case USB_MSG_DETACH:
+ s->state = USB_STATE_NOTATTACHED;
+ return 0;
+
+ case USB_MSG_RESET:
+ s->remote_wakeup = 0;
+ s->addr = 0;
+ s->state = USB_STATE_DEFAULT;
+ s->handle_reset(s);
+ return 0;
+ }
+
+ /* Rest of the PIDs must match our address */
+ if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr)
+ return USB_RET_NODEV;
+
+ switch (p->pid) {
+ case USB_TOKEN_SETUP:
+ return do_token_setup(s, p);
+
+ case USB_TOKEN_IN:
+ return do_token_in(s, p);
+
+ case USB_TOKEN_OUT:
+ return do_token_out(s, p);
+
+ default:
+ return USB_RET_STALL;
+ }
+}
+
+/* returns 1 on problem encountered or 0 for success */
+static int usb_linux_update_endp_table(USBHostDevice *s)
+{
+ uint8_t *descriptors;
+ uint8_t devep, type, configuration, alt_interface;
+ struct usb_ctrltransfer ct;
+ int interface, ret, length, i;
+
+ ct.bRequestType = USB_DIR_IN;
+ ct.bRequest = USB_REQ_GET_CONFIGURATION;
+ ct.wValue = 0;
+ ct.wIndex = 0;
+ ct.wLength = 1;
+ ct.data = &configuration;
+ ct.timeout = 50;
+
+ ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+ if (ret < 0) {
+ perror("usb_linux_update_endp_table");
+ return 1;
+ }
+
+ /* in address state */
+ if (configuration == 0)
+ return 1;
+
+ /* get the desired configuration, interface, and endpoint descriptors
+ * from device description */
+ descriptors = &s->descr[18];
+ length = s->descr_len - 18;
+ i = 0;
+
+ if (descriptors[i + 1] != USB_DT_CONFIG ||
+ descriptors[i + 5] != configuration) {
+ dprintf("invalid descriptor data - configuration\n");
+ return 1;
+ }
+ i += descriptors[i];
+
+ while (i < length) {
+ if (descriptors[i + 1] != USB_DT_INTERFACE ||
+ (descriptors[i + 1] == USB_DT_INTERFACE &&
+ descriptors[i + 4] == 0)) {
+ i += descriptors[i];
+ continue;
+ }
+
+ interface = descriptors[i + 2];
+
+ ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE;
+ ct.bRequest = USB_REQ_GET_INTERFACE;
+ ct.wValue = 0;
+ ct.wIndex = interface;
+ ct.wLength = 1;
+ ct.data = &alt_interface;
+ ct.timeout = 50;
+
+ ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+ if (ret < 0) {
+ perror("usb_linux_update_endp_table");
+ return 1;
+ }
+
+ /* the current interface descriptor is the active interface
+ * and has endpoints */
+ if (descriptors[i + 3] != alt_interface) {
+ i += descriptors[i];
+ continue;
+ }
+
+ /* advance to the endpoints */
+ while (i < length && descriptors[i +1] != USB_DT_ENDPOINT)
+ i += descriptors[i];
+
+ if (i >= length)
+ break;
+
+ while (i < length) {
+ if (descriptors[i + 1] != USB_DT_ENDPOINT)
+ break;
+
+ devep = descriptors[i + 2];
+ switch (descriptors[i + 3] & 0x3) {
+ case 0x00:
+ type = USBDEVFS_URB_TYPE_CONTROL;
+ break;
+ case 0x01:
+ type = USBDEVFS_URB_TYPE_ISO;
+ break;
+ case 0x02:
+ type = USBDEVFS_URB_TYPE_BULK;
+ break;
+ case 0x03:
+ type = USBDEVFS_URB_TYPE_INTERRUPT;
+ break;