diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2012-02-15 17:25:25 -0600 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2012-02-15 17:25:25 -0600 |
commit | 7718564ba1295f35188a5fb3ac8633c29d43b166 (patch) | |
tree | 5aa858b930de7f4c25867f05f867c049d4f713f5 /usb-redir.c | |
parent | 65b31cc207b8ab949033870acf55bb124d12848e (diff) | |
parent | 7c605a23b2c7606f5f06b7d83d8927b1dc111478 (diff) |
Merge remote-tracking branch 'kraxel/usb.38' into staging
* kraxel/usb.38: (28 commits)
xhci: handle USB_RET_NAK
xhci: remote wakeup support
xhci: kill port arg from xhci_setup_packet
xhci: stop on errors
xhci: add trb type name lookup support.
xhci: signal low- and fullspeed support
usb: add USBBusOps->wakeup_endpoint
usb: pass USBEndpoint to usb_wakeup
usb: maintain async packet list per endpoint
usb: Set USBEndpoint in usb_packet_setup().
usb: add USBEndpoint->{nr,pid}
usb: USBPacket: add status, rename owner -> ep
usb: fold usb_generic_handle_packet into usb_handle_packet
usb: kill handle_packet callback
usb-xhci: switch to usb_find_device()
usb-musb: switch to usb_find_device()
usb-ohci: switch to usb_find_device()
usb-ehci: switch to usb_find_device()
usb-uhci: switch to usb_find_device()
usb: handle dev == NULL in usb_handle_packet()
...
Diffstat (limited to 'usb-redir.c')
-rw-r--r-- | usb-redir.c | 118 |
1 files changed, 106 insertions, 12 deletions
diff --git a/usb-redir.c b/usb-redir.c index 82e7e2103c..a59b347f01 100644 --- a/usb-redir.c +++ b/usb-redir.c @@ -34,6 +34,7 @@ #include <sys/ioctl.h> #include <signal.h> #include <usbredirparser.h> +#include <usbredirfilter.h> #include "hw/usb.h" @@ -72,6 +73,7 @@ struct USBRedirDevice { /* Properties */ CharDriverState *cs; uint8_t debug; + char *filter_str; /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ const uint8_t *read_buf; int read_buf_size; @@ -84,6 +86,11 @@ struct USBRedirDevice { struct endp_data endpoint[MAX_ENDPOINTS]; uint32_t packet_id; QTAILQ_HEAD(, AsyncURB) asyncq; + /* Data for device filtering */ + struct usb_redir_device_connect_header device_info; + struct usb_redir_interface_info_header interface_info; + struct usbredirfilter_rule *filter_rules; + int filter_rules_count; }; struct AsyncURB { @@ -603,7 +610,7 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p) USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); uint8_t ep; - ep = p->devep; + ep = p->ep->nr; if (p->pid == USB_TOKEN_IN) { ep |= USB_DIR_IN; } @@ -780,6 +787,7 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, static void usbredir_open_close_bh(void *opaque) { USBRedirDevice *dev = opaque; + uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; usbredir_device_disconnect(dev); @@ -810,7 +818,9 @@ static void usbredir_open_close_bh(void *opaque) dev->parser->interrupt_packet_func = usbredir_interrupt_packet; dev->read_buf = NULL; dev->read_buf_size = 0; - usbredirparser_init(dev->parser, VERSION, NULL, 0, 0); + + usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); + usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0); usbredirparser_do_write(dev->parser); } } @@ -880,6 +890,17 @@ static int usbredir_initfn(USBDevice *udev) return -1; } + if (dev->filter_str) { + i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|", + &dev->filter_rules, + &dev->filter_rules_count); + if (i) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "filter", + "a usb device filter string"); + return -1; + } + } + dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev); dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); @@ -929,6 +950,44 @@ static void usbredir_handle_destroy(USBDevice *udev) if (dev->parser) { usbredirparser_destroy(dev->parser); } + + free(dev->filter_rules); +} + +static int usbredir_check_filter(USBRedirDevice *dev) +{ + if (dev->interface_info.interface_count == 0) { + ERROR("No interface info for device\n"); + return -1; + } + + if (dev->filter_rules) { + if (!usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_connect_device_version)) { + ERROR("Device filter specified and peer does not have the " + "connect_device_version capability\n"); + return -1; + } + + if (usbredirfilter_check( + dev->filter_rules, + dev->filter_rules_count, + dev->device_info.device_class, + dev->device_info.device_subclass, + dev->device_info.device_protocol, + dev->interface_info.interface_class, + dev->interface_info.interface_subclass, + dev->interface_info.interface_protocol, + dev->interface_info.interface_count, + dev->device_info.vendor_id, + dev->device_info.product_id, + dev->device_info.device_version_bcd, + 0) != 0) { + return -1; + } + } + + return 0; } /* @@ -957,6 +1016,7 @@ static void usbredir_device_connect(void *priv, struct usb_redir_device_connect_header *device_connect) { USBRedirDevice *dev = priv; + const char *speed; if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) { ERROR("Received device connect while already connected\n"); @@ -965,26 +1025,48 @@ static void usbredir_device_connect(void *priv, switch (device_connect->speed) { case usb_redir_speed_low: - DPRINTF("attaching low speed device\n"); + speed = "low speed"; dev->dev.speed = USB_SPEED_LOW; break; case usb_redir_speed_full: - DPRINTF("attaching full speed device\n"); + speed = "full speed"; dev->dev.speed = USB_SPEED_FULL; break; case usb_redir_speed_high: - DPRINTF("attaching high speed device\n"); + speed = "high speed"; dev->dev.speed = USB_SPEED_HIGH; break; case usb_redir_speed_super: - DPRINTF("attaching super speed device\n"); + speed = "super speed"; dev->dev.speed = USB_SPEED_SUPER; break; default: - DPRINTF("attaching unknown speed device, assuming full speed\n"); + speed = "unknown speed"; dev->dev.speed = USB_SPEED_FULL; } + + if (usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_connect_device_version)) { + INFO("attaching %s device %04x:%04x version %d.%d class %02x\n", + speed, device_connect->vendor_id, device_connect->product_id, + device_connect->device_version_bcd >> 8, + device_connect->device_version_bcd & 0xff, + device_connect->device_class); + } else { + INFO("attaching %s device %04x:%04x class %02x\n", speed, + device_connect->vendor_id, device_connect->product_id, + device_connect->device_class); + } + dev->dev.speedmask = (1 << dev->dev.speed); + dev->device_info = *device_connect; + + if (usbredir_check_filter(dev)) { + WARNING("Device %04x:%04x rejected by device filter, not attaching\n", + device_connect->vendor_id, device_connect->product_id); + return; + } + qemu_mod_timer(dev->attach_timer, dev->next_attach_time); } @@ -1011,15 +1093,27 @@ static void usbredir_device_disconnect(void *priv) for (i = 0; i < MAX_ENDPOINTS; i++) { QTAILQ_INIT(&dev->endpoint[i].bufpq); } + dev->interface_info.interface_count = 0; } static void usbredir_interface_info(void *priv, struct usb_redir_interface_info_header *interface_info) { - /* The intention is to allow specifying acceptable interface classes - for redirection on the cmdline and in the future verify this here, - and disconnect (or never connect) the device if a not accepted - interface class is detected */ + USBRedirDevice *dev = priv; + + dev->interface_info = *interface_info; + + /* + * If we receive interface info after the device has already been + * connected (ie on a set_config), re-check the filter. + */ + if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) { + if (usbredir_check_filter(dev)) { + ERROR("Device no longer matches filter after interface info " + "change, disconnecting!\n"); + usbredir_device_disconnect(dev); + } + } } static void usbredir_ep_info(void *priv, @@ -1318,6 +1412,7 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id, static Property usbredir_properties[] = { DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), + DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), DEFINE_PROP_END_OF_LIST(), }; @@ -1329,7 +1424,6 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) uc->init = usbredir_initfn; uc->product_desc = "USB Redirection Device"; uc->handle_destroy = usbredir_handle_destroy; - uc->handle_packet = usb_generic_handle_packet; uc->cancel_packet = usbredir_cancel_packet; uc->handle_reset = usbredir_handle_reset; uc->handle_data = usbredir_handle_data; |