diff --git a/Makefile.target b/Makefile.target index a22b2e0807..30a21c5ee1 100644 --- a/Makefile.target +++ b/Makefile.target @@ -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 324f5367d1..918615c925 100644 --- 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 */ diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 4dd0c63815..0f0c185aae 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -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, diff --git a/hw/usb.c b/hw/usb.c index 0728db29fa..b2830c5900 100644 --- 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, diff --git a/hw/usb.h b/hw/usb.h index 7a5c8ad26e..b4dee2340e 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -47,6 +47,20 @@ //#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); diff --git a/monitor.c b/monitor.c index 0153e8d8b7..cf70b575ad 100644 --- 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, }, }; diff --git a/usb-linux.c b/usb-linux.c index 87f3d120b9..cde94cd0a0 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -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; - - s->hub_devices[i] = (USBDevice *)dev; - - /* activate device on hub */ - usb_attach(s->hub_ports[i], s->hub_devices[i]); + return (USBDevice *)dev; } -static void scan_host_devices(USBHostHubState *s, const char *bus_path) +static int get_tag_value(char *buf, int buf_size, + const char *str, const char *tag, + const char *stopchars) { - DIR *d; - struct dirent *de; - char buf[1024]; + 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; +} - d = opendir(bus_path); - if (!d) - return; +static int usb_host_scan(void *opaque, USBScanFunc *func) +{ + FILE *f; + char line[1024]; + char buf[1024]; + 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); + } + the_end: + fclose(f); + return ret; +} + +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) +{ + 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; + } +} + +/* 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; } -static void scan_host_buses(USBHostHubState *s) -{ - DIR *d; - struct dirent *de; - char buf[1024]; +/**********************/ +/* USB host device info */ - d = opendir(USBDEVFS_PATH); - if (!d) - return; - for(;;) { - de = readdir(d); - if (!de) +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) +{ + const struct usb_class_info *p; + for(p = usb_class_info; p->class != -1; p++) { + if (p->class == class) break; - if (isdigit(de->d_name[0])) { - snprintf(buf, sizeof(buf), "%s/%s", USBDEVFS_PATH, de->d_name); - scan_host_devices(s, buf); - } } - closedir(d); + return p->class_name; } -/* virtual hub containing the USB devices of the host */ -USBDevice *usb_host_hub_init(void) +void usb_info_device(int bus_num, int addr, int class_id, + int vendor_id, int product_id, + const char *product_name, + int speed) { - 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 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; } - scan_host_buses(s); - return s->hub_dev; + + 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 810db8152b..cab2eb05f3 100644 --- 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 */ @@ -1737,6 +1739,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 */ @@ -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 72d4f87906..4f181c50ee 100644 --- 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 */