From e6a2f50042580796de2df38e7eb7a907f4305839 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 22 Aug 2011 14:13:20 +0200 Subject: usb-host: start tracing support Add a bunch of trace points to usb-linux.c Drop a bunch of DPRINTK's in favor of the trace points. Also cleanup error reporting a bit while being at it. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 73 ++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 25 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index 2e20f8e..4e4df61 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -34,6 +34,7 @@ #include "qemu-timer.h" #include "monitor.h" #include "sysemu.h" +#include "trace.h" #include #include @@ -165,11 +166,13 @@ static int is_halted(USBHostDevice *s, int ep) static void clear_halt(USBHostDevice *s, int ep) { + trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep); get_endp(s, ep)->halted = 0; } static void set_halt(USBHostDevice *s, int ep) { + trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep); get_endp(s, ep)->halted = 1; } @@ -180,12 +183,15 @@ static int is_iso_started(USBHostDevice *s, int ep) static void clear_iso_started(USBHostDevice *s, int ep) { + trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep); get_endp(s, ep)->iso_started = 0; } static void set_iso_started(USBHostDevice *s, int ep) { struct endp_data *e = get_endp(s, ep); + + trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep); if (!e->iso_started) { e->iso_started = 1; e->inflight = 0; @@ -285,8 +291,6 @@ static void async_free(AsyncURB *aurb) static void do_disconnect(USBHostDevice *s) { - printf("husb: device %d.%d disconnected\n", - s->bus_num, s->addr); usb_host_close(s); usb_host_auto_check(NULL); } @@ -309,11 +313,12 @@ static void async_complete(void *opaque) return; } if (errno == ENODEV && !s->closing) { + trace_usb_host_disconnect(s->bus_num, s->addr); do_disconnect(s); return; } - DPRINTF("husb: async. reap urb failed errno %d\n", errno); + perror("USBDEVFS_REAPURBNDELAY"); return; } @@ -337,6 +342,8 @@ static void async_complete(void *opaque) } p = aurb->packet; + trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status, + aurb->urb.actual_length, aurb->more); if (p) { switch (aurb->urb.status) { @@ -355,8 +362,10 @@ static void async_complete(void *opaque) } if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) { + trace_usb_host_req_complete(s->bus_num, s->addr, p->result); usb_generic_async_ctrl_complete(&s->dev, p); } else if (!aurb->more) { + trace_usb_host_req_complete(s->bus_num, s->addr, p->result); usb_packet_complete(&s->dev, p); } } @@ -418,7 +427,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) } config_descr_len = dev->descr[i]; - printf("husb: config #%d need %d\n", dev->descr[i + 5], configuration); + DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration); if (configuration < 0 || configuration == dev->descr[i + 5]) { configuration = dev->descr[i + 5]; @@ -457,17 +466,12 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) op = "USBDEVFS_CLAIMINTERFACE"; 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"); - } goto fail; } } - printf("husb: %d interfaces claimed for configuration %d\n", - nb_interfaces, configuration); + trace_usb_host_claim_interfaces(dev->bus_num, dev->addr, + nb_interfaces, configuration); dev->ninterfaces = nb_interfaces; dev->configuration = configuration; @@ -485,16 +489,15 @@ static int usb_host_release_interfaces(USBHostDevice *s) { int ret, i; - DPRINTF("husb: releasing interfaces\n"); + trace_usb_host_release_interfaces(s->bus_num, s->addr); for (i = 0; i < s->ninterfaces; i++) { ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i); if (ret < 0) { - perror("husb: failed to release interface"); + perror("USBDEVFS_RELEASEINTERFACE"); return 0; } } - return 1; } @@ -502,7 +505,7 @@ static void usb_host_handle_reset(USBDevice *dev) { USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); - DPRINTF("husb: reset device %u.%u\n", s->bus_num, s->addr); + trace_usb_host_reset(s->bus_num, s->addr); ioctl(s->fd, USBDEVFS_RESET); @@ -564,7 +567,7 @@ static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) 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); + perror("USBDEVFS_DISCARDURB"); free = 0; continue; } @@ -680,7 +683,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) 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); + perror("USBDEVFS_SUBMITURB"); if (!in || len == 0) { switch(errno) { case ETIMEDOUT: @@ -711,7 +714,12 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) uint8_t *pbuf; uint8_t ep; + trace_usb_host_req_data(s->bus_num, s->addr, + p->pid == USB_TOKEN_IN, + p->devep, p->iov.size); + if (!is_valid(s, p->devep)) { + trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); return USB_RET_NAK; } @@ -724,8 +732,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) if (is_halted(s, p->devep)) { ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &ep); if (ret < 0) { - DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n", - ep, errno); + perror("USBDEVFS_CLEAR_HALT"); + trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); return USB_RET_NAK; } clear_halt(s, p->devep); @@ -767,20 +775,24 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) aurb->more = 1; } + trace_usb_host_urb_submit(s->bus_num, s->addr, aurb, + urb->buffer_length, aurb->more); ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); 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); + perror("USBDEVFS_SUBMITURB"); async_free(aurb); switch(errno) { case ETIMEDOUT: + trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); return USB_RET_NAK; case EPIPE: default: + trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_STALL); return USB_RET_STALL; } } @@ -800,13 +812,15 @@ static int ctrl_error(void) static int usb_host_set_address(USBHostDevice *s, int addr) { - DPRINTF("husb: ctrl set addr %u\n", addr); + trace_usb_host_set_address(s->bus_num, s->addr, addr); s->dev.addr = addr; return 0; } static int usb_host_set_config(USBHostDevice *s, int config) { + trace_usb_host_set_config(s->bus_num, s->addr, config); + usb_host_release_interfaces(s); int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config); @@ -825,6 +839,8 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) struct usbdevfs_setinterface si; int i, ret; + trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt); + for (i = 1; i <= MAX_ENDPOINTS; i++) { if (is_isoc(s, i)) { usb_host_stop_n_free_iso(s, i); @@ -859,8 +875,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, */ /* Note request is (bRequestType << 8) | bRequest */ - DPRINTF("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n", - request >> 8, request & 0xff, value, index, length); + trace_usb_host_req_control(s->bus_num, s->addr, request, value, index); switch (request) { case DeviceOutRequest | USB_REQ_SET_ADDRESS: @@ -900,6 +915,8 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, urb->usercontext = s; + trace_usb_host_urb_submit(s->bus_num, s->addr, aurb, + urb->buffer_length, aurb->more); ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb); @@ -1140,10 +1157,11 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, int fd = -1, ret; char buf[1024]; + trace_usb_host_open_started(bus_num, addr); + if (dev->fd != -1) { goto fail; } - printf("husb: open device %d.%d\n", bus_num, addr); if (!usb_host_device_path) { perror("husb: USB Host Device Path not set"); @@ -1218,7 +1236,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, dev->dev.speedmask |= USB_SPEED_MASK_FULL; } - printf("husb: grabbed usb device %d.%d\n", bus_num, addr); + trace_usb_host_open_success(bus_num, addr); if (!prod_name || prod_name[0] == '\0') { snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), @@ -1239,6 +1257,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, return 0; fail: + trace_usb_host_open_failure(bus_num, addr); if (dev->fd != -1) { close(dev->fd); dev->fd = -1; @@ -1254,6 +1273,8 @@ static int usb_host_close(USBHostDevice *dev) return -1; } + trace_usb_host_close(dev->bus_num, dev->addr); + qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); dev->closing = 1; for (i = 1; i <= MAX_ENDPOINTS; i++) { @@ -1776,6 +1797,7 @@ static void usb_host_auto_check(void *unused) /* nothing to watch */ if (usb_auto_timer) { qemu_del_timer(usb_auto_timer); + trace_usb_host_auto_scan_disabled(); } return; } @@ -1785,6 +1807,7 @@ static void usb_host_auto_check(void *unused) if (!usb_auto_timer) { return; } + trace_usb_host_auto_scan_enabled(); } qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000); } -- cgit v1.1 From 40197c359b1f06cbaae5460336bca241c35f266e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 22 Aug 2011 14:18:21 +0200 Subject: usb-host: reapurb error report fix Don't report errors on devices which are in disconnected and closing state. --- usb-linux.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index 4e4df61..12e8772 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -312,9 +312,11 @@ static void async_complete(void *opaque) } return; } - if (errno == ENODEV && !s->closing) { - trace_usb_host_disconnect(s->bus_num, s->addr); - do_disconnect(s); + if (errno == ENODEV) { + if (!s->closing) { + trace_usb_host_disconnect(s->bus_num, s->addr); + do_disconnect(s); + } return; } -- cgit v1.1 From 9b87e19bc7adea69bd55ae9c64b8a7fb81214726 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 24 Aug 2011 10:55:40 +0200 Subject: usb-host: fix halted endpoints Two fixes for the price of one ;) First, reinitialize the endpoint table after device reset. This is needed anyway as the reset might have switched interfaces. It also clears the endpoint halted state. Second the CLEAR_HALT ioctl wants a unsigned int passed in as argument, not uint8_t. This gets my usb sd card reader (sandisk micromate) going. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index 12e8772..344af22 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -143,6 +143,7 @@ static int parse_filter(const char *spec, struct USBAutoFilter *f); static void usb_host_auto_check(void *unused); static int usb_host_read_file(char *line, size_t line_size, const char *device_file, const char *device_name); +static int usb_linux_update_endp_table(USBHostDevice *s); static struct endp_data *get_endp(USBHostDevice *s, int ep) { @@ -512,6 +513,7 @@ static void usb_host_handle_reset(USBDevice *dev) ioctl(s->fd, USBDEVFS_RESET); usb_host_claim_interfaces(s, s->configuration); + usb_linux_update_endp_table(s); } static void usb_host_handle_destroy(USBDevice *dev) @@ -523,8 +525,6 @@ static void usb_host_handle_destroy(USBDevice *dev) qemu_remove_exit_notifier(&s->exit); } -static int usb_linux_update_endp_table(USBHostDevice *s); - /* 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. */ @@ -732,7 +732,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) } if (is_halted(s, p->devep)) { - ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &ep); + unsigned int arg = ep; + ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg); if (ret < 0) { perror("USBDEVFS_CLEAR_HALT"); trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); -- cgit v1.1 From 3ee886c5ba77a65d6b2c2a372a091d6796ed502b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 24 Aug 2011 13:45:06 +0200 Subject: usb-host: limit open retries Limit the number of times qemu tries to open host devices to three. Reset error counter when the device goes away, after un-plugging and re-plugging the device qemu will try again three times. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index 344af22..36d25d7 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -132,6 +132,7 @@ typedef struct USBHostDevice { int addr; char port[MAX_PORTLEN]; struct USBAutoFilter match; + int seen, errcount; QTAILQ_ENTRY(USBHostDevice) next; } USBHostDevice; @@ -1769,6 +1770,10 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port, continue; } /* We got a match */ + s->seen++; + if (s->errcount >= 3) { + return 0; + } /* Already attached ? */ if (s->fd != -1) { @@ -1776,7 +1781,9 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port, } DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); - usb_host_open(s, bus_num, addr, port, product_name, speed); + if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) { + s->errcount++; + } break; } @@ -1794,6 +1801,10 @@ static void usb_host_auto_check(void *unused) if (s->fd == -1) { unconnected++; } + if (s->seen == 0) { + s->errcount = 0; + } + s->seen = 0; } if (unconnected == 0) { -- cgit v1.1 From eb7700bb999544151d6e4350362b9b6d9d20abe8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 24 Aug 2011 14:45:07 +0200 Subject: usb-host: fix configuration tracking. It is perfectly fine to leave the usb device in unconfigured state (USBHostDevice->configuration == 0). Just do that and wait for the guest to explicitly set a configuration. This is closer to what real hardware does and it also simplifies the device initialization. There is no need to figure how the device is configured on the host. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 82 ++++++++++++++----------------------------------------------- 1 file changed, 19 insertions(+), 63 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index 36d25d7..a903023 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -407,8 +407,11 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) int interface, nb_interfaces; int ret, i; - if (configuration == 0) /* address state - ignore */ + if (configuration == 0) { /* address state - ignore */ + dev->ninterfaces = 0; + dev->configuration = 0; return 1; + } DPRINTF("husb: claiming interfaces. config %d\n", configuration); @@ -433,7 +436,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration); - if (configuration < 0 || configuration == dev->descr[i + 5]) { + if (configuration == dev->descr[i + 5]) { configuration = dev->descr[i + 5]; break; } @@ -513,7 +516,7 @@ static void usb_host_handle_reset(USBDevice *dev) ioctl(s->fd, USBDEVFS_RESET); - usb_host_claim_interfaces(s, s->configuration); + usb_host_claim_interfaces(s, 0); usb_linux_update_endp_table(s); } @@ -835,6 +838,7 @@ static int usb_host_set_config(USBHostDevice *s, int config) return ctrl_error(); } usb_host_claim_interfaces(s, config); + usb_linux_update_endp_table(s); return 0; } @@ -941,51 +945,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, return USB_RET_ASYNC; } -static int usb_linux_get_configuration(USBHostDevice *s) -{ - uint8_t configuration; - struct usb_ctrltransfer ct; - int ret; - - if (usb_fs_type == USB_FS_SYS) { - char device_name[32], line[1024]; - int configuration; - - sprintf(device_name, "%d-%s", s->bus_num, s->port); - - if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue", - device_name)) { - goto usbdevfs; - } - if (sscanf(line, "%d", &configuration) != 1) { - goto usbdevfs; - } - return configuration; - } - -usbdevfs: - 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_get_configuration"); - return -1; - } - - /* in address state */ - if (configuration == 0) { - return -1; - } - - return configuration; -} - static uint8_t usb_linux_get_alt_setting(USBHostDevice *s, uint8_t configuration, uint8_t interface) { @@ -1031,16 +990,16 @@ usbdevfs: static int usb_linux_update_endp_table(USBHostDevice *s) { uint8_t *descriptors; - uint8_t devep, type, configuration, alt_interface; + uint8_t devep, type, alt_interface; 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) - return 1; - configuration = i; + if (s->configuration == 0) { + /* not configured yet -- leave all endpoints disabled */ + return 0; + } /* get the desired configuration, interface, and endpoint descriptors * from device description */ @@ -1049,8 +1008,9 @@ static int usb_linux_update_endp_table(USBHostDevice *s) i = 0; if (descriptors[i + 1] != USB_DT_CONFIG || - descriptors[i + 5] != configuration) { - DPRINTF("invalid descriptor data - configuration\n"); + descriptors[i + 5] != s->configuration) { + fprintf(stderr, "invalid descriptor data - configuration %d\n", + s->configuration); return 1; } i += descriptors[i]; @@ -1064,7 +1024,8 @@ static int usb_linux_update_endp_table(USBHostDevice *s) } interface = descriptors[i + 2]; - alt_interface = usb_linux_get_alt_setting(s, configuration, interface); + alt_interface = usb_linux_get_alt_setting(s, s->configuration, + interface); /* the current interface descriptor is the active interface * and has endpoints */ @@ -1204,13 +1165,8 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, #endif - /* - * Initial configuration is -1 which makes us claim first - * available config. We used to start with 1, which does not - * always work. I've seen devices where first config starts - * with 2. - */ - if (!usb_host_claim_interfaces(dev, -1)) { + /* start unconfigured -- we'll wait for the guest to set a configuration */ + if (!usb_host_claim_interfaces(dev, 0)) { goto fail; } -- cgit v1.1 From 9516bb4772815f3bbf94d4779905681a7d254850 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 24 Aug 2011 13:34:17 +0200 Subject: usb-host: claim port When configured to pass through a specific host port (using hostbus and hostport properties), try to claim the port if supported by the kernel. That will avoid any kernel drivers binding to devices plugged into that port. It will not stop any userspace apps (such as usb_modeswitch) access the device via usbfs though. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index a903023..ce0eadd 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -115,6 +115,7 @@ struct USBAutoFilter { typedef struct USBHostDevice { USBDevice dev; int fd; + int hub_fd; uint8_t descr[8192]; int descr_len; @@ -525,6 +526,9 @@ static void usb_host_handle_destroy(USBDevice *dev) USBHostDevice *s = (USBHostDevice *)dev; usb_host_close(s); + if (s->hub_fd != -1) { + close(s->hub_fd); + } QTAILQ_REMOVE(&hostdevs, s, next); qemu_remove_exit_notifier(&s->exit); } @@ -1266,10 +1270,63 @@ static int usb_host_initfn(USBDevice *dev) dev->auto_attach = 0; s->fd = -1; + s->hub_fd = -1; + QTAILQ_INSERT_TAIL(&hostdevs, s, next); s->exit.notify = usb_host_exit_notifier; qemu_add_exit_notifier(&s->exit); usb_host_auto_check(NULL); + +#ifdef USBDEVFS_CLAIM_PORT + if (s->match.bus_num != 0 && s->match.port != NULL) { + char *h, hub_name[64], line[1024]; + int hub_addr, portnr, ret; + + snprintf(hub_name, sizeof(hub_name), "%d-%s", + s->match.bus_num, s->match.port); + + /* try strip off last ".$portnr" to get hub */ + h = strrchr(hub_name, '.'); + if (h != NULL) { + portnr = atoi(h+1); + *h = '\0'; + } else { + /* no dot in there -> it is the root hub */ + snprintf(hub_name, sizeof(hub_name), "usb%d", + s->match.bus_num); + portnr = atoi(s->match.port); + } + + if (!usb_host_read_file(line, sizeof(line), "devnum", + hub_name)) { + goto out; + } + if (sscanf(line, "%d", &hub_addr) != 1) { + goto out; + } + + if (!usb_host_device_path) { + goto out; + } + snprintf(line, sizeof(line), "%s/%03d/%03d", + usb_host_device_path, s->match.bus_num, hub_addr); + s->hub_fd = open(line, O_RDWR | O_NONBLOCK); + if (s->hub_fd < 0) { + goto out; + } + + ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &portnr); + if (ret < 0) { + close(s->hub_fd); + s->hub_fd = -1; + goto out; + } + + trace_usb_host_claim_port(s->match.bus_num, hub_addr, portnr); + } +out: +#endif + return 0; } -- cgit v1.1 From c0e5750bc35366b7231693d9ae433bea9a485ef0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Aug 2011 16:27:41 +0200 Subject: usb-host: endpoint table fixup USB Devices can have up to 15 IN and 15 OUT endpoints, not 15 endpoints total. Move from one array to two arrays (one IN, one OUT) to maintain the endpoint state. --- usb-linux.c | 179 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 101 insertions(+), 78 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index ce0eadd..6490582 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -125,7 +125,8 @@ typedef struct USBHostDevice { uint32_t iso_urb_count; Notifier exit; - struct endp_data endp_table[MAX_ENDPOINTS]; + struct endp_data ep_in[MAX_ENDPOINTS]; + struct endp_data ep_out[MAX_ENDPOINTS]; QLIST_HEAD(, AsyncURB) aurbs; /* Host side address */ @@ -147,52 +148,57 @@ static int usb_host_read_file(char *line, size_t line_size, const char *device_file, const char *device_name); static int usb_linux_update_endp_table(USBHostDevice *s); -static struct endp_data *get_endp(USBHostDevice *s, int ep) +static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep) { - return s->endp_table + ep - 1; + struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out; + assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT); + assert(ep > 0 && ep <= MAX_ENDPOINTS); + return eps + ep - 1; } -static int is_isoc(USBHostDevice *s, int ep) +static int is_isoc(USBHostDevice *s, int pid, int ep) { - return get_endp(s, ep)->type == USBDEVFS_URB_TYPE_ISO; + return get_endp(s, pid, ep)->type == USBDEVFS_URB_TYPE_ISO; } -static int is_valid(USBHostDevice *s, int ep) +static int is_valid(USBHostDevice *s, int pid, int ep) { - return get_endp(s, ep)->type != INVALID_EP_TYPE; + return get_endp(s, pid, ep)->type != INVALID_EP_TYPE; } -static int is_halted(USBHostDevice *s, int ep) +static int is_halted(USBHostDevice *s, int pid, int ep) { - return get_endp(s, ep)->halted; + return get_endp(s, pid, ep)->halted; } -static void clear_halt(USBHostDevice *s, int ep) +static void clear_halt(USBHostDevice *s, int pid, int ep) { trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep); - get_endp(s, ep)->halted = 0; + get_endp(s, pid, ep)->halted = 0; } -static void set_halt(USBHostDevice *s, int ep) +static void set_halt(USBHostDevice *s, int pid, int ep) { - trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep); - get_endp(s, ep)->halted = 1; + if (ep != 0) { + trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep); + get_endp(s, pid, ep)->halted = 1; + } } -static int is_iso_started(USBHostDevice *s, int ep) +static int is_iso_started(USBHostDevice *s, int pid, int ep) { - return get_endp(s, ep)->iso_started; + return get_endp(s, pid, ep)->iso_started; } -static void clear_iso_started(USBHostDevice *s, int ep) +static void clear_iso_started(USBHostDevice *s, int pid, int ep) { trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep); - get_endp(s, ep)->iso_started = 0; + get_endp(s, pid, ep)->iso_started = 0; } -static void set_iso_started(USBHostDevice *s, int ep) +static void set_iso_started(USBHostDevice *s, int pid, int ep) { - struct endp_data *e = get_endp(s, ep); + struct endp_data *e = get_endp(s, pid, ep); trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep); if (!e->iso_started) { @@ -201,45 +207,46 @@ static void set_iso_started(USBHostDevice *s, int ep) } } -static int change_iso_inflight(USBHostDevice *s, int ep, int value) +static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value) { - struct endp_data *e = get_endp(s, ep); + struct endp_data *e = get_endp(s, pid, ep); e->inflight += value; return e->inflight; } -static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb) +static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb) { - get_endp(s, ep)->iso_urb = iso_urb; + get_endp(s, pid, ep)->iso_urb = iso_urb; } -static AsyncURB *get_iso_urb(USBHostDevice *s, int ep) +static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep) { - return get_endp(s, ep)->iso_urb; + return get_endp(s, pid, ep)->iso_urb; } -static void set_iso_urb_idx(USBHostDevice *s, int ep, int i) +static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i) { - get_endp(s, ep)->iso_urb_idx = i; + get_endp(s, pid, ep)->iso_urb_idx = i; } -static int get_iso_urb_idx(USBHostDevice *s, int ep) +static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep) { - return get_endp(s, ep)->iso_urb_idx; + return get_endp(s, pid, ep)->iso_urb_idx; } -static void set_iso_buffer_used(USBHostDevice *s, int ep, int i) +static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i) { - get_endp(s, ep)->iso_buffer_used = i; + get_endp(s, pid, ep)->iso_buffer_used = i; } -static int get_iso_buffer_used(USBHostDevice *s, int ep) +static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep) { - return get_endp(s, ep)->iso_buffer_used; + return get_endp(s, pid, ep)->iso_buffer_used; } -static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor) +static void set_max_packet_size(USBHostDevice *s, int pid, int ep, + uint8_t *descriptor) { int raw = descriptor[4] + (descriptor[5] << 8); int size, microframes; @@ -250,12 +257,12 @@ static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor) case 2: microframes = 3; break; default: microframes = 1; break; } - get_endp(s, ep)->max_packet_size = size * microframes; + get_endp(s, pid, ep)->max_packet_size = size * microframes; } -static int get_max_packet_size(USBHostDevice *s, int ep) +static int get_max_packet_size(USBHostDevice *s, int pid, int ep) { - return get_endp(s, ep)->max_packet_size; + return get_endp(s, pid, ep)->max_packet_size; } /* @@ -334,13 +341,16 @@ static void async_complete(void *opaque) anything else (it is handled further in usb_host_handle_iso_data) */ if (aurb->iso_frame_idx == -1) { int inflight; + int pid = (aurb->urb.endpoint & USB_DIR_IN) ? + USB_TOKEN_IN : USB_TOKEN_OUT; + int ep = aurb->urb.endpoint & 0xf; if (aurb->urb.status == -EPIPE) { - set_halt(s, aurb->urb.endpoint & 0xf); + set_halt(s, pid, ep); } aurb->iso_frame_idx = 0; urbs++; - inflight = change_iso_inflight(s, aurb->urb.endpoint & 0xf, -1); - if (inflight == 0 && is_iso_started(s, aurb->urb.endpoint & 0xf)) { + inflight = change_iso_inflight(s, pid, ep, -1); + if (inflight == 0 && is_iso_started(s, pid, ep)) { fprintf(stderr, "husb: out of buffers for iso stream\n"); } continue; @@ -357,7 +367,7 @@ static void async_complete(void *opaque) break; case -EPIPE: - set_halt(s, p->devep); + set_halt(s, p->pid, p->devep); p->result = USB_RET_STALL; break; @@ -536,10 +546,10 @@ static void usb_host_handle_destroy(USBDevice *dev) /* 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) +static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep) { AsyncURB *aurb; - int i, j, len = get_max_packet_size(s, ep); + int i, j, len = get_max_packet_size(s, pid, ep); aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb)); for (i = 0; i < s->iso_urb_count; i++) { @@ -551,23 +561,23 @@ static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in) 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) { + if (pid == USB_TOKEN_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); + set_iso_urb(s, pid, ep, aurb); return aurb; } -static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) +static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep) { AsyncURB *aurb; int i, ret, killed = 0, free = 1; - aurb = get_iso_urb(s, ep); + aurb = get_iso_urb(s, pid, ep); if (!aurb) { return; } @@ -598,9 +608,9 @@ static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) g_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); + set_iso_urb(s, pid, ep, NULL); + set_iso_urb_idx(s, pid, ep, 0); + clear_iso_started(s, pid, ep); } static int urb_status_to_usb_ret(int status) @@ -619,16 +629,16 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) int i, j, ret, max_packet_size, offset, len = 0; uint8_t *buf; - max_packet_size = get_max_packet_size(s, p->devep); + max_packet_size = get_max_packet_size(s, p->pid, p->devep); if (max_packet_size == 0) return USB_RET_NAK; - aurb = get_iso_urb(s, p->devep); + aurb = get_iso_urb(s, p->pid, p->devep); if (!aurb) { - aurb = usb_host_alloc_iso(s, p->devep, in); + aurb = usb_host_alloc_iso(s, p->pid, p->devep); } - i = get_iso_urb_idx(s, p->devep); + i = get_iso_urb_idx(s, p->pid, p->devep); j = aurb[i].iso_frame_idx; if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { if (in) { @@ -655,7 +665,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) } } else { len = p->iov.size; - offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep); + offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->devep); /* Check the frame fits */ if (len > max_packet_size) { @@ -667,27 +677,27 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) usb_packet_copy(p, aurb[i].urb.buffer + offset, len); aurb[i].urb.iso_frame_desc[j].length = len; offset += len; - set_iso_buffer_used(s, p->devep, offset); + set_iso_buffer_used(s, p->pid, 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); + if (!is_iso_started(s, p->pid, p->devep) && i == 1 && j == 8) { + set_iso_started(s, p->pid, p->devep); } } aurb[i].iso_frame_idx++; if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { i = (i + 1) % s->iso_urb_count; - set_iso_urb_idx(s, p->devep, i); + set_iso_urb_idx(s, p->pid, p->devep, i); } } else { if (in) { - set_iso_started(s, p->devep); + set_iso_started(s, p->pid, p->devep); } else { DPRINTF("hubs: iso out error no free buffer, dropping packet\n"); } } - if (is_iso_started(s, p->devep)) { + if (is_iso_started(s, p->pid, p->devep)) { /* (Re)-submit all fully consumed / filled urbs */ for (i = 0; i < s->iso_urb_count; i++) { if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { @@ -707,7 +717,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) break; } aurb[i].iso_frame_idx = -1; - change_iso_inflight(s, p->devep, +1); + change_iso_inflight(s, p->pid, p->devep, 1); } } } @@ -728,7 +738,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) p->pid == USB_TOKEN_IN, p->devep, p->iov.size); - if (!is_valid(s, p->devep)) { + if (!is_valid(s, p->pid, p->devep)) { trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); return USB_RET_NAK; } @@ -739,7 +749,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) ep = p->devep; } - if (is_halted(s, p->devep)) { + if (is_halted(s, p->pid, p->devep)) { unsigned int arg = ep; ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg); if (ret < 0) { @@ -747,10 +757,10 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); return USB_RET_NAK; } - clear_halt(s, p->devep); + clear_halt(s, p->pid, p->devep); } - if (is_isoc(s, p->devep)) { + if (is_isoc(s, p->pid, p->devep)) { return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); } @@ -854,8 +864,11 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt); for (i = 1; i <= MAX_ENDPOINTS; i++) { - if (is_isoc(s, i)) { - usb_host_stop_n_free_iso(s, i); + if (is_isoc(s, USB_TOKEN_IN, i)) { + usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i); + } + if (is_isoc(s, USB_TOKEN_OUT, i)) { + usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i); } } @@ -995,10 +1008,13 @@ static int usb_linux_update_endp_table(USBHostDevice *s) { uint8_t *descriptors; uint8_t devep, type, alt_interface; - int interface, length, i; + int interface, length, i, ep, pid; + struct endp_data *epd; - for (i = 0; i < MAX_ENDPOINTS; i++) - s->endp_table[i].type = INVALID_EP_TYPE; + for (i = 0; i < MAX_ENDPOINTS; i++) { + s->ep_in[i].type = INVALID_EP_TYPE; + s->ep_out[i].type = INVALID_EP_TYPE; + } if (s->configuration == 0) { /* not configured yet -- leave all endpoints disabled */ @@ -1052,7 +1068,9 @@ static int usb_linux_update_endp_table(USBHostDevice *s) } devep = descriptors[i + 2]; - if ((devep & 0x0f) == 0) { + pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; + ep = devep & 0xf; + if (ep == 0) { fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n"); return 1; } @@ -1063,7 +1081,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); + set_max_packet_size(s, pid, ep, descriptors + i); break; case 0x02: type = USBDEVFS_URB_TYPE_BULK; @@ -1075,8 +1093,10 @@ static int usb_linux_update_endp_table(USBHostDevice *s) DPRINTF("usb_host: malformed endpoint type\n"); type = USBDEVFS_URB_TYPE_BULK; } - s->endp_table[(devep & 0xf) - 1].type = type; - s->endp_table[(devep & 0xf) - 1].halted = 0; + epd = get_endp(s, pid, ep); + assert(epd->type == INVALID_EP_TYPE); + epd->type = type; + epd->halted = 0; i += descriptors[i]; } @@ -1242,8 +1262,11 @@ static int usb_host_close(USBHostDevice *dev) 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); + if (is_isoc(dev, USB_TOKEN_IN, i)) { + usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i); + } + if (is_isoc(dev, USB_TOKEN_OUT, i)) { + usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i); } } async_complete(dev); -- cgit v1.1 From ba9acab9bfc23bedbf639217af7c68142f022a07 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 17 Aug 2011 23:35:45 +0200 Subject: usb-host: constify port Signed-off-by: Gerd Hoffmann --- usb-linux.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index 6490582..ef29a76 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -54,7 +54,7 @@ struct usb_ctrltransfer { void *data; }; -typedef int USBScanFunc(void *opaque, int bus_num, int addr, char *port, +typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port, int class_id, int vendor_id, int product_id, const char *product_name, int speed); @@ -1141,7 +1141,8 @@ static int usb_linux_full_speed_compat(USBHostDevice *dev) } static int usb_host_open(USBHostDevice *dev, int bus_num, - int addr, char *port, const char *prod_name, int speed) + int addr, const char *port, + const char *prod_name, int speed) { int fd = -1, ret; char buf[1024]; @@ -1774,7 +1775,8 @@ 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, char *port, +static int usb_host_auto_scan(void *opaque, int bus_num, + int addr, const char *port, int class_id, int vendor_id, int product_id, const char *product_name, int speed) { @@ -1948,7 +1950,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, char *port, +static void usb_info_device(Monitor *mon, int bus_num, + int addr, const char *port, int class_id, int vendor_id, int product_id, const char *product_name, int speed) @@ -1989,7 +1992,7 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port, } static int usb_host_info_device(void *opaque, int bus_num, int addr, - char *path, int class_id, + const char *path, int class_id, int vendor_id, int product_id, const char *product_name, int speed) -- cgit v1.1 From 0c402e5abb8c2755390eee864b43a98280fc2453 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 17 Aug 2011 23:36:46 +0200 Subject: usb-host: parse port in /proc/bus/usb/devices scan Unfortunaly this is limited to root ports. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index ef29a76..67639f3 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1483,7 +1483,8 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func) FILE *f = NULL; char line[1024]; char buf[1024]; - int bus_num, addr, speed, device_count, class_id, product_id, vendor_id; + int bus_num, addr, speed, device_count; + int class_id, product_id, vendor_id, port; char product_name[512]; int ret = 0; @@ -1499,7 +1500,7 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func) } device_count = 0; - bus_num = addr = class_id = product_id = vendor_id = 0; + bus_num = addr = class_id = product_id = vendor_id = port = 0; speed = -1; /* Can't get the speed from /[proc|dev]/bus/usb/devices */ for(;;) { if (fgets(line, sizeof(line), f) == NULL) { @@ -1521,6 +1522,10 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func) goto fail; } bus_num = atoi(buf); + if (get_tag_value(buf, sizeof(buf), line, "Port=", " ") < 0) { + goto fail; + } + port = atoi(buf); if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0) { goto fail; } @@ -1566,7 +1571,12 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func) } if (device_count && (vendor_id || product_id)) { /* Add the last device. */ - ret = func(opaque, bus_num, addr, 0, class_id, vendor_id, + if (port > 0) { + snprintf(buf, sizeof(buf), "%d", port); + } else { + snprintf(buf, sizeof(buf), "?"); + } + ret = func(opaque, bus_num, addr, buf, class_id, vendor_id, product_id, product_name, speed); } the_end: -- cgit v1.1 From d67915786822b42094ce99768e595994cf6de0bf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 31 Aug 2011 11:44:24 +0200 Subject: usb-host: tag as unmigratable Signed-off-by: Gerd Hoffmann --- usb-linux.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index 67639f3..2075c4c 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1354,10 +1354,16 @@ out: return 0; } +static const VMStateDescription vmstate_usb_host = { + .name = "usb-host", + .unmigratable = 1, +}; + static struct USBDeviceInfo usb_host_dev_info = { .product_desc = "USB Host Device", .qdev.name = "usb-host", .qdev.size = sizeof(USBHostDevice), + .qdev.vmsd = &vmstate_usb_host, .init = usb_host_initfn, .handle_packet = usb_generic_handle_packet, .cancel_packet = usb_host_async_cancel, -- cgit v1.1