* [Qemu-devel] [5048] husb: support for USB host device auto connect (Max Krasnyansky)
@ 2008-08-21 19:28 Anthony Liguori
2008-08-22 10:53 ` [Qemu-devel] " Jan Kiszka
0 siblings, 1 reply; 3+ messages in thread
From: Anthony Liguori @ 2008-08-21 19:28 UTC (permalink / raw)
To: qemu-devel
Revision: 5048
http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=5048
Author: aliguori
Date: 2008-08-21 19:28:55 +0000 (Thu, 21 Aug 2008)
Log Message:
-----------
husb: support for USB host device auto connect (Max Krasnyansky)
QEMU can now automatically grab host USB devices that match the filter.
For now I just extended 'host:X.Y' and 'host:VID:PID' syntax to handle
wildcards. So for example if you do something like
usb_add host:5.*
QEMU will automatically grab any non-hub device with host address 5.*.
Same with the 'host:PID:*', we grab any device that matches PID.
Filtering itself is very generic so we can probably add more elaborate
syntax like 'host:BUS.ADDR:VID:PID'. So that we can do 'host:5.*:6000:*'.
Anyway, it's implemented using a periodic timer that scans host devices
and grabs those that match the filter. Timer is started when the first
filter is added.
We now keep the list of all host devices that we grabbed to make sure that
we do not grab the same device twice.
btw It's currently possible to grab the same host device more than once.
ie You can just do "usb_add host:1.1" more than once, which of course does
not work. So this patch fixes that issue too.
Along with auto disconnect patch that I send a minute ago the setup is very
seamless now. You can just allocate some usb ports to the VMs and plug/unplug
devices at any time.
Signed-off-by: Max Krasnyansky <maxk@kernel.org>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Modified Paths:
--------------
trunk/hw/usb.h
trunk/usb-linux.c
trunk/vl.c
Modified: trunk/hw/usb.h
===================================================================
--- trunk/hw/usb.h 2008-08-21 19:27:48 UTC (rev 5047)
+++ trunk/hw/usb.h 2008-08-21 19:28:55 UTC (rev 5048)
@@ -197,6 +197,7 @@
p->cancel_cb(p, p->cancel_opaque);
}
+int usb_device_add_dev(USBDevice *dev);
int usb_device_del_addr(int bus_num, int addr);
void usb_attach(USBPort *port, USBDevice *dev);
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
Modified: trunk/usb-linux.c
===================================================================
--- trunk/usb-linux.c 2008-08-21 19:27:48 UTC (rev 5047)
+++ trunk/usb-linux.c 2008-08-21 19:28:55 UTC (rev 5048)
@@ -3,6 +3,9 @@
*
* Copyright (c) 2005 Fabrice Bellard
*
+ * Support for host device auto connect & disconnect
+ * Copyright (c) 2008 Max Krasnyansky
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
@@ -67,6 +70,8 @@
uint8_t type;
};
+
+
/* FIXME: move USBPacket to PendingURB */
typedef struct USBHostDevice {
USBDevice dev;
@@ -78,9 +83,51 @@
uint8_t descr[1024];
int descr_len;
int urbs_ready;
+
QEMUTimer *timer;
+
+ /* Host side address */
+ int bus_num;
+ int addr;
+
+ struct USBHostDevice *next;
} USBHostDevice;
+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;
+}
+
typedef struct PendingURB {
struct usbdevfs_urb *urb;
int status;
@@ -238,6 +285,8 @@
qemu_del_timer(s->timer);
+ hostdev_unlink(s);
+
if (s->fd >= 0)
close(s->fd);
@@ -619,32 +668,26 @@
qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
}
-/* XXX: exclude high speed devices or implement EHCI */
-USBDevice *usb_host_device_open(const char *devname)
+static USBDevice *usb_host_device_open_addr(int bus_num, int addr, const char *prod_name)
{
int fd = -1, ret;
USBHostDevice *dev = NULL;
struct usbdevfs_connectinfo ci;
char buf[1024];
- int bus_num, addr;
- char product_name[PRODUCT_NAME_SZ];
- if (usb_host_find_device(&bus_num, &addr,
- product_name, sizeof(product_name),
- devname) < 0)
- return NULL;
-
-
dev = qemu_mallocz(sizeof(USBHostDevice));
if (!dev)
goto fail;
+ dev->bus_num = bus_num;
+ dev->addr = addr;
+
dev->timer = qemu_new_timer(rt_clock, usb_host_device_check, (void *) dev);
if (!dev->timer)
goto fail;
#ifdef DEBUG
- printf("usb_host_device_open %s\n", devname);
+ printf("usb_host_device_open %d.%d\n", bus_num, addr);
#endif
snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d",
@@ -704,12 +747,12 @@
dev->dev.handle_data = usb_host_handle_data;
dev->dev.handle_destroy = usb_host_handle_destroy;
- if (product_name[0] == '\0')
+ if (!prod_name || prod_name[0] == '\0')
snprintf(dev->dev.devname, sizeof(dev->dev.devname),
- "host:%s", devname);
+ "host:%d.%d", bus_num, addr);
else
pstrcpy(dev->dev.devname, sizeof(dev->dev.devname),
- product_name);
+ prod_name);
#ifdef USE_ASYNCIO
/* set up the signal handlers */
@@ -735,8 +778,11 @@
/* Start the timer to detect disconnect */
qemu_mod_timer(dev->timer, qemu_get_clock(rt_clock) + 1000);
+ hostdev_link(dev);
+
dev->urbs_ready = 0;
return (USBDevice *)dev;
+
fail:
if (dev) {
if (dev->timer)
@@ -747,6 +793,24 @@
return NULL;
}
+USBDevice *usb_host_device_open(const char *devname)
+{
+ int bus_num, addr;
+ char product_name[PRODUCT_NAME_SZ];
+
+ if (usb_host_find_device(&bus_num, &addr,
+ product_name, sizeof(product_name),
+ devname) < 0)
+ return NULL;
+
+ if (hostdev_find(bus_num, addr)) {
+ printf("host usb device %d.%d is already open\n", bus_num, addr);
+ return NULL;
+ }
+
+ return usb_host_device_open_addr(bus_num, addr, product_name);
+}
+
static int get_tag_value(char *buf, int buf_size,
const char *str, const char *tag,
const char *stopchars)
@@ -846,6 +910,108 @@
return ret;
}
+struct USBAutoFilter {
+ struct USBAutoFilter *next;
+ int bus_num;
+ int addr;
+ int vendor_id;
+ int product_id;
+};
+
+static QEMUTimer *usb_auto_timer;
+static struct USBAutoFilter *usb_auto_filter;
+
+static int usb_host_auto_scan(void *opaque, int bus_num, int addr,
+ int class_id, int vendor_id, int product_id,
+ const char *product_name, int speed)
+{
+ struct USBAutoFilter *f;
+ struct USBDevice *dev;
+
+ /* Ignore hubs */
+ if (class_id == 9)
+ return 0;
+
+ for (f = usb_auto_filter; f; f = f->next) {
+ // printf("Auto match: bus_num %d addr %d vid %d pid %d\n",
+ // bus_num, addr, vendor_id, product_id);
+
+ if (f->bus_num >= 0 && f->bus_num != bus_num)
+ continue;
+
+ if (f->addr >= 0 && f->addr != addr)
+ continue;
+
+ if (f->vendor_id >= 0 && f->vendor_id != vendor_id)
+ continue;
+
+ if (f->product_id >= 0 && f->product_id != product_id)
+ continue;
+
+ /* We got a match */
+
+ /* Allredy attached ? */
+ if (hostdev_find(bus_num, addr))
+ return 0;
+
+ printf("Auto open: bus_num %d addr %d\n", bus_num, addr);
+
+ dev = usb_host_device_open_addr(bus_num, addr, product_name);
+ if (dev)
+ usb_device_add_dev(dev);
+ }
+
+ return 0;
+}
+
+static void usb_host_auto_timer(void *unused)
+{
+ usb_host_scan(NULL, usb_host_auto_scan);
+ qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
+}
+
+/*
+ * Add autoconnect filter
+ * -1 means 'any' (device, vendor, etc)
+ */
+static void usb_host_auto_add(int bus_num, int addr, int vendor_id, int product_id)
+{
+ struct USBAutoFilter *f = qemu_mallocz(sizeof(*f));
+ if (!f) {
+ printf("Failed to allocate auto filter\n");
+ return;
+ }
+
+ f->bus_num = bus_num;
+ f->addr = addr;
+ f->vendor_id = vendor_id;
+ f->product_id = product_id;
+
+ if (!usb_auto_filter) {
+ /*
+ * First entry. Init and start the monitor.
+ * Right now we're using timer to check for new devices.
+ * If this turns out to be too expensive we can move that into a
+ * separate thread.
+ */
+ usb_auto_timer = qemu_new_timer(rt_clock, usb_host_auto_timer, NULL);
+ if (!usb_auto_timer) {
+ printf("Failed to allocate timer\n");
+ qemu_free(f);
+ return;
+ }
+
+ /* Check for new devices every two seconds */
+ qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
+ }
+
+ printf("Auto filter: bus_num %d addr %d vid %d pid %d\n",
+ bus_num, addr, vendor_id, product_id);
+
+ f->next = usb_auto_filter;
+ usb_auto_filter = f;
+}
+
typedef struct FindDeviceState {
int vendor_id;
int product_id;
@@ -887,6 +1053,12 @@
p = strchr(devname, '.');
if (p) {
*pbus_num = strtoul(devname, NULL, 0);
+
+ if (*(p + 1) == '*') {
+ usb_host_auto_add(*pbus_num, -1, -1, -1);
+ return -1;
+ }
+
*paddr = strtoul(p + 1, NULL, 0);
fs.bus_num = *pbus_num;
fs.addr = *paddr;
@@ -898,6 +1070,12 @@
p = strchr(devname, ':');
if (p) {
fs.vendor_id = strtoul(devname, NULL, 16);
+
+ if (*(p + 1) == '*') {
+ usb_host_auto_add(-1, -1, fs.vendor_id, -1);
+ return -1;
+ }
+
fs.product_id = strtoul(p + 1, NULL, 16);
ret = usb_host_scan(&fs, usb_host_find_device_scan);
if (ret) {
@@ -996,6 +1174,9 @@
usb_host_scan(NULL, usb_host_info_device);
}
+
+
+
#else
void usb_host_info(void)
Modified: trunk/vl.c
===================================================================
--- trunk/vl.c 2008-08-21 19:27:48 UTC (rev 5047)
+++ trunk/vl.c 2008-08-21 19:28:55 UTC (rev 5048)
@@ -5747,6 +5747,32 @@
free_usb_ports = port;
}
+int usb_device_add_dev(USBDevice *dev)
+{
+ USBPort *port;
+
+ /* Find a USB port to add the device to. */
+ port = free_usb_ports;
+ if (!port->next) {
+ USBDevice *hub;
+
+ /* Create a new hub and chain it on. */
+ free_usb_ports = NULL;
+ port->next = used_usb_ports;
+ used_usb_ports = port;
+
+ hub = usb_hub_init(VM_USB_HUB_SIZE);
+ usb_attach(port, hub);
+ port = free_usb_ports;
+ }
+
+ free_usb_ports = port->next;
+ port->next = used_usb_ports;
+ used_usb_ports = port;
+ usb_attach(port, dev);
+ return 0;
+}
+
static int usb_device_add(const char *devname)
{
const char *p;
@@ -5787,26 +5813,7 @@
if (!dev)
return -1;
- /* Find a USB port to add the device to. */
- port = free_usb_ports;
- if (!port->next) {
- USBDevice *hub;
-
- /* Create a new hub and chain it on. */
- free_usb_ports = NULL;
- port->next = used_usb_ports;
- used_usb_ports = port;
-
- hub = usb_hub_init(VM_USB_HUB_SIZE);
- usb_attach(port, hub);
- port = free_usb_ports;
- }
-
- free_usb_ports = port->next;
- port->next = used_usb_ports;
- used_usb_ports = port;
- usb_attach(port, dev);
- return 0;
+ return usb_device_add_dev(dev);
}
int usb_device_del_addr(int bus_num, int addr)
@@ -5859,18 +5866,12 @@
void do_usb_add(const char *devname)
{
- int ret;
- ret = usb_device_add(devname);
- if (ret < 0)
- term_printf("Could not add USB device '%s'\n", devname);
+ usb_device_add(devname);
}
void do_usb_del(const char *devname)
{
- int ret;
- ret = usb_device_del(devname);
- if (ret < 0)
- term_printf("Could not remove USB device '%s'\n", devname);
+ usb_device_del(devname);
}
void usb_info(void)
^ permalink raw reply [flat|nested] 3+ messages in thread
* [Qemu-devel] Re: [5048] husb: support for USB host device auto connect (Max Krasnyansky)
2008-08-21 19:28 [Qemu-devel] [5048] husb: support for USB host device auto connect (Max Krasnyansky) Anthony Liguori
@ 2008-08-22 10:53 ` Jan Kiszka
2008-09-02 11:22 ` Jan Kiszka
0 siblings, 1 reply; 3+ messages in thread
From: Jan Kiszka @ 2008-08-22 10:53 UTC (permalink / raw)
To: qemu-devel
Anthony Liguori wrote:
> Revision: 5048
> http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=5048
> Author: aliguori
> Date: 2008-08-21 19:28:55 +0000 (Thu, 21 Aug 2008)
>
> Log Message:
> -----------
> husb: support for USB host device auto connect (Max Krasnyansky)
>
> QEMU can now automatically grab host USB devices that match the filter.
> For now I just extended 'host:X.Y' and 'host:VID:PID' syntax to handle
> wildcards. So for example if you do something like
> usb_add host:5.*
> QEMU will automatically grab any non-hub device with host address 5.*.
>
> Same with the 'host:PID:*', we grab any device that matches PID.
>
> Filtering itself is very generic so we can probably add more elaborate
> syntax like 'host:BUS.ADDR:VID:PID'. So that we can do 'host:5.*:6000:*'.
>
> Anyway, it's implemented using a periodic timer that scans host devices
> and grabs those that match the filter. Timer is started when the first
> filter is added.
>
> We now keep the list of all host devices that we grabbed to make sure that
> we do not grab the same device twice.
>
> btw It's currently possible to grab the same host device more than once.
> ie You can just do "usb_add host:1.1" more than once, which of course does
> not work. So this patch fixes that issue too.
>
> Along with auto disconnect patch that I send a minute ago the setup is very
> seamless now. You can just allocate some usb ports to the VMs and plug/unplug
> devices at any time.
>
> Signed-off-by: Max Krasnyansky <maxk@kernel.org>
> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
>
...
>
> Modified: trunk/vl.c
> ===================================================================
> --- trunk/vl.c 2008-08-21 19:27:48 UTC (rev 5047)
> +++ trunk/vl.c 2008-08-21 19:28:55 UTC (rev 5048)
> @@ -5747,6 +5747,32 @@
> free_usb_ports = port;
> }
>
> +int usb_device_add_dev(USBDevice *dev)
> +{
> + USBPort *port;
> +
> + /* Find a USB port to add the device to. */
> + port = free_usb_ports;
> + if (!port->next) {
> + USBDevice *hub;
> +
> + /* Create a new hub and chain it on. */
> + free_usb_ports = NULL;
> + port->next = used_usb_ports;
> + used_usb_ports = port;
> +
> + hub = usb_hub_init(VM_USB_HUB_SIZE);
> + usb_attach(port, hub);
> + port = free_usb_ports;
> + }
> +
> + free_usb_ports = port->next;
> + port->next = used_usb_ports;
> + used_usb_ports = port;
> + usb_attach(port, dev);
> + return 0;
> +}
> +
> static int usb_device_add(const char *devname)
> {
> const char *p;
> @@ -5787,26 +5813,7 @@
> if (!dev)
> return -1;
>
> - /* Find a USB port to add the device to. */
> - port = free_usb_ports;
> - if (!port->next) {
> - USBDevice *hub;
> -
> - /* Create a new hub and chain it on. */
> - free_usb_ports = NULL;
> - port->next = used_usb_ports;
> - used_usb_ports = port;
> -
> - hub = usb_hub_init(VM_USB_HUB_SIZE);
> - usb_attach(port, hub);
> - port = free_usb_ports;
> - }
> -
> - free_usb_ports = port->next;
> - port->next = used_usb_ports;
> - used_usb_ports = port;
> - usb_attach(port, dev);
> - return 0;
> + return usb_device_add_dev(dev);
> }
>
> int usb_device_del_addr(int bus_num, int addr)
Same complaint here: Please avoid adding new warnings, it makes tracking
my own regressions only harder. ;)
Jan
-------
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
vl.c | 1 -
1 file changed, 1 deletion(-)
Index: b/vl.c
===================================================================
--- a/vl.c
+++ b/vl.c
@@ -5779,7 +5779,6 @@ static int usb_device_add(const char *de
{
const char *p;
USBDevice *dev;
- USBPort *port;
if (!free_usb_ports)
return -1;
^ permalink raw reply [flat|nested] 3+ messages in thread
* [Qemu-devel] Re: [5048] husb: support for USB host device auto connect (Max Krasnyansky)
2008-08-22 10:53 ` [Qemu-devel] " Jan Kiszka
@ 2008-09-02 11:22 ` Jan Kiszka
0 siblings, 0 replies; 3+ messages in thread
From: Jan Kiszka @ 2008-09-02 11:22 UTC (permalink / raw)
To: qemu-devel
Jan Kiszka wrote:
> Anthony Liguori wrote:
>> Revision: 5048
>> http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=5048
>> Author: aliguori
>> Date: 2008-08-21 19:28:55 +0000 (Thu, 21 Aug 2008)
>>
>> Log Message:
>> -----------
>> husb: support for USB host device auto connect (Max Krasnyansky)
>>
>> QEMU can now automatically grab host USB devices that match the filter.
>> For now I just extended 'host:X.Y' and 'host:VID:PID' syntax to handle
>> wildcards. So for example if you do something like
>> usb_add host:5.*
>> QEMU will automatically grab any non-hub device with host address 5.*.
>>
>> Same with the 'host:PID:*', we grab any device that matches PID.
>>
>> Filtering itself is very generic so we can probably add more elaborate
>> syntax like 'host:BUS.ADDR:VID:PID'. So that we can do 'host:5.*:6000:*'.
>>
>> Anyway, it's implemented using a periodic timer that scans host devices
>> and grabs those that match the filter. Timer is started when the first
>> filter is added.
>>
>> We now keep the list of all host devices that we grabbed to make sure that
>> we do not grab the same device twice.
>>
>> btw It's currently possible to grab the same host device more than once.
>> ie You can just do "usb_add host:1.1" more than once, which of course does
>> not work. So this patch fixes that issue too.
>>
>> Along with auto disconnect patch that I send a minute ago the setup is very
>> seamless now. You can just allocate some usb ports to the VMs and plug/unplug
>> devices at any time.
>>
>> Signed-off-by: Max Krasnyansky <maxk@kernel.org>
>> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
>>
>
> ...
>
>> Modified: trunk/vl.c
>> ===================================================================
>> --- trunk/vl.c 2008-08-21 19:27:48 UTC (rev 5047)
>> +++ trunk/vl.c 2008-08-21 19:28:55 UTC (rev 5048)
>> @@ -5747,6 +5747,32 @@
>> free_usb_ports = port;
>> }
>>
>> +int usb_device_add_dev(USBDevice *dev)
>> +{
>> + USBPort *port;
>> +
>> + /* Find a USB port to add the device to. */
>> + port = free_usb_ports;
>> + if (!port->next) {
>> + USBDevice *hub;
>> +
>> + /* Create a new hub and chain it on. */
>> + free_usb_ports = NULL;
>> + port->next = used_usb_ports;
>> + used_usb_ports = port;
>> +
>> + hub = usb_hub_init(VM_USB_HUB_SIZE);
>> + usb_attach(port, hub);
>> + port = free_usb_ports;
>> + }
>> +
>> + free_usb_ports = port->next;
>> + port->next = used_usb_ports;
>> + used_usb_ports = port;
>> + usb_attach(port, dev);
>> + return 0;
>> +}
>> +
>> static int usb_device_add(const char *devname)
>> {
>> const char *p;
>> @@ -5787,26 +5813,7 @@
>> if (!dev)
>> return -1;
>>
>> - /* Find a USB port to add the device to. */
>> - port = free_usb_ports;
>> - if (!port->next) {
>> - USBDevice *hub;
>> -
>> - /* Create a new hub and chain it on. */
>> - free_usb_ports = NULL;
>> - port->next = used_usb_ports;
>> - used_usb_ports = port;
>> -
>> - hub = usb_hub_init(VM_USB_HUB_SIZE);
>> - usb_attach(port, hub);
>> - port = free_usb_ports;
>> - }
>> -
>> - free_usb_ports = port->next;
>> - port->next = used_usb_ports;
>> - used_usb_ports = port;
>> - usb_attach(port, dev);
>> - return 0;
>> + return usb_device_add_dev(dev);
>> }
>>
>> int usb_device_del_addr(int bus_num, int addr)
>
> Same complaint here: Please avoid adding new warnings, it makes tracking
> my own regressions only harder. ;)
>
> Jan
>
> -------
>
> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
> ---
> vl.c | 1 -
> 1 file changed, 1 deletion(-)
>
> Index: b/vl.c
> ===================================================================
> --- a/vl.c
> +++ b/vl.c
> @@ -5779,7 +5779,6 @@ static int usb_device_add(const char *de
> {
> const char *p;
> USBDevice *dev;
> - USBPort *port;
>
> if (!free_usb_ports)
> return -1;
It's still throwing a warning at me...
Jan
--
Siemens AG, Corporate Technology, CT SE 2
Corporate Competence Center Embedded Linux
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2008-09-02 11:22 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-21 19:28 [Qemu-devel] [5048] husb: support for USB host device auto connect (Max Krasnyansky) Anthony Liguori
2008-08-22 10:53 ` [Qemu-devel] " Jan Kiszka
2008-09-02 11:22 ` Jan Kiszka
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).