+static int is_isoc(USBHostDevice *s, int ep)
+{
+ return s->endp_table[ep - 1].type == USBDEVFS_URB_TYPE_ISO;
+}
+
+static int is_halted(USBHostDevice *s, int ep)
+{
+ return s->endp_table[ep - 1].halted;
+}
+
+static void clear_halt(USBHostDevice *s, int ep)
+{
+ s->endp_table[ep - 1].halted = 0;
+}
+
+static void set_halt(USBHostDevice *s, int ep)
+{
+ s->endp_table[ep - 1].halted = 1;
+}
+
+static USBHostDevice *hostdev_list;
+
+static void hostdev_link(USBHostDevice *dev)
+{
+ dev->next = hostdev_list;
+ hostdev_list = dev;
+}
+
+static void hostdev_unlink(USBHostDevice *dev)
+{
+ USBHostDevice *pdev = hostdev_list;
+ USBHostDevice **prev = &hostdev_list;
+
+ while (pdev) {
+ if (pdev == dev) {
+ *prev = dev->next;
+ return;
+ }
+
+ prev = &pdev->next;
+ pdev = pdev->next;
+ }
+}
+
+static USBHostDevice *hostdev_find(int bus_num, int addr)
+{
+ USBHostDevice *s = hostdev_list;
+ while (s) {
+ if (s->bus_num == bus_num && s->addr == addr)
+ return s;
+ s = s->next;
+ }
+ return NULL;
+}
+
+/*
+ * Async URB state.
+ * We always allocate one isoc descriptor even for bulk transfers
+ * to simplify allocation and casts.
+ */
+typedef struct AsyncURB
+{
+ struct usbdevfs_urb urb;
+ struct usbdevfs_iso_packet_desc isocpd;
+
+ USBPacket *packet;
+ USBHostDevice *hdev;
+} AsyncURB;
+
+static AsyncURB *async_alloc(void)
+{
+ return (AsyncURB *) qemu_mallocz(sizeof(AsyncURB));
+}
+
+static void async_free(AsyncURB *aurb)
+{
+ qemu_free(aurb);
+}
+
+static void async_complete_ctrl(USBHostDevice *s, USBPacket *p)
+{
+ switch(s->ctrl.state) {
+ case CTRL_STATE_SETUP:
+ if (p->len < s->ctrl.len)
+ s->ctrl.len = p->len;
+ s->ctrl.state = CTRL_STATE_DATA;
+ p->len = 8;
+ break;
+
+ case CTRL_STATE_ACK:
+ s->ctrl.state = CTRL_STATE_IDLE;
+ p->len = 0;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void async_complete(void *opaque)
+{
+ USBHostDevice *s = opaque;
+ AsyncURB *aurb;
+
+ while (1) {
+ USBPacket *p;
+
+ int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
+ if (r < 0) {
+ if (errno == EAGAIN)
+ return;
+
+ if (errno == ENODEV && !s->closing) {
+ printf("husb: device %d.%d disconnected\n", s->bus_num, s->addr);
+ usb_device_del_addr(0, s->dev.addr);
+ return;
+ }
+
+ dprintf("husb: async. reap urb failed errno %d\n", errno);
+ return;
+ }
+
+ p = aurb->packet;
+
+ dprintf("husb: async completed. aurb %p status %d alen %d\n",
+ aurb, aurb->urb.status, aurb->urb.actual_length);
+
+ if (p) {
+ switch (aurb->urb.status) {
+ case 0:
+ p->len = aurb->urb.actual_length;
+ if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL)
+ async_complete_ctrl(s, p);
+ break;
+
+ case -EPIPE:
+ set_halt(s, p->devep);
+ /* fall through */
+ default:
+ p->len = USB_RET_NAK;
+ break;
+ }
+
+ usb_packet_complete(p);
+ }
+
+ async_free(aurb);
+ }
+}
+
+static void async_cancel(USBPacket *unused, void *opaque)
+{
+ AsyncURB *aurb = opaque;
+ USBHostDevice *s = aurb->hdev;
+
+ dprintf("husb: async cancel. aurb %p\n", aurb);
+
+ /* Mark it as dead (see async_complete above) */
+ aurb->packet = NULL;
+
+ int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
+ if (r < 0) {
+ dprintf("husb: async. discard urb failed errno %d\n", errno);
+ }
+}
+
+static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
+{
+ int dev_descr_len, config_descr_len;
+ int interface, nb_interfaces, nb_configurations;
+ int ret, i;
+
+ if (configuration == 0) /* address state - ignore */
+ return 1;
+
+ dprintf("husb: claiming interfaces. config %d\n", configuration);
+
+ i = 0;
+ dev_descr_len = dev->descr[0];
+ if (dev_descr_len > dev->descr_len)
+ goto fail;
+ nb_configurations = dev->descr[17];
+
+ i += dev_descr_len;
+ while (i < dev->descr_len) {
+ dprintf("husb: i is %d, descr_len is %d, dl %d, dt %d\n", i, dev->descr_len,
+ dev->descr[i], dev->descr[i+1]);
+
+ if (dev->descr[i+1] != USB_DT_CONFIG) {
+ i += dev->descr[i];
+ continue;
+ }
+ config_descr_len = dev->descr[i];
+
+ printf("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
+
+ if (configuration < 0 || configuration == dev->descr[i + 5]) {
+ configuration = dev->descr[i + 5];
+ break;
+ }
+
+ i += config_descr_len;
+ }
+
+ if (i >= dev->descr_len) {
+ fprintf(stderr, "husb: update iface failed. no matching configuration\n");
+ goto fail;
+ }
+ nb_interfaces = dev->descr[i + 4];
+
+#ifdef USBDEVFS_DISCONNECT
+ /* earlier Linux 2.4 do not support that */
+ {
+ struct usbdevfs_ioctl ctrl;
+ for (interface = 0; interface < nb_interfaces; interface++) {
+ ctrl.ioctl_code = USBDEVFS_DISCONNECT;
+ ctrl.ifno = interface;
+ ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
+ if (ret < 0 && errno != ENODATA) {
+ perror("USBDEVFS_DISCONNECT");
+ goto fail;
+ }
+ }
+ }
+#endif
+
+ /* XXX: only grab if all interfaces are free */
+ for (interface = 0; interface < nb_interfaces; interface++) {
+ ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
+ if (ret < 0) {
+ if (errno == EBUSY) {
+ printf("husb: update iface. device already grabbed\n");
+ } else {
+ perror("husb: failed to claim interface");
+ }
+ fail:
+ return 0;
+ }
+ }
+
+ printf("husb: %d interfaces claimed for configuration %d\n",
+ nb_interfaces, configuration);
+
+ dev->ninterfaces = nb_interfaces;
+ dev->configuration = configuration;
+ return 1;
+}
+
+static int usb_host_release_interfaces(USBHostDevice *s)
+{
+ int ret, i;
+
+ dprintf("husb: releasing interfaces\n");
+
+ for (i = 0; i < s->ninterfaces; i++) {
+ ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
+ if (ret < 0) {
+ perror("husb: failed to release interface");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+