USB user interface
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 6 Nov 2005 16:13:29 +0000 (16:13 +0000)
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 6 Nov 2005 16:13:29 +0000 (16:13 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1603 c046a42c-6fe2-441c-8c8c-71466251a162

Makefile.target
hw/pc.c
hw/usb-uhci.c
hw/usb.c
hw/usb.h
monitor.c
usb-linux.c
vl.c
vl.h

index a22b2e0..30a21c5 100644 (file)
@@ -290,12 +290,14 @@ ifdef CONFIG_ADLIB
 SOUND_HW += fmopl.o adlib.o
 endif
 
+# USB layer
+VL_OBJS+= usb.o usb-uhci.o usb-linux.o usb-hid.o
+
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
 VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
 VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
 VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o
-VL_OBJS+= usb.o usb-uhci.o usb-linux.o usb-hid.o
 DEFINES += -DHAS_AUDIO
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
diff --git a/hw/pc.c b/hw/pc.c
index 324f536..918615c 100644 (file)
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -42,6 +42,7 @@ static fdctrl_t *floppy_controller;
 static RTCState *rtc_state;
 static PITState *pit;
 static IOAPICState *ioapic;
+static USBPort *usb_root_ports[2];
 
 static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
 {
@@ -625,32 +626,8 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device,
     cmos_init(ram_size, boot_device, bs_table);
 
     if (pci_enabled && usb_enabled) {
-        USBPort *usb_root_ports[2];
-        USBDevice *usb_dev;
         usb_uhci_init(pci_bus, usb_root_ports);
-#if 0
-        {
-            USBPort *usb_hub1_ports[4];
-            USBPort *usb_hub2_ports[2];
-            /* test: we simulate a USB hub */
-            usb_dev = usb_hub_init(usb_hub1_ports, 4);
-            usb_attach(usb_root_ports[0], usb_dev);
-            
-            /* test: we simulate a USB hub */
-            usb_dev = usb_hub_init(usb_hub2_ports, 2);
-            usb_attach(usb_hub1_ports[0], usb_dev);
-        }
-#endif
-#if 0
-        /* USB mouse */
-        usb_dev = usb_mouse_init();
-        usb_attach(usb_root_ports[0], usb_dev);
-#endif
-#if 1
-        /* simulated hub with the host USB devices connected to it */
-        usb_dev = usb_host_hub_init();
-        usb_attach(usb_root_ports[0], usb_dev);
-#endif
+        usb_attach(usb_root_ports[0], vm_usb_hub);
     }
 
     /* must be done after all PCI devices are instanciated */
index 4dd0c63..0f0c185 100644 (file)
@@ -63,7 +63,6 @@
 typedef struct UHCIPort {
     USBPort port;
     uint16_t ctrl;
-    USBDevice *dev; /* connected device */
 } UHCIPort;
 
 typedef struct UHCIState {
@@ -128,8 +127,8 @@ static void uhci_reset(UHCIState *s)
     for(i = 0; i < NB_PORTS; i++) {
         port = &s->ports[i];
         port->ctrl = 0x0080;
-        if (port->dev)
-            uhci_attach(&port->port, port->dev);
+        if (port->port.dev)
+            uhci_attach(&port->port, port->port.dev);
     }
 }
 
@@ -183,7 +182,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
             /* send reset on the USB bus */
             for(i = 0; i < NB_PORTS; i++) {
                 port = &s->ports[i];
-                dev = port->dev;
+                dev = port->port.dev;
                 if (dev) {
                     dev->handle_packet(dev, 
                                        USB_MSG_RESET, 0, 0, NULL, 0);
@@ -224,7 +223,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
             if (n >= NB_PORTS)
                 return;
             port = &s->ports[n];
-            dev = port->dev;
+            dev = port->port.dev;
             if (dev) {
                 /* port reset */
                 if ( (val & UHCI_PORT_RESET) && 
@@ -320,7 +319,7 @@ static void uhci_attach(USBPort *port1, USBDevice *dev)
     UHCIPort *port = &s->ports[port1->index];
 
     if (dev) {
-        if (port->dev) {
+        if (port->port.dev) {
             usb_attach(port1, NULL);
         }
         /* set connect status */
@@ -332,7 +331,7 @@ static void uhci_attach(USBPort *port1, USBDevice *dev)
             port->ctrl |= UHCI_PORT_LSDA;
         else
             port->ctrl &= ~UHCI_PORT_LSDA;
-        port->dev = dev;
+        port->port.dev = dev;
         /* send the attach message */
         dev->handle_packet(dev, 
                            USB_MSG_ATTACH, 0, 0, NULL, 0);
@@ -346,13 +345,13 @@ static void uhci_attach(USBPort *port1, USBDevice *dev)
             port->ctrl &= ~UHCI_PORT_EN;
             port->ctrl |= UHCI_PORT_ENC;
         }
-        dev = port->dev;
+        dev = port->port.dev;
         if (dev) {
             /* send the detach message */
             dev->handle_packet(dev, 
                                USB_MSG_DETACH, 0, 0, NULL, 0);
         }
-        port->dev = NULL;
+        port->port.dev = NULL;
     }
 }
 
@@ -386,7 +385,7 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid,
 #endif
     for(i = 0; i < NB_PORTS; i++) {
         port = &s->ports[i];
-        dev = port->dev;
+        dev = port->port.dev;
         if (dev && (port->ctrl & UHCI_PORT_EN)) {
             ret = dev->handle_packet(dev, pid, 
                                      devaddr, devep,
index 0728db2..b2830c5 100644 (file)
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -201,7 +201,6 @@ int set_usb_string(uint8_t *buf, const char *str)
 
 typedef struct USBHubPort {
     USBPort port;
-    USBDevice *dev;
     uint16_t wPortStatus;
     uint16_t wPortChange;
 } USBHubPort;
@@ -342,7 +341,7 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
     USBHubPort *port = &s->ports[port1->index];
     
     if (dev) {
-        if (port->dev)
+        if (port->port.dev)
             usb_attach(port1, NULL);
         
         port->wPortStatus |= PORT_STAT_CONNECTION;
@@ -351,9 +350,9 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
             port->wPortStatus |= PORT_STAT_LOW_SPEED;
         else
             port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
-        port->dev = dev;
+        port->port.dev = dev;
     } else {
-        dev = port->dev;
+        dev = port->port.dev;
         if (dev) {
             port->wPortStatus &= ~PORT_STAT_CONNECTION;
             port->wPortChange |= PORT_STAT_C_CONNECTION;
@@ -361,7 +360,7 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
                 port->wPortStatus &= ~PORT_STAT_ENABLE;
                 port->wPortChange |= PORT_STAT_C_ENABLE;
             }
-            port->dev = NULL;
+            port->port.dev = NULL;
         }
     }
 }
@@ -498,7 +497,7 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
             if (n >= s->nb_ports)
                 goto fail;
             port = &s->ports[n];
-            dev = port->dev;
+            dev = port->port.dev;
             switch(value) {
             case PORT_SUSPEND:
                 port->wPortStatus |= PORT_STAT_SUSPEND;
@@ -529,7 +528,7 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
             if (n >= s->nb_ports)
                 goto fail;
             port = &s->ports[n];
-            dev = port->dev;
+            dev = port->port.dev;
             switch(value) {
             case PORT_ENABLE:
                 port->wPortStatus &= ~PORT_STAT_ENABLE;
@@ -624,7 +623,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, int pid,
 
     for(i = 0; i < s->nb_ports; i++) {
         port = &s->ports[i];
-        dev = port->dev;
+        dev = port->port.dev;
         if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
             ret = dev->handle_packet(dev, pid, 
                                      devaddr, devep,
index 7a5c8ad..b4dee23 100644 (file)
--- a/hw/usb.h
+++ b/hw/usb.h
 //#define      USB_STATE_CONFIGURED  5
 #define USB_STATE_SUSPENDED   6
 
+#define USB_CLASS_AUDIO                        1
+#define USB_CLASS_COMM                 2
+#define USB_CLASS_HID                  3
+#define USB_CLASS_PHYSICAL             5
+#define USB_CLASS_STILL_IMAGE          6
+#define USB_CLASS_PRINTER              7
+#define USB_CLASS_MASS_STORAGE         8
+#define USB_CLASS_HUB                  9
+#define USB_CLASS_CDC_DATA             0x0a
+#define USB_CLASS_CSCID                        0x0b
+#define USB_CLASS_CONTENT_SEC          0x0d
+#define USB_CLASS_APP_SPEC             0xfe
+#define USB_CLASS_VENDOR_SPEC          0xff
+
 #define USB_DIR_OUT                    0
 #define USB_DIR_IN                     0x80
 
@@ -125,6 +139,7 @@ struct USBDevice {
 
 /* USB port on which a device can be connected */
 struct USBPort {
+    USBDevice *dev;
     void (*attach)(USBPort *port, USBDevice *dev);
     void *opaque;
     int index; /* internal port index, may be used with the opaque */
@@ -143,7 +158,8 @@ USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports);
 void usb_uhci_init(PCIBus *bus, USBPort **usb_ports);
 
 /* usb-linux.c */
-USBDevice *usb_host_hub_init(void);
+USBDevice *usb_host_device_open(const char *devname);
+void usb_host_info(void);
 
 /* usb-hid.c */
 USBDevice *usb_mouse_init(void);
index 0153e8d..cf70b57 100644 (file)
--- a/monitor.c
+++ b/monitor.c
@@ -901,7 +901,6 @@ static void do_info_kqemu(void)
 #endif
 } 
 
-
 static term_cmd_t term_cmds[] = {
     { "help|?", "s?", do_help, 
       "[cmd]", "show the help" },
@@ -948,6 +947,10 @@ static term_cmd_t term_cmds[] = {
       "", "send system power down event" },
     { "sum", "ii", do_sum, 
       "addr size", "compute the checksum of a memory region" },
+    { "usb_add", "s", do_usb_add,
+      "device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" },
+    { "usb_del", "s", do_usb_del,
+      "device", "remove USB device 'bus.addr'" },
     { NULL, NULL, }, 
 };
 
@@ -978,6 +981,10 @@ static term_cmd_t info_cmds[] = {
       "", "show dynamic compiler info", },
     { "kqemu", "", do_info_kqemu,
       "", "show kqemu information", },
+    { "usb", "", usb_info,
+      "", "show guest USB devices", },
+    { "usbhost", "", usb_host_info,
+      "", "show host USB devices", },
     { NULL, NULL, },
 };
 
index 87f3d12..cde94cd 100644 (file)
@@ -40,9 +40,13 @@ struct usb_ctrltransfer {
     void *data;
 };
 
-//#define DEBUG
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
+                        int vendor_id, int product_id, 
+                        const char *product_name, int speed);
+static int usb_host_find_device(int *pbus_num, int *paddr, 
+                                const char *devname);
 
-#define MAX_DEVICES 8
+//#define DEBUG
 
 #define USBDEVFS_PATH "/proc/bus/usb"
 
@@ -51,12 +55,6 @@ typedef struct USBHostDevice {
     int fd;
 } USBHostDevice;
 
-typedef struct USBHostHubState {
-    USBDevice *hub_dev;
-    USBPort *hub_ports[MAX_DEVICES];
-    USBDevice *hub_devices[MAX_DEVICES];
-} USBHostHubState;
-
 static void usb_host_handle_reset(USBDevice *dev)
 {
 #if 0
@@ -137,29 +135,26 @@ static int usb_host_handle_data(USBDevice *dev, int pid,
     }
 }
 
-static int usb_host_handle_packet(USBDevice *dev, int pid, 
-                                  uint8_t devaddr, uint8_t devep,
-                                  uint8_t *data, int len)
-{
-    return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len);
-}
-
 /* XXX: exclude high speed devices or implement EHCI */
-static void scan_host_device(USBHostHubState *s, const char *filename)
+USBDevice *usb_host_device_open(const char *devname)
 {
     int fd, interface, ret, i;
     USBHostDevice *dev;
     struct usbdevfs_connectinfo ci;
     uint8_t descr[1024];
+    char buf[1024];
     int descr_len, dev_descr_len, config_descr_len, nb_interfaces;
+    int bus_num, addr;
 
-#ifdef DEBUG
-    printf("scanning %s\n", filename);
-#endif
-    fd = open(filename, O_RDWR);
+    if (usb_host_find_device(&bus_num, &addr, devname) < 0) 
+        return NULL;
+    
+    snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d", 
+             bus_num, addr);
+    fd = open(buf, O_RDWR);
     if (fd < 0) {
-        perror(filename);
-        return;
+        perror(buf);
+        return NULL;
     }
 
     /* read the config description */
@@ -180,22 +175,31 @@ static void scan_host_device(USBHostHubState *s, const char *filename)
     nb_interfaces = descr[i + 4];
     if (nb_interfaces != 1) {
         /* NOTE: currently we grab only one interface */
+        fprintf(stderr, "usb_host: only one interface supported\n");
+        goto fail;
+    }
+
+#ifdef USBDEVFS_DISCONNECT
+    /* earlier Linux 2.4 do not support that */
+    ret = ioctl(fd, USBDEVFS_DISCONNECT);
+    if (ret < 0 && errno != ENODATA) {
+        perror("USBDEVFS_DISCONNECT");
         goto fail;
     }
+#endif
+
     /* XXX: only grab if all interfaces are free */
     interface = 0;
     ret = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &interface);
     if (ret < 0) {
         if (errno == EBUSY) {
-#ifdef DEBUG
-            printf("%s already grabbed\n", filename);
-#endif            
+            fprintf(stderr, "usb_host: device already grabbed\n");
         } else {
             perror("USBDEVFS_CLAIMINTERFACE");
         }
     fail:
         close(fd);
-        return;
+        return NULL;
     }
 
     ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
@@ -205,21 +209,9 @@ static void scan_host_device(USBHostHubState *s, const char *filename)
     }
 
 #ifdef DEBUG
-    printf("%s grabbed\n", filename);
+    printf("host USB device %d.%d grabbed\n", bus_num, addr);
 #endif    
 
-    /* find a free slot */
-    for(i = 0; i < MAX_DEVICES; i++) {
-        if (!s->hub_devices[i])
-            break;
-    }
-    if (i == MAX_DEVICES) {
-#ifdef DEBUG
-        printf("too many host devices\n");
-        goto fail;
-#endif
-    }
-
     dev = qemu_mallocz(sizeof(USBHostDevice));
     if (!dev)
         goto fail;
@@ -228,80 +220,259 @@ static void scan_host_device(USBHostHubState *s, const char *filename)
         dev->dev.speed = USB_SPEED_LOW;
     else
         dev->dev.speed = USB_SPEED_HIGH;
-    dev->dev.handle_packet = usb_host_handle_packet;
+    dev->dev.handle_packet = usb_generic_handle_packet;
 
     dev->dev.handle_reset = usb_host_handle_reset;
     dev->dev.handle_control = usb_host_handle_control;
     dev->dev.handle_data = usb_host_handle_data;
+    return (USBDevice *)dev;
+}
 
-    s->hub_devices[i] = (USBDevice *)dev;
-
-    /* activate device on hub */
-    usb_attach(s->hub_ports[i], s->hub_devices[i]);
+static int get_tag_value(char *buf, int buf_size,
+                         const char *str, const char *tag, 
+                         const char *stopchars)
+{
+    const char *p;
+    char *q;
+    p = strstr(str, tag);
+    if (!p)
+        return -1;
+    p += strlen(tag);
+    while (isspace(*p))
+        p++;
+    q = buf;
+    while (*p != '\0' && !strchr(stopchars, *p)) {
+        if ((q - buf) < (buf_size - 1))
+            *q++ = *p;
+        p++;
+    }
+    *q = '\0';
+    return q - buf;
 }
 
-static void scan_host_devices(USBHostHubState *s, const char *bus_path)
+static int usb_host_scan(void *opaque, USBScanFunc *func)
 {
-    DIR *d;
-    struct dirent *de;
+    FILE *f;
+    char line[1024];
     char buf[1024];
-
-    d = opendir(bus_path);
-    if (!d)
-        return;
+    int bus_num, addr, speed, device_count, class_id, product_id, vendor_id;
+    int ret;
+    char product_name[512];
+    
+    f = fopen(USBDEVFS_PATH "/devices", "r");
+    if (!f) {
+        term_printf("Could not open %s\n", USBDEVFS_PATH "/devices");
+        return 0;
+    }
+    device_count = 0;
+    bus_num = addr = speed = class_id = product_id = vendor_id = 0;
+    ret = 0;
     for(;;) {
-        de = readdir(d);
-        if (!de)
+        if (fgets(line, sizeof(line), f) == NULL)
             break;
-        if (de->d_name[0] != '.') {
-            snprintf(buf, sizeof(buf), "%s/%s", bus_path, de->d_name);
-            scan_host_device(s, buf);
+        if (strlen(line) > 0)
+            line[strlen(line) - 1] = '\0';
+        if (line[0] == 'T' && line[1] == ':') {
+            if (device_count) {
+                ret = func(opaque, bus_num, addr, class_id, vendor_id, 
+                           product_id, product_name, speed);
+                if (ret)
+                    goto the_end;
+            }
+            if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0)
+                goto fail;
+            bus_num = atoi(buf);
+            if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0)
+                goto fail;
+            addr = atoi(buf);
+            if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0)
+                goto fail;
+            if (!strcmp(buf, "480"))
+                speed = USB_SPEED_HIGH;
+            else if (!strcmp(buf, "1.5"))
+                speed = USB_SPEED_LOW;
+            else
+                speed = USB_SPEED_FULL;
+            product_name[0] = '\0';
+            class_id = 0xff;
+            device_count++;
+            product_id = 0;
+            vendor_id = 0;
+        } else if (line[0] == 'P' && line[1] == ':') {
+            if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0)
+                goto fail;
+            vendor_id = strtoul(buf, NULL, 16);
+            if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0)
+                goto fail;
+            product_id = strtoul(buf, NULL, 16);
+        } else if (line[0] == 'S' && line[1] == ':') {
+            if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0)
+                goto fail;
+            pstrcpy(product_name, sizeof(product_name), buf);
+        } else if (line[0] == 'D' && line[1] == ':') {
+            if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0)
+                goto fail;
+            class_id = strtoul(buf, NULL, 16);
         }
