Merge remote-tracking branch 'kraxel/usb.39' into staging
* kraxel/usb.39: (21 commits) usb: Resolve warnings about unassigned bus on usb device creation usb-redir: Return USB_RET_NAK when we've no data for an interrupt endpoint usb-redir: Limit return values returned by iso packets usb-redir: Let the usb-host know about our device filtering usb-redir: Always clear device state on filter reject usb-redir: Fix printing of device version ehci: drop old stuff usb-ehci: Handle ISO packets failing with an error other then NAK libcacard: fix reported ATR length usb-ccid: advertise SELF_POWERED libcacard: link with glib for g_strndup usb-desc: fix user trigerrable segfaults (!config) usb-ehci: sanity-check iso xfers usb: add tracepoint for usb packet state changes. usb-xhci: enable packet queuing usb-uhci: implement packet queuing usb-uhci: process uhci_handle_td return code via switch. usb-uhci: add UHCIQueue usb-uhci: cleanup UHCIAsync allocation & initialization. usb-ehci: fix reset ...
This commit is contained in:
commit
5ca2358ac8
6
configure
vendored
6
configure
vendored
|
@ -2578,8 +2578,8 @@ if test "$smartcard" != "no" ; then
|
||||||
int main(void) { PK11_FreeSlot(0); return 0; }
|
int main(void) { PK11_FreeSlot(0); return 0; }
|
||||||
EOF
|
EOF
|
||||||
smartcard_cflags="-I\$(SRC_PATH)/libcacard"
|
smartcard_cflags="-I\$(SRC_PATH)/libcacard"
|
||||||
libcacard_libs=$($pkg_config --libs nss 2>/dev/null)
|
libcacard_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs"
|
||||||
libcacard_cflags=$($pkg_config --cflags nss 2>/dev/null)
|
libcacard_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags"
|
||||||
if $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 && \
|
if $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 && \
|
||||||
compile_prog "$smartcard_cflags $libcacard_cflags" "$libcacard_libs"; then
|
compile_prog "$smartcard_cflags $libcacard_cflags" "$libcacard_libs"; then
|
||||||
smartcard_nss="yes"
|
smartcard_nss="yes"
|
||||||
|
@ -2599,7 +2599,7 @@ fi
|
||||||
|
|
||||||
# check for usbredirparser for usb network redirection support
|
# check for usbredirparser for usb network redirection support
|
||||||
if test "$usb_redir" != "no" ; then
|
if test "$usb_redir" != "no" ; then
|
||||||
if $pkg_config --atleast-version=0.3.3 libusbredirparser >/dev/null 2>&1 ; then
|
if $pkg_config --atleast-version=0.3.4 libusbredirparser >/dev/null 2>&1 ; then
|
||||||
usb_redir="yes"
|
usb_redir="yes"
|
||||||
usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null)
|
usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null)
|
||||||
usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null)
|
usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null)
|
||||||
|
|
|
@ -498,14 +498,14 @@ static int usb_bt_initfn(USBDevice *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
USBDevice *usb_bt_init(HCIInfo *hci)
|
USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci)
|
||||||
{
|
{
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
struct USBBtState *s;
|
struct USBBtState *s;
|
||||||
|
|
||||||
if (!hci)
|
if (!hci)
|
||||||
return NULL;
|
return NULL;
|
||||||
dev = usb_create_simple(NULL /* FIXME */, "usb-bt-dongle");
|
dev = usb_create_simple(bus, "usb-bt-dongle");
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
18
hw/usb-bus.c
18
hw/usb-bus.c
|
@ -203,13 +203,14 @@ typedef struct LegacyUSBFactory
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *usbdevice_name;
|
const char *usbdevice_name;
|
||||||
USBDevice *(*usbdevice_init)(const char *params);
|
USBDevice *(*usbdevice_init)(USBBus *bus, const char *params);
|
||||||
} LegacyUSBFactory;
|
} LegacyUSBFactory;
|
||||||
|
|
||||||
static GSList *legacy_usb_factory;
|
static GSList *legacy_usb_factory;
|
||||||
|
|
||||||
void usb_legacy_register(const char *typename, const char *usbdevice_name,
|
void usb_legacy_register(const char *typename, const char *usbdevice_name,
|
||||||
USBDevice *(*usbdevice_init)(const char *params))
|
USBDevice *(*usbdevice_init)(USBBus *bus,
|
||||||
|
const char *params))
|
||||||
{
|
{
|
||||||
if (usbdevice_name) {
|
if (usbdevice_name) {
|
||||||
LegacyUSBFactory *f = g_malloc0(sizeof(*f));
|
LegacyUSBFactory *f = g_malloc0(sizeof(*f));
|
||||||
|
@ -224,17 +225,6 @@ USBDevice *usb_create(USBBus *bus, const char *name)
|
||||||
{
|
{
|
||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
|
|
||||||
#if 1
|
|
||||||
/* temporary stopgap until all usb is properly qdev-ified */
|
|
||||||
if (!bus) {
|
|
||||||
bus = usb_bus_find(-1);
|
|
||||||
if (!bus)
|
|
||||||
return NULL;
|
|
||||||
error_report("%s: no bus specified, using \"%s\" for \"%s\"",
|
|
||||||
__FUNCTION__, bus->qbus.name, name);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
dev = qdev_create(&bus->qbus, name);
|
dev = qdev_create(&bus->qbus, name);
|
||||||
return USB_DEVICE(dev);
|
return USB_DEVICE(dev);
|
||||||
}
|
}
|
||||||
|
@ -565,7 +555,7 @@ USBDevice *usbdevice_create(const char *cmdline)
|
||||||
}
|
}
|
||||||
return usb_create_simple(bus, f->name);
|
return usb_create_simple(bus, f->name);
|
||||||
}
|
}
|
||||||
return f->usbdevice_init(params);
|
return f->usbdevice_init(bus, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_device_class_init(ObjectClass *klass, void *data)
|
static void usb_device_class_init(ObjectClass *klass, void *data)
|
||||||
|
|
|
@ -447,7 +447,7 @@ static const USBDescDevice desc_device = {
|
||||||
{
|
{
|
||||||
.bNumInterfaces = 1,
|
.bNumInterfaces = 1,
|
||||||
.bConfigurationValue = 1,
|
.bConfigurationValue = 1,
|
||||||
.bmAttributes = 0xa0,
|
.bmAttributes = 0xe0,
|
||||||
.bMaxPower = 50,
|
.bMaxPower = 50,
|
||||||
.nif = 1,
|
.nif = 1,
|
||||||
.ifs = &desc_iface0,
|
.ifs = &desc_iface0,
|
||||||
|
|
|
@ -536,7 +536,11 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
||||||
data[0] = dev->config->bConfigurationValue;
|
/*
|
||||||
|
* 9.4.2: 0 should be returned if the device is unconfigured, otherwise
|
||||||
|
* the non zero value of bConfigurationValue.
|
||||||
|
*/
|
||||||
|
data[0] = dev->config ? dev->config->bConfigurationValue : 0;
|
||||||
ret = 1;
|
ret = 1;
|
||||||
break;
|
break;
|
||||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||||
|
@ -544,9 +548,18 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
||||||
trace_usb_set_config(dev->addr, value, ret);
|
trace_usb_set_config(dev->addr, value, ret);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
case DeviceRequest | USB_REQ_GET_STATUS: {
|
||||||
|
const USBDescConfig *config = dev->config ?
|
||||||
|
dev->config : &dev->device->confs[0];
|
||||||
|
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
if (dev->config->bmAttributes & 0x40) {
|
/*
|
||||||
|
* Default state: Device behavior when this request is received while
|
||||||
|
* the device is in the Default state is not specified.
|
||||||
|
* We return the same value that a configured device would return if
|
||||||
|
* it used the first configuration.
|
||||||
|
*/
|
||||||
|
if (config->bmAttributes & 0x40) {
|
||||||
data[0] |= 1 << USB_DEVICE_SELF_POWERED;
|
data[0] |= 1 << USB_DEVICE_SELF_POWERED;
|
||||||
}
|
}
|
||||||
if (dev->remote_wakeup) {
|
if (dev->remote_wakeup) {
|
||||||
|
@ -555,6 +568,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
||||||
data[1] = 0x00;
|
data[1] = 0x00;
|
||||||
ret = 2;
|
ret = 2;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
||||||
dev->remote_wakeup = 0;
|
dev->remote_wakeup = 0;
|
||||||
|
|
|
@ -912,6 +912,7 @@ static void ehci_reset(void *opaque)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ehci_queues_rip_all(s);
|
ehci_queues_rip_all(s);
|
||||||
|
qemu_del_timer(s->frame_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
|
static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
|
||||||
|
@ -1070,7 +1071,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
|
||||||
|
|
||||||
if (val & USBCMD_HCRESET) {
|
if (val & USBCMD_HCRESET) {
|
||||||
ehci_reset(s);
|
ehci_reset(s);
|
||||||
val &= ~USBCMD_HCRESET;
|
val = s->usbcmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* not supporting dynamic frame list size at the moment */
|
/* not supporting dynamic frame list size at the moment */
|
||||||
|
@ -1458,44 +1459,22 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||||
|
|
||||||
dev = ehci_find_device(ehci, devaddr);
|
dev = ehci_find_device(ehci, devaddr);
|
||||||
ep = usb_ep_get(dev, pid, endp);
|
ep = usb_ep_get(dev, pid, endp);
|
||||||
usb_packet_setup(&ehci->ipacket, pid, ep);
|
if (ep->type == USB_ENDPOINT_XFER_ISOC) {
|
||||||
usb_packet_map(&ehci->ipacket, &ehci->isgl);
|
usb_packet_setup(&ehci->ipacket, pid, ep);
|
||||||
|
usb_packet_map(&ehci->ipacket, &ehci->isgl);
|
||||||
ret = usb_handle_packet(dev, &ehci->ipacket);
|
ret = usb_handle_packet(dev, &ehci->ipacket);
|
||||||
|
assert(ret != USB_RET_ASYNC);
|
||||||
usb_packet_unmap(&ehci->ipacket);
|
usb_packet_unmap(&ehci->ipacket);
|
||||||
|
} else {
|
||||||
|
DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
|
||||||
|
ret = USB_RET_NAK;
|
||||||
|
}
|
||||||
qemu_sglist_destroy(&ehci->isgl);
|
qemu_sglist_destroy(&ehci->isgl);
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* In isoch, there is no facility to indicate a NAK so let's
|
|
||||||
* instead just complete a zero-byte transaction. Setting
|
|
||||||
* DBERR seems too draconian.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (ret == USB_RET_NAK) {
|
|
||||||
if (ehci->isoch_pause > 0) {
|
|
||||||
DPRINTF("ISOCH: received a NAK but paused so returning\n");
|
|
||||||
ehci->isoch_pause--;
|
|
||||||
return 0;
|
|
||||||
} else if (ehci->isoch_pause == -1) {
|
|
||||||
DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
|
|
||||||
// Pause frindex for up to 50 msec waiting for data from
|
|
||||||
// remote
|
|
||||||
ehci->isoch_pause = 50;
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
DPRINTF("ISOCH: isoch pause timeout! return 0\n");
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DPRINTF("ISOCH: received ACK, clearing pause\n");
|
|
||||||
ehci->isoch_pause = -1;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (ret == USB_RET_NAK) {
|
if (ret == USB_RET_NAK) {
|
||||||
|
/* no data for us, so do a zero-length transfer */
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
|
@ -1505,11 +1484,27 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||||
/* IN */
|
/* IN */
|
||||||
set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
|
set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if (itd->transact[i] & ITD_XACT_IOC) {
|
switch (ret) {
|
||||||
ehci_record_interrupt(ehci, USBSTS_INT);
|
default:
|
||||||
|
fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
|
||||||
|
/* Fall through */
|
||||||
|
case USB_RET_NODEV:
|
||||||
|
/* 3.3.2: XACTERR is only allowed on IN transactions */
|
||||||
|
if (dir) {
|
||||||
|
itd->transact[i] |= ITD_XACT_XACTERR;
|
||||||
|
ehci_record_interrupt(ehci, USBSTS_ERRINT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case USB_RET_BABBLE:
|
||||||
|
itd->transact[i] |= ITD_XACT_BABBLE;
|
||||||
|
ehci_record_interrupt(ehci, USBSTS_ERRINT);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (itd->transact[i] & ITD_XACT_IOC) {
|
||||||
|
ehci_record_interrupt(ehci, USBSTS_INT);
|
||||||
|
}
|
||||||
itd->transact[i] &= ~ITD_XACT_ACTIVE;
|
itd->transact[i] &= ~ITD_XACT_ACTIVE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2368,8 +2363,6 @@ static int usb_ehci_initfn(PCIDevice *dev)
|
||||||
memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
|
memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
|
||||||
pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
|
pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
|
||||||
|
|
||||||
fprintf(stderr, "*** EHCI support is under development ***\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -466,6 +466,9 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
|
||||||
case USB_TOKEN_IN:
|
case USB_TOKEN_IN:
|
||||||
if (p->ep->nr == 1) {
|
if (p->ep->nr == 1) {
|
||||||
int64_t curtime = qemu_get_clock_ns(vm_clock);
|
int64_t curtime = qemu_get_clock_ns(vm_clock);
|
||||||
|
if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
|
||||||
|
hid_pointer_activate(hs);
|
||||||
|
}
|
||||||
if (!hid_has_events(hs) &&
|
if (!hid_has_events(hs) &&
|
||||||
(!hs->idle || hs->next_idle_clock - curtime > 0)) {
|
(!hs->idle || hs->next_idle_clock - curtime > 0)) {
|
||||||
return USB_RET_NAK;
|
return USB_RET_NAK;
|
||||||
|
|
|
@ -568,7 +568,7 @@ static int usb_msd_initfn(USBDevice *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static USBDevice *usb_msd_init(const char *filename)
|
static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
|
||||||
{
|
{
|
||||||
static int nr=0;
|
static int nr=0;
|
||||||
char id[8];
|
char id[8];
|
||||||
|
@ -611,7 +611,7 @@ static USBDevice *usb_msd_init(const char *filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create guest device */
|
/* create guest device */
|
||||||
dev = usb_create(NULL /* FIXME */, "usb-storage");
|
dev = usb_create(bus, "usb-storage");
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1353,7 +1353,7 @@ static int usb_net_initfn(USBDevice *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static USBDevice *usb_net_init(const char *cmdline)
|
static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
|
||||||
{
|
{
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
|
@ -1371,7 +1371,7 @@ static USBDevice *usb_net_init(const char *cmdline)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev = usb_create(NULL /* FIXME */, "usb-net");
|
dev = usb_create(bus, "usb-net");
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -492,7 +492,7 @@ static int usb_serial_initfn(USBDevice *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static USBDevice *usb_serial_init(const char *filename)
|
static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
|
||||||
{
|
{
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
CharDriverState *cdrv;
|
CharDriverState *cdrv;
|
||||||
|
@ -535,7 +535,7 @@ static USBDevice *usb_serial_init(const char *filename)
|
||||||
if (!cdrv)
|
if (!cdrv)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
dev = usb_create(NULL /* FIXME */, "usb-serial");
|
dev = usb_create(bus, "usb-serial");
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -549,7 +549,7 @@ static USBDevice *usb_serial_init(const char *filename)
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static USBDevice *usb_braille_init(const char *unused)
|
static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
|
||||||
{
|
{
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
CharDriverState *cdrv;
|
CharDriverState *cdrv;
|
||||||
|
@ -558,7 +558,7 @@ static USBDevice *usb_braille_init(const char *unused)
|
||||||
if (!cdrv)
|
if (!cdrv)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
dev = usb_create(NULL /* FIXME */, "usb-braille");
|
dev = usb_create(bus, "usb-braille");
|
||||||
qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
|
qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
|
||||||
qdev_init_nofail(&dev->qdev);
|
qdev_init_nofail(&dev->qdev);
|
||||||
|
|
||||||
|
|
324
hw/usb-uhci.c
324
hw/usb-uhci.c
|
@ -95,23 +95,32 @@ static const char *pid2str(int pid)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct UHCIState UHCIState;
|
typedef struct UHCIState UHCIState;
|
||||||
|
typedef struct UHCIAsync UHCIAsync;
|
||||||
|
typedef struct UHCIQueue UHCIQueue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pending async transaction.
|
* Pending async transaction.
|
||||||
* 'packet' must be the first field because completion
|
* 'packet' must be the first field because completion
|
||||||
* handler does "(UHCIAsync *) pkt" cast.
|
* handler does "(UHCIAsync *) pkt" cast.
|
||||||
*/
|
*/
|
||||||
typedef struct UHCIAsync {
|
|
||||||
|
struct UHCIAsync {
|
||||||
USBPacket packet;
|
USBPacket packet;
|
||||||
QEMUSGList sgl;
|
QEMUSGList sgl;
|
||||||
UHCIState *uhci;
|
UHCIQueue *queue;
|
||||||
QTAILQ_ENTRY(UHCIAsync) next;
|
QTAILQ_ENTRY(UHCIAsync) next;
|
||||||
uint32_t td;
|
uint32_t td;
|
||||||
uint32_t token;
|
|
||||||
int8_t valid;
|
|
||||||
uint8_t isoc;
|
uint8_t isoc;
|
||||||
uint8_t done;
|
uint8_t done;
|
||||||
} UHCIAsync;
|
};
|
||||||
|
|
||||||
|
struct UHCIQueue {
|
||||||
|
uint32_t token;
|
||||||
|
UHCIState *uhci;
|
||||||
|
QTAILQ_ENTRY(UHCIQueue) next;
|
||||||
|
QTAILQ_HEAD(, UHCIAsync) asyncs;
|
||||||
|
int8_t valid;
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct UHCIPort {
|
typedef struct UHCIPort {
|
||||||
USBPort port;
|
USBPort port;
|
||||||
|
@ -137,7 +146,7 @@ struct UHCIState {
|
||||||
uint32_t pending_int_mask;
|
uint32_t pending_int_mask;
|
||||||
|
|
||||||
/* Active packets */
|
/* Active packets */
|
||||||
QTAILQ_HEAD(,UHCIAsync) async_pending;
|
QTAILQ_HEAD(, UHCIQueue) queues;
|
||||||
uint8_t num_ports_vmstate;
|
uint8_t num_ports_vmstate;
|
||||||
|
|
||||||
/* Properties */
|
/* Properties */
|
||||||
|
@ -157,62 +166,90 @@ typedef struct UHCI_QH {
|
||||||
uint32_t el_link;
|
uint32_t el_link;
|
||||||
} UHCI_QH;
|
} UHCI_QH;
|
||||||
|
|
||||||
static UHCIAsync *uhci_async_alloc(UHCIState *s)
|
static inline int32_t uhci_queue_token(UHCI_TD *td)
|
||||||
{
|
{
|
||||||
UHCIAsync *async = g_malloc(sizeof(UHCIAsync));
|
/* covers ep, dev, pid -> identifies the endpoint */
|
||||||
|
return td->token & 0x7ffff;
|
||||||
|
}
|
||||||
|
|
||||||
memset(&async->packet, 0, sizeof(async->packet));
|
static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
|
||||||
async->uhci = s;
|
{
|
||||||
async->valid = 0;
|
uint32_t token = uhci_queue_token(td);
|
||||||
async->td = 0;
|
UHCIQueue *queue;
|
||||||
async->token = 0;
|
|
||||||
async->done = 0;
|
QTAILQ_FOREACH(queue, &s->queues, next) {
|
||||||
async->isoc = 0;
|
if (queue->token == token) {
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue = g_new0(UHCIQueue, 1);
|
||||||
|
queue->uhci = s;
|
||||||
|
queue->token = token;
|
||||||
|
QTAILQ_INIT(&queue->asyncs);
|
||||||
|
QTAILQ_INSERT_HEAD(&s->queues, queue, next);
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uhci_queue_free(UHCIQueue *queue)
|
||||||
|
{
|
||||||
|
UHCIState *s = queue->uhci;
|
||||||
|
|
||||||
|
QTAILQ_REMOVE(&s->queues, queue, next);
|
||||||
|
g_free(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UHCIAsync *uhci_async_alloc(UHCIQueue *queue)
|
||||||
|
{
|
||||||
|
UHCIAsync *async = g_new0(UHCIAsync, 1);
|
||||||
|
|
||||||
|
async->queue = queue;
|
||||||
usb_packet_init(&async->packet);
|
usb_packet_init(&async->packet);
|
||||||
pci_dma_sglist_init(&async->sgl, &s->dev, 1);
|
pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
|
||||||
|
|
||||||
return async;
|
return async;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_async_free(UHCIState *s, UHCIAsync *async)
|
static void uhci_async_free(UHCIAsync *async)
|
||||||
{
|
{
|
||||||
usb_packet_cleanup(&async->packet);
|
usb_packet_cleanup(&async->packet);
|
||||||
qemu_sglist_destroy(&async->sgl);
|
qemu_sglist_destroy(&async->sgl);
|
||||||
g_free(async);
|
g_free(async);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_async_link(UHCIState *s, UHCIAsync *async)
|
static void uhci_async_link(UHCIAsync *async)
|
||||||
{
|
{
|
||||||
QTAILQ_INSERT_HEAD(&s->async_pending, async, next);
|
UHCIQueue *queue = async->queue;
|
||||||
|
QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_async_unlink(UHCIState *s, UHCIAsync *async)
|
static void uhci_async_unlink(UHCIAsync *async)
|
||||||
{
|
{
|
||||||
QTAILQ_REMOVE(&s->async_pending, async, next);
|
UHCIQueue *queue = async->queue;
|
||||||
|
QTAILQ_REMOVE(&queue->asyncs, async, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_async_cancel(UHCIState *s, UHCIAsync *async)
|
static void uhci_async_cancel(UHCIAsync *async)
|
||||||
{
|
{
|
||||||
DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
|
DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
|
||||||
async->td, async->token, async->done);
|
async->td, async->token, async->done);
|
||||||
|
|
||||||
if (!async->done)
|
if (!async->done)
|
||||||
usb_cancel_packet(&async->packet);
|
usb_cancel_packet(&async->packet);
|
||||||
uhci_async_free(s, async);
|
uhci_async_free(async);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mark all outstanding async packets as invalid.
|
* Mark all outstanding async packets as invalid.
|
||||||
* This is used for canceling them when TDs are removed by the HCD.
|
* This is used for canceling them when TDs are removed by the HCD.
|
||||||
*/
|
*/
|
||||||
static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
|
static void uhci_async_validate_begin(UHCIState *s)
|
||||||
{
|
{
|
||||||
UHCIAsync *async;
|
UHCIQueue *queue;
|
||||||
|
|
||||||
QTAILQ_FOREACH(async, &s->async_pending, next) {
|
QTAILQ_FOREACH(queue, &s->queues, next) {
|
||||||
async->valid--;
|
queue->valid--;
|
||||||
}
|
}
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -220,77 +257,74 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
|
||||||
*/
|
*/
|
||||||
static void uhci_async_validate_end(UHCIState *s)
|
static void uhci_async_validate_end(UHCIState *s)
|
||||||
{
|
{
|
||||||
UHCIAsync *curr, *n;
|
UHCIQueue *queue, *n;
|
||||||
|
UHCIAsync *async;
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
|
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
|
||||||
if (curr->valid > 0) {
|
if (queue->valid > 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
uhci_async_unlink(s, curr);
|
while (!QTAILQ_EMPTY(&queue->asyncs)) {
|
||||||
uhci_async_cancel(s, curr);
|
async = QTAILQ_FIRST(&queue->asyncs);
|
||||||
|
uhci_async_unlink(async);
|
||||||
|
uhci_async_cancel(async);
|
||||||
|
}
|
||||||
|
uhci_queue_free(queue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
|
static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
|
||||||
{
|
{
|
||||||
|
UHCIQueue *queue;
|
||||||
UHCIAsync *curr, *n;
|
UHCIAsync *curr, *n;
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
|
QTAILQ_FOREACH(queue, &s->queues, next) {
|
||||||
if (!usb_packet_is_inflight(&curr->packet) ||
|
QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
|
||||||
curr->packet.ep->dev != dev) {
|
if (!usb_packet_is_inflight(&curr->packet) ||
|
||||||
continue;
|
curr->packet.ep->dev != dev) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uhci_async_unlink(curr);
|
||||||
|
uhci_async_cancel(curr);
|
||||||
}
|
}
|
||||||
uhci_async_unlink(s, curr);
|
|
||||||
uhci_async_cancel(s, curr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_async_cancel_all(UHCIState *s)
|
static void uhci_async_cancel_all(UHCIState *s)
|
||||||
{
|
{
|
||||||
|
UHCIQueue *queue;
|
||||||
UHCIAsync *curr, *n;
|
UHCIAsync *curr, *n;
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
|
QTAILQ_FOREACH(queue, &s->queues, next) {
|
||||||
uhci_async_unlink(s, curr);
|
QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
|
||||||
uhci_async_cancel(s, curr);
|
uhci_async_unlink(curr);
|
||||||
|
uhci_async_cancel(curr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token)
|
static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td)
|
||||||
{
|
{
|
||||||
|
uint32_t token = uhci_queue_token(td);
|
||||||
|
UHCIQueue *queue;
|
||||||
UHCIAsync *async;
|
UHCIAsync *async;
|
||||||
UHCIAsync *match = NULL;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
/*
|
QTAILQ_FOREACH(queue, &s->queues, next) {
|
||||||
* We're looking for the best match here. ie both td addr and token.
|
if (queue->token == token) {
|
||||||
* Otherwise we return last good match. ie just token.
|
break;
|
||||||
* It's ok to match just token because it identifies the transaction
|
|
||||||
* rather well, token includes: device addr, endpoint, size, etc.
|
|
||||||
*
|
|
||||||
* Also since we queue async transactions in reverse order by returning
|
|
||||||
* last good match we restores the order.
|
|
||||||
*
|
|
||||||
* It's expected that we wont have a ton of outstanding transactions.
|
|
||||||
* If we ever do we'd want to optimize this algorithm.
|
|
||||||
*/
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(async, &s->async_pending, next) {
|
|
||||||
if (async->token == token) {
|
|
||||||
/* Good match */
|
|
||||||
match = async;
|
|
||||||
|
|
||||||
if (async->td == addr) {
|
|
||||||
/* Best match */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
count++;
|
}
|
||||||
|
if (queue == NULL) {
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > 64)
|
QTAILQ_FOREACH(async, &queue->asyncs, next) {
|
||||||
fprintf(stderr, "uhci: warning lots of async transactions\n");
|
if (async->td == addr) {
|
||||||
|
return async;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return match;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_update_irq(UHCIState *s)
|
static void uhci_update_irq(UHCIState *s)
|
||||||
|
@ -759,8 +793,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
|
||||||
{
|
{
|
||||||
UHCIAsync *async;
|
UHCIAsync *async;
|
||||||
int len = 0, max_len;
|
int len = 0, max_len;
|
||||||
uint8_t pid, isoc;
|
uint8_t pid;
|
||||||
uint32_t token;
|
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
USBEndpoint *ep;
|
USBEndpoint *ep;
|
||||||
|
|
||||||
|
@ -768,41 +801,29 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
|
||||||
if (!(td->ctrl & TD_CTRL_ACTIVE))
|
if (!(td->ctrl & TD_CTRL_ACTIVE))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* token field is not unique for isochronous requests,
|
async = uhci_async_find_td(s, addr, td);
|
||||||
* so use the destination buffer
|
|
||||||
*/
|
|
||||||
if (td->ctrl & TD_CTRL_IOS) {
|
|
||||||
token = td->buffer;
|
|
||||||
isoc = 1;
|
|
||||||
} else {
|
|
||||||
token = td->token;
|
|
||||||
isoc = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
async = uhci_async_find_td(s, addr, token);
|
|
||||||
if (async) {
|
if (async) {
|
||||||
/* Already submitted */
|
/* Already submitted */
|
||||||
async->valid = 32;
|
async->queue->valid = 32;
|
||||||
|
|
||||||
if (!async->done)
|
if (!async->done)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
uhci_async_unlink(s, async);
|
uhci_async_unlink(async);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate new packet */
|
/* Allocate new packet */
|
||||||
async = uhci_async_alloc(s);
|
async = uhci_async_alloc(uhci_queue_get(s, td));
|
||||||
if (!async)
|
if (!async)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* valid needs to be large enough to handle 10 frame delay
|
/* valid needs to be large enough to handle 10 frame delay
|
||||||
* for initial isochronous requests
|
* for initial isochronous requests
|
||||||
*/
|
*/
|
||||||
async->valid = 32;
|
async->queue->valid = 32;
|
||||||
async->td = addr;
|
async->td = addr;
|
||||||
async->token = token;
|
async->isoc = td->ctrl & TD_CTRL_IOS;
|
||||||
async->isoc = isoc;
|
|
||||||
|
|
||||||
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
||||||
pid = td->token & 0xff;
|
pid = td->token & 0xff;
|
||||||
|
@ -827,14 +848,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* invalid pid : frame interrupted */
|
/* invalid pid : frame interrupted */
|
||||||
uhci_async_free(s, async);
|
uhci_async_free(async);
|
||||||
s->status |= UHCI_STS_HCPERR;
|
s->status |= UHCI_STS_HCPERR;
|
||||||
uhci_update_irq(s);
|
uhci_update_irq(s);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len == USB_RET_ASYNC) {
|
if (len == USB_RET_ASYNC) {
|
||||||
uhci_async_link(s, async);
|
uhci_async_link(async);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -843,14 +864,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
|
||||||
done:
|
done:
|
||||||
len = uhci_complete_td(s, td, async, int_mask);
|
len = uhci_complete_td(s, td, async, int_mask);
|
||||||
usb_packet_unmap(&async->packet);
|
usb_packet_unmap(&async->packet);
|
||||||
uhci_async_free(s, async);
|
uhci_async_free(async);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_async_complete(USBPort *port, USBPacket *packet)
|
static void uhci_async_complete(USBPort *port, USBPacket *packet)
|
||||||
{
|
{
|
||||||
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
|
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
|
||||||
UHCIState *s = async->uhci;
|
UHCIState *s = async->queue->uhci;
|
||||||
|
|
||||||
DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
|
DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
|
||||||
|
|
||||||
|
@ -865,14 +886,14 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
|
||||||
le32_to_cpus(&td.token);
|
le32_to_cpus(&td.token);
|
||||||
le32_to_cpus(&td.buffer);
|
le32_to_cpus(&td.buffer);
|
||||||
|
|
||||||
uhci_async_unlink(s, async);
|
uhci_async_unlink(async);
|
||||||
uhci_complete_td(s, &td, async, &int_mask);
|
uhci_complete_td(s, &td, async, &int_mask);
|
||||||
s->pending_int_mask |= int_mask;
|
s->pending_int_mask |= int_mask;
|
||||||
|
|
||||||
/* update the status bits of the TD */
|
/* update the status bits of the TD */
|
||||||
val = cpu_to_le32(td.ctrl);
|
val = cpu_to_le32(td.ctrl);
|
||||||
pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
|
pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
|
||||||
uhci_async_free(s, async);
|
uhci_async_free(async);
|
||||||
} else {
|
} else {
|
||||||
async->done = 1;
|
async->done = 1;
|
||||||
uhci_process_frame(s);
|
uhci_process_frame(s);
|
||||||
|
@ -921,6 +942,34 @@ static int qhdb_insert(QhDb *db, uint32_t addr)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
|
||||||
|
{
|
||||||
|
uint32_t int_mask = 0;
|
||||||
|
uint32_t plink = td->link;
|
||||||
|
uint32_t token = uhci_queue_token(td);
|
||||||
|
UHCI_TD ptd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s: -- %x\n", __func__, token);
|
||||||
|
while (is_valid(plink)) {
|
||||||
|
pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
|
||||||
|
le32_to_cpus(&ptd.link);
|
||||||
|
le32_to_cpus(&ptd.ctrl);
|
||||||
|
le32_to_cpus(&ptd.token);
|
||||||
|
le32_to_cpus(&ptd.buffer);
|
||||||
|
if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (uhci_queue_token(&ptd) != token) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret = uhci_handle_td(s, plink, &ptd, &int_mask);
|
||||||
|
assert(ret == 2); /* got USB_RET_ASYNC */
|
||||||
|
assert(int_mask == 0);
|
||||||
|
plink = ptd.link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void uhci_process_frame(UHCIState *s)
|
static void uhci_process_frame(UHCIState *s)
|
||||||
{
|
{
|
||||||
uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
|
uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
|
||||||
|
@ -1008,49 +1057,62 @@ static void uhci_process_frame(UHCIState *s)
|
||||||
pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
|
pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
switch (ret) {
|
||||||
/* interrupted frame */
|
case -1: /* interrupted frame */
|
||||||
break;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == 2 || ret == 1) {
|
|
||||||
DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
|
|
||||||
link, ret == 2 ? "pend" : "skip",
|
|
||||||
td.link, td.ctrl, td.token, curr_qh);
|
|
||||||
|
|
||||||
|
case 1: /* goto next queue */
|
||||||
|
DPRINTF("uhci: TD 0x%x skip. "
|
||||||
|
"link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
|
||||||
|
link, td.link, td.ctrl, td.token, curr_qh);
|
||||||
link = curr_qh ? qh.link : td.link;
|
link = curr_qh ? qh.link : td.link;
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
/* completed TD */
|
case 2: /* got USB_RET_ASYNC */
|
||||||
|
DPRINTF("uhci: TD 0x%x async. "
|
||||||
DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
|
"link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
|
||||||
link, td.link, td.ctrl, td.token, curr_qh);
|
link, td.link, td.ctrl, td.token, curr_qh);
|
||||||
|
if (is_valid(td.link)) {
|
||||||
link = td.link;
|
uhci_fill_queue(s, &td);
|
||||||
td_count++;
|
|
||||||
bytes_count += (td.ctrl & 0x7ff) + 1;
|
|
||||||
|
|
||||||
if (curr_qh) {
|
|
||||||
/* update QH element link */
|
|
||||||
qh.el_link = link;
|
|
||||||
val = cpu_to_le32(qh.el_link);
|
|
||||||
pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
|
|
||||||
|
|
||||||
if (!depth_first(link)) {
|
|
||||||
/* done with this QH */
|
|
||||||
|
|
||||||
DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
|
|
||||||
curr_qh, qh.link, qh.el_link);
|
|
||||||
|
|
||||||
curr_qh = 0;
|
|
||||||
link = qh.link;
|
|
||||||
}
|
}
|
||||||
|
link = curr_qh ? qh.link : td.link;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case 0: /* completed TD */
|
||||||
|
DPRINTF("uhci: TD 0x%x done. "
|
||||||
|
"link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
|
||||||
|
link, td.link, td.ctrl, td.token, curr_qh);
|
||||||
|
|
||||||
|
link = td.link;
|
||||||
|
td_count++;
|
||||||
|
bytes_count += (td.ctrl & 0x7ff) + 1;
|
||||||
|
|
||||||
|
if (curr_qh) {
|
||||||
|
/* update QH element link */
|
||||||
|
qh.el_link = link;
|
||||||
|
val = cpu_to_le32(qh.el_link);
|
||||||
|
pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
|
||||||
|
|
||||||
|
if (!depth_first(link)) {
|
||||||
|
/* done with this QH */
|
||||||
|
|
||||||
|
DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
|
||||||
|
curr_qh, qh.link, qh.el_link);
|
||||||
|
|
||||||
|
curr_qh = 0;
|
||||||
|
link = qh.link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(!"unknown return code");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* go to the next entry */
|
/* go to the next entry */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
s->pending_int_mask |= int_mask;
|
s->pending_int_mask |= int_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1148,7 +1210,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
|
||||||
}
|
}
|
||||||
s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
|
s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
|
||||||
s->num_ports_vmstate = NB_PORTS;
|
s->num_ports_vmstate = NB_PORTS;
|
||||||
QTAILQ_INIT(&s->async_pending);
|
QTAILQ_INIT(&s->queues);
|
||||||
|
|
||||||
qemu_register_reset(uhci_reset, s);
|
qemu_register_reset(uhci_reset, s);
|
||||||
|
|
||||||
|
|
|
@ -1769,12 +1769,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
|
||||||
epctx->retry = xfer;
|
epctx->retry = xfer;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Qemu usb can't handle multiple in-flight xfers.
|
|
||||||
* Stop here for now.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
hw/usb.c
27
hw/usb.c
|
@ -26,6 +26,7 @@
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
#include "iov.h"
|
#include "iov.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
void usb_attach(USBPort *port)
|
void usb_attach(USBPort *port)
|
||||||
{
|
{
|
||||||
|
@ -390,7 +391,6 @@ void usb_packet_init(USBPacket *p)
|
||||||
|
|
||||||
void usb_packet_set_state(USBPacket *p, USBPacketState state)
|
void usb_packet_set_state(USBPacket *p, USBPacketState state)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
|
||||||
static const char *name[] = {
|
static const char *name[] = {
|
||||||
[USB_PACKET_UNDEFINED] = "undef",
|
[USB_PACKET_UNDEFINED] = "undef",
|
||||||
[USB_PACKET_SETUP] = "setup",
|
[USB_PACKET_SETUP] = "setup",
|
||||||
|
@ -399,28 +399,11 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
|
||||||
[USB_PACKET_COMPLETE] = "complete",
|
[USB_PACKET_COMPLETE] = "complete",
|
||||||
[USB_PACKET_CANCELED] = "canceled",
|
[USB_PACKET_CANCELED] = "canceled",
|
||||||
};
|
};
|
||||||
static const char *rets[] = {
|
USBDevice *dev = p->ep->dev;
|
||||||
[-USB_RET_NODEV] = "NODEV",
|
USBBus *bus = usb_bus_from_device(dev);
|
||||||
[-USB_RET_NAK] = "NAK",
|
|
||||||
[-USB_RET_STALL] = "STALL",
|
|
||||||
[-USB_RET_BABBLE] = "BABBLE",
|
|
||||||
[-USB_RET_ASYNC] = "ASYNC",
|
|
||||||
};
|
|
||||||
char add[16] = "";
|
|
||||||
|
|
||||||
if (state == USB_PACKET_COMPLETE) {
|
trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr,
|
||||||
if (p->result < 0) {
|
p, name[p->state], name[state]);
|
||||||
snprintf(add, sizeof(add), " - %s", rets[-p->result]);
|
|
||||||
} else {
|
|
||||||
snprintf(add, sizeof(add), " - %d", p->result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf(stderr, "bus %s, port %s, dev %d, ep %d: packet %p: %s -> %s%s\n",
|
|
||||||
p->ep->dev->qdev.parent_bus->name,
|
|
||||||
p->ep->dev->port->path,
|
|
||||||
p->ep->dev->addr, p->ep->nr,
|
|
||||||
p, name[p->state], name[state], add);
|
|
||||||
#endif
|
|
||||||
p->state = state;
|
p->state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
hw/usb.h
7
hw/usb.h
|
@ -373,12 +373,12 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
|
||||||
int set_usb_string(uint8_t *buf, const char *str);
|
int set_usb_string(uint8_t *buf, const char *str);
|
||||||
|
|
||||||
/* usb-linux.c */
|
/* usb-linux.c */
|
||||||
USBDevice *usb_host_device_open(const char *devname);
|
USBDevice *usb_host_device_open(USBBus *bus, const char *devname);
|
||||||
int usb_host_device_close(const char *devname);
|
int usb_host_device_close(const char *devname);
|
||||||
void usb_host_info(Monitor *mon);
|
void usb_host_info(Monitor *mon);
|
||||||
|
|
||||||
/* usb-bt.c */
|
/* usb-bt.c */
|
||||||
USBDevice *usb_bt_init(HCIInfo *hci);
|
USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci);
|
||||||
|
|
||||||
/* usb ports of the VM */
|
/* usb ports of the VM */
|
||||||
|
|
||||||
|
@ -431,7 +431,8 @@ struct USBBusOps {
|
||||||
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
|
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
|
||||||
USBBus *usb_bus_find(int busnr);
|
USBBus *usb_bus_find(int busnr);
|
||||||
void usb_legacy_register(const char *typename, const char *usbdevice_name,
|
void usb_legacy_register(const char *typename, const char *usbdevice_name,
|
||||||
USBDevice *(*usbdevice_init)(const char *params));
|
USBDevice *(*usbdevice_init)(USBBus *bus,
|
||||||
|
const char *params));
|
||||||
USBDevice *usb_create(USBBus *bus, const char *name);
|
USBDevice *usb_create(USBBus *bus, const char *name);
|
||||||
USBDevice *usb_create_simple(USBBus *bus, const char *name);
|
USBDevice *usb_create_simple(USBBus *bus, const char *name);
|
||||||
USBDevice *usbdevice_create(const char *cmdline);
|
USBDevice *usbdevice_create(const char *cmdline);
|
||||||
|
|
|
@ -26,8 +26,8 @@ typedef struct VCardEmulStruct VCardEmul;
|
||||||
#define MAX_CHANNEL 4
|
#define MAX_CHANNEL 4
|
||||||
|
|
||||||
/* create an ATR with appropriate historical bytes */
|
/* create an ATR with appropriate historical bytes */
|
||||||
#define VCARD_ATR_PREFIX(size) 0x3b, 0x66+(size), 0x00, 0xff, \
|
#define VCARD_ATR_PREFIX(size) (0x3b, 0x68+(size), 0x00, 0xff, \
|
||||||
'V', 'C', 'A', 'R', 'D', '_'
|
'V', 'C', 'A', 'R', 'D', '_')
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
|
@ -227,6 +227,9 @@ sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "get flags
|
||||||
sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
|
sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
|
||||||
sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64
|
sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64
|
||||||
|
|
||||||
|
# hw/usb.c
|
||||||
|
usb_packet_state_change(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s -> %s"
|
||||||
|
|
||||||
# hw/usb-bus.c
|
# hw/usb-bus.c
|
||||||
usb_port_claim(int bus, const char *port) "bus %d, port %s"
|
usb_port_claim(int bus, const char *port) "bus %d, port %s"
|
||||||
usb_port_attach(int bus, const char *port) "bus %d, port %s"
|
usb_port_attach(int bus, const char *port) "bus %d, port %s"
|
||||||
|
|
|
@ -298,7 +298,7 @@ static int usb_host_initfn(USBDevice *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
USBDevice *usb_host_device_open(const char *devname)
|
USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname)
|
||||||
{
|
{
|
||||||
struct usb_device_info bus_info, dev_info;
|
struct usb_device_info bus_info, dev_info;
|
||||||
USBDevice *d = NULL, *ret = NULL;
|
USBDevice *d = NULL, *ret = NULL;
|
||||||
|
@ -358,7 +358,7 @@ USBDevice *usb_host_device_open(const char *devname)
|
||||||
goto fail_dfd;
|
goto fail_dfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
d = usb_create(NULL /* FIXME */, "usb-host");
|
d = usb_create(guest_bus, "usb-host");
|
||||||
dev = DO_UPCAST(USBHostDevice, dev, d);
|
dev = DO_UPCAST(USBHostDevice, dev, d);
|
||||||
|
|
||||||
if (dev_info.udi_speed == 1) {
|
if (dev_info.udi_speed == 1) {
|
||||||
|
|
|
@ -1443,13 +1443,13 @@ static void usb_host_register_types(void)
|
||||||
|
|
||||||
type_init(usb_host_register_types)
|
type_init(usb_host_register_types)
|
||||||
|
|
||||||
USBDevice *usb_host_device_open(const char *devname)
|
USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
|
||||||
{
|
{
|
||||||
struct USBAutoFilter filter;
|
struct USBAutoFilter filter;
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
dev = usb_create(NULL /* FIXME */, "usb-host");
|
dev = usb_create(bus, "usb-host");
|
||||||
|
|
||||||
if (strstr(devname, "auto:")) {
|
if (strstr(devname, "auto:")) {
|
||||||
if (parse_filter(devname, &filter) < 0) {
|
if (parse_filter(devname, &filter) < 0) {
|
||||||
|
|
46
usb-redir.c
46
usb-redir.c
|
@ -106,6 +106,7 @@ struct AsyncURB {
|
||||||
QTAILQ_ENTRY(AsyncURB)next;
|
QTAILQ_ENTRY(AsyncURB)next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
|
||||||
static void usbredir_device_connect(void *priv,
|
static void usbredir_device_connect(void *priv,
|
||||||
struct usb_redir_device_connect_header *device_connect);
|
struct usb_redir_device_connect_header *device_connect);
|
||||||
static void usbredir_device_disconnect(void *priv);
|
static void usbredir_device_disconnect(void *priv);
|
||||||
|
@ -430,7 +431,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
|
||||||
/* Check iso_error for stream errors, otherwise its an underrun */
|
/* Check iso_error for stream errors, otherwise its an underrun */
|
||||||
status = dev->endpoint[EP2I(ep)].iso_error;
|
status = dev->endpoint[EP2I(ep)].iso_error;
|
||||||
dev->endpoint[EP2I(ep)].iso_error = 0;
|
dev->endpoint[EP2I(ep)].iso_error = 0;
|
||||||
return usbredir_handle_status(dev, status, 0);
|
return status ? USB_RET_NAK : 0;
|
||||||
}
|
}
|
||||||
DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
|
DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
|
||||||
isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
|
isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
|
||||||
|
@ -438,7 +439,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
|
||||||
status = isop->status;
|
status = isop->status;
|
||||||
if (status != usb_redir_success) {
|
if (status != usb_redir_success) {
|
||||||
bufp_free(dev, isop, ep);
|
bufp_free(dev, isop, ep);
|
||||||
return usbredir_handle_status(dev, status, 0);
|
return USB_RET_NAK;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = isop->len;
|
len = isop->len;
|
||||||
|
@ -547,7 +548,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
|
||||||
/* Check interrupt_error for stream errors */
|
/* Check interrupt_error for stream errors */
|
||||||
status = dev->endpoint[EP2I(ep)].interrupt_error;
|
status = dev->endpoint[EP2I(ep)].interrupt_error;
|
||||||
dev->endpoint[EP2I(ep)].interrupt_error = 0;
|
dev->endpoint[EP2I(ep)].interrupt_error = 0;
|
||||||
return usbredir_handle_status(dev, status, 0);
|
if (status) {
|
||||||
|
return usbredir_handle_status(dev, status, 0);
|
||||||
|
}
|
||||||
|
return USB_RET_NAK;
|
||||||
}
|
}
|
||||||
DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
|
DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
|
||||||
intp->status, intp->len);
|
intp->status, intp->len);
|
||||||
|
@ -802,6 +806,7 @@ static void usbredir_open_close_bh(void *opaque)
|
||||||
dev->parser->log_func = usbredir_log;
|
dev->parser->log_func = usbredir_log;
|
||||||
dev->parser->read_func = usbredir_read;
|
dev->parser->read_func = usbredir_read;
|
||||||
dev->parser->write_func = usbredir_write;
|
dev->parser->write_func = usbredir_write;
|
||||||
|
dev->parser->hello_func = usbredir_hello;
|
||||||
dev->parser->device_connect_func = usbredir_device_connect;
|
dev->parser->device_connect_func = usbredir_device_connect;
|
||||||
dev->parser->device_disconnect_func = usbredir_device_disconnect;
|
dev->parser->device_disconnect_func = usbredir_device_disconnect;
|
||||||
dev->parser->interface_info_func = usbredir_interface_info;
|
dev->parser->interface_info_func = usbredir_interface_info;
|
||||||
|
@ -820,6 +825,7 @@ static void usbredir_open_close_bh(void *opaque)
|
||||||
dev->read_buf_size = 0;
|
dev->read_buf_size = 0;
|
||||||
|
|
||||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
|
usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
|
||||||
|
usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
|
||||||
usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0);
|
usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0);
|
||||||
usbredirparser_do_write(dev->parser);
|
usbredirparser_do_write(dev->parser);
|
||||||
}
|
}
|
||||||
|
@ -958,7 +964,7 @@ static int usbredir_check_filter(USBRedirDevice *dev)
|
||||||
{
|
{
|
||||||
if (dev->interface_info.interface_count == 0) {
|
if (dev->interface_info.interface_count == 0) {
|
||||||
ERROR("No interface info for device\n");
|
ERROR("No interface info for device\n");
|
||||||
return -1;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->filter_rules) {
|
if (dev->filter_rules) {
|
||||||
|
@ -966,7 +972,7 @@ static int usbredir_check_filter(USBRedirDevice *dev)
|
||||||
usb_redir_cap_connect_device_version)) {
|
usb_redir_cap_connect_device_version)) {
|
||||||
ERROR("Device filter specified and peer does not have the "
|
ERROR("Device filter specified and peer does not have the "
|
||||||
"connect_device_version capability\n");
|
"connect_device_version capability\n");
|
||||||
return -1;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usbredirfilter_check(
|
if (usbredirfilter_check(
|
||||||
|
@ -983,11 +989,19 @@ static int usbredir_check_filter(USBRedirDevice *dev)
|
||||||
dev->device_info.product_id,
|
dev->device_info.product_id,
|
||||||
dev->device_info.device_version_bcd,
|
dev->device_info.device_version_bcd,
|
||||||
0) != 0) {
|
0) != 0) {
|
||||||
return -1;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
usbredir_device_disconnect(dev);
|
||||||
|
if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
|
||||||
|
usbredirparser_send_filter_reject(dev->parser);
|
||||||
|
usbredirparser_do_write(dev->parser);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1012,6 +1026,19 @@ static int usbredir_handle_status(USBRedirDevice *dev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void usbredir_hello(void *priv, struct usb_redir_hello_header *h)
|
||||||
|
{
|
||||||
|
USBRedirDevice *dev = priv;
|
||||||
|
|
||||||
|
/* Try to send the filter info now that we've the usb-host's caps */
|
||||||
|
if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) &&
|
||||||
|
dev->filter_rules) {
|
||||||
|
usbredirparser_send_filter_filter(dev->parser, dev->filter_rules,
|
||||||
|
dev->filter_rules_count);
|
||||||
|
usbredirparser_do_write(dev->parser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void usbredir_device_connect(void *priv,
|
static void usbredir_device_connect(void *priv,
|
||||||
struct usb_redir_device_connect_header *device_connect)
|
struct usb_redir_device_connect_header *device_connect)
|
||||||
{
|
{
|
||||||
|
@ -1049,8 +1076,10 @@ static void usbredir_device_connect(void *priv,
|
||||||
usb_redir_cap_connect_device_version)) {
|
usb_redir_cap_connect_device_version)) {
|
||||||
INFO("attaching %s device %04x:%04x version %d.%d class %02x\n",
|
INFO("attaching %s device %04x:%04x version %d.%d class %02x\n",
|
||||||
speed, device_connect->vendor_id, device_connect->product_id,
|
speed, device_connect->vendor_id, device_connect->product_id,
|
||||||
device_connect->device_version_bcd >> 8,
|
((device_connect->device_version_bcd & 0xf000) >> 12) * 10 +
|
||||||
device_connect->device_version_bcd & 0xff,
|
((device_connect->device_version_bcd & 0x0f00) >> 8),
|
||||||
|
((device_connect->device_version_bcd & 0x00f0) >> 4) * 10 +
|
||||||
|
((device_connect->device_version_bcd & 0x000f) >> 0),
|
||||||
device_connect->device_class);
|
device_connect->device_class);
|
||||||
} else {
|
} else {
|
||||||
INFO("attaching %s device %04x:%04x class %02x\n", speed,
|
INFO("attaching %s device %04x:%04x class %02x\n", speed,
|
||||||
|
@ -1111,7 +1140,6 @@ static void usbredir_interface_info(void *priv,
|
||||||
if (usbredir_check_filter(dev)) {
|
if (usbredir_check_filter(dev)) {
|
||||||
ERROR("Device no longer matches filter after interface info "
|
ERROR("Device no longer matches filter after interface info "
|
||||||
"change, disconnecting!\n");
|
"change, disconnecting!\n");
|
||||||
usbredir_device_disconnect(dev);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
7
vl.c
7
vl.c
|
@ -1052,12 +1052,13 @@ static int usb_device_add(const char *devname)
|
||||||
#ifndef CONFIG_LINUX
|
#ifndef CONFIG_LINUX
|
||||||
/* only the linux version is qdev-ified, usb-bsd still needs this */
|
/* only the linux version is qdev-ified, usb-bsd still needs this */
|
||||||
if (strstart(devname, "host:", &p)) {
|
if (strstart(devname, "host:", &p)) {
|
||||||
dev = usb_host_device_open(p);
|
dev = usb_host_device_open(usb_bus_find(-1), p);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) {
|
if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) {
|
||||||
dev = usb_bt_init(devname[2] ? hci_init(p) :
|
dev = usb_bt_init(usb_bus_find(-1),
|
||||||
bt_new_hci(qemu_find_bt_vlan(0)));
|
devname[2] ? hci_init(p)
|
||||||
|
: bt_new_hci(qemu_find_bt_vlan(0)));
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue