Merge remote-tracking branch 'kraxel/usb.14.pull' into staging
This commit is contained in:
commit
f590f4c4b6
@ -193,6 +193,7 @@ hw-obj-$(CONFIG_PCSPK) += pcspk.o
|
||||
hw-obj-$(CONFIG_PCKBD) += pckbd.o
|
||||
hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o
|
||||
hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o
|
||||
hw-obj-$(CONFIG_USB_EHCI) += usb-ehci.o
|
||||
hw-obj-$(CONFIG_FDC) += fdc.o
|
||||
hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
|
||||
hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
|
||||
|
@ -3,6 +3,7 @@ CONFIG_VIRTIO_PCI=y
|
||||
CONFIG_VIRTIO=y
|
||||
CONFIG_USB_UHCI=y
|
||||
CONFIG_USB_OHCI=y
|
||||
CONFIG_USB_EHCI=y
|
||||
CONFIG_NE2000_PCI=y
|
||||
CONFIG_EEPRO100_PCI=y
|
||||
CONFIG_PCNET_PCI=y
|
||||
|
38
docs/usb2.txt
Normal file
38
docs/usb2.txt
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
USB 2.0 Quick Start
|
||||
===================
|
||||
|
||||
The QEMU EHCI Adapter does *not* support companion controllers. That
|
||||
implies there are two completely separate USB busses: One USB 1.1 bus
|
||||
driven by the UHCI controller and one USB 2.0 bus driven by the EHCI
|
||||
controller. Devices must be attached to the correct controller
|
||||
manually.
|
||||
|
||||
The '-usb' switch will make qemu create the UHCI controller as part of
|
||||
the PIIX3 chipset. The USB 1.1 bus will carry the name "usb.0".
|
||||
|
||||
You can use the standard -device switch to add a EHCI controller to
|
||||
your virtual machine. It is strongly recommended to specify an ID for
|
||||
the controller so the USB 2.0 bus gets a individual name, for example
|
||||
'-device usb-ehci,id=ehci". This will give you a USB 2.0 bus named
|
||||
"ehci.0".
|
||||
|
||||
I strongly recomment to also use -device to attach usb devices because
|
||||
you can specify the bus they should be attached to this way. Here is
|
||||
a complete example:
|
||||
|
||||
qemu -M pc ${otheroptions} \
|
||||
-drive if=none,id=usbstick,file=/path/to/image \
|
||||
-usb \
|
||||
-device usb-ehci,id=ehci \
|
||||
-device usb-tablet,bus=usb.0 \
|
||||
-device usb-storage,bus=ehci.0,drive=usbstick
|
||||
|
||||
This attaches a usb tablet to the UHCI adapter and a usb mass storage
|
||||
device to the EHCI adapter.
|
||||
|
||||
enjoy,
|
||||
Gerd
|
||||
|
||||
--
|
||||
Gerd Hoffmann <kraxel@redhat.com>
|
@ -323,7 +323,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
|
||||
break;
|
||||
}
|
||||
s->proto = parameter;
|
||||
s->usbdev->info->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0,
|
||||
s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0,
|
||||
NULL);
|
||||
ret = BT_HS_SUCCESSFUL;
|
||||
break;
|
||||
@ -333,7 +333,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
|
||||
ret = BT_HS_ERR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
s->usbdev->info->handle_control(s->usbdev, GET_IDLE, 0, 0, 1,
|
||||
s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1,
|
||||
s->control->sdu_out(s->control, 1));
|
||||
s->control->sdu_submit(s->control);
|
||||
break;
|
||||
@ -346,7 +346,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
|
||||
|
||||
/* We don't need to know about the Idle Rate here really,
|
||||
* so just pass it on to the device. */
|
||||
ret = s->usbdev->info->handle_control(s->usbdev,
|
||||
ret = s->usbdev->info->handle_control(s->usbdev, NULL,
|
||||
SET_IDLE, data[1], 0, 0, NULL) ?
|
||||
BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
|
||||
/* XXX: Does this generate a handshake? */
|
||||
|
@ -100,6 +100,7 @@
|
||||
#define PCI_VENDOR_ID_INTEL 0x8086
|
||||
#define PCI_DEVICE_ID_INTEL_82441 0x1237
|
||||
#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415
|
||||
#define PCI_DEVICE_ID_INTEL_82801D 0x24CD
|
||||
#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab
|
||||
#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000
|
||||
#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010
|
||||
|
@ -372,13 +372,13 @@ static void usb_bt_handle_reset(USBDevice *dev)
|
||||
s->altsetting = 0;
|
||||
}
|
||||
|
||||
static int usb_bt_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
struct USBBtState *s = (struct USBBtState *) dev->opaque;
|
||||
int ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
switch (request) {
|
||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
||||
|
@ -602,8 +602,8 @@ static void ccid_handle_reset(USBDevice *dev)
|
||||
ccid_reset(s);
|
||||
}
|
||||
|
||||
static int ccid_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
|
||||
int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
|
||||
int ret = 0;
|
||||
|
@ -76,7 +76,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
|
||||
{
|
||||
uint8_t bLength = 0x09;
|
||||
uint16_t wTotalLength = 0;
|
||||
int i, rc, count;
|
||||
int i, rc;
|
||||
|
||||
if (len < bLength) {
|
||||
return -1;
|
||||
@ -91,8 +91,19 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
|
||||
dest[0x08] = conf->bMaxPower;
|
||||
wTotalLength += bLength;
|
||||
|
||||
count = conf->nif ? conf->nif : conf->bNumInterfaces;
|
||||
for (i = 0; i < count; i++) {
|
||||
/* handle grouped interfaces if any*/
|
||||
for (i = 0; i < conf->nif_groups; i++) {
|
||||
rc = usb_desc_iface_group(&(conf->if_groups[i]),
|
||||
dest + wTotalLength,
|
||||
len - wTotalLength);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
wTotalLength += rc;
|
||||
}
|
||||
|
||||
/* handle normal (ungrouped / no IAD) interfaces if any */
|
||||
for (i = 0; i < conf->nif; i++) {
|
||||
rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
@ -105,6 +116,41 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
|
||||
return wTotalLength;
|
||||
}
|
||||
|
||||
int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
|
||||
size_t len)
|
||||
{
|
||||
int pos = 0;
|
||||
int i = 0;
|
||||
|
||||
/* handle interface association descriptor */
|
||||
uint8_t bLength = 0x08;
|
||||
|
||||
if (len < bLength) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dest[0x00] = bLength;
|
||||
dest[0x01] = USB_DT_INTERFACE_ASSOC;
|
||||
dest[0x02] = iad->bFirstInterface;
|
||||
dest[0x03] = iad->bInterfaceCount;
|
||||
dest[0x04] = iad->bFunctionClass;
|
||||
dest[0x05] = iad->bFunctionSubClass;
|
||||
dest[0x06] = iad->bFunctionProtocol;
|
||||
dest[0x07] = iad->iFunction;
|
||||
pos += bLength;
|
||||
|
||||
/* handle associated interfaces in this group */
|
||||
for (i = 0; i < iad->nif; i++) {
|
||||
int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
pos += rc;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
|
||||
{
|
||||
uint8_t bLength = 0x09;
|
||||
@ -344,8 +390,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usb_desc_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
const USBDesc *desc = dev->info->usb_desc;
|
||||
int i, ret = -1;
|
||||
|
@ -30,6 +30,24 @@ struct USBDescConfig {
|
||||
uint8_t bmAttributes;
|
||||
uint8_t bMaxPower;
|
||||
|
||||
/* grouped interfaces */
|
||||
uint8_t nif_groups;
|
||||
const USBDescIfaceAssoc *if_groups;
|
||||
|
||||
/* "normal" interfaces */
|
||||
uint8_t nif;
|
||||
const USBDescIface *ifs;
|
||||
};
|
||||
|
||||
/* conceptually an Interface Association Descriptor, and releated interfaces */
|
||||
struct USBDescIfaceAssoc {
|
||||
uint8_t bFirstInterface;
|
||||
uint8_t bInterfaceCount;
|
||||
uint8_t bFunctionClass;
|
||||
uint8_t bFunctionSubClass;
|
||||
uint8_t bFunctionProtocol;
|
||||
uint8_t iFunction;
|
||||
|
||||
uint8_t nif;
|
||||
const USBDescIface *ifs;
|
||||
};
|
||||
@ -75,6 +93,8 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
|
||||
int usb_desc_device_qualifier(const USBDescDevice *dev,
|
||||
uint8_t *dest, size_t len);
|
||||
int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
|
||||
int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
|
||||
size_t len);
|
||||
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len);
|
||||
int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
|
||||
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
|
||||
@ -86,7 +106,7 @@ void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
|
||||
const char *usb_desc_get_string(USBDevice *dev, uint8_t index);
|
||||
int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
|
||||
int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len);
|
||||
int usb_desc_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data);
|
||||
int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data);
|
||||
|
||||
#endif /* QEMU_HW_USB_DESC_H */
|
||||
|
2037
hw/usb-ehci.c
Normal file
2037
hw/usb-ehci.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -211,6 +211,7 @@ static const USBDescDevice desc_device_mouse = {
|
||||
.iConfiguration = STR_CONFIG_MOUSE,
|
||||
.bmAttributes = 0xa0,
|
||||
.bMaxPower = 50,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_mouse,
|
||||
},
|
||||
},
|
||||
@ -227,6 +228,7 @@ static const USBDescDevice desc_device_tablet = {
|
||||
.iConfiguration = STR_CONFIG_TABLET,
|
||||
.bmAttributes = 0xa0,
|
||||
.bMaxPower = 50,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_tablet,
|
||||
},
|
||||
},
|
||||
@ -243,6 +245,7 @@ static const USBDescDevice desc_device_keyboard = {
|
||||
.iConfiguration = STR_CONFIG_KEYBOARD,
|
||||
.bmAttributes = 0xa0,
|
||||
.bMaxPower = 50,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_keyboard,
|
||||
},
|
||||
},
|
||||
@ -724,13 +727,13 @@ static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime)
|
||||
s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000;
|
||||
}
|
||||
|
||||
static int usb_hid_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBHIDState *s = (USBHIDState *)dev;
|
||||
int ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -119,6 +119,7 @@ static const USBDescDevice desc_device_hub = {
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = 0xe0,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_hub,
|
||||
},
|
||||
},
|
||||
@ -284,13 +285,13 @@ static void usb_hub_handle_reset(USBDevice *dev)
|
||||
/* XXX: do it */
|
||||
}
|
||||
|
||||
static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBHubState *s = (USBHubState *)dev;
|
||||
int ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
@ -494,7 +495,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
|
||||
port = &s->ports[i];
|
||||
dev = port->port.dev;
|
||||
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
|
||||
ret = dev->info->handle_packet(dev, p);
|
||||
ret = usb_handle_packet(dev, p);
|
||||
if (ret != USB_RET_NODEV) {
|
||||
return ret;
|
||||
}
|
||||
|
18
hw/usb-msd.c
18
hw/usb-msd.c
@ -119,6 +119,7 @@ static const USBDescDevice desc_device_full = {
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = STR_CONFIG_FULL,
|
||||
.bmAttributes = 0xc0,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_full,
|
||||
},
|
||||
},
|
||||
@ -153,6 +154,7 @@ static const USBDescDevice desc_device_high = {
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = STR_CONFIG_HIGH,
|
||||
.bmAttributes = 0xc0,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_high,
|
||||
},
|
||||
},
|
||||
@ -251,7 +253,7 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
||||
s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
|
||||
if (p) {
|
||||
usb_msd_copy_data(s);
|
||||
if (s->usb_len == 0) {
|
||||
if (s->packet && s->usb_len == 0) {
|
||||
/* Set s->packet to NULL before calling usb_packet_complete
|
||||
because another request may be issued before
|
||||
usb_packet_complete returns. */
|
||||
@ -270,13 +272,13 @@ static void usb_msd_handle_reset(USBDevice *dev)
|
||||
s->mode = USB_MSDM_CBW;
|
||||
}
|
||||
|
||||
static int usb_msd_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
MSDState *s = (MSDState *)dev;
|
||||
int ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
@ -313,9 +315,9 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_msd_cancel_io(USBPacket *p, void *opaque)
|
||||
static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
MSDState *s = opaque;
|
||||
MSDState *s = DO_UPCAST(MSDState, dev, dev);
|
||||
s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
|
||||
s->packet = NULL;
|
||||
s->scsi_len = 0;
|
||||
@ -396,7 +398,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
}
|
||||
if (s->usb_len) {
|
||||
DPRINTF("Deferring packet %p\n", p);
|
||||
usb_defer_packet(p, usb_msd_cancel_io, s);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
} else {
|
||||
@ -419,7 +420,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
if (s->data_len != 0 || len < 13)
|
||||
goto fail;
|
||||
/* Waiting for SCSI write to complete. */
|
||||
usb_defer_packet(p, usb_msd_cancel_io, s);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
break;
|
||||
@ -453,7 +453,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
}
|
||||
if (s->usb_len) {
|
||||
DPRINTF("Deferring packet %p\n", p);
|
||||
usb_defer_packet(p, usb_msd_cancel_io, s);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
} else {
|
||||
@ -602,6 +601,7 @@ static struct USBDeviceInfo msd_info = {
|
||||
.usb_desc = &desc,
|
||||
.init = usb_msd_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.cancel_packet = usb_msd_cancel_io,
|
||||
.handle_attach = usb_desc_attach,
|
||||
.handle_reset = usb_msd_handle_reset,
|
||||
.handle_control = usb_msd_handle_control,
|
||||
|
@ -601,7 +601,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
||||
ep->packey[dir].dir = dir;
|
||||
|
||||
if (s->port.dev)
|
||||
ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir].p);
|
||||
ret = usb_handle_packet(s->port.dev, &ep->packey[dir].p);
|
||||
else
|
||||
ret = USB_RET_NODEV;
|
||||
|
||||
|
@ -1042,13 +1042,13 @@ static void usb_net_handle_reset(USBDevice *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static int usb_net_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBNetState *s = (USBNetState *) dev;
|
||||
int ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -748,7 +748,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
||||
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
|
||||
ohci->usb_packet.data = ohci->usb_buf;
|
||||
ohci->usb_packet.len = len;
|
||||
ret = dev->info->handle_packet(dev, &ohci->usb_packet);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
}
|
||||
@ -944,7 +944,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
|
||||
ohci->usb_packet.data = ohci->usb_buf;
|
||||
ohci->usb_packet.len = len;
|
||||
ret = dev->info->handle_packet(dev, &ohci->usb_packet);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
}
|
||||
|
@ -146,6 +146,7 @@ static const USBDescDevice desc_device = {
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 50,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface0,
|
||||
},
|
||||
},
|
||||
@ -218,14 +219,14 @@ static uint8_t usb_get_modem_lines(USBSerialState *s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usb_serial_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBSerialState *s = (USBSerialState *)dev;
|
||||
int ret;
|
||||
|
||||
DPRINTF("got control %x, value %x\n",request, value);
|
||||
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -632,7 +632,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
|
||||
USBDevice *dev = port->port.dev;
|
||||
|
||||
if (dev && (port->ctrl & UHCI_PORT_EN))
|
||||
ret = dev->info->handle_packet(dev, p);
|
||||
ret = usb_handle_packet(dev, p);
|
||||
}
|
||||
|
||||
DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len);
|
||||
@ -702,11 +702,15 @@ out:
|
||||
case USB_RET_STALL:
|
||||
td->ctrl |= TD_CTRL_STALL;
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
s->status |= UHCI_STS_USBERR;
|
||||
uhci_update_irq(s);
|
||||
return 1;
|
||||
|
||||
case USB_RET_BABBLE:
|
||||
td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
s->status |= UHCI_STS_USBERR;
|
||||
uhci_update_irq(s);
|
||||
/* frame interrupted */
|
||||
return -1;
|
||||
|
||||
|
@ -108,6 +108,7 @@ static const USBDescDevice desc_device_wacom = {
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 40,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_wacom,
|
||||
},
|
||||
},
|
||||
@ -249,13 +250,13 @@ static void usb_wacom_handle_reset(USBDevice *dev)
|
||||
s->mode = WACOM_MODE_HID;
|
||||
}
|
||||
|
||||
static int usb_wacom_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBWacomState *s = (USBWacomState *) dev;
|
||||
int ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
97
hw/usb.c
97
hw/usb.c
@ -64,8 +64,9 @@ void usb_wakeup(USBDevice *dev)
|
||||
*/
|
||||
|
||||
#define SETUP_STATE_IDLE 0
|
||||
#define SETUP_STATE_DATA 1
|
||||
#define SETUP_STATE_ACK 2
|
||||
#define SETUP_STATE_SETUP 1
|
||||
#define SETUP_STATE_DATA 2
|
||||
#define SETUP_STATE_ACK 3
|
||||
|
||||
static int do_token_setup(USBDevice *s, USBPacket *p)
|
||||
{
|
||||
@ -84,8 +85,12 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
|
||||
index = (s->setup_buf[5] << 8) | s->setup_buf[4];
|
||||
|
||||
if (s->setup_buf[0] & USB_DIR_IN) {
|
||||
ret = s->info->handle_control(s, request, value, index,
|
||||
ret = s->info->handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
s->setup_state = SETUP_STATE_SETUP;
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -123,9 +128,12 @@ static int do_token_in(USBDevice *s, USBPacket *p)
|
||||
switch(s->setup_state) {
|
||||
case SETUP_STATE_ACK:
|
||||
if (!(s->setup_buf[0] & USB_DIR_IN)) {
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
ret = s->info->handle_control(s, request, value, index,
|
||||
ret = s->info->handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
if (ret > 0)
|
||||
return 0;
|
||||
return ret;
|
||||
@ -238,6 +246,36 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
|
||||
}
|
||||
}
|
||||
|
||||
/* ctrl complete function for devices which use usb_generic_handle_packet and
|
||||
may return USB_RET_ASYNC from their handle_control callback. Device code
|
||||
which does this *must* call this function instead of the normal
|
||||
usb_packet_complete to complete their async control packets. */
|
||||
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
|
||||
{
|
||||
if (p->len < 0) {
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
}
|
||||
|
||||
switch (s->setup_state) {
|
||||
case SETUP_STATE_SETUP:
|
||||
if (p->len < s->setup_len) {
|
||||
s->setup_len = p->len;
|
||||
}
|
||||
s->setup_state = SETUP_STATE_DATA;
|
||||
p->len = 8;
|
||||
break;
|
||||
|
||||
case SETUP_STATE_ACK:
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
p->len = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
usb_packet_complete(s, p);
|
||||
}
|
||||
|
||||
/* XXX: fix overflow */
|
||||
int set_usb_string(uint8_t *buf, const char *str)
|
||||
{
|
||||
@ -259,9 +297,54 @@ int set_usb_string(uint8_t *buf, const char *str)
|
||||
void usb_send_msg(USBDevice *dev, int msg)
|
||||
{
|
||||
USBPacket p;
|
||||
int ret;
|
||||
|
||||
memset(&p, 0, sizeof(p));
|
||||
p.pid = msg;
|
||||
dev->info->handle_packet(dev, &p);
|
||||
|
||||
ret = usb_handle_packet(dev, &p);
|
||||
/* This _must_ be synchronous */
|
||||
assert(ret != USB_RET_ASYNC);
|
||||
}
|
||||
|
||||
/* Hand over a packet to a device for processing. Return value
|
||||
USB_RET_ASYNC indicates the processing isn't finished yet, the
|
||||
driver will call usb_packet_complete() when done processing it. */
|
||||
int usb_handle_packet(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(p->owner == NULL);
|
||||
ret = dev->info->handle_packet(dev, p);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
if (p->owner == NULL) {
|
||||
p->owner = dev;
|
||||
} else {
|
||||
/* We'll end up here when usb_handle_packet is called
|
||||
* recursively due to a hub being in the chain. Nothing
|
||||
* to do. Leave p->owner pointing to the device, not the
|
||||
* hub. */;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Notify the controller that an async packet is complete. This should only
|
||||
be called for packets previously deferred by returning USB_RET_ASYNC from
|
||||
handle_packet. */
|
||||
void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
/* Note: p->owner != dev is possible in case dev is a hub */
|
||||
assert(p->owner != NULL);
|
||||
dev->port->ops->complete(dev, p);
|
||||
p->owner = NULL;
|
||||
}
|
||||
|
||||
/* Cancel an active packet. The packed must have been deferred by
|
||||
returning USB_RET_ASYNC from handle_packet, and not yet
|
||||
completed. */
|
||||
void usb_cancel_packet(USBPacket * p)
|
||||
{
|
||||
assert(p->owner != NULL);
|
||||
p->owner->info->cancel_packet(p->owner, p);
|
||||
p->owner = NULL;
|
||||
}
|
||||
|
40
hw/usb.h
40
hw/usb.h
@ -124,6 +124,7 @@
|
||||
#define USB_DT_ENDPOINT 0x05
|
||||
#define USB_DT_DEVICE_QUALIFIER 0x06
|
||||
#define USB_DT_OTHER_SPEED_CONFIG 0x07
|
||||
#define USB_DT_INTERFACE_ASSOC 0x0B
|
||||
|
||||
#define USB_ENDPOINT_XFER_CONTROL 0
|
||||
#define USB_ENDPOINT_XFER_ISOC 1
|
||||
@ -140,6 +141,7 @@ typedef struct USBDesc USBDesc;
|
||||
typedef struct USBDescID USBDescID;
|
||||
typedef struct USBDescDevice USBDescDevice;
|
||||
typedef struct USBDescConfig USBDescConfig;
|
||||
typedef struct USBDescIfaceAssoc USBDescIfaceAssoc;
|
||||
typedef struct USBDescIface USBDescIface;
|
||||
typedef struct USBDescEndpoint USBDescEndpoint;
|
||||
typedef struct USBDescOther USBDescOther;
|
||||
@ -191,6 +193,11 @@ struct USBDeviceInfo {
|
||||
*/
|
||||
int (*handle_packet)(USBDevice *dev, USBPacket *p);
|
||||
|
||||
/*
|
||||
* Called when a packet is canceled.
|
||||
*/
|
||||
void (*cancel_packet)(USBDevice *dev, USBPacket *p);
|
||||
|
||||
/*
|
||||
* Called when device is destroyed.
|
||||
*/
|
||||
@ -212,7 +219,7 @@ struct USBDeviceInfo {
|
||||
*
|
||||
* Returns length or one of the USB_RET_ codes.
|
||||
*/
|
||||
int (*handle_control)(USBDevice *dev, int request, int value,
|
||||
int (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
|
||||
int index, int length, uint8_t *data);
|
||||
|
||||
/*
|
||||
@ -260,38 +267,17 @@ struct USBPacket {
|
||||
uint8_t *data;
|
||||
int len;
|
||||
/* Internal use by the USB layer. */
|
||||
USBCallback *cancel_cb;
|
||||
void *cancel_opaque;
|
||||
USBDevice *owner;
|
||||
};
|
||||
|
||||
/* Defer completion of a USB packet. The hadle_packet routine should then
|
||||
return USB_RET_ASYNC. Packets that complete immediately (before
|
||||
handle_packet returns) should not call this method. */
|
||||
static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
|
||||
void * opaque)
|
||||
{
|
||||
p->cancel_cb = cancel;
|
||||
p->cancel_opaque = opaque;
|
||||
}
|
||||
|
||||
/* Notify the controller that an async packet is complete. This should only
|
||||
be called for packets previously deferred with usb_defer_packet, and
|
||||
should never be called from within handle_packet. */
|
||||
static inline void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
dev->port->ops->complete(dev, p);
|
||||
}
|
||||
|
||||
/* Cancel an active packet. The packed must have been deferred with
|
||||
usb_defer_packet, and not yet completed. */
|
||||
static inline void usb_cancel_packet(USBPacket * p)
|
||||
{
|
||||
p->cancel_cb(p, p->cancel_opaque);
|
||||
}
|
||||
int usb_handle_packet(USBDevice *dev, USBPacket *p);
|
||||
void usb_packet_complete(USBDevice *dev, USBPacket *p);
|
||||
void usb_cancel_packet(USBPacket * p);
|
||||
|
||||
void usb_attach(USBPort *port, USBDevice *dev);
|
||||
void usb_wakeup(USBDevice *dev);
|
||||
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
|
||||
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
|
||||
int set_usb_string(uint8_t *buf, const char *str);
|
||||
void usb_send_msg(USBDevice *dev, int msg);
|
||||
|
||||
|
@ -126,6 +126,7 @@ static void usb_host_handle_reset(USBDevice *dev)
|
||||
* and return appropriate response
|
||||
*/
|
||||
static int usb_host_handle_control(USBDevice *dev,
|
||||
USBPacket *p,
|
||||
int request,
|
||||
int value,
|
||||
int index,
|
||||
|
400
usb-linux.c
400
usb-linux.c
@ -54,15 +54,7 @@ struct usb_ctrltransfer {
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct usb_ctrlrequest {
|
||||
uint8_t bRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
};
|
||||
|
||||
typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
|
||||
typedef int USBScanFunc(void *opaque, int bus_num, int addr, char *port,
|
||||
int class_id, int vendor_id, int product_id,
|
||||
const char *product_name, int speed);
|
||||
|
||||
@ -79,6 +71,7 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
|
||||
#define USBPROCBUS_PATH "/proc/bus/usb"
|
||||
#define PRODUCT_NAME_SZ 32
|
||||
#define MAX_ENDPOINTS 15
|
||||
#define MAX_PORTLEN 16
|
||||
#define USBDEVBUS_PATH "/dev/bus/usb"
|
||||
#define USBSYSBUS_PATH "/sys/bus/usb"
|
||||
|
||||
@ -96,6 +89,9 @@ static int usb_fs_type;
|
||||
#define ISO_URB_COUNT 3
|
||||
#define INVALID_EP_TYPE 255
|
||||
|
||||
/* devio.c limits single requests to 16k */
|
||||
#define MAX_USBFS_BUFFER_SIZE 16384
|
||||
|
||||
typedef struct AsyncURB AsyncURB;
|
||||
|
||||
struct endp_data {
|
||||
@ -108,29 +104,10 @@ struct endp_data {
|
||||
int max_packet_size;
|
||||
};
|
||||
|
||||
enum {
|
||||
CTRL_STATE_IDLE = 0,
|
||||
CTRL_STATE_SETUP,
|
||||
CTRL_STATE_DATA,
|
||||
CTRL_STATE_ACK
|
||||
};
|
||||
|
||||
/*
|
||||
* Control transfer state.
|
||||
* Note that 'buffer' _must_ follow 'req' field because
|
||||
* we need contiguous buffer when we submit control URB.
|
||||
*/
|
||||
struct ctrl_struct {
|
||||
uint16_t len;
|
||||
uint16_t offset;
|
||||
uint8_t state;
|
||||
struct usb_ctrlrequest req;
|
||||
uint8_t buffer[8192];
|
||||
};
|
||||
|
||||
struct USBAutoFilter {
|
||||
uint32_t bus_num;
|
||||
uint32_t addr;
|
||||
char *port;
|
||||
uint32_t vendor_id;
|
||||
uint32_t product_id;
|
||||
};
|
||||
@ -146,13 +123,13 @@ typedef struct USBHostDevice {
|
||||
int closing;
|
||||
Notifier exit;
|
||||
|
||||
struct ctrl_struct ctrl;
|
||||
struct endp_data endp_table[MAX_ENDPOINTS];
|
||||
QLIST_HEAD(, AsyncURB) aurbs;
|
||||
|
||||
/* Host side address */
|
||||
int bus_num;
|
||||
int addr;
|
||||
int devpath;
|
||||
char port[MAX_PORTLEN];
|
||||
struct USBAutoFilter match;
|
||||
|
||||
QTAILQ_ENTRY(USBHostDevice) next;
|
||||
@ -236,6 +213,22 @@ static int get_iso_buffer_used(USBHostDevice *s, int ep)
|
||||
return s->endp_table[ep - 1].iso_buffer_used;
|
||||
}
|
||||
|
||||
static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor)
|
||||
{
|
||||
int raw = descriptor[4] + (descriptor[5] << 8);
|
||||
int size, microframes;
|
||||
|
||||
size = raw & 0x7ff;
|
||||
switch ((raw >> 11) & 3) {
|
||||
case 1: microframes = 2; break;
|
||||
case 2: microframes = 3; break;
|
||||
default: microframes = 1; break;
|
||||
}
|
||||
DPRINTF("husb: max packet size: 0x%x -> %d x %d\n",
|
||||
raw, microframes, size);
|
||||
s->endp_table[ep - 1].max_packet_size = size * microframes;
|
||||
}
|
||||
|
||||
static int get_max_packet_size(USBHostDevice *s, int ep)
|
||||
{
|
||||
return s->endp_table[ep - 1].max_packet_size;
|
||||
@ -250,45 +243,31 @@ struct AsyncURB
|
||||
{
|
||||
struct usbdevfs_urb urb;
|
||||
struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
|
||||
USBHostDevice *hdev;
|
||||
QLIST_ENTRY(AsyncURB) next;
|
||||
|
||||
/* For regular async urbs */
|
||||
USBPacket *packet;
|
||||
USBHostDevice *hdev;
|
||||
int more; /* large transfer, more urbs follow */
|
||||
|
||||
/* For buffered iso handling */
|
||||
int iso_frame_idx; /* -1 means in flight */
|
||||
};
|
||||
|
||||
static AsyncURB *async_alloc(void)
|
||||
static AsyncURB *async_alloc(USBHostDevice *s)
|
||||
{
|
||||
return (AsyncURB *) qemu_mallocz(sizeof(AsyncURB));
|
||||
AsyncURB *aurb = qemu_mallocz(sizeof(AsyncURB));
|
||||
aurb->hdev = s;
|
||||
QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
|
||||
return aurb;
|
||||
}
|
||||
|
||||
static void async_free(AsyncURB *aurb)
|
||||
{
|
||||
QLIST_REMOVE(aurb, next);
|
||||
qemu_free(aurb);
|
||||
}
|
||||
|
||||
static void async_complete_ctrl(USBHostDevice *s, USBPacket *p)
|
||||
{
|
||||
switch(s->ctrl.state) {
|
||||
case CTRL_STATE_SETUP:
|
||||
if (p->len < s->ctrl.len)
|
||||
s->ctrl.len = p->len;
|
||||
s->ctrl.state = CTRL_STATE_DATA;
|
||||
p->len = 8;
|
||||
break;
|
||||
|
||||
case CTRL_STATE_ACK:
|
||||
s->ctrl.state = CTRL_STATE_IDLE;
|
||||
p->len = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void async_complete(void *opaque)
|
||||
{
|
||||
USBHostDevice *s = opaque;
|
||||
@ -332,10 +311,7 @@ static void async_complete(void *opaque)
|
||||
if (p) {
|
||||
switch (aurb->urb.status) {
|
||||
case 0:
|
||||
p->len = aurb->urb.actual_length;
|
||||
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
|
||||
async_complete_ctrl(s, p);
|
||||
}
|
||||
p->len += aurb->urb.actual_length;
|
||||
break;
|
||||
|
||||
case -EPIPE:
|
||||
@ -348,19 +324,28 @@ static void async_complete(void *opaque)
|
||||
break;
|
||||
}
|
||||
|
||||
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
|
||||
usb_generic_async_ctrl_complete(&s->dev, p);
|
||||
} else if (!aurb->more) {
|
||||
usb_packet_complete(&s->dev, p);
|
||||
}
|
||||
}
|
||||
|
||||
async_free(aurb);
|
||||
}
|
||||
}
|
||||
|
||||
static void async_cancel(USBPacket *unused, void *opaque)
|
||||
static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
AsyncURB *aurb = opaque;
|
||||
USBHostDevice *s = aurb->hdev;
|
||||
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
||||
AsyncURB *aurb;
|
||||
|
||||
DPRINTF("husb: async cancel. aurb %p\n", aurb);
|
||||
QLIST_FOREACH(aurb, &s->aurbs, next) {
|
||||
if (p != aurb->packet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb);
|
||||
|
||||
/* Mark it as dead (see async_complete above) */
|
||||
aurb->packet = NULL;
|
||||
@ -369,6 +354,7 @@ static void async_cancel(USBPacket *unused, void *opaque)
|
||||
if (r < 0) {
|
||||
DPRINTF("husb: async. discard urb failed errno %d\n", errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||
@ -675,11 +661,13 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
|
||||
return len;
|
||||
}
|
||||
|
||||
static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
|
||||
static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
||||
struct usbdevfs_urb *urb;
|
||||
AsyncURB *aurb;
|
||||
int ret;
|
||||
int ret, rem;
|
||||
uint8_t *pbuf;
|
||||
uint8_t ep;
|
||||
|
||||
if (!is_valid(s, p->devep)) {
|
||||
@ -706,22 +694,33 @@ static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
|
||||
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
|
||||
}
|
||||
|
||||
aurb = async_alloc();
|
||||
aurb->hdev = s;
|
||||
rem = p->len;
|
||||
pbuf = p->data;
|
||||
p->len = 0;
|
||||
while (rem) {
|
||||
aurb = async_alloc(s);
|
||||
aurb->packet = p;
|
||||
|
||||
urb = &aurb->urb;
|
||||
|
||||
urb->endpoint = ep;
|
||||
urb->buffer = p->data;
|
||||
urb->buffer_length = p->len;
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
urb->usercontext = s;
|
||||
urb->buffer = pbuf;
|
||||
|
||||
if (rem > MAX_USBFS_BUFFER_SIZE) {
|
||||
urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
|
||||
aurb->more = 1;
|
||||
} else {
|
||||
urb->buffer_length = rem;
|
||||
aurb->more = 0;
|
||||
}
|
||||
pbuf += urb->buffer_length;
|
||||
rem -= urb->buffer_length;
|
||||
|
||||
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
|
||||
|
||||
DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n",
|
||||
urb->endpoint, p->len, aurb);
|
||||
DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
|
||||
urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
|
||||
|
||||
if (ret < 0) {
|
||||
DPRINTF("husb: submit failed. errno %d\n", errno);
|
||||
@ -735,8 +734,8 @@ static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usb_defer_packet(p, async_cancel, aurb);
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
@ -796,50 +795,43 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
|
||||
static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
||||
struct usbdevfs_urb *urb;
|
||||
AsyncURB *aurb;
|
||||
int ret, value, index;
|
||||
int buffer_len;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Process certain standard device requests.
|
||||
* These are infrequent and are processed synchronously.
|
||||
*/
|
||||
value = le16_to_cpu(s->ctrl.req.wValue);
|
||||
index = le16_to_cpu(s->ctrl.req.wIndex);
|
||||
|
||||
/* Note request is (bRequestType << 8) | bRequest */
|
||||
DPRINTF("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n",
|
||||
s->ctrl.req.bRequestType, s->ctrl.req.bRequest, value, index,
|
||||
s->ctrl.len);
|
||||
request >> 8, request & 0xff, value, index, length);
|
||||
|
||||
if (s->ctrl.req.bRequestType == 0) {
|
||||
switch (s->ctrl.req.bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
switch (request) {
|
||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
||||
return usb_host_set_address(s, value);
|
||||
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||
return usb_host_set_config(s, value & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
if (s->ctrl.req.bRequestType == 1 &&
|
||||
s->ctrl.req.bRequest == USB_REQ_SET_INTERFACE) {
|
||||
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
||||
return usb_host_set_interface(s, index, value);
|
||||
}
|
||||
|
||||
/* The rest are asynchronous */
|
||||
|
||||
buffer_len = 8 + s->ctrl.len;
|
||||
if (buffer_len > sizeof(s->ctrl.buffer)) {
|
||||
fprintf(stderr, "husb: ctrl buffer too small (%u > %zu)\n",
|
||||
buffer_len, sizeof(s->ctrl.buffer));
|
||||
if (length > sizeof(dev->data_buf)) {
|
||||
fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
|
||||
length, sizeof(dev->data_buf));
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
|
||||
aurb = async_alloc();
|
||||
aurb->hdev = s;
|
||||
aurb = async_alloc(s);
|
||||
aurb->packet = p;
|
||||
|
||||
/*
|
||||
@ -853,8 +845,8 @@ static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
|
||||
urb->type = USBDEVFS_URB_TYPE_CONTROL;
|
||||
urb->endpoint = p->devep;
|
||||
|
||||
urb->buffer = &s->ctrl.req;
|
||||
urb->buffer_length = buffer_len;
|
||||
urb->buffer = &dev->setup_buf;
|
||||
urb->buffer_length = length + 8;
|
||||
|
||||
urb->usercontext = s;
|
||||
|
||||
@ -875,174 +867,9 @@ static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
|
||||
}
|
||||
}
|
||||
|
||||
usb_defer_packet(p, async_cancel, aurb);
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
static int do_token_setup(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBHostDevice *s = (USBHostDevice *) dev;
|
||||
int ret = 0;
|
||||
|
||||
if (p->len != 8) {
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
|
||||
memcpy(&s->ctrl.req, p->data, 8);
|
||||
s->ctrl.len = le16_to_cpu(s->ctrl.req.wLength);
|
||||
s->ctrl.offset = 0;
|
||||
s->ctrl.state = CTRL_STATE_SETUP;
|
||||
|
||||
if (s->ctrl.req.bRequestType & USB_DIR_IN) {
|
||||
ret = usb_host_handle_control(s, p);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret < s->ctrl.len) {
|
||||
s->ctrl.len = ret;
|
||||
}
|
||||
s->ctrl.state = CTRL_STATE_DATA;
|
||||
} else {
|
||||
if (s->ctrl.len == 0) {
|
||||
s->ctrl.state = CTRL_STATE_ACK;
|
||||
} else {
|
||||
s->ctrl.state = CTRL_STATE_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_token_in(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBHostDevice *s = (USBHostDevice *) dev;
|
||||
int ret = 0;
|
||||
|
||||
if (p->devep != 0) {
|
||||
return usb_host_handle_data(s, p);
|
||||
}
|
||||
|
||||
switch(s->ctrl.state) {
|
||||
case CTRL_STATE_ACK:
|
||||
if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
|
||||
ret = usb_host_handle_control(s, p);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
s->ctrl.state = CTRL_STATE_IDLE;
|
||||
return ret > 0 ? 0 : ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case CTRL_STATE_DATA:
|
||||
if (s->ctrl.req.bRequestType & USB_DIR_IN) {
|
||||
int len = s->ctrl.len - s->ctrl.offset;
|
||||
if (len > p->len) {
|
||||
len = p->len;
|
||||
}
|
||||
memcpy(p->data, s->ctrl.buffer + s->ctrl.offset, len);
|
||||
s->ctrl.offset += len;
|
||||
if (s->ctrl.offset >= s->ctrl.len) {
|
||||
s->ctrl.state = CTRL_STATE_ACK;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
s->ctrl.state = CTRL_STATE_IDLE;
|
||||
return USB_RET_STALL;
|
||||
|
||||
default:
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
|
||||
static int do_token_out(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBHostDevice *s = (USBHostDevice *) dev;
|
||||
|
||||
if (p->devep != 0) {
|
||||
return usb_host_handle_data(s, p);
|
||||
}
|
||||
|
||||
switch(s->ctrl.state) {
|
||||
case CTRL_STATE_ACK:
|
||||
if (s->ctrl.req.bRequestType & USB_DIR_IN) {
|
||||
s->ctrl.state = CTRL_STATE_IDLE;
|
||||
/* transfer OK */
|
||||
} else {
|
||||
/* ignore additional output */
|
||||
}
|
||||
return 0;
|
||||
|
||||
case CTRL_STATE_DATA:
|
||||
if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
|
||||
int len = s->ctrl.len - s->ctrl.offset;
|
||||
if (len > p->len) {
|
||||
len = p->len;
|
||||
}
|
||||
memcpy(s->ctrl.buffer + s->ctrl.offset, p->data, len);
|
||||
s->ctrl.offset += len;
|
||||
if (s->ctrl.offset >= s->ctrl.len) {
|
||||
s->ctrl.state = CTRL_STATE_ACK;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
s->ctrl.state = CTRL_STATE_IDLE;
|
||||
return USB_RET_STALL;
|
||||
|
||||
default:
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Packet handler.
|
||||
* Called by the HC (host controller).
|
||||
*
|
||||
* Returns length of the transaction or one of the USB_RET_XXX codes.
|
||||
*/
|
||||
static int usb_host_handle_packet(USBDevice *s, USBPacket *p)
|
||||
{
|
||||
switch(p->pid) {
|
||||
case USB_MSG_ATTACH:
|
||||
s->state = USB_STATE_ATTACHED;
|
||||
return 0;
|
||||
|
||||
case USB_MSG_DETACH:
|
||||
s->state = USB_STATE_NOTATTACHED;
|
||||
return 0;
|
||||
|
||||
case USB_MSG_RESET:
|
||||
s->remote_wakeup = 0;
|
||||
s->addr = 0;
|
||||
s->state = USB_STATE_DEFAULT;
|
||||
s->info->handle_reset(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Rest of the PIDs must match our address */
|
||||
if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) {
|
||||
return USB_RET_NODEV;
|
||||
}
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
return do_token_setup(s, p);
|
||||
|
||||
case USB_TOKEN_IN:
|
||||
return do_token_in(s, p);
|
||||
|
||||
case USB_TOKEN_OUT:
|
||||
return do_token_out(s, p);
|
||||
|
||||
default:
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_linux_get_configuration(USBHostDevice *s)
|
||||
{
|
||||
uint8_t configuration;
|
||||
@ -1053,7 +880,7 @@ static int usb_linux_get_configuration(USBHostDevice *s)
|
||||
char device_name[32], line[1024];
|
||||
int configuration;
|
||||
|
||||
sprintf(device_name, "%d-%d", s->bus_num, s->devpath);
|
||||
sprintf(device_name, "%d-%s", s->bus_num, s->port);
|
||||
|
||||
if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue",
|
||||
device_name)) {
|
||||
@ -1099,7 +926,7 @@ static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
|
||||
char device_name[64], line[1024];
|
||||
int alt_setting;
|
||||
|
||||
sprintf(device_name, "%d-%d:%d.%d", s->bus_num, s->devpath,
|
||||
sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
|
||||
(int)configuration, (int)interface);
|
||||
|
||||
if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
|
||||
@ -1195,8 +1022,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
|
||||
break;
|
||||
case 0x01:
|
||||
type = USBDEVFS_URB_TYPE_ISO;
|
||||
s->endp_table[(devep & 0xf) - 1].max_packet_size =
|
||||
descriptors[i + 4] + (descriptors[i + 5] << 8);
|
||||
set_max_packet_size(s, (devep & 0xf), descriptors + i);
|
||||
break;
|
||||
case 0x02:
|
||||
type = USBDEVFS_URB_TYPE_BULK;
|
||||
@ -1218,7 +1044,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
|
||||
}
|
||||
|
||||
static int usb_host_open(USBHostDevice *dev, int bus_num,
|
||||
int addr, int devpath, const char *prod_name)
|
||||
int addr, char *port, const char *prod_name)
|
||||
{
|
||||
int fd = -1, ret;
|
||||
struct usbdevfs_connectinfo ci;
|
||||
@ -1244,7 +1070,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
|
||||
|
||||
dev->bus_num = bus_num;
|
||||
dev->addr = addr;
|
||||
dev->devpath = devpath;
|
||||
strcpy(dev->port, port);
|
||||
dev->fd = fd;
|
||||
|
||||
/* read the device description */
|
||||
@ -1368,7 +1194,10 @@ static struct USBDeviceInfo usb_host_dev_info = {
|
||||
.qdev.name = "usb-host",
|
||||
.qdev.size = sizeof(USBHostDevice),
|
||||
.init = usb_host_initfn,
|
||||
.handle_packet = usb_host_handle_packet,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.cancel_packet = usb_host_async_cancel,
|
||||
.handle_data = usb_host_handle_data,
|
||||
.handle_control = usb_host_handle_control,
|
||||
.handle_reset = usb_host_handle_reset,
|
||||
.handle_destroy = usb_host_handle_destroy,
|
||||
.usbdevice_name = "host",
|
||||
@ -1376,6 +1205,7 @@ static struct USBDeviceInfo usb_host_dev_info = {
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0),
|
||||
DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0),
|
||||
DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
|
||||
DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0),
|
||||
DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
@ -1616,8 +1446,9 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
|
||||
{
|
||||
DIR *dir = NULL;
|
||||
char line[1024];
|
||||
int bus_num, addr, devpath, speed, class_id, product_id, vendor_id;
|
||||
int bus_num, addr, speed, class_id, product_id, vendor_id;
|
||||
int ret = 0;
|
||||
char port[MAX_PORTLEN];
|
||||
char product_name[512];
|
||||
struct dirent *de;
|
||||
|
||||
@ -1629,12 +1460,8 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
|
||||
char *tmpstr = de->d_name;
|
||||
if (!strncmp(de->d_name, "usb", 3)) {
|
||||
tmpstr += 3;
|
||||
}
|
||||
if (sscanf(tmpstr, "%d-%d", &bus_num, &devpath) < 1) {
|
||||
goto the_end;
|
||||
if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
|
||||
@ -1686,7 +1513,7 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
|
||||
speed = USB_SPEED_FULL;
|
||||
}
|
||||
|
||||
ret = func(opaque, bus_num, addr, devpath, class_id, vendor_id,
|
||||
ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
|
||||
product_id, product_name, speed);
|
||||
if (ret) {
|
||||
goto the_end;
|
||||
@ -1777,7 +1604,7 @@ static int usb_host_scan(void *opaque, USBScanFunc *func)
|
||||
|
||||
static QEMUTimer *usb_auto_timer;
|
||||
|
||||
static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
|
||||
static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port,
|
||||
int class_id, int vendor_id, int product_id,
|
||||
const char *product_name, int speed)
|
||||
{
|
||||
@ -1797,6 +1624,9 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
|
||||
if (f->addr > 0 && f->addr != addr) {
|
||||
continue;
|
||||
}
|
||||
if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
|
||||
continue;
|
||||
@ -1813,7 +1643,7 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
|
||||
}
|
||||
DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
|
||||
|
||||
usb_host_open(s, bus_num, addr, devpath, product_name);
|
||||
usb_host_open(s, bus_num, addr, port, product_name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1935,8 +1765,8 @@ static const char *usb_class_str(uint8_t class)
|
||||
return p->class_name;
|
||||
}
|
||||
|
||||
static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
|
||||
int vendor_id, int product_id,
|
||||
static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port,
|
||||
int class_id, int vendor_id, int product_id,
|
||||
const char *product_name,
|
||||
int speed)
|
||||
{
|
||||
@ -1957,8 +1787,8 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
|
||||
break;
|
||||
}
|
||||
|
||||
monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n",
|
||||
bus_num, addr, speed_str);
|
||||
monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
|
||||
bus_num, addr, port, speed_str);
|
||||
class_str = usb_class_str(class_id);
|
||||
if (class_str) {
|
||||
monitor_printf(mon, " %s:", class_str);
|
||||
@ -1973,14 +1803,14 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
|
||||
}
|
||||
|
||||
static int usb_host_info_device(void *opaque, int bus_num, int addr,
|
||||
int devpath, int class_id,
|
||||
char *path, int class_id,
|
||||
int vendor_id, int product_id,
|
||||
const char *product_name,
|
||||
int speed)
|
||||
{
|
||||
Monitor *mon = opaque;
|
||||
|
||||
usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id,
|
||||
usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
|
||||
product_name, speed);
|
||||
return 0;
|
||||
}
|
||||
@ -2022,7 +1852,7 @@ void usb_host_info(Monitor *mon)
|
||||
dec2str(f->addr, addr, sizeof(addr));
|
||||
hex2str(f->vendor_id, vid, sizeof(vid));
|
||||
hex2str(f->product_id, pid, sizeof(pid));
|
||||
monitor_printf(mon, " Device %s.%s ID %s:%s\n",
|
||||
bus, addr, vid, pid);
|
||||
monitor_printf(mon, " Bus %s, Addr %s, Port %s, ID %s:%s\n",
|
||||
bus, addr, f->port ? f->port : "*", vid, pid);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user