diff options
Diffstat (limited to 'usb-linux.c')
-rw-r--r-- | usb-linux.c | 814 |
1 files changed, 474 insertions, 340 deletions
diff --git a/usb-linux.c b/usb-linux.c index 1f33c2c230..fcfa09e4b8 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -37,7 +37,6 @@ #include <dirent.h> #include <sys/ioctl.h> -#include <signal.h> #include <linux/usbdevice_fs.h> #include <linux/version.h> @@ -54,15 +53,7 @@ struct usb_ctrltransfer { void *data; }; -struct usb_ctrlrequest { - uint8_t bRequestType; - uint8_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; -}; - -typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath, +typedef int USBScanFunc(void *opaque, int bus_num, int addr, char *port, int class_id, int vendor_id, int product_id, const char *product_name, int speed); @@ -78,7 +69,8 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath, #define USBPROCBUS_PATH "/proc/bus/usb" #define PRODUCT_NAME_SZ 32 -#define MAX_ENDPOINTS 16 +#define MAX_ENDPOINTS 15 +#define MAX_PORTLEN 16 #define USBDEVBUS_PATH "/dev/bus/usb" #define USBSYSBUS_PATH "/sys/bus/usb" @@ -92,34 +84,29 @@ static char *usb_host_device_path; static int usb_fs_type; /* endpoint association data */ +#define ISO_FRAME_DESC_PER_URB 32 +#define ISO_URB_COUNT 3 +#define INVALID_EP_TYPE 255 + +/* devio.c limits single requests to 16k */ +#define MAX_USBFS_BUFFER_SIZE 16384 + +typedef struct AsyncURB AsyncURB; + struct endp_data { uint8_t type; uint8_t halted; -}; - -enum { - CTRL_STATE_IDLE = 0, - CTRL_STATE_SETUP, - CTRL_STATE_DATA, - CTRL_STATE_ACK -}; - -/* - * Control transfer state. - * Note that 'buffer' _must_ follow 'req' field because - * we need contiguous buffer when we submit control URB. - */ -struct ctrl_struct { - uint16_t len; - uint16_t offset; - uint8_t state; - struct usb_ctrlrequest req; - uint8_t buffer[8192]; + uint8_t iso_started; + AsyncURB *iso_urb; + int iso_urb_idx; + int iso_buffer_used; + int max_packet_size; }; struct USBAutoFilter { uint32_t bus_num; uint32_t addr; + char *port; uint32_t vendor_id; uint32_t product_id; }; @@ -135,13 +122,13 @@ typedef struct USBHostDevice { int closing; Notifier exit; - struct ctrl_struct ctrl; struct endp_data endp_table[MAX_ENDPOINTS]; + QLIST_HEAD(, AsyncURB) aurbs; /* Host side address */ int bus_num; int addr; - int devpath; + char port[MAX_PORTLEN]; struct USBAutoFilter match; QTAILQ_ENTRY(USBHostDevice) next; @@ -160,6 +147,11 @@ static int is_isoc(USBHostDevice *s, int ep) return s->endp_table[ep - 1].type == USBDEVFS_URB_TYPE_ISO; } +static int is_valid(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].type != INVALID_EP_TYPE; +} + static int is_halted(USBHostDevice *s, int ep) { return s->endp_table[ep - 1].halted; @@ -175,50 +167,106 @@ static void set_halt(USBHostDevice *s, int ep) s->endp_table[ep - 1].halted = 1; } +static int is_iso_started(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].iso_started; +} + +static void clear_iso_started(USBHostDevice *s, int ep) +{ + s->endp_table[ep - 1].iso_started = 0; +} + +static void set_iso_started(USBHostDevice *s, int ep) +{ + s->endp_table[ep - 1].iso_started = 1; +} + +static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb) +{ + s->endp_table[ep - 1].iso_urb = iso_urb; +} + +static AsyncURB *get_iso_urb(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].iso_urb; +} + +static void set_iso_urb_idx(USBHostDevice *s, int ep, int i) +{ + s->endp_table[ep - 1].iso_urb_idx = i; +} + +static int get_iso_urb_idx(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].iso_urb_idx; +} + +static void set_iso_buffer_used(USBHostDevice *s, int ep, int i) +{ + s->endp_table[ep - 1].iso_buffer_used = i; +} + +static int get_iso_buffer_used(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].iso_buffer_used; +} + +static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor) +{ + int raw = descriptor[4] + (descriptor[5] << 8); + int size, microframes; + + size = raw & 0x7ff; + switch ((raw >> 11) & 3) { + case 1: microframes = 2; break; + case 2: microframes = 3; break; + default: microframes = 1; break; + } + DPRINTF("husb: max packet size: 0x%x -> %d x %d\n", + raw, microframes, size); + s->endp_table[ep - 1].max_packet_size = size * microframes; +} + +static int get_max_packet_size(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].max_packet_size; +} + /* * Async URB state. - * We always allocate one isoc descriptor even for bulk transfers + * We always allocate iso packet descriptors even for bulk transfers * to simplify allocation and casts. */ -typedef struct AsyncURB +struct AsyncURB { struct usbdevfs_urb urb; - struct usbdevfs_iso_packet_desc isocpd; + struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB]; + USBHostDevice *hdev; + QLIST_ENTRY(AsyncURB) next; + /* For regular async urbs */ USBPacket *packet; - USBHostDevice *hdev; -} AsyncURB; + int more; /* large transfer, more urbs follow */ + + /* For buffered iso handling */ + int iso_frame_idx; /* -1 means in flight */ +}; -static AsyncURB *async_alloc(void) +static AsyncURB *async_alloc(USBHostDevice *s) { - return (AsyncURB *) qemu_mallocz(sizeof(AsyncURB)); + AsyncURB *aurb = qemu_mallocz(sizeof(AsyncURB)); + aurb->hdev = s; + QLIST_INSERT_HEAD(&s->aurbs, aurb, next); + return aurb; } static void async_free(AsyncURB *aurb) { + QLIST_REMOVE(aurb, next); 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; @@ -244,18 +292,25 @@ static void async_complete(void *opaque) return; } - p = aurb->packet; - DPRINTF("husb: async completed. aurb %p status %d alen %d\n", aurb, aurb->urb.status, aurb->urb.actual_length); + /* If this is a buffered iso urb mark it as complete and don't do + anything else (it is handled further in usb_host_handle_iso_data) */ + if (aurb->iso_frame_idx == -1) { + if (aurb->urb.status == -EPIPE) { + set_halt(s, aurb->urb.endpoint & 0xf); + } + aurb->iso_frame_idx = 0; + continue; + } + + p = aurb->packet; + 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); - } + p->len += aurb->urb.actual_length; break; case -EPIPE: @@ -268,26 +323,36 @@ static void async_complete(void *opaque) break; } - usb_packet_complete(p); + if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) { + usb_generic_async_ctrl_complete(&s->dev, p); + } else if (!aurb->more) { + usb_packet_complete(&s->dev, p); + } } async_free(aurb); } } -static void async_cancel(USBPacket *unused, void *opaque) +static void usb_host_async_cancel(USBDevice *dev, USBPacket *p) { - AsyncURB *aurb = opaque; - USBHostDevice *s = aurb->hdev; + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); + AsyncURB *aurb; + + QLIST_FOREACH(aurb, &s->aurbs, next) { + if (p != aurb->packet) { + continue; + } - DPRINTF("husb: async cancel. aurb %p\n", aurb); + DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb); - /* Mark it as dead (see async_complete above) */ - aurb->packet = NULL; + /* 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); + int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb); + if (r < 0) { + DPRINTF("husb: async. discard urb failed errno %d\n", errno); + } } } @@ -415,69 +480,261 @@ static void usb_host_handle_destroy(USBDevice *dev) static int usb_linux_update_endp_table(USBHostDevice *s); -static int usb_host_handle_data(USBHostDevice *s, USBPacket *p) +/* iso data is special, we need to keep enough urbs in flight to make sure + that the controller never runs out of them, otherwise the device will + likely suffer a buffer underrun / overrun. */ +static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in) { - struct usbdevfs_urb *urb; AsyncURB *aurb; - int ret; + int i, j, len = get_max_packet_size(s, ep); + + aurb = qemu_mallocz(ISO_URB_COUNT * sizeof(*aurb)); + for (i = 0; i < ISO_URB_COUNT; i++) { + aurb[i].urb.endpoint = ep; + aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len; + aurb[i].urb.buffer = qemu_malloc(aurb[i].urb.buffer_length); + aurb[i].urb.type = USBDEVFS_URB_TYPE_ISO; + aurb[i].urb.flags = USBDEVFS_URB_ISO_ASAP; + aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB; + for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++) + aurb[i].urb.iso_frame_desc[j].length = len; + if (in) { + aurb[i].urb.endpoint |= 0x80; + /* Mark as fully consumed (idle) */ + aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB; + } + } + set_iso_urb(s, ep, aurb); - aurb = async_alloc(); - aurb->hdev = s; - aurb->packet = p; + return aurb; +} - urb = &aurb->urb; +static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) +{ + AsyncURB *aurb; + int i, ret, killed = 0, free = 1; + + aurb = get_iso_urb(s, ep); + if (!aurb) { + return; + } + + for (i = 0; i < ISO_URB_COUNT; i++) { + /* in flight? */ + if (aurb[i].iso_frame_idx == -1) { + ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]); + if (ret < 0) { + printf("husb: discard isoc in urb failed errno %d\n", errno); + free = 0; + continue; + } + killed++; + } + } + + /* Make sure any urbs we've killed are reaped before we free them */ + if (killed) { + async_complete(s); + } + + for (i = 0; i < ISO_URB_COUNT; i++) { + qemu_free(aurb[i].urb.buffer); + } + + if (free) + qemu_free(aurb); + else + printf("husb: leaking iso urbs because of discard failure\n"); + set_iso_urb(s, ep, NULL); + set_iso_urb_idx(s, ep, 0); + clear_iso_started(s, ep); +} + +static int urb_status_to_usb_ret(int status) +{ + switch (status) { + case -EPIPE: + return USB_RET_STALL; + default: + return USB_RET_NAK; + } +} + +static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) +{ + AsyncURB *aurb; + int i, j, ret, max_packet_size, offset, len = 0; + + max_packet_size = get_max_packet_size(s, p->devep); + if (max_packet_size == 0) + return USB_RET_NAK; + + aurb = get_iso_urb(s, p->devep); + if (!aurb) { + aurb = usb_host_alloc_iso(s, p->devep, in); + } + + i = get_iso_urb_idx(s, p->devep); + j = aurb[i].iso_frame_idx; + if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { + if (in) { + /* Check urb status */ + if (aurb[i].urb.status) { + len = urb_status_to_usb_ret(aurb[i].urb.status); + /* Move to the next urb */ + aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1; + /* Check frame status */ + } else if (aurb[i].urb.iso_frame_desc[j].status) { + len = urb_status_to_usb_ret( + aurb[i].urb.iso_frame_desc[j].status); + /* Check the frame fits */ + } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) { + printf("husb: received iso data is larger then packet\n"); + len = USB_RET_NAK; + /* All good copy data over */ + } else { + len = aurb[i].urb.iso_frame_desc[j].actual_length; + memcpy(p->data, + aurb[i].urb.buffer + + j * aurb[i].urb.iso_frame_desc[0].length, + len); + } + } else { + len = p->len; + offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep); + + /* Check the frame fits */ + if (len > max_packet_size) { + printf("husb: send iso data is larger then max packet size\n"); + return USB_RET_NAK; + } + + /* All good copy data over */ + memcpy(aurb[i].urb.buffer + offset, p->data, len); + aurb[i].urb.iso_frame_desc[j].length = len; + offset += len; + set_iso_buffer_used(s, p->devep, offset); + + /* Start the stream once we have buffered enough data */ + if (!is_iso_started(s, p->devep) && i == 1 && j == 8) { + set_iso_started(s, p->devep); + } + } + aurb[i].iso_frame_idx++; + if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { + i = (i + 1) % ISO_URB_COUNT; + set_iso_urb_idx(s, p->devep, i); + } + } else { + if (in) { + set_iso_started(s, p->devep); + } else { + DPRINTF("hubs: iso out error no free buffer, dropping packet\n"); + } + } + + if (is_iso_started(s, p->devep)) { + /* (Re)-submit all fully consumed / filled urbs */ + for (i = 0; i < ISO_URB_COUNT; i++) { + if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { + ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]); + if (ret < 0) { + printf("husb error submitting iso urb %d: %d\n", i, errno); + if (!in || len == 0) { + switch(errno) { + case ETIMEDOUT: + len = USB_RET_NAK; + break; + case EPIPE: + default: + len = USB_RET_STALL; + } + } + break; + } + aurb[i].iso_frame_idx = -1; + } + } + } + + return len; +} + +static int usb_host_handle_data(USBDevice *dev, USBPacket *p) +{ + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); + struct usbdevfs_urb *urb; + AsyncURB *aurb; + int ret, rem; + uint8_t *pbuf; + uint8_t ep; + + if (!is_valid(s, p->devep)) { + return USB_RET_NAK; + } if (p->pid == USB_TOKEN_IN) { - urb->endpoint = p->devep | 0x80; + ep = p->devep | 0x80; } else { - urb->endpoint = p->devep; + ep = p->devep; } if (is_halted(s, p->devep)) { - ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint); + ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &ep); if (ret < 0) { DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n", - urb->endpoint, errno); + ep, errno); return USB_RET_NAK; } clear_halt(s, p->devep); } - urb->buffer = p->data; - urb->buffer_length = p->len; - if (is_isoc(s, p->devep)) { - /* Setup ISOC transfer */ - urb->type = USBDEVFS_URB_TYPE_ISO; - urb->flags = USBDEVFS_URB_ISO_ASAP; - urb->number_of_packets = 1; - urb->iso_frame_desc[0].length = p->len; - } else { - /* Setup bulk transfer */ - urb->type = USBDEVFS_URB_TYPE_BULK; + return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); } - urb->usercontext = s; + rem = p->len; + pbuf = p->data; + p->len = 0; + while (rem) { + aurb = async_alloc(s); + aurb->packet = p; - ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); + urb = &aurb->urb; + urb->endpoint = ep; + urb->type = USBDEVFS_URB_TYPE_BULK; + urb->usercontext = s; + urb->buffer = pbuf; - DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n", - urb->endpoint, p->len, aurb); + if (rem > MAX_USBFS_BUFFER_SIZE) { + urb->buffer_length = MAX_USBFS_BUFFER_SIZE; + aurb->more = 1; + } else { + urb->buffer_length = rem; + aurb->more = 0; + } + pbuf += urb->buffer_length; + rem -= urb->buffer_length; - if (ret < 0) { - DPRINTF("husb: submit failed. errno %d\n", errno); - async_free(aurb); + ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); - switch(errno) { - case ETIMEDOUT: - return USB_RET_NAK; - case EPIPE: - default: - return USB_RET_STALL; + DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n", + urb->endpoint, urb->buffer_length, aurb->more, p, 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; } @@ -515,7 +772,13 @@ static int usb_host_set_config(USBHostDevice *s, int config) static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) { struct usbdevfs_setinterface si; - int ret; + int i, ret; + + for (i = 1; i <= MAX_ENDPOINTS; i++) { + if (is_isoc(s, i)) { + usb_host_stop_n_free_iso(s, i); + } + } si.interface = iface; si.altsetting = alt; @@ -531,50 +794,43 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) return 0; } -static int usb_host_handle_control(USBHostDevice *s, USBPacket *p) +static int usb_host_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); struct usbdevfs_urb *urb; AsyncURB *aurb; - int ret, value, index; - int buffer_len; + int ret; /* * 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); + /* Note request is (bRequestType << 8) | bRequest */ 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); + request >> 8, request & 0xff, value, index, length); - if (s->ctrl.req.bRequestType == 0) { - switch (s->ctrl.req.bRequest) { - case USB_REQ_SET_ADDRESS: - return usb_host_set_address(s, value); + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + return usb_host_set_address(s, value); - case USB_REQ_SET_CONFIGURATION: - return usb_host_set_config(s, value & 0xff); - } - } + case DeviceOutRequest | 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) { + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: return usb_host_set_interface(s, index, value); } /* The rest are asynchronous */ - buffer_len = 8 + s->ctrl.len; - if (buffer_len > sizeof(s->ctrl.buffer)) { - fprintf(stderr, "husb: ctrl buffer too small (%u > %zu)\n", - buffer_len, sizeof(s->ctrl.buffer)); + if (length > sizeof(dev->data_buf)) { + fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n", + length, sizeof(dev->data_buf)); return USB_RET_STALL; } - aurb = async_alloc(); - aurb->hdev = s; + aurb = async_alloc(s); aurb->packet = p; /* @@ -588,8 +844,8 @@ static int usb_host_handle_control(USBHostDevice *s, USBPacket *p) urb->type = USBDEVFS_URB_TYPE_CONTROL; urb->endpoint = p->devep; - urb->buffer = &s->ctrl.req; - urb->buffer_length = buffer_len; + urb->buffer = &dev->setup_buf; + urb->buffer_length = length + 8; urb->usercontext = s; @@ -610,174 +866,9 @@ static int usb_host_handle_control(USBHostDevice *s, USBPacket *p) } } - 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->info->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; - } -} - static int usb_linux_get_configuration(USBHostDevice *s) { uint8_t configuration; @@ -788,7 +879,7 @@ static int usb_linux_get_configuration(USBHostDevice *s) char device_name[32], line[1024]; int configuration; - sprintf(device_name, "%d-%d", s->bus_num, s->devpath); + sprintf(device_name, "%d-%s", s->bus_num, s->port); if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue", device_name)) { @@ -823,13 +914,56 @@ usbdevfs: return configuration; } +static uint8_t usb_linux_get_alt_setting(USBHostDevice *s, + uint8_t configuration, uint8_t interface) +{ + uint8_t alt_setting; + struct usb_ctrltransfer ct; + int ret; + + if (usb_fs_type == USB_FS_SYS) { + char device_name[64], line[1024]; + int alt_setting; + + sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port, + (int)configuration, (int)interface); + + if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting", + device_name)) { + goto usbdevfs; + } + if (sscanf(line, "%d", &alt_setting) != 1) { + goto usbdevfs; + } + return alt_setting; + } + +usbdevfs: + 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_setting; + ct.timeout = 50; + ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct); + if (ret < 0) { + /* Assume alt 0 on error */ + return 0; + } + + return alt_setting; +} + /* 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; + int interface, length, i; + + for (i = 0; i < MAX_ENDPOINTS; i++) + s->endp_table[i].type = INVALID_EP_TYPE; i = usb_linux_get_configuration(s); if (i < 0) @@ -858,19 +992,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s) } 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) { - alt_interface = interface; - } + alt_interface = usb_linux_get_alt_setting(s, configuration, interface); /* the current interface descriptor is the active interface * and has endpoints */ @@ -899,6 +1021,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s) break; case 0x01: type = USBDEVFS_URB_TYPE_ISO; + set_max_packet_size(s, (devep & 0xf), descriptors + i); break; case 0x02: type = USBDEVFS_URB_TYPE_BULK; @@ -920,7 +1043,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s) } static int usb_host_open(USBHostDevice *dev, int bus_num, - int addr, int devpath, const char *prod_name) + int addr, char *port, const char *prod_name) { int fd = -1, ret; struct usbdevfs_connectinfo ci; @@ -946,7 +1069,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, dev->bus_num = bus_num; dev->addr = addr; - dev->devpath = devpath; + strcpy(dev->port, port); dev->fd = fd; /* read the device description */ @@ -1021,12 +1144,19 @@ fail: static int usb_host_close(USBHostDevice *dev) { + int i; + if (dev->fd == -1) { return -1; } qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); dev->closing = 1; + for (i = 1; i <= MAX_ENDPOINTS; i++) { + if (is_isoc(dev, i)) { + usb_host_stop_n_free_iso(dev, i); + } + } async_complete(dev); dev->closing = 0; usb_device_detach(&dev->dev); @@ -1063,7 +1193,10 @@ static struct USBDeviceInfo usb_host_dev_info = { .qdev.name = "usb-host", .qdev.size = sizeof(USBHostDevice), .init = usb_host_initfn, - .handle_packet = usb_host_handle_packet, + .handle_packet = usb_generic_handle_packet, + .cancel_packet = usb_host_async_cancel, + .handle_data = usb_host_handle_data, + .handle_control = usb_host_handle_control, .handle_reset = usb_host_handle_reset, .handle_destroy = usb_host_handle_destroy, .usbdevice_name = "host", @@ -1071,6 +1204,7 @@ static struct USBDeviceInfo usb_host_dev_info = { .qdev.props = (Property[]) { DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0), DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0), + DEFINE_PROP_STRING("hostport", USBHostDevice, match.port), DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0), DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0), DEFINE_PROP_END_OF_LIST(), @@ -1311,8 +1445,9 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func) { DIR *dir = NULL; char line[1024]; - int bus_num, addr, devpath, speed, class_id, product_id, vendor_id; + int bus_num, addr, speed, class_id, product_id, vendor_id; int ret = 0; + char port[MAX_PORTLEN]; char product_name[512]; struct dirent *de; @@ -1324,12 +1459,8 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func) while ((de = readdir(dir))) { if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) { - char *tmpstr = de->d_name; - if (!strncmp(de->d_name, "usb", 3)) { - tmpstr += 3; - } - if (sscanf(tmpstr, "%d-%d", &bus_num, &devpath) < 1) { - goto the_end; + if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) { + continue; } if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) { @@ -1381,7 +1512,7 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func) speed = USB_SPEED_FULL; } - ret = func(opaque, bus_num, addr, devpath, class_id, vendor_id, + ret = func(opaque, bus_num, addr, port, class_id, vendor_id, product_id, product_name, speed); if (ret) { goto the_end; @@ -1472,7 +1603,7 @@ static int usb_host_scan(void *opaque, USBScanFunc *func) static QEMUTimer *usb_auto_timer; -static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath, +static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port, int class_id, int vendor_id, int product_id, const char *product_name, int speed) { @@ -1492,6 +1623,9 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath, if (f->addr > 0 && f->addr != addr) { continue; } + if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) { + continue; + } if (f->vendor_id > 0 && f->vendor_id != vendor_id) { continue; @@ -1508,7 +1642,7 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath, } DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); - usb_host_open(s, bus_num, addr, devpath, product_name); + usb_host_open(s, bus_num, addr, port, product_name); } return 0; @@ -1630,8 +1764,8 @@ static const char *usb_class_str(uint8_t class) return p->class_name; } -static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id, - int vendor_id, int product_id, +static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port, + int class_id, int vendor_id, int product_id, const char *product_name, int speed) { @@ -1652,8 +1786,8 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id, break; } - monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n", - bus_num, addr, speed_str); + monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n", + bus_num, addr, port, speed_str); class_str = usb_class_str(class_id); if (class_str) { monitor_printf(mon, " %s:", class_str); @@ -1668,14 +1802,14 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id, } static int usb_host_info_device(void *opaque, int bus_num, int addr, - int devpath, int class_id, + char *path, int class_id, int vendor_id, int product_id, const char *product_name, int speed) { Monitor *mon = opaque; - usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id, + usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id, product_name, speed); return 0; } @@ -1717,7 +1851,7 @@ void usb_host_info(Monitor *mon) dec2str(f->addr, addr, sizeof(addr)); hex2str(f->vendor_id, vid, sizeof(vid)); hex2str(f->product_id, pid, sizeof(pid)); - monitor_printf(mon, " Device %s.%s ID %s:%s\n", - bus, addr, vid, pid); + monitor_printf(mon, " Bus %s, Addr %s, Port %s, ID %s:%s\n", + bus, addr, f->port ? f->port : "*", vid, pid); } } |