+    fail: ;
+    }
+    if (device_count) {
+        ret = func(opaque, bus_num, addr, class_id, vendor_id, 
+                   product_id, product_name, speed);
     }
-    closedir(d);
+ the_end:
+    fclose(f);
+    return ret;
 }
 
-static void scan_host_buses(USBHostHubState *s)
+typedef struct FindDeviceState {
+    int vendor_id;
+    int product_id;
+    int bus_num;
+    int addr;
+} FindDeviceState;
+
+static int usb_host_find_device_scan(void *opaque, int bus_num, int addr, 
+                                     int class_id,
+                                     int vendor_id, int product_id, 
+                                     const char *product_name, int speed)
 {
-    DIR *d;
-    struct dirent *de;
-    char buf[1024];
+    FindDeviceState *s = opaque;
+    if (vendor_id == s->vendor_id &&
+        product_id == s->product_id) {
+        s->bus_num = bus_num;
+        s->addr = addr;
+        return 1;
+    } else {
+        return 0;
+    }
+}
 
-    d = opendir(USBDEVFS_PATH);
-    if (!d)
-        return;
-    for(;;) {
-        de = readdir(d);
-        if (!de)
-            break;
-        if (isdigit(de->d_name[0])) {
-            snprintf(buf, sizeof(buf), "%s/%s", USBDEVFS_PATH, de->d_name);
-            scan_host_devices(s, buf);
+/* the syntax is : 
+   'bus.addr' (decimal numbers) or 
+   'vendor_id:product_id' (hexa numbers) */
+static int usb_host_find_device(int *pbus_num, int *paddr, 
+                                const char *devname)
+{
+    const char *p;
+    int ret;
+    FindDeviceState fs;
+
+    p = strchr(devname, '.');
+    if (p) {
+        *pbus_num = strtoul(devname, NULL, 0);
+        *paddr = strtoul(p + 1, NULL, 0);
+        return 0;
+    }
+    p = strchr(devname, ':');
+    if (p) {
+        fs.vendor_id = strtoul(devname, NULL, 16);
+        fs.product_id = strtoul(p + 1, NULL, 16);
+        ret = usb_host_scan(&fs, usb_host_find_device_scan);
+        if (ret) {
+            *pbus_num = fs.bus_num;
+            *paddr = fs.addr;
+            return 0;
         }
     }
-    closedir(d);
+    return -1;
 }
 
-/* virtual hub containing the USB devices of the host */
-USBDevice *usb_host_hub_init(void)
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+    int class;
+    const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+    { USB_CLASS_AUDIO, "Audio"},
+    { USB_CLASS_COMM, "Communication"},
+    { USB_CLASS_HID, "HID"},
+    { USB_CLASS_HUB, "Hub" },
+    { USB_CLASS_PHYSICAL, "Physical" },
+    { USB_CLASS_PRINTER, "Printer" },
+    { USB_CLASS_MASS_STORAGE, "Storage" },
+    { USB_CLASS_CDC_DATA, "Data" },
+    { USB_CLASS_APP_SPEC, "Application Specific" },
+    { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+    { USB_CLASS_STILL_IMAGE, "Still Image" },
+    { USB_CLASS_CSCID,         "Smart Card" },
+    { USB_CLASS_CONTENT_SEC, "Content Security" },
+    { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
 {
-    USBHostHubState *s;
-    s = qemu_mallocz(sizeof(USBHostHubState));
-    if (!s)
-        return NULL;
-    s->hub_dev = usb_hub_init(s->hub_ports, MAX_DEVICES);
-    if (!s->hub_dev) {
-        free(s);
-        return NULL;
+    const struct usb_class_info *p;
+    for(p = usb_class_info; p->class != -1; p++) {
+        if (p->class == class)
+            break;
     }
-    scan_host_buses(s);
-    return s->hub_dev;
+    return p->class_name;
+}
+
+void usb_info_device(int bus_num, int addr, int class_id,
+                     int vendor_id, int product_id, 
+                     const char *product_name,
+                     int speed)
+{
+    const char *class_str, *speed_str;
+
+    switch(speed) {
+    case USB_SPEED_LOW: 
+        speed_str = "1.5"; 
+        break;
+    case USB_SPEED_FULL: 
+        speed_str = "12"; 
+        break;
+    case USB_SPEED_HIGH: 
+        speed_str = "480"; 
+        break;
+    default:
+        speed_str = "?"; 
+        break;
+    }
+
+    term_printf("  Device %d.%d, speed %s Mb/s\n", 
+                bus_num, addr, speed_str);
+    class_str = usb_class_str(class_id);
+    if (class_str) 
+        term_printf("    %s:", class_str);
+    else
+        term_printf("    Class %02x:", class_id);
+    term_printf(" USB device %04x:%04x", vendor_id, product_id);
+    if (product_name[0] != '\0')
+        term_printf(", %s", product_name);
+    term_printf("\n");
+}
+
+static int usb_host_info_device(void *opaque, int bus_num, int addr, 
+                                int class_id,
+                                int vendor_id, int product_id, 
+                                const char *product_name,
+                                int speed)
+{
+    usb_info_device(bus_num, addr, class_id, vendor_id, product_id,
+                    product_name, speed);
+    return 0;
+}
+
+void usb_host_info(void)
+{
+    usb_host_scan(NULL, usb_host_info_device);
 }
 
 #else
 
+void usb_host_info(void)
+{
+    term_printf("USB host devices not supported\n");
+}
+
 /* XXX: modify configure to compile the right host driver */
-USBDevice *usb_host_hub_init(void)
+USBDevice *usb_host_device_open(const char *devname)
 {
     return NULL;
 }
diff --git a/vl.c b/vl.c
index 810db81..cab2eb0 100644 (file)
--- a/vl.c
+++ b/vl.c
@@ -154,6 +154,8 @@ CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 int win2k_install_hack = 0;
 #endif
 int usb_enabled = 0;
+USBPort *vm_usb_ports[MAX_VM_USB_PORTS];
+USBDevice *vm_usb_hub;
 
 /***********************************************************/
 /* x86 ISA bus support */
@@ -1738,6 +1740,117 @@ static int net_fd_init(NetDriverState *nd, int fd)
 #endif /* !_WIN32 */
 
 /***********************************************************/
+/* USB devices */
+
+static int usb_device_add(const char *devname)
+{
+    const char *p;
+    USBDevice *dev;
+    int i;
+
+    if (!vm_usb_hub)
+        return -1;
+    for(i = 0;i < MAX_VM_USB_PORTS; i++) {
+        if (!vm_usb_ports[i]->dev)
+            break;
+    }
+    if (i == MAX_VM_USB_PORTS)
+        return -1;
+
+    if (strstart(devname, "host:", &p)) {
+        dev = usb_host_device_open(p);
+        if (!dev)
+            return -1;
+    } else if (!strcmp(devname, "mouse")) {
+        dev = usb_mouse_init();
+        if (!dev)
+            return -1;
+    } else {
+        return -1;
+    }
+    usb_attach(vm_usb_ports[i], dev);
+    return 0;
+}
+
+static int usb_device_del(const char *devname)
+{
+    USBDevice *dev;
+    int bus_num, addr, i;
+    const char *p;
+
+    if (!vm_usb_hub)
+        return -1;
+
+    p = strchr(devname, '.');
+    if (!p) 
+        return -1;
+    bus_num = strtoul(devname, NULL, 0);
+    addr = strtoul(p + 1, NULL, 0);
+    if (bus_num != 0)
+        return -1;
+    for(i = 0;i < MAX_VM_USB_PORTS; i++) {
+        dev = vm_usb_ports[i]->dev;
+        if (dev && dev->addr == addr)
+            break;
+    }
+    if (i == MAX_VM_USB_PORTS)
+        return -1;
+    usb_attach(vm_usb_ports[i], NULL);
+    return 0;
+}
+
+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);
+}
+
+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);
+}
+
+void usb_info(void)
+{
+    USBDevice *dev;
+    int i;
+    const char *speed_str;
+
+    if (!vm_usb_hub) {
+        term_printf("USB support not enabled\n");
+        return;
+    }
+
+    for(i = 0; i < MAX_VM_USB_PORTS; i++) {
+        dev = vm_usb_ports[i]->dev;
+        if (dev) {
+            term_printf("Hub port %d:\n", i);
+            switch(dev->speed) {
+            case USB_SPEED_LOW: 
+                speed_str = "1.5"; 
+                break;
+            case USB_SPEED_FULL: 
+                speed_str = "12"; 
+                break;
+            case USB_SPEED_HIGH: 
+                speed_str = "480"; 
+                break;
+            default:
+                speed_str = "?"; 
+                break;
+            }
+            term_printf("  Device %d.%d, speed %s Mb/s\n", 
+                        0, dev->addr, speed_str);
+        }
+    }
+}
+
+/***********************************************************/
 /* pid file */
 
 static char *pid_filename;
@@ -2989,6 +3102,7 @@ enum {
     QEMU_OPTION_no_kqemu,
     QEMU_OPTION_win2k_hack,
     QEMU_OPTION_usb,
+    QEMU_OPTION_usbdevice,
 };
 
 typedef struct QEMUOption {
@@ -3063,9 +3177,10 @@ const QEMUOption qemu_options[] = {
     { "full-screen", 0, QEMU_OPTION_full_screen },
     { "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
     { "win2k-hack", 0, QEMU_OPTION_win2k_hack },
-    { "usb", 0, QEMU_OPTION_usb },
+    { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
     
     /* temporary options */
+    { "usb", 0, QEMU_OPTION_usb },
     { "pci", 0, QEMU_OPTION_pci },
     { "cirrusvga", 0, QEMU_OPTION_cirrusvga },
     { NULL },
@@ -3239,7 +3354,9 @@ int main(int argc, char **argv)
     int parallel_device_index;
     const char *loadvm = NULL;
     QEMUMachine *machine;
-
+    char usb_devices[MAX_VM_USB_PORTS][128];
+    int usb_devices_index;
+    
 #if !defined(CONFIG_SOFTMMU)
     /* we never want that malloc() uses mmap() */
     mallopt(M_MMAP_THRESHOLD, 4096 * 1024);
@@ -3282,6 +3399,8 @@ int main(int argc, char **argv)
         parallel_devices[i][0] = '\0';
     parallel_device_index = 0;
     
+    usb_devices_index = 0;
+    
     nb_tun_fds = 0;
     net_if_type = -1;
     nb_nics = 1;
@@ -3653,6 +3772,17 @@ int main(int argc, char **argv)
             case QEMU_OPTION_usb:
                 usb_enabled = 1;
                 break;
+            case QEMU_OPTION_usbdevice:
+                usb_enabled = 1;
+                if (usb_devices_index >= MAX_VM_USB_PORTS) {
+                    fprintf(stderr, "Too many USB devices\n");
+                    exit(1);
+                }
+                pstrcpy(usb_devices[usb_devices_index],
+                        sizeof(usb_devices[usb_devices_index]),
+                        optarg);
+                usb_devices_index++;
+                break;
             }
         }
     }
@@ -3818,6 +3948,17 @@ int main(int argc, char **argv)
         }
     }
 
+    /* init USB devices */
+    if (usb_enabled) {
+        vm_usb_hub = usb_hub_init(vm_usb_ports, MAX_VM_USB_PORTS);
+        for(i = 0; i < usb_devices_index; i++) {
+            if (usb_device_add(usb_devices[i]) < 0) {
+                fprintf(stderr, "Warning: could not add USB device %s\n",
+                        usb_devices[i]);
+            }
+        }
+    }
+
     /* init CPU state */
     env = cpu_init();
     global_env = env;
diff --git a/vl.h b/vl.h
index 72d4f87..4f181c5 100644 (file)
--- a/vl.h
+++ b/vl.h
@@ -862,6 +862,17 @@ int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq);
 
 #include "hw/usb.h"
 
+/* usb ports of the VM */
+
+#define MAX_VM_USB_PORTS 8
+
+extern USBPort *vm_usb_ports[MAX_VM_USB_PORTS];
+extern USBDevice *vm_usb_hub;
+
+void do_usb_add(const char *devname);
+void do_usb_del(const char *devname);
+void usb_info(void);
+
 #endif /* defined(QEMU_TOOL) */
 
 /* monitor.c */