Merge remote-tracking branch 'kraxel/usb.16' into staging
This commit is contained in:
commit
f897235e0a
@ -31,6 +31,91 @@ a complete example:
|
||||
This attaches a usb tablet to the UHCI adapter and a usb mass storage
|
||||
device to the EHCI adapter.
|
||||
|
||||
|
||||
More USB tips & tricks
|
||||
======================
|
||||
|
||||
Recently the usb pass through driver (also known as usb-host) and the
|
||||
qemu usb subsystem gained a few capabilities which are available only
|
||||
via qdev properties, i,e. when using '-device'.
|
||||
|
||||
|
||||
physical port addressing
|
||||
------------------------
|
||||
|
||||
First you can (for all usb devices) specify the physical port where
|
||||
the device will show up in the guest. This can be done using the
|
||||
"port" property. UHCI has two root ports (1,2). EHCI has four root
|
||||
ports (1-4), the emulated (1.1) USB hub has eight ports.
|
||||
|
||||
Plugging a tablet into UHCI port 1 works like this:
|
||||
|
||||
-device usb-tablet,bus=usb.0,port=1
|
||||
|
||||
Plugging a hub into UHCI port 2 works like this:
|
||||
|
||||
-device usb-hub,bus=usb.0,port=2
|
||||
|
||||
Plugging a virtual usb stick into port 4 of the hub just plugged works
|
||||
this way:
|
||||
|
||||
-device usb-storage,bus=usb.0,port=2.4,drive=...
|
||||
|
||||
You can do basically the same in the monitor using the device_add
|
||||
command. If you want to unplug devices too you should specify some
|
||||
unique id which you can use to refer to the device ...
|
||||
|
||||
(qemu) device_add usb-tablet,bus=usb.0,port=1,id=my-tablet
|
||||
(qemu) device_del my-tablet
|
||||
|
||||
... when unplugging it with device_del.
|
||||
|
||||
|
||||
USB pass through hints
|
||||
----------------------
|
||||
|
||||
The usb-host driver has a bunch of properties to specify the device
|
||||
which should be passed to the guest:
|
||||
|
||||
hostbus=<nr> -- Specifies the bus number the device must be attached
|
||||
to.
|
||||
|
||||
hostaddr=<nr> -- Specifies the device address the device got
|
||||
assigned by the guest os.
|
||||
|
||||
hostport=<str> -- Specifies the physical port the device is attached
|
||||
to.
|
||||
|
||||
vendorid=<hexnr> -- Specifies the vendor ID of the device.
|
||||
productid=<hexnr> -- Specifies the product ID of the device.
|
||||
|
||||
In theory you can combine all these properties as you like. In
|
||||
practice only a few combinations are useful:
|
||||
|
||||
(1) vendorid+productid -- match for a specific device, pass it to
|
||||
the guest when it shows up somewhere in the host.
|
||||
|
||||
(2) hostbus+hostport -- match for a specific physical port in the
|
||||
host, any device which is plugged in there gets passed to the
|
||||
guest.
|
||||
|
||||
(3) hostbus+hostaddr -- most useful for ad-hoc pass through as the
|
||||
hostaddr isn't stable, the next time you plug in the device it
|
||||
gets a new one ...
|
||||
|
||||
Note that USB 1.1 devices are handled by UHCI/OHCI and USB 2.0 by
|
||||
EHCI. That means a device plugged into the very same physical port
|
||||
may show up on different busses depending on the speed. The port I'm
|
||||
using for testing is bus 1 + port 1 for 2.0 devices and bus 3 + port 1
|
||||
for 1.1 devices. Passing through any device plugged into that port
|
||||
and also assign them to the correct bus can be done this way:
|
||||
|
||||
qemu -M pc ${otheroptions} \
|
||||
-usb \
|
||||
-device usb-ehci,id=ehci \
|
||||
-device usb-host,bus=usb.0,hostbus=3,hostport=1 \
|
||||
-device usb-host,bus=ehci.0,hostbus=1,hostport=1
|
||||
|
||||
enjoy,
|
||||
Gerd
|
||||
|
||||
|
@ -247,10 +247,18 @@ static void softusb_attach(USBPort *port)
|
||||
{
|
||||
}
|
||||
|
||||
static void softusb_device_destroy(USBBus *bus, USBDevice *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static USBPortOps softusb_ops = {
|
||||
.attach = softusb_attach,
|
||||
};
|
||||
|
||||
static USBBusOps softusb_bus_ops = {
|
||||
.device_destroy = softusb_device_destroy,
|
||||
};
|
||||
|
||||
static void milkymist_softusb_reset(DeviceState *d)
|
||||
{
|
||||
MilkymistSoftUsbState *s =
|
||||
@ -294,7 +302,7 @@ static int milkymist_softusb_init(SysBusDevice *dev)
|
||||
qemu_add_mouse_event_handler(softusb_mouse_event, s, 0, "Milkymist Mouse");
|
||||
|
||||
/* create our usb bus */
|
||||
usb_bus_new(&s->usbbus, NULL);
|
||||
usb_bus_new(&s->usbbus, &softusb_bus_ops, NULL);
|
||||
|
||||
/* our two ports */
|
||||
usb_register_port(&s->usbbus, &s->usbport[0], NULL, 0, &softusb_ops,
|
||||
|
10
hw/usb-bus.c
10
hw/usb-bus.c
@ -39,9 +39,10 @@ const VMStateDescription vmstate_usb_device = {
|
||||
}
|
||||
};
|
||||
|
||||
void usb_bus_new(USBBus *bus, DeviceState *host)
|
||||
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host)
|
||||
{
|
||||
qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
|
||||
bus->ops = ops;
|
||||
bus->busnr = next_usb_bus++;
|
||||
bus->qbus.allow_hotplug = 1; /* Yes, we can */
|
||||
QTAILQ_INIT(&bus->free);
|
||||
@ -81,8 +82,12 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
|
||||
static int usb_qdev_exit(DeviceState *qdev)
|
||||
{
|
||||
USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
|
||||
USBBus *bus = usb_bus_from_device(dev);
|
||||
|
||||
usb_device_detach(dev);
|
||||
if (dev->attached) {
|
||||
usb_device_detach(dev);
|
||||
}
|
||||
bus->ops->device_destroy(bus, dev);
|
||||
if (dev->info->handle_destroy) {
|
||||
dev->info->handle_destroy(dev);
|
||||
}
|
||||
@ -270,6 +275,7 @@ static const char *usb_speed(unsigned int speed)
|
||||
[ USB_SPEED_LOW ] = "1.5",
|
||||
[ USB_SPEED_FULL ] = "12",
|
||||
[ USB_SPEED_HIGH ] = "480",
|
||||
[ USB_SPEED_SUPER ] = "5000",
|
||||
};
|
||||
if (speed >= ARRAY_SIZE(txt))
|
||||
return "?";
|
||||
|
1264
hw/usb-ehci.c
1264
hw/usb-ehci.c
File diff suppressed because it is too large
Load Diff
@ -142,7 +142,6 @@ static const USBDescIface desc_iface_tablet = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceSubClass = 0x01, /* boot */
|
||||
.bInterfaceProtocol = 0x02,
|
||||
.ndesc = 1,
|
||||
.descs = (USBDescOther[]) {
|
||||
@ -782,13 +781,13 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
|
||||
goto fail;
|
||||
break;
|
||||
case GET_PROTOCOL:
|
||||
if (s->kind != USB_KEYBOARD)
|
||||
if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE)
|
||||
goto fail;
|
||||
ret = 1;
|
||||
data[0] = s->protocol;
|
||||
break;
|
||||
case SET_PROTOCOL:
|
||||
if (s->kind != USB_KEYBOARD)
|
||||
if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE)
|
||||
goto fail;
|
||||
ret = 0;
|
||||
s->protocol = value;
|
||||
|
@ -262,6 +262,7 @@
|
||||
static void musb_attach(USBPort *port);
|
||||
static void musb_detach(USBPort *port);
|
||||
static void musb_schedule_cb(USBDevice *dev, USBPacket *p);
|
||||
static void musb_device_destroy(USBBus *bus, USBDevice *dev);
|
||||
|
||||
static USBPortOps musb_port_ops = {
|
||||
.attach = musb_attach,
|
||||
@ -269,6 +270,10 @@ static USBPortOps musb_port_ops = {
|
||||
.complete = musb_schedule_cb,
|
||||
};
|
||||
|
||||
static USBBusOps musb_bus_ops = {
|
||||
.device_destroy = musb_device_destroy,
|
||||
};
|
||||
|
||||
typedef struct MUSBPacket MUSBPacket;
|
||||
typedef struct MUSBEndPoint MUSBEndPoint;
|
||||
|
||||
@ -361,7 +366,7 @@ struct MUSBState *musb_init(qemu_irq *irqs)
|
||||
s->ep[i].epnum = i;
|
||||
}
|
||||
|
||||
usb_bus_new(&s->bus, NULL /* FIXME */);
|
||||
usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */);
|
||||
usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||
usb_port_location(&s->port, NULL, 1);
|
||||
@ -778,6 +783,22 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
|
||||
musb_rx_intr_set(s, epnum, 1);
|
||||
}
|
||||
|
||||
static void musb_device_destroy(USBBus *bus, USBDevice *dev)
|
||||
{
|
||||
MUSBState *s = container_of(bus, MUSBState, bus);
|
||||
int ep, dir;
|
||||
|
||||
for (ep = 0; ep < 16; ep++) {
|
||||
for (dir = 0; dir < 2; dir++) {
|
||||
if (s->ep[ep].packey[dir].p.owner != dev) {
|
||||
continue;
|
||||
}
|
||||
usb_cancel_packet(&s->ep[ep].packey[dir].p);
|
||||
/* status updates needed here? */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void musb_tx_rdy(MUSBState *s, int epnum)
|
||||
{
|
||||
MUSBEndPoint *ep = s->ep + epnum;
|
||||
|
@ -367,6 +367,22 @@ static void ohci_detach(USBPort *port1)
|
||||
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
||||
}
|
||||
|
||||
static void ohci_wakeup(USBDevice *dev)
|
||||
{
|
||||
USBBus *bus = usb_bus_from_device(dev);
|
||||
OHCIState *s = container_of(bus, OHCIState, bus);
|
||||
int portnum = dev->port->index;
|
||||
OHCIPort *port = &s->rhport[portnum];
|
||||
if (port->ctrl & OHCI_PORT_PSS) {
|
||||
DPRINTF("usb-ohci: port %d: wakeup\n", portnum);
|
||||
port->ctrl |= OHCI_PORT_PSSC;
|
||||
port->ctrl &= ~OHCI_PORT_PSS;
|
||||
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
|
||||
ohci_set_interrupt(s, OHCI_INTR_RD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the controller */
|
||||
static void ohci_reset(void *opaque)
|
||||
{
|
||||
@ -1575,6 +1591,10 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
|
||||
ohci->hcca = val & OHCI_HCCA_MASK;
|
||||
break;
|
||||
|
||||
case 7: /* HcPeriodCurrentED */
|
||||
/* Ignore writes to this read-only register, Linux does them */
|
||||
break;
|
||||
|
||||
case 8: /* HcControlHeadED */
|
||||
ohci->ctrl_head = val & OHCI_EDPTR_MASK;
|
||||
break;
|
||||
@ -1644,6 +1664,16 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
|
||||
}
|
||||
}
|
||||
|
||||
static void ohci_device_destroy(USBBus *bus, USBDevice *dev)
|
||||
{
|
||||
OHCIState *ohci = container_of(bus, OHCIState, bus);
|
||||
|
||||
if (ohci->async_td && ohci->usb_packet.owner == dev) {
|
||||
usb_cancel_packet(&ohci->usb_packet);
|
||||
ohci->async_td = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only dword reads are defined on OHCI register space */
|
||||
static CPUReadMemoryFunc * const ohci_readfn[3]={
|
||||
ohci_mem_read,
|
||||
@ -1661,9 +1691,14 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={
|
||||
static USBPortOps ohci_port_ops = {
|
||||
.attach = ohci_attach,
|
||||
.detach = ohci_detach,
|
||||
.wakeup = ohci_wakeup,
|
||||
.complete = ohci_async_complete_packet,
|
||||
};
|
||||
|
||||
static USBBusOps ohci_bus_ops = {
|
||||
.device_destroy = ohci_device_destroy,
|
||||
};
|
||||
|
||||
static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||
int num_ports, uint32_t localmem_base)
|
||||
{
|
||||
@ -1691,7 +1726,7 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||
|
||||
ohci->name = dev->info->name;
|
||||
|
||||
usb_bus_new(&ohci->bus, dev);
|
||||
usb_bus_new(&ohci->bus, &ohci_bus_ops, dev);
|
||||
ohci->num_ports = num_ports;
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops,
|
||||
|
@ -234,6 +234,19 @@ static void uhci_async_validate_end(UHCIState *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
|
||||
{
|
||||
UHCIAsync *curr, *n;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
|
||||
if (curr->packet.owner != dev) {
|
||||
continue;
|
||||
}
|
||||
uhci_async_unlink(s, curr);
|
||||
uhci_async_cancel(s, curr);
|
||||
}
|
||||
}
|
||||
|
||||
static void uhci_async_cancel_all(UHCIState *s)
|
||||
{
|
||||
UHCIAsync *curr, *n;
|
||||
@ -411,6 +424,8 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
|
||||
case 0x00:
|
||||
if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
|
||||
/* start frame processing */
|
||||
s->expire_time = qemu_get_clock_ns(vm_clock) +
|
||||
(get_ticks_per_sec() / FRAME_TIMER_FREQ);
|
||||
qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
|
||||
s->status &= ~UHCI_STS_HCHALTED;
|
||||
} else if (!(val & UHCI_CMD_RS)) {
|
||||
@ -1081,6 +1096,13 @@ static void uhci_map(PCIDevice *pci_dev, int region_num,
|
||||
register_ioport_read(addr, 32, 1, uhci_ioport_readb, s);
|
||||
}
|
||||
|
||||
static void uhci_device_destroy(USBBus *bus, USBDevice *dev)
|
||||
{
|
||||
UHCIState *s = container_of(bus, UHCIState, bus);
|
||||
|
||||
uhci_async_cancel_device(s, dev);
|
||||
}
|
||||
|
||||
static USBPortOps uhci_port_ops = {
|
||||
.attach = uhci_attach,
|
||||
.detach = uhci_detach,
|
||||
@ -1088,6 +1110,10 @@ static USBPortOps uhci_port_ops = {
|
||||
.complete = uhci_async_complete,
|
||||
};
|
||||
|
||||
static USBBusOps uhci_bus_ops = {
|
||||
.device_destroy = uhci_device_destroy,
|
||||
};
|
||||
|
||||
static int usb_uhci_common_initfn(UHCIState *s)
|
||||
{
|
||||
uint8_t *pci_conf = s->dev.config;
|
||||
@ -1098,17 +1124,15 @@ static int usb_uhci_common_initfn(UHCIState *s)
|
||||
pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB);
|
||||
/* TODO: reset value should be 0. */
|
||||
pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3
|
||||
pci_conf[0x60] = 0x10; // release number
|
||||
pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
|
||||
|
||||
usb_bus_new(&s->bus, &s->dev.qdev);
|
||||
usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev);
|
||||
for(i = 0; i < NB_PORTS; i++) {
|
||||
usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||
usb_port_location(&s->ports[i].port, NULL, i+1);
|
||||
}
|
||||
s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
|
||||
s->expire_time = qemu_get_clock_ns(vm_clock) +
|
||||
(get_ticks_per_sec() / FRAME_TIMER_FREQ);
|
||||
s->num_ports_vmstate = NB_PORTS;
|
||||
QTAILQ_INIT(&s->async_pending);
|
||||
|
||||
|
14
hw/usb.h
14
hw/usb.h
@ -26,6 +26,12 @@
|
||||
#include "qdev.h"
|
||||
#include "qemu-queue.h"
|
||||
|
||||
/* Constants related to the USB / PCI interaction */
|
||||
#define USB_SBRN 0x60 /* Serial Bus Release Number Register */
|
||||
#define USB_RELEASE_1 0x10 /* USB 1.0 */
|
||||
#define USB_RELEASE_2 0x20 /* USB 2.0 */
|
||||
#define USB_RELEASE_3 0x30 /* USB 3.0 */
|
||||
|
||||
#define USB_TOKEN_SETUP 0x2d
|
||||
#define USB_TOKEN_IN 0x69 /* device -> host */
|
||||
#define USB_TOKEN_OUT 0xe1 /* host -> device */
|
||||
@ -132,6 +138,7 @@
|
||||
#define USB_ENDPOINT_XFER_INT 3
|
||||
|
||||
typedef struct USBBus USBBus;
|
||||
typedef struct USBBusOps USBBusOps;
|
||||
typedef struct USBPort USBPort;
|
||||
typedef struct USBDevice USBDevice;
|
||||
typedef struct USBDeviceInfo USBDeviceInfo;
|
||||
@ -323,6 +330,7 @@ void musb_set_size(MUSBState *s, int epnum, int size, int is_tx);
|
||||
|
||||
struct USBBus {
|
||||
BusState qbus;
|
||||
USBBusOps *ops;
|
||||
int busnr;
|
||||
int nfree;
|
||||
int nused;
|
||||
@ -331,7 +339,11 @@ struct USBBus {
|
||||
QTAILQ_ENTRY(USBBus) next;
|
||||
};
|
||||
|
||||
void usb_bus_new(USBBus *bus, DeviceState *host);
|
||||
struct USBBusOps {
|
||||
void (*device_destroy)(USBBus *bus, USBDevice *dev);
|
||||
};
|
||||
|
||||
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
|
||||
USBBus *usb_bus_find(int busnr);
|
||||
void usb_qdev_register(USBDeviceInfo *info);
|
||||
void usb_qdev_register_many(USBDeviceInfo *info);
|
||||
|
20
trace-events
20
trace-events
@ -194,6 +194,26 @@ disable sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "g
|
||||
disable sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
|
||||
disable sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64""
|
||||
|
||||
# hw/usb-ehci.c
|
||||
disable usb_ehci_reset(void) "=== RESET ==="
|
||||
disable usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
|
||||
disable usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x"
|
||||
disable usb_ehci_mmio_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)"
|
||||
disable usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d"
|
||||
disable usb_ehci_state(const char *schedule, const char *state) "%s schedule %s"
|
||||
disable usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t next, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x"
|
||||
disable usb_ehci_qh_fields(uint32_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ %08x - rl %d, mplen %d, eps %d, ep %d, dev %d"
|
||||
disable usb_ehci_qh_bits(uint32_t addr, int c, int h, int dtc, int i) "QH @ %08x - c %d, h %d, dtc %d, i %d"
|
||||
disable usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t next, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x"
|
||||
disable usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ %08x - tbytes %d, cpage %d, cerr %d, pid %d"
|
||||
disable usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d"
|
||||
disable usb_ehci_itd(uint32_t addr, uint32_t next, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d"
|
||||
disable usb_ehci_port_attach(uint32_t port, const char *device) "attach port #%d - %s"
|
||||
disable usb_ehci_port_detach(uint32_t port) "detach port #%d"
|
||||
disable usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d"
|
||||
disable usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t len, uint32_t bufpos) "write %d, cpage %d, offset 0x%03x, addr 0x%08x, len %d, bufpos %d"
|
||||
disable usb_ehci_queue_action(void *q, const char *action) "q %p: %s"
|
||||
|
||||
# hw/usb-desc.c
|
||||
disable usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
|
||||
disable usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d"
|
||||
|
96
usb-linux.c
96
usb-linux.c
@ -115,7 +115,7 @@ typedef struct USBHostDevice {
|
||||
USBDevice dev;
|
||||
int fd;
|
||||
|
||||
uint8_t descr[1024];
|
||||
uint8_t descr[8192];
|
||||
int descr_len;
|
||||
int configuration;
|
||||
int ninterfaces;
|
||||
@ -267,6 +267,14 @@ static void async_free(AsyncURB *aurb)
|
||||
qemu_free(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);
|
||||
}
|
||||
|
||||
static void async_complete(void *opaque)
|
||||
{
|
||||
USBHostDevice *s = opaque;
|
||||
@ -281,10 +289,7 @@ static void async_complete(void *opaque)
|
||||
return;
|
||||
}
|
||||
if (errno == ENODEV && !s->closing) {
|
||||
printf("husb: device %d.%d disconnected\n",
|
||||
s->bus_num, s->addr);
|
||||
usb_host_close(s);
|
||||
usb_host_auto_check(NULL);
|
||||
do_disconnect(s);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -358,6 +363,7 @@ static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
|
||||
|
||||
static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||
{
|
||||
const char *op = NULL;
|
||||
int dev_descr_len, config_descr_len;
|
||||
int interface, nb_interfaces;
|
||||
int ret, i;
|
||||
@ -370,7 +376,8 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||
i = 0;
|
||||
dev_descr_len = dev->descr[0];
|
||||
if (dev_descr_len > dev->descr_len) {
|
||||
goto fail;
|
||||
fprintf(stderr, "husb: update iface failed. descr too short\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
i += dev_descr_len;
|
||||
@ -398,7 +405,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||
if (i >= dev->descr_len) {
|
||||
fprintf(stderr,
|
||||
"husb: update iface failed. no matching configuration\n");
|
||||
goto fail;
|
||||
return 0;
|
||||
}
|
||||
nb_interfaces = dev->descr[i + 4];
|
||||
|
||||
@ -410,9 +417,9 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||
ctrl.ioctl_code = USBDEVFS_DISCONNECT;
|
||||
ctrl.ifno = interface;
|
||||
ctrl.data = 0;
|
||||
op = "USBDEVFS_DISCONNECT";
|
||||
ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
|
||||
if (ret < 0 && errno != ENODATA) {
|
||||
perror("USBDEVFS_DISCONNECT");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@ -421,6 +428,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||
|
||||
/* XXX: only grab if all interfaces are free */
|
||||
for (interface = 0; interface < nb_interfaces; interface++) {
|
||||
op = "USBDEVFS_CLAIMINTERFACE";
|
||||
ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
|
||||
if (ret < 0) {
|
||||
if (errno == EBUSY) {
|
||||
@ -428,8 +436,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||
} else {
|
||||
perror("husb: failed to claim interface");
|
||||
}
|
||||
fail:
|
||||
return 0;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
@ -439,6 +446,13 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||
dev->ninterfaces = nb_interfaces;
|
||||
dev->configuration = configuration;
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
if (errno == ENODEV) {
|
||||
do_disconnect(dev);
|
||||
}
|
||||
perror(op);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_host_release_interfaces(USBHostDevice *s)
|
||||
@ -1015,6 +1029,11 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
|
||||
}
|
||||
|
||||
devep = descriptors[i + 2];
|
||||
if ((devep & 0x0f) == 0) {
|
||||
fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (descriptors[i + 3] & 0x3) {
|
||||
case 0x00:
|
||||
type = USBDEVFS_URB_TYPE_CONTROL;
|
||||
@ -1043,10 +1062,9 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
|
||||
}
|
||||
|
||||
static int usb_host_open(USBHostDevice *dev, int bus_num,
|
||||
int addr, char *port, const char *prod_name)
|
||||
int addr, char *port, const char *prod_name, int speed)
|
||||
{
|
||||
int fd = -1, ret;
|
||||
struct usbdevfs_connectinfo ci;
|
||||
char buf[1024];
|
||||
|
||||
if (dev->fd != -1) {
|
||||
@ -1101,24 +1119,29 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
|
||||
if (ret < 0) {
|
||||
perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printf("husb: grabbed usb device %d.%d\n", bus_num, addr);
|
||||
|
||||
ret = usb_linux_update_endp_table(dev);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ci.slow) {
|
||||
dev->dev.speed = USB_SPEED_LOW;
|
||||
} else {
|
||||
dev->dev.speed = USB_SPEED_HIGH;
|
||||
if (speed == -1) {
|
||||
struct usbdevfs_connectinfo ci;
|
||||
|
||||
ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
|
||||
if (ret < 0) {
|
||||
perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ci.slow) {
|
||||
speed = USB_SPEED_LOW;
|
||||
} else {
|
||||
speed = USB_SPEED_HIGH;
|
||||
}
|
||||
}
|
||||
dev->dev.speed = speed;
|
||||
|
||||
printf("husb: grabbed usb device %d.%d\n", bus_num, addr);
|
||||
|
||||
if (!prod_name || prod_name[0] == '\0') {
|
||||
snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
|
||||
@ -1135,9 +1158,9 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
dev->fd = -1;
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
if (dev->fd != -1) {
|
||||
close(dev->fd);
|
||||
dev->fd = -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -1146,7 +1169,7 @@ static int usb_host_close(USBHostDevice *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dev->fd == -1) {
|
||||
if (dev->fd == -1 || !dev->dev.attached) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1332,7 +1355,8 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func)
|
||||
}
|
||||
|
||||
device_count = 0;
|
||||
bus_num = addr = speed = class_id = product_id = vendor_id = 0;
|
||||
bus_num = addr = class_id = product_id = vendor_id = 0;
|
||||
speed = -1; /* Can't get the speed from /[proc|dev]/bus/usb/devices */
|
||||
for(;;) {
|
||||
if (fgets(line, sizeof(line), f) == NULL) {
|
||||
break;
|
||||
@ -1360,7 +1384,9 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func)
|
||||
if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (!strcmp(buf, "480")) {
|
||||
if (!strcmp(buf, "5000")) {
|
||||
speed = USB_SPEED_SUPER;
|
||||
} else if (!strcmp(buf, "480")) {
|
||||
speed = USB_SPEED_HIGH;
|
||||
} else if (!strcmp(buf, "1.5")) {
|
||||
speed = USB_SPEED_LOW;
|
||||
@ -1504,7 +1530,9 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
|
||||
if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) {
|
||||
goto the_end;
|
||||
}
|
||||
if (!strcmp(line, "480\n")) {
|
||||
if (!strcmp(line, "5000\n")) {
|
||||
speed = USB_SPEED_SUPER;
|
||||
} else if (!strcmp(line, "480\n")) {
|
||||
speed = USB_SPEED_HIGH;
|
||||
} else if (!strcmp(line, "1.5\n")) {
|
||||
speed = USB_SPEED_LOW;
|
||||
@ -1642,7 +1670,8 @@ 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);
|
||||
usb_host_open(s, bus_num, addr, port, product_name, speed);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1781,6 +1810,9 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port,
|
||||
case USB_SPEED_HIGH:
|
||||
speed_str = "480";
|
||||
break;
|
||||
case USB_SPEED_SUPER:
|
||||
speed_str = "5000";
|
||||
break;
|
||||
default:
|
||||
speed_str = "?";
|
||||
break;
|
||||
|
6
vl.c
6
vl.c
@ -925,9 +925,13 @@ static int usb_device_add(const char *devname)
|
||||
goto done;
|
||||
|
||||
/* the other ones */
|
||||
#ifndef CONFIG_LINUX
|
||||
/* only the linux version is qdev-ified, usb-bsd still needs this */
|
||||
if (strstart(devname, "host:", &p)) {
|
||||
dev = usb_host_device_open(p);
|
||||
} else if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) {
|
||||
} else
|
||||
#endif
|
||||
if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) {
|
||||
dev = usb_bt_init(devname[2] ? hci_init(p) :
|
||||
bt_new_hci(qemu_find_bt_vlan(0)));
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user