diff options
Diffstat (limited to 'qemu/hw/usb')
40 files changed, 0 insertions, 34379 deletions
diff --git a/qemu/hw/usb/Makefile.objs b/qemu/hw/usb/Makefile.objs deleted file mode 100644 index 2717027d3..000000000 --- a/qemu/hw/usb/Makefile.objs +++ /dev/null @@ -1,40 +0,0 @@ -# usb subsystem core -common-obj-y += core.o combined-packet.o bus.o libhw.o -common-obj-$(CONFIG_USB) += desc.o desc-msos.o - -# usb host adapters -common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o -common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o -common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o -common-obj-$(CONFIG_USB_EHCI_SYSBUS) += hcd-ehci-sysbus.o -common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o -common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o - -obj-$(CONFIG_TUSB6010) += tusb6010.o - -# emulated usb devices -common-obj-$(CONFIG_USB) += dev-hub.o -common-obj-$(CONFIG_USB) += dev-hid.o -common-obj-$(CONFIG_USB_TABLET_WACOM) += dev-wacom.o -common-obj-$(CONFIG_USB_STORAGE_BOT) += dev-storage.o -common-obj-$(CONFIG_USB_STORAGE_UAS) += dev-uas.o -common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o -common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o -common-obj-$(CONFIG_USB_NETWORK) += dev-network.o -common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o - -ifeq ($(CONFIG_USB_SMARTCARD),y) -common-obj-y += dev-smartcard-reader.o -common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o -common-obj-$(CONFIG_SMARTCARD) += ccid-card-emulated.o -endif - -ifeq ($(CONFIG_POSIX),y) -common-obj-$(CONFIG_USB_STORAGE_MTP) += dev-mtp.o -endif - -# usb redirection -common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o - -# usb pass-through -common-obj-y += $(patsubst %,host-%.o,$(HOST_USB)) diff --git a/qemu/hw/usb/bus.c b/qemu/hw/usb/bus.c deleted file mode 100644 index 16c3461d9..000000000 --- a/qemu/hw/usb/bus.c +++ /dev/null @@ -1,763 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/usb.h" -#include "hw/qdev.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "sysemu/sysemu.h" -#include "monitor/monitor.h" -#include "trace.h" -#include "qemu/cutils.h" - -static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); - -static char *usb_get_dev_path(DeviceState *dev); -static char *usb_get_fw_dev_path(DeviceState *qdev); -static void usb_qdev_unrealize(DeviceState *qdev, Error **errp); - -static Property usb_props[] = { - DEFINE_PROP_STRING("port", USBDevice, port_path), - DEFINE_PROP_STRING("serial", USBDevice, serial), - DEFINE_PROP_BIT("full-path", USBDevice, flags, - USB_DEV_FLAG_FULL_PATH, true), - DEFINE_PROP_BIT("msos-desc", USBDevice, flags, - USB_DEV_FLAG_MSOS_DESC_ENABLE, true), - DEFINE_PROP_END_OF_LIST() -}; - -static void usb_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - k->print_dev = usb_bus_dev_print; - k->get_dev_path = usb_get_dev_path; - k->get_fw_dev_path = usb_get_fw_dev_path; - hc->unplug = qdev_simple_device_unplug_cb; -} - -static const TypeInfo usb_bus_info = { - .name = TYPE_USB_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(USBBus), - .class_init = usb_bus_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static int next_usb_bus = 0; -static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses); - -static int usb_device_post_load(void *opaque, int version_id) -{ - USBDevice *dev = opaque; - - if (dev->state == USB_STATE_NOTATTACHED) { - dev->attached = 0; - } else { - dev->attached = 1; - } - if (dev->setup_index < 0 || - dev->setup_len < 0 || - dev->setup_index > dev->setup_len || - dev->setup_len > sizeof(dev->data_buf)) { - return -EINVAL; - } - return 0; -} - -const VMStateDescription vmstate_usb_device = { - .name = "USBDevice", - .version_id = 1, - .minimum_version_id = 1, - .post_load = usb_device_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(addr, USBDevice), - VMSTATE_INT32(state, USBDevice), - VMSTATE_INT32(remote_wakeup, USBDevice), - VMSTATE_INT32(setup_state, USBDevice), - VMSTATE_INT32(setup_len, USBDevice), - VMSTATE_INT32(setup_index, USBDevice), - VMSTATE_UINT8_ARRAY(setup_buf, USBDevice, 8), - VMSTATE_END_OF_LIST(), - } -}; - -void usb_bus_new(USBBus *bus, size_t bus_size, - USBBusOps *ops, DeviceState *host) -{ - qbus_create_inplace(bus, bus_size, TYPE_USB_BUS, host, NULL); - qbus_set_bus_hotplug_handler(BUS(bus), &error_abort); - bus->ops = ops; - bus->busnr = next_usb_bus++; - QTAILQ_INIT(&bus->free); - QTAILQ_INIT(&bus->used); - QTAILQ_INSERT_TAIL(&busses, bus, next); -} - -void usb_bus_release(USBBus *bus) -{ - assert(next_usb_bus > 0); - - QTAILQ_REMOVE(&busses, bus, next); -} - -USBBus *usb_bus_find(int busnr) -{ - USBBus *bus; - - if (-1 == busnr) - return QTAILQ_FIRST(&busses); - QTAILQ_FOREACH(bus, &busses, next) { - if (bus->busnr == busnr) - return bus; - } - return NULL; -} - -static void usb_device_realize(USBDevice *dev, Error **errp) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - - if (klass->realize) { - klass->realize(dev, errp); - } -} - -USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->find_device) { - return klass->find_device(dev, addr); - } - return NULL; -} - -static void usb_device_handle_destroy(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_destroy) { - klass->handle_destroy(dev); - } -} - -void usb_device_cancel_packet(USBDevice *dev, USBPacket *p) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->cancel_packet) { - klass->cancel_packet(dev, p); - } -} - -void usb_device_handle_attach(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_attach) { - klass->handle_attach(dev); - } -} - -void usb_device_handle_reset(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_reset) { - klass->handle_reset(dev); - } -} - -void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request, - int value, int index, int length, uint8_t *data) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_control) { - klass->handle_control(dev, p, request, value, index, length, data); - } -} - -void usb_device_handle_data(USBDevice *dev, USBPacket *p) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_data) { - klass->handle_data(dev, p); - } -} - -const char *usb_device_get_product_desc(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - return klass->product_desc; -} - -const USBDesc *usb_device_get_usb_desc(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (dev->usb_desc) { - return dev->usb_desc; - } - return klass->usb_desc; -} - -void usb_device_set_interface(USBDevice *dev, int interface, - int alt_old, int alt_new) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->set_interface) { - klass->set_interface(dev, interface, alt_old, alt_new); - } -} - -void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->flush_ep_queue) { - klass->flush_ep_queue(dev, ep); - } -} - -void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->ep_stopped) { - klass->ep_stopped(dev, ep); - } -} - -int usb_device_alloc_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps, - int streams) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->alloc_streams) { - return klass->alloc_streams(dev, eps, nr_eps, streams); - } - return 0; -} - -void usb_device_free_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->free_streams) { - klass->free_streams(dev, eps, nr_eps); - } -} - -static void usb_qdev_realize(DeviceState *qdev, Error **errp) -{ - USBDevice *dev = USB_DEVICE(qdev); - Error *local_err = NULL; - - pstrcpy(dev->product_desc, sizeof(dev->product_desc), - usb_device_get_product_desc(dev)); - dev->auto_attach = 1; - QLIST_INIT(&dev->strings); - usb_ep_init(dev); - - usb_claim_port(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - usb_device_realize(dev, &local_err); - if (local_err) { - usb_release_port(dev); - error_propagate(errp, local_err); - return; - } - - if (dev->auto_attach) { - usb_device_attach(dev, &local_err); - if (local_err) { - usb_qdev_unrealize(qdev, NULL); - error_propagate(errp, local_err); - return; - } - } -} - -static void usb_qdev_unrealize(DeviceState *qdev, Error **errp) -{ - USBDevice *dev = USB_DEVICE(qdev); - - if (dev->attached) { - usb_device_detach(dev); - } - usb_device_handle_destroy(dev); - if (dev->port) { - usb_release_port(dev); - } -} - -typedef struct LegacyUSBFactory -{ - const char *name; - const char *usbdevice_name; - USBDevice *(*usbdevice_init)(USBBus *bus, const char *params); -} LegacyUSBFactory; - -static GSList *legacy_usb_factory; - -void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(USBBus *bus, - const char *params)) -{ - if (usbdevice_name) { - LegacyUSBFactory *f = g_malloc0(sizeof(*f)); - f->name = typename; - f->usbdevice_name = usbdevice_name; - f->usbdevice_init = usbdevice_init; - legacy_usb_factory = g_slist_append(legacy_usb_factory, f); - } -} - -USBDevice *usb_create(USBBus *bus, const char *name) -{ - DeviceState *dev; - - dev = qdev_create(&bus->qbus, name); - return USB_DEVICE(dev); -} - -static USBDevice *usb_try_create_simple(USBBus *bus, const char *name, - Error **errp) -{ - Error *err = NULL; - USBDevice *dev; - - dev = USB_DEVICE(qdev_try_create(&bus->qbus, name)); - if (!dev) { - error_setg(errp, "Failed to create USB device '%s'", name); - return NULL; - } - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - error_prepend(errp, "Failed to initialize USB device '%s': ", - name); - object_unparent(OBJECT(dev)); - return NULL; - } - return dev; -} - -USBDevice *usb_create_simple(USBBus *bus, const char *name) -{ - return usb_try_create_simple(bus, name, &error_abort); -} - -static void usb_fill_port(USBPort *port, void *opaque, int index, - USBPortOps *ops, int speedmask) -{ - port->opaque = opaque; - port->index = index; - port->ops = ops; - port->speedmask = speedmask; - usb_port_location(port, NULL, index + 1); -} - -void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, - USBPortOps *ops, int speedmask) -{ - usb_fill_port(port, opaque, index, ops, speedmask); - QTAILQ_INSERT_TAIL(&bus->free, port, next); - bus->nfree++; -} - -void usb_register_companion(const char *masterbus, USBPort *ports[], - uint32_t portcount, uint32_t firstport, - void *opaque, USBPortOps *ops, int speedmask, - Error **errp) -{ - USBBus *bus; - int i; - - QTAILQ_FOREACH(bus, &busses, next) { - if (strcmp(bus->qbus.name, masterbus) == 0) { - break; - } - } - - if (!bus) { - error_setg(errp, "USB bus '%s' not found", masterbus); - return; - } - if (!bus->ops->register_companion) { - error_setg(errp, "Can't use USB bus '%s' as masterbus," - " it doesn't support companion controllers", - masterbus); - return; - } - - for (i = 0; i < portcount; i++) { - usb_fill_port(ports[i], opaque, i, ops, speedmask); - } - - bus->ops->register_companion(bus, ports, portcount, firstport, errp); -} - -void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) -{ - if (upstream) { - snprintf(downstream->path, sizeof(downstream->path), "%s.%d", - upstream->path, portnr); - downstream->hubcount = upstream->hubcount + 1; - } else { - snprintf(downstream->path, sizeof(downstream->path), "%d", portnr); - downstream->hubcount = 0; - } -} - -void usb_unregister_port(USBBus *bus, USBPort *port) -{ - if (port->dev) { - object_unparent(OBJECT(port->dev)); - } - QTAILQ_REMOVE(&bus->free, port, next); - bus->nfree--; -} - -void usb_claim_port(USBDevice *dev, Error **errp) -{ - USBBus *bus = usb_bus_from_device(dev); - USBPort *port; - - assert(dev->port == NULL); - - if (dev->port_path) { - QTAILQ_FOREACH(port, &bus->free, next) { - if (strcmp(port->path, dev->port_path) == 0) { - break; - } - } - if (port == NULL) { - error_setg(errp, "usb port %s (bus %s) not found (in use?)", - dev->port_path, bus->qbus.name); - return; - } - } else { - if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) { - /* Create a new hub and chain it on */ - usb_try_create_simple(bus, "usb-hub", NULL); - } - if (bus->nfree == 0) { - error_setg(errp, "tried to attach usb device %s to a bus " - "with no free ports", dev->product_desc); - return; - } - port = QTAILQ_FIRST(&bus->free); - } - trace_usb_port_claim(bus->busnr, port->path); - - QTAILQ_REMOVE(&bus->free, port, next); - bus->nfree--; - - dev->port = port; - port->dev = dev; - - QTAILQ_INSERT_TAIL(&bus->used, port, next); - bus->nused++; -} - -void usb_release_port(USBDevice *dev) -{ - USBBus *bus = usb_bus_from_device(dev); - USBPort *port = dev->port; - - assert(port != NULL); - trace_usb_port_release(bus->busnr, port->path); - - QTAILQ_REMOVE(&bus->used, port, next); - bus->nused--; - - dev->port = NULL; - port->dev = NULL; - - QTAILQ_INSERT_TAIL(&bus->free, port, next); - bus->nfree++; -} - -static void usb_mask_to_str(char *dest, size_t size, - unsigned int speedmask) -{ - static const struct { - unsigned int mask; - const char *name; - } speeds[] = { - { .mask = USB_SPEED_MASK_FULL, .name = "full" }, - { .mask = USB_SPEED_MASK_HIGH, .name = "high" }, - { .mask = USB_SPEED_MASK_SUPER, .name = "super" }, - }; - int i, pos = 0; - - for (i = 0; i < ARRAY_SIZE(speeds); i++) { - if (speeds[i].mask & speedmask) { - pos += snprintf(dest + pos, size - pos, "%s%s", - pos ? "+" : "", - speeds[i].name); - } - } -} - -void usb_check_attach(USBDevice *dev, Error **errp) -{ - USBBus *bus = usb_bus_from_device(dev); - USBPort *port = dev->port; - char devspeed[32], portspeed[32]; - - assert(port != NULL); - assert(!dev->attached); - usb_mask_to_str(devspeed, sizeof(devspeed), dev->speedmask); - usb_mask_to_str(portspeed, sizeof(portspeed), port->speedmask); - trace_usb_port_attach(bus->busnr, port->path, - devspeed, portspeed); - - if (!(port->speedmask & dev->speedmask)) { - error_setg(errp, "Warning: speed mismatch trying to attach" - " usb device \"%s\" (%s speed)" - " to bus \"%s\", port \"%s\" (%s speed)", - dev->product_desc, devspeed, - bus->qbus.name, port->path, portspeed); - return; - } -} - -void usb_device_attach(USBDevice *dev, Error **errp) -{ - USBPort *port = dev->port; - Error *local_err = NULL; - - usb_check_attach(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - dev->attached++; - usb_attach(port); -} - -int usb_device_detach(USBDevice *dev) -{ - USBBus *bus = usb_bus_from_device(dev); - USBPort *port = dev->port; - - assert(port != NULL); - assert(dev->attached); - trace_usb_port_detach(bus->busnr, port->path); - - usb_detach(port); - dev->attached--; - return 0; -} - -int usb_device_delete_addr(int busnr, int addr) -{ - USBBus *bus; - USBPort *port; - USBDevice *dev; - - bus = usb_bus_find(busnr); - if (!bus) - return -1; - - QTAILQ_FOREACH(port, &bus->used, next) { - if (port->dev->addr == addr) - break; - } - if (!port) - return -1; - dev = port->dev; - - object_unparent(OBJECT(dev)); - return 0; -} - -static const char *usb_speed(unsigned int speed) -{ - static const char *txt[] = { - [ USB_SPEED_LOW ] = "1.5", - [ USB_SPEED_FULL ] = "12", - [ USB_SPEED_HIGH ] = "480", - [ USB_SPEED_SUPER ] = "5000", - }; - if (speed >= ARRAY_SIZE(txt)) - return "?"; - return txt[speed]; -} - -static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) -{ - USBDevice *dev = USB_DEVICE(qdev); - USBBus *bus = usb_bus_from_device(dev); - - monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n", - indent, "", bus->busnr, dev->addr, - dev->port ? dev->port->path : "-", - usb_speed(dev->speed), dev->product_desc, - dev->attached ? ", attached" : ""); -} - -static char *usb_get_dev_path(DeviceState *qdev) -{ - USBDevice *dev = USB_DEVICE(qdev); - DeviceState *hcd = qdev->parent_bus->parent; - char *id = NULL; - - if (dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) { - id = qdev_get_dev_path(hcd); - } - if (id) { - char *ret = g_strdup_printf("%s/%s", id, dev->port->path); - g_free(id); - return ret; - } else { - return g_strdup(dev->port->path); - } -} - -static char *usb_get_fw_dev_path(DeviceState *qdev) -{ - USBDevice *dev = USB_DEVICE(qdev); - char *fw_path, *in; - ssize_t pos = 0, fw_len; - long nr; - - fw_len = 32 + strlen(dev->port->path) * 6; - fw_path = g_malloc(fw_len); - in = dev->port->path; - while (fw_len - pos > 0) { - nr = strtol(in, &in, 10); - if (in[0] == '.') { - /* some hub between root port and device */ - pos += snprintf(fw_path + pos, fw_len - pos, "hub@%lx/", nr); - in++; - } else { - /* the device itself */ - pos += snprintf(fw_path + pos, fw_len - pos, "%s@%lx", - qdev_fw_name(qdev), nr); - break; - } - } - return fw_path; -} - -void hmp_info_usb(Monitor *mon, const QDict *qdict) -{ - USBBus *bus; - USBDevice *dev; - USBPort *port; - - if (QTAILQ_EMPTY(&busses)) { - monitor_printf(mon, "USB support not enabled\n"); - return; - } - - QTAILQ_FOREACH(bus, &busses, next) { - QTAILQ_FOREACH(port, &bus->used, next) { - dev = port->dev; - if (!dev) - continue; - monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, " - "Product %s%s%s\n", - bus->busnr, dev->addr, port->path, - usb_speed(dev->speed), dev->product_desc, - dev->qdev.id ? ", ID: " : "", - dev->qdev.id ?: ""); - } - } -} - -/* handle legacy -usbdevice cmd line option */ -USBDevice *usbdevice_create(const char *cmdline) -{ - USBBus *bus = usb_bus_find(-1 /* any */); - LegacyUSBFactory *f = NULL; - Error *err = NULL; - GSList *i; - char driver[32]; - const char *params; - int len; - USBDevice *dev; - - params = strchr(cmdline,':'); - if (params) { - params++; - len = params - cmdline; - if (len > sizeof(driver)) - len = sizeof(driver); - pstrcpy(driver, len, cmdline); - } else { - params = ""; - pstrcpy(driver, sizeof(driver), cmdline); - } - - for (i = legacy_usb_factory; i; i = i->next) { - f = i->data; - if (strcmp(f->usbdevice_name, driver) == 0) { - break; - } - } - if (i == NULL) { -#if 0 - /* no error because some drivers are not converted (yet) */ - error_report("usbdevice %s not found", driver); -#endif - return NULL; - } - - if (!bus) { - error_report("Error: no usb bus to attach usbdevice %s, " - "please try -machine usb=on and check that " - "the machine model supports USB", driver); - return NULL; - } - - if (f->usbdevice_init) { - dev = f->usbdevice_init(bus, params); - } else { - if (*params) { - error_report("usbdevice %s accepts no params", driver); - return NULL; - } - dev = usb_create(bus, f->name); - } - if (!dev) { - error_report("Failed to create USB device '%s'", f->name); - return NULL; - } - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err) { - error_reportf_err(err, "Failed to initialize USB device '%s': ", - f->name); - object_unparent(OBJECT(dev)); - return NULL; - } - return dev; -} - -static void usb_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->bus_type = TYPE_USB_BUS; - k->realize = usb_qdev_realize; - k->unrealize = usb_qdev_unrealize; - k->props = usb_props; -} - -static const TypeInfo usb_device_type_info = { - .name = TYPE_USB_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(USBDevice), - .abstract = true, - .class_size = sizeof(USBDeviceClass), - .class_init = usb_device_class_init, -}; - -static void usb_register_types(void) -{ - type_register_static(&usb_bus_info); - type_register_static(&usb_device_type_info); -} - -type_init(usb_register_types) diff --git a/qemu/hw/usb/ccid-card-emulated.c b/qemu/hw/usb/ccid-card-emulated.c deleted file mode 100644 index 3213f9f8a..000000000 --- a/qemu/hw/usb/ccid-card-emulated.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * CCID Card Device. Emulated card. - * - * Copyright (c) 2011 Red Hat. - * Written by Alon Levy. - * - * This code is licensed under the GNU LGPL, version 2 or later. - */ - -/* - * It can be used to provide access to the local hardware in a non exclusive - * way, or it can use certificates. It requires the usb-ccid bus. - * - * Usage 1: standard, mirror hardware reader+card: - * qemu .. -usb -device usb-ccid -device ccid-card-emulated - * - * Usage 2: use certificates, no hardware required - * one time: create the certificates: - * for i in 1 2 3; do - * certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i - * done - * qemu .. -usb -device usb-ccid \ - * -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3 - * - * If you use a non default db for the certificates you can specify it using - * the db parameter. - */ - -#include "qemu/osdep.h" -#include <eventt.h> -#include <vevent.h> -#include <vreader.h> -#include <vcard_emul.h> - -#include "qemu/thread.h" -#include "sysemu/char.h" -#include "ccid.h" - -#define DPRINTF(card, lvl, fmt, ...) \ -do {\ - if (lvl <= card->debug) {\ - printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\ - } \ -} while (0) - - -#define TYPE_EMULATED_CCID "ccid-card-emulated" -#define EMULATED_CCID_CARD(obj) \ - OBJECT_CHECK(EmulatedState, (obj), TYPE_EMULATED_CCID) - -#define BACKEND_NSS_EMULATED_NAME "nss-emulated" -#define BACKEND_CERTIFICATES_NAME "certificates" - -enum { - BACKEND_NSS_EMULATED = 1, - BACKEND_CERTIFICATES -}; - -#define DEFAULT_BACKEND BACKEND_NSS_EMULATED - -typedef struct EmulatedState EmulatedState; - -enum { - EMUL_READER_INSERT = 0, - EMUL_READER_REMOVE, - EMUL_CARD_INSERT, - EMUL_CARD_REMOVE, - EMUL_GUEST_APDU, - EMUL_RESPONSE_APDU, - EMUL_ERROR, -}; - -static const char *emul_event_to_string(uint32_t emul_event) -{ - switch (emul_event) { - case EMUL_READER_INSERT: - return "EMUL_READER_INSERT"; - case EMUL_READER_REMOVE: - return "EMUL_READER_REMOVE"; - case EMUL_CARD_INSERT: - return "EMUL_CARD_INSERT"; - case EMUL_CARD_REMOVE: - return "EMUL_CARD_REMOVE"; - case EMUL_GUEST_APDU: - return "EMUL_GUEST_APDU"; - case EMUL_RESPONSE_APDU: - return "EMUL_RESPONSE_APDU"; - case EMUL_ERROR: - return "EMUL_ERROR"; - } - return "UNKNOWN"; -} - -typedef struct EmulEvent { - QSIMPLEQ_ENTRY(EmulEvent) entry; - union { - struct { - uint32_t type; - } gen; - struct { - uint32_t type; - uint64_t code; - } error; - struct { - uint32_t type; - uint32_t len; - uint8_t data[]; - } data; - } p; -} EmulEvent; - -#define MAX_ATR_SIZE 40 -struct EmulatedState { - CCIDCardState base; - uint8_t debug; - char *backend_str; - uint32_t backend; - char *cert1; - char *cert2; - char *cert3; - char *db; - uint8_t atr[MAX_ATR_SIZE]; - uint8_t atr_length; - QSIMPLEQ_HEAD(event_list, EmulEvent) event_list; - QemuMutex event_list_mutex; - QemuThread event_thread_id; - VReader *reader; - QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; - QemuMutex vreader_mutex; /* and guest_apdu_list mutex */ - QemuMutex handle_apdu_mutex; - QemuCond handle_apdu_cond; - EventNotifier notifier; - int quit_apdu_thread; - QemuThread apdu_thread_id; -}; - -static void emulated_apdu_from_guest(CCIDCardState *base, - const uint8_t *apdu, uint32_t len) -{ - EmulatedState *card = EMULATED_CCID_CARD(base); - EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); - - assert(event); - event->p.data.type = EMUL_GUEST_APDU; - event->p.data.len = len; - memcpy(event->p.data.data, apdu, len); - qemu_mutex_lock(&card->vreader_mutex); - QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry); - qemu_mutex_unlock(&card->vreader_mutex); - qemu_mutex_lock(&card->handle_apdu_mutex); - qemu_cond_signal(&card->handle_apdu_cond); - qemu_mutex_unlock(&card->handle_apdu_mutex); -} - -static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) -{ - EmulatedState *card = EMULATED_CCID_CARD(base); - - *len = card->atr_length; - return card->atr; -} - -static void emulated_push_event(EmulatedState *card, EmulEvent *event) -{ - qemu_mutex_lock(&card->event_list_mutex); - QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry); - qemu_mutex_unlock(&card->event_list_mutex); - event_notifier_set(&card->notifier); -} - -static void emulated_push_type(EmulatedState *card, uint32_t type) -{ - EmulEvent *event = g_new(EmulEvent, 1); - - assert(event); - event->p.gen.type = type; - emulated_push_event(card, event); -} - -static void emulated_push_error(EmulatedState *card, uint64_t code) -{ - EmulEvent *event = g_new(EmulEvent, 1); - - assert(event); - event->p.error.type = EMUL_ERROR; - event->p.error.code = code; - emulated_push_event(card, event); -} - -static void emulated_push_data_type(EmulatedState *card, uint32_t type, - const uint8_t *data, uint32_t len) -{ - EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); - - assert(event); - event->p.data.type = type; - event->p.data.len = len; - memcpy(event->p.data.data, data, len); - emulated_push_event(card, event); -} - -static void emulated_push_reader_insert(EmulatedState *card) -{ - emulated_push_type(card, EMUL_READER_INSERT); -} - -static void emulated_push_reader_remove(EmulatedState *card) -{ - emulated_push_type(card, EMUL_READER_REMOVE); -} - -static void emulated_push_card_insert(EmulatedState *card, - const uint8_t *atr, uint32_t len) -{ - emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len); -} - -static void emulated_push_card_remove(EmulatedState *card) -{ - emulated_push_type(card, EMUL_CARD_REMOVE); -} - -static void emulated_push_response_apdu(EmulatedState *card, - const uint8_t *apdu, uint32_t len) -{ - emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len); -} - -#define APDU_BUF_SIZE 270 -static void *handle_apdu_thread(void* arg) -{ - EmulatedState *card = arg; - uint8_t recv_data[APDU_BUF_SIZE]; - int recv_len; - VReaderStatus reader_status; - EmulEvent *event; - - while (1) { - qemu_mutex_lock(&card->handle_apdu_mutex); - qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex); - qemu_mutex_unlock(&card->handle_apdu_mutex); - if (card->quit_apdu_thread) { - card->quit_apdu_thread = 0; /* debugging */ - break; - } - qemu_mutex_lock(&card->vreader_mutex); - while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) { - event = QSIMPLEQ_FIRST(&card->guest_apdu_list); - assert((unsigned long)event > 1000); - QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry); - if (event->p.data.type != EMUL_GUEST_APDU) { - DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n"); - g_free(event); - continue; - } - if (card->reader == NULL) { - DPRINTF(card, 1, "reader is NULL\n"); - g_free(event); - continue; - } - recv_len = sizeof(recv_data); - reader_status = vreader_xfr_bytes(card->reader, - event->p.data.data, event->p.data.len, - recv_data, &recv_len); - DPRINTF(card, 2, "got back apdu of length %d\n", recv_len); - if (reader_status == VREADER_OK) { - emulated_push_response_apdu(card, recv_data, recv_len); - } else { - emulated_push_error(card, reader_status); - } - g_free(event); - } - qemu_mutex_unlock(&card->vreader_mutex); - } - return NULL; -} - -static void *event_thread(void *arg) -{ - int atr_len = MAX_ATR_SIZE; - uint8_t atr[MAX_ATR_SIZE]; - VEvent *event = NULL; - EmulatedState *card = arg; - - while (1) { - const char *reader_name; - - event = vevent_wait_next_vevent(); - if (event == NULL || event->type == VEVENT_LAST) { - break; - } - if (event->type != VEVENT_READER_INSERT) { - if (card->reader == NULL && event->reader != NULL) { - /* Happens after device_add followed by card remove or insert. - * XXX: create synthetic add_reader events if vcard_emul_init - * already called, which happens if device_del and device_add - * are called */ - card->reader = vreader_reference(event->reader); - } else { - if (event->reader != card->reader) { - fprintf(stderr, - "ERROR: wrong reader: quiting event_thread\n"); - break; - } - } - } - switch (event->type) { - case VEVENT_READER_INSERT: - /* TODO: take a specific reader. i.e. track which reader - * we are seeing here, check it is the one we want (the first, - * or by a particular name), and ignore if we don't want it. - */ - reader_name = vreader_get_name(event->reader); - if (card->reader != NULL) { - DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n", - vreader_get_name(card->reader), reader_name); - qemu_mutex_lock(&card->vreader_mutex); - vreader_free(card->reader); - qemu_mutex_unlock(&card->vreader_mutex); - emulated_push_reader_remove(card); - } - qemu_mutex_lock(&card->vreader_mutex); - DPRINTF(card, 2, "READER INSERT %s\n", reader_name); - card->reader = vreader_reference(event->reader); - qemu_mutex_unlock(&card->vreader_mutex); - emulated_push_reader_insert(card); - break; - case VEVENT_READER_REMOVE: - DPRINTF(card, 2, " READER REMOVE: %s\n", - vreader_get_name(event->reader)); - qemu_mutex_lock(&card->vreader_mutex); - vreader_free(card->reader); - card->reader = NULL; - qemu_mutex_unlock(&card->vreader_mutex); - emulated_push_reader_remove(card); - break; - case VEVENT_CARD_INSERT: - /* get the ATR (intended as a response to a power on from the - * reader */ - atr_len = MAX_ATR_SIZE; - vreader_power_on(event->reader, atr, &atr_len); - card->atr_length = (uint8_t)atr_len; - DPRINTF(card, 2, " CARD INSERT\n"); - emulated_push_card_insert(card, atr, atr_len); - break; - case VEVENT_CARD_REMOVE: - DPRINTF(card, 2, " CARD REMOVE\n"); - emulated_push_card_remove(card); - break; - case VEVENT_LAST: /* quit */ - vevent_delete(event); - return NULL; - break; - default: - break; - } - vevent_delete(event); - } - return NULL; -} - -static void card_event_handler(EventNotifier *notifier) -{ - EmulatedState *card = container_of(notifier, EmulatedState, notifier); - EmulEvent *event, *next; - - event_notifier_test_and_clear(&card->notifier); - qemu_mutex_lock(&card->event_list_mutex); - QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) { - DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type)); - switch (event->p.gen.type) { - case EMUL_RESPONSE_APDU: - ccid_card_send_apdu_to_guest(&card->base, event->p.data.data, - event->p.data.len); - break; - case EMUL_READER_INSERT: - ccid_card_ccid_attach(&card->base); - break; - case EMUL_READER_REMOVE: - ccid_card_ccid_detach(&card->base); - break; - case EMUL_CARD_INSERT: - assert(event->p.data.len <= MAX_ATR_SIZE); - card->atr_length = event->p.data.len; - memcpy(card->atr, event->p.data.data, card->atr_length); - ccid_card_card_inserted(&card->base); - break; - case EMUL_CARD_REMOVE: - ccid_card_card_removed(&card->base); - break; - case EMUL_ERROR: - ccid_card_card_error(&card->base, event->p.error.code); - break; - default: - DPRINTF(card, 2, "unexpected event\n"); - break; - } - g_free(event); - } - QSIMPLEQ_INIT(&card->event_list); - qemu_mutex_unlock(&card->event_list_mutex); -} - -static int init_event_notifier(EmulatedState *card) -{ - if (event_notifier_init(&card->notifier, false) < 0) { - DPRINTF(card, 2, "event notifier creation failed\n"); - return -1; - } - event_notifier_set_handler(&card->notifier, false, card_event_handler); - return 0; -} - -#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" -#define CERTIFICATES_ARGS_TEMPLATE\ - "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)" - -static int wrap_vcard_emul_init(VCardEmulOptions *options) -{ - static int called; - static int options_was_null; - - if (called) { - if ((options == NULL) != options_was_null) { - printf("%s: warning: running emulated with certificates" - " and emulated side by side is not supported\n", - __func__); - return VCARD_EMUL_FAIL; - } - vcard_emul_replay_insertion_events(); - return VCARD_EMUL_OK; - } - options_was_null = (options == NULL); - called = 1; - return vcard_emul_init(options); -} - -static int emulated_initialize_vcard_from_certificates(EmulatedState *card) -{ - char emul_args[200]; - VCardEmulOptions *options = NULL; - - snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE, - card->db ? card->db : CERTIFICATES_DEFAULT_DB, - card->cert1, card->cert2, card->cert3); - options = vcard_emul_options(emul_args); - if (options == NULL) { - printf("%s: warning: not using certificates due to" - " initialization error\n", __func__); - } - return wrap_vcard_emul_init(options); -} - -typedef struct EnumTable { - const char *name; - uint32_t value; -} EnumTable; - -static const EnumTable backend_enum_table[] = { - {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED}, - {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES}, - {NULL, 0}, -}; - -static uint32_t parse_enumeration(char *str, - const EnumTable *table, uint32_t not_found_value) -{ - uint32_t ret = not_found_value; - - if (str == NULL) - return 0; - - while (table->name != NULL) { - if (strcmp(table->name, str) == 0) { - ret = table->value; - break; - } - table++; - } - return ret; -} - -static int emulated_initfn(CCIDCardState *base) -{ - EmulatedState *card = EMULATED_CCID_CARD(base); - VCardEmulError ret; - const EnumTable *ptable; - - QSIMPLEQ_INIT(&card->event_list); - QSIMPLEQ_INIT(&card->guest_apdu_list); - qemu_mutex_init(&card->event_list_mutex); - qemu_mutex_init(&card->vreader_mutex); - qemu_mutex_init(&card->handle_apdu_mutex); - qemu_cond_init(&card->handle_apdu_cond); - card->reader = NULL; - card->quit_apdu_thread = 0; - if (init_event_notifier(card) < 0) { - return -1; - } - - card->backend = 0; - if (card->backend_str) { - card->backend = parse_enumeration(card->backend_str, - backend_enum_table, 0); - } - - if (card->backend == 0) { - printf("backend must be one of:\n"); - for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { - printf("%s\n", ptable->name); - } - return -1; - } - - /* TODO: a passthru backened that works on local machine. third card type?*/ - if (card->backend == BACKEND_CERTIFICATES) { - if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { - ret = emulated_initialize_vcard_from_certificates(card); - } else { - printf("%s: you must provide all three certs for" - " certificates backend\n", TYPE_EMULATED_CCID); - return -1; - } - } else { - if (card->backend != BACKEND_NSS_EMULATED) { - printf("%s: bad backend specified. The options are:\n%s (default)," - " %s.\n", TYPE_EMULATED_CCID, BACKEND_NSS_EMULATED_NAME, - BACKEND_CERTIFICATES_NAME); - return -1; - } - if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { - printf("%s: unexpected cert parameters to nss emulated backend\n", - TYPE_EMULATED_CCID); - return -1; - } - /* default to mirroring the local hardware readers */ - ret = wrap_vcard_emul_init(NULL); - } - if (ret != VCARD_EMUL_OK) { - printf("%s: failed to initialize vcard\n", TYPE_EMULATED_CCID); - return -1; - } - qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread, - card, QEMU_THREAD_JOINABLE); - qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread, - card, QEMU_THREAD_JOINABLE); - return 0; -} - -static int emulated_exitfn(CCIDCardState *base) -{ - EmulatedState *card = EMULATED_CCID_CARD(base); - VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); - - vevent_queue_vevent(vevent); /* stop vevent thread */ - qemu_thread_join(&card->event_thread_id); - - card->quit_apdu_thread = 1; /* stop handle_apdu thread */ - qemu_cond_signal(&card->handle_apdu_cond); - qemu_thread_join(&card->apdu_thread_id); - - /* threads exited, can destroy all condvars/mutexes */ - qemu_cond_destroy(&card->handle_apdu_cond); - qemu_mutex_destroy(&card->handle_apdu_mutex); - qemu_mutex_destroy(&card->vreader_mutex); - qemu_mutex_destroy(&card->event_list_mutex); - return 0; -} - -static Property emulated_card_properties[] = { - DEFINE_PROP_STRING("backend", EmulatedState, backend_str), - DEFINE_PROP_STRING("cert1", EmulatedState, cert1), - DEFINE_PROP_STRING("cert2", EmulatedState, cert2), - DEFINE_PROP_STRING("cert3", EmulatedState, cert3), - DEFINE_PROP_STRING("db", EmulatedState, db), - DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void emulated_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - CCIDCardClass *cc = CCID_CARD_CLASS(klass); - - cc->initfn = emulated_initfn; - cc->exitfn = emulated_exitfn; - cc->get_atr = emulated_get_atr; - cc->apdu_from_guest = emulated_apdu_from_guest; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->desc = "emulated smartcard"; - dc->props = emulated_card_properties; -} - -static const TypeInfo emulated_card_info = { - .name = TYPE_EMULATED_CCID, - .parent = TYPE_CCID_CARD, - .instance_size = sizeof(EmulatedState), - .class_init = emulated_class_initfn, -}; - -static void ccid_card_emulated_register_types(void) -{ - type_register_static(&emulated_card_info); -} - -type_init(ccid_card_emulated_register_types) diff --git a/qemu/hw/usb/ccid-card-passthru.c b/qemu/hw/usb/ccid-card-passthru.c deleted file mode 100644 index c0e90e501..000000000 --- a/qemu/hw/usb/ccid-card-passthru.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * CCID Passthru Card Device emulation - * - * Copyright (c) 2011 Red Hat. - * Written by Alon Levy. - * - * This work is licensed under the terms of the GNU GPL, version 2.1 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "sysemu/char.h" -#include "qemu/error-report.h" -#include "qemu/sockets.h" -#include "ccid.h" -#include "cacard/vscard_common.h" - -#define DPRINTF(card, lvl, fmt, ...) \ -do { \ - if (lvl <= card->debug) { \ - printf("ccid-card-passthru: " fmt , ## __VA_ARGS__); \ - } \ -} while (0) - -#define D_WARN 1 -#define D_INFO 2 -#define D_MORE_INFO 3 -#define D_VERBOSE 4 - -/* TODO: do we still need this? */ -static const uint8_t DEFAULT_ATR[] = { -/* - * From some example somewhere - * 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28 - */ - -/* From an Athena smart card */ - 0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21, - 0x13, 0x08 -}; - -#define VSCARD_IN_SIZE 65536 - -/* maximum size of ATR - from 7816-3 */ -#define MAX_ATR_SIZE 40 - -typedef struct PassthruState PassthruState; - -struct PassthruState { - CCIDCardState base; - CharDriverState *cs; - uint8_t vscard_in_data[VSCARD_IN_SIZE]; - uint32_t vscard_in_pos; - uint32_t vscard_in_hdr; - uint8_t atr[MAX_ATR_SIZE]; - uint8_t atr_length; - uint8_t debug; -}; - -#define TYPE_CCID_PASSTHRU "ccid-card-passthru" -#define PASSTHRU_CCID_CARD(obj) \ - OBJECT_CHECK(PassthruState, (obj), TYPE_CCID_PASSTHRU) - -/* - * VSCard protocol over chardev - * This code should not depend on the card type. - */ - -static void ccid_card_vscard_send_msg(PassthruState *s, - VSCMsgType type, uint32_t reader_id, - const uint8_t *payload, uint32_t length) -{ - VSCMsgHeader scr_msg_header; - - scr_msg_header.type = htonl(type); - scr_msg_header.reader_id = htonl(reader_id); - scr_msg_header.length = htonl(length); - qemu_chr_fe_write(s->cs, (uint8_t *)&scr_msg_header, sizeof(VSCMsgHeader)); - qemu_chr_fe_write(s->cs, payload, length); -} - -static void ccid_card_vscard_send_apdu(PassthruState *s, - const uint8_t *apdu, uint32_t length) -{ - ccid_card_vscard_send_msg( - s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length); -} - -static void ccid_card_vscard_send_error(PassthruState *s, - uint32_t reader_id, VSCErrorCode code) -{ - VSCMsgError msg = {.code = htonl(code)}; - - ccid_card_vscard_send_msg( - s, VSC_Error, reader_id, (uint8_t *)&msg, sizeof(msg)); -} - -static void ccid_card_vscard_send_init(PassthruState *s) -{ - VSCMsgInit msg = { - .version = htonl(VSCARD_VERSION), - .magic = VSCARD_MAGIC, - .capabilities = {0} - }; - - ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID, - (uint8_t *)&msg, sizeof(msg)); -} - -static int ccid_card_vscard_can_read(void *opaque) -{ - PassthruState *card = opaque; - - return VSCARD_IN_SIZE >= card->vscard_in_pos ? - VSCARD_IN_SIZE - card->vscard_in_pos : 0; -} - -static void ccid_card_vscard_handle_init( - PassthruState *card, VSCMsgHeader *hdr, VSCMsgInit *init) -{ - uint32_t *capabilities; - int num_capabilities; - int i; - - capabilities = init->capabilities; - num_capabilities = - 1 + ((hdr->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); - init->version = ntohl(init->version); - for (i = 0 ; i < num_capabilities; ++i) { - capabilities[i] = ntohl(capabilities[i]); - } - if (init->magic != VSCARD_MAGIC) { - error_report("wrong magic"); - /* we can't disconnect the chardev */ - } - if (init->version != VSCARD_VERSION) { - DPRINTF(card, D_WARN, - "got version %d, have %d", init->version, VSCARD_VERSION); - } - /* future handling of capabilities, none exist atm */ - ccid_card_vscard_send_init(card); -} - -static int check_atr(PassthruState *card, uint8_t *data, int len) -{ - int historical_length, opt_bytes; - int td_count = 0; - int td; - - if (len < 2) { - return 0; - } - historical_length = data[1] & 0xf; - opt_bytes = 0; - if (data[0] != 0x3b && data[0] != 0x3f) { - DPRINTF(card, D_WARN, "atr's T0 is 0x%X, not in {0x3b, 0x3f}\n", - data[0]); - return 0; - } - td_count = 0; - td = data[1] >> 4; - while (td && td_count < 2 && opt_bytes + historical_length + 2 < len) { - td_count++; - if (td & 0x1) { - opt_bytes++; - } - if (td & 0x2) { - opt_bytes++; - } - if (td & 0x4) { - opt_bytes++; - } - if (td & 0x8) { - opt_bytes++; - td = data[opt_bytes + 2] >> 4; - } - } - if (len < 2 + historical_length + opt_bytes) { - DPRINTF(card, D_WARN, - "atr too short: len %d, but historical_len %d, T1 0x%X\n", - len, historical_length, data[1]); - return 0; - } - if (len > 2 + historical_length + opt_bytes) { - DPRINTF(card, D_WARN, - "atr too long: len %d, but hist/opt %d/%d, T1 0x%X\n", - len, historical_length, opt_bytes, data[1]); - /* let it through */ - } - DPRINTF(card, D_VERBOSE, - "atr passes check: %d total length, %d historical, %d optional\n", - len, historical_length, opt_bytes); - - return 1; -} - -static void ccid_card_vscard_handle_message(PassthruState *card, - VSCMsgHeader *scr_msg_header) -{ - uint8_t *data = (uint8_t *)&scr_msg_header[1]; - - switch (scr_msg_header->type) { - case VSC_ATR: - DPRINTF(card, D_INFO, "VSC_ATR %d\n", scr_msg_header->length); - if (scr_msg_header->length > MAX_ATR_SIZE) { - error_report("ATR size exceeds spec, ignoring"); - ccid_card_vscard_send_error(card, scr_msg_header->reader_id, - VSC_GENERAL_ERROR); - break; - } - if (!check_atr(card, data, scr_msg_header->length)) { - error_report("ATR is inconsistent, ignoring"); - ccid_card_vscard_send_error(card, scr_msg_header->reader_id, - VSC_GENERAL_ERROR); - break; - } - memcpy(card->atr, data, scr_msg_header->length); - card->atr_length = scr_msg_header->length; - ccid_card_card_inserted(&card->base); - ccid_card_vscard_send_error(card, scr_msg_header->reader_id, - VSC_SUCCESS); - break; - case VSC_APDU: - ccid_card_send_apdu_to_guest( - &card->base, data, scr_msg_header->length); - break; - case VSC_CardRemove: - DPRINTF(card, D_INFO, "VSC_CardRemove\n"); - ccid_card_card_removed(&card->base); - ccid_card_vscard_send_error(card, - scr_msg_header->reader_id, VSC_SUCCESS); - break; - case VSC_Init: - ccid_card_vscard_handle_init( - card, scr_msg_header, (VSCMsgInit *)data); - break; - case VSC_Error: - ccid_card_card_error(&card->base, *(uint32_t *)data); - break; - case VSC_ReaderAdd: - if (ccid_card_ccid_attach(&card->base) < 0) { - ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID, - VSC_CANNOT_ADD_MORE_READERS); - } else { - ccid_card_vscard_send_error(card, VSCARD_MINIMAL_READER_ID, - VSC_SUCCESS); - } - break; - case VSC_ReaderRemove: - ccid_card_ccid_detach(&card->base); - ccid_card_vscard_send_error(card, - scr_msg_header->reader_id, VSC_SUCCESS); - break; - default: - printf("usb-ccid: chardev: unexpected message of type %X\n", - scr_msg_header->type); - ccid_card_vscard_send_error(card, scr_msg_header->reader_id, - VSC_GENERAL_ERROR); - } -} - -static void ccid_card_vscard_drop_connection(PassthruState *card) -{ - qemu_chr_delete(card->cs); - card->vscard_in_pos = card->vscard_in_hdr = 0; -} - -static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size) -{ - PassthruState *card = opaque; - VSCMsgHeader *hdr; - - if (card->vscard_in_pos + size > VSCARD_IN_SIZE) { - error_report( - "no room for data: pos %d + size %d > %d. dropping connection.", - card->vscard_in_pos, size, VSCARD_IN_SIZE); - ccid_card_vscard_drop_connection(card); - return; - } - assert(card->vscard_in_pos < VSCARD_IN_SIZE); - assert(card->vscard_in_hdr < VSCARD_IN_SIZE); - memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size); - card->vscard_in_pos += size; - hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr); - - while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader)) - &&(card->vscard_in_pos - card->vscard_in_hdr >= - sizeof(VSCMsgHeader) + ntohl(hdr->length))) { - hdr->reader_id = ntohl(hdr->reader_id); - hdr->length = ntohl(hdr->length); - hdr->type = ntohl(hdr->type); - ccid_card_vscard_handle_message(card, hdr); - card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader); - hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr); - } - if (card->vscard_in_hdr == card->vscard_in_pos) { - card->vscard_in_pos = card->vscard_in_hdr = 0; - } -} - -static void ccid_card_vscard_event(void *opaque, int event) -{ - PassthruState *card = opaque; - - switch (event) { - case CHR_EVENT_BREAK: - card->vscard_in_pos = card->vscard_in_hdr = 0; - break; - case CHR_EVENT_FOCUS: - break; - case CHR_EVENT_OPENED: - DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__); - break; - } -} - -/* End VSCard handling */ - -static void passthru_apdu_from_guest( - CCIDCardState *base, const uint8_t *apdu, uint32_t len) -{ - PassthruState *card = PASSTHRU_CCID_CARD(base); - - if (!card->cs) { - printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); - return; - } - ccid_card_vscard_send_apdu(card, apdu, len); -} - -static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len) -{ - PassthruState *card = PASSTHRU_CCID_CARD(base); - - *len = card->atr_length; - return card->atr; -} - -static int passthru_initfn(CCIDCardState *base) -{ - PassthruState *card = PASSTHRU_CCID_CARD(base); - - card->vscard_in_pos = 0; - card->vscard_in_hdr = 0; - if (card->cs) { - DPRINTF(card, D_INFO, "initing chardev\n"); - qemu_chr_add_handlers(card->cs, - ccid_card_vscard_can_read, - ccid_card_vscard_read, - ccid_card_vscard_event, card); - ccid_card_vscard_send_init(card); - } else { - error_report("missing chardev"); - return -1; - } - card->debug = parse_debug_env("QEMU_CCID_PASSTHRU_DEBUG", D_VERBOSE, - card->debug); - assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE); - memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR)); - card->atr_length = sizeof(DEFAULT_ATR); - return 0; -} - -static int passthru_exitfn(CCIDCardState *base) -{ - return 0; -} - -static VMStateDescription passthru_vmstate = { - .name = "ccid-card-passthru", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(vscard_in_data, PassthruState), - VMSTATE_UINT32(vscard_in_pos, PassthruState), - VMSTATE_UINT32(vscard_in_hdr, PassthruState), - VMSTATE_BUFFER(atr, PassthruState), - VMSTATE_UINT8(atr_length, PassthruState), - VMSTATE_END_OF_LIST() - } -}; - -static Property passthru_card_properties[] = { - DEFINE_PROP_CHR("chardev", PassthruState, cs), - DEFINE_PROP_UINT8("debug", PassthruState, debug, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void passthru_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - CCIDCardClass *cc = CCID_CARD_CLASS(klass); - - cc->initfn = passthru_initfn; - cc->exitfn = passthru_exitfn; - cc->get_atr = passthru_get_atr; - cc->apdu_from_guest = passthru_apdu_from_guest; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->desc = "passthrough smartcard"; - dc->vmsd = &passthru_vmstate; - dc->props = passthru_card_properties; -} - -static const TypeInfo passthru_card_info = { - .name = TYPE_CCID_PASSTHRU, - .parent = TYPE_CCID_CARD, - .instance_size = sizeof(PassthruState), - .class_init = passthru_class_initfn, -}; - -static void ccid_card_passthru_register_types(void) -{ - type_register_static(&passthru_card_info); -} - -type_init(ccid_card_passthru_register_types) diff --git a/qemu/hw/usb/ccid.h b/qemu/hw/usb/ccid.h deleted file mode 100644 index 9334da8ac..000000000 --- a/qemu/hw/usb/ccid.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * CCID Passthru Card Device emulation - * - * Copyright (c) 2011 Red Hat. - * Written by Alon Levy. - * - * This code is licensed under the GNU LGPL, version 2 or later. - */ - -#ifndef CCID_H -#define CCID_H - -#include "hw/qdev.h" - -typedef struct CCIDCardState CCIDCardState; -typedef struct CCIDCardInfo CCIDCardInfo; - -#define TYPE_CCID_CARD "ccid-card" -#define CCID_CARD(obj) \ - OBJECT_CHECK(CCIDCardState, (obj), TYPE_CCID_CARD) -#define CCID_CARD_CLASS(klass) \ - OBJECT_CLASS_CHECK(CCIDCardClass, (klass), TYPE_CCID_CARD) -#define CCID_CARD_GET_CLASS(obj) \ - OBJECT_GET_CLASS(CCIDCardClass, (obj), TYPE_CCID_CARD) - -/* - * callbacks to be used by the CCID device (hw/usb-ccid.c) to call - * into the smartcard device (hw/ccid-card-*.c) - */ -typedef struct CCIDCardClass { - DeviceClass parent_class; - const uint8_t *(*get_atr)(CCIDCardState *card, uint32_t *len); - void (*apdu_from_guest)(CCIDCardState *card, - const uint8_t *apdu, - uint32_t len); - int (*exitfn)(CCIDCardState *card); - int (*initfn)(CCIDCardState *card); -} CCIDCardClass; - -/* - * state of the CCID Card device (i.e. hw/ccid-card-*.c) - */ -struct CCIDCardState { - DeviceState qdev; - uint32_t slot; /* For future use with multiple slot reader. */ -}; - -/* - * API for smartcard calling the CCID device (used by hw/ccid-card-*.c) - */ -void ccid_card_send_apdu_to_guest(CCIDCardState *card, - uint8_t *apdu, - uint32_t len); -void ccid_card_card_removed(CCIDCardState *card); -void ccid_card_card_inserted(CCIDCardState *card); -void ccid_card_card_error(CCIDCardState *card, uint64_t error); - -/* - * support guest visible insertion/removal of ccid devices based on actual - * devices connected/removed. Called by card implementation (passthru, local) - */ -int ccid_card_ccid_attach(CCIDCardState *card); -void ccid_card_ccid_detach(CCIDCardState *card); - -#endif /* CCID_H */ diff --git a/qemu/hw/usb/combined-packet.c b/qemu/hw/usb/combined-packet.c deleted file mode 100644 index 48cac87f6..000000000 --- a/qemu/hw/usb/combined-packet.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * QEMU USB packet combining code (for input pipelining) - * - * Copyright(c) 2012 Red Hat, Inc. - * - * Red Hat Authors: - * Hans de Goede <hdegoede@redhat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/usb.h" -#include "qemu/iov.h" -#include "trace.h" - -static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p) -{ - qemu_iovec_concat(&combined->iov, &p->iov, 0, p->iov.size); - QTAILQ_INSERT_TAIL(&combined->packets, p, combined_entry); - p->combined = combined; -} - -/* Note will free combined when the last packet gets removed */ -static void usb_combined_packet_remove(USBCombinedPacket *combined, - USBPacket *p) -{ - assert(p->combined == combined); - p->combined = NULL; - QTAILQ_REMOVE(&combined->packets, p, combined_entry); - if (QTAILQ_EMPTY(&combined->packets)) { - qemu_iovec_destroy(&combined->iov); - g_free(combined); - } -} - -/* Also handles completion of non combined packets for pipelined input eps */ -void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p) -{ - USBCombinedPacket *combined = p->combined; - USBEndpoint *ep = p->ep; - USBPacket *next; - int status, actual_length; - bool short_not_ok, done = false; - - if (combined == NULL) { - usb_packet_complete_one(dev, p); - goto leave; - } - - assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets)); - - status = combined->first->status; - actual_length = combined->first->actual_length; - short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok; - - QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) { - if (!done) { - /* Distribute data over uncombined packets */ - if (actual_length >= p->iov.size) { - p->actual_length = p->iov.size; - } else { - /* Send short or error packet to complete the transfer */ - p->actual_length = actual_length; - done = true; - } - /* Report status on the last packet */ - if (done || next == NULL) { - p->status = status; - } else { - p->status = USB_RET_SUCCESS; - } - p->short_not_ok = short_not_ok; - /* Note will free combined when the last packet gets removed! */ - usb_combined_packet_remove(combined, p); - usb_packet_complete_one(dev, p); - actual_length -= p->actual_length; - } else { - /* Remove any leftover packets from the queue */ - p->status = USB_RET_REMOVE_FROM_QUEUE; - /* Note will free combined on the last packet! */ - dev->port->ops->complete(dev->port, p); - } - } - /* Do not use combined here, it has been freed! */ -leave: - /* Check if there are packets in the queue waiting for our completion */ - usb_ep_combine_input_packets(ep); -} - -/* May only be called for combined packets! */ -void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p) -{ - USBCombinedPacket *combined = p->combined; - assert(combined != NULL); - USBPacket *first = p->combined->first; - - /* Note will free combined on the last packet! */ - usb_combined_packet_remove(combined, p); - if (p == first) { - usb_device_cancel_packet(dev, p); - } -} - -/* - * Large input transfers can get split into multiple input packets, this - * function recombines them, removing the short_not_ok checks which all but - * the last packet of such splits transfers have, thereby allowing input - * transfer pipelining (which we cannot do on short_not_ok transfers) - */ -void usb_ep_combine_input_packets(USBEndpoint *ep) -{ - USBPacket *p, *u, *next, *prev = NULL, *first = NULL; - USBPort *port = ep->dev->port; - int totalsize; - - assert(ep->pipeline); - assert(ep->pid == USB_TOKEN_IN); - - QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) { - /* Empty the queue on a halt */ - if (ep->halted) { - p->status = USB_RET_REMOVE_FROM_QUEUE; - port->ops->complete(port, p); - continue; - } - - /* Skip packets already submitted to the device */ - if (p->state == USB_PACKET_ASYNC) { - prev = p; - continue; - } - usb_packet_check_state(p, USB_PACKET_QUEUED); - - /* - * If the previous (combined) packet has the short_not_ok flag set - * stop, as we must not submit packets to the device after a transfer - * ending with short_not_ok packet. - */ - if (prev && prev->short_not_ok) { - break; - } - - if (first) { - if (first->combined == NULL) { - USBCombinedPacket *combined = g_new0(USBCombinedPacket, 1); - - combined->first = first; - QTAILQ_INIT(&combined->packets); - qemu_iovec_init(&combined->iov, 2); - usb_combined_packet_add(combined, first); - } - usb_combined_packet_add(first->combined, p); - } else { - first = p; - } - - /* Is this packet the last one of a (combined) transfer? */ - totalsize = (p->combined) ? p->combined->iov.size : p->iov.size; - if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok || - next == NULL || - /* Work around for Linux usbfs bulk splitting + migration */ - (totalsize == 16348 && p->int_req)) { - usb_device_handle_data(ep->dev, first); - assert(first->status == USB_RET_ASYNC); - if (first->combined) { - QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) { - usb_packet_set_state(u, USB_PACKET_ASYNC); - } - } else { - usb_packet_set_state(first, USB_PACKET_ASYNC); - } - first = NULL; - prev = p; - } - } -} diff --git a/qemu/hw/usb/core.c b/qemu/hw/usb/core.c deleted file mode 100644 index 45fa00c51..000000000 --- a/qemu/hw/usb/core.c +++ /dev/null @@ -1,795 +0,0 @@ -/* - * QEMU USB emulation - * - * Copyright (c) 2005 Fabrice Bellard - * - * 2008 Generic packet handler rewrite by Max Krasnyansky - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/usb.h" -#include "qemu/iov.h" -#include "trace.h" - -void usb_pick_speed(USBPort *port) -{ - static const int speeds[] = { - USB_SPEED_SUPER, - USB_SPEED_HIGH, - USB_SPEED_FULL, - USB_SPEED_LOW, - }; - USBDevice *udev = port->dev; - int i; - - for (i = 0; i < ARRAY_SIZE(speeds); i++) { - if ((udev->speedmask & (1 << speeds[i])) && - (port->speedmask & (1 << speeds[i]))) { - udev->speed = speeds[i]; - return; - } - } -} - -void usb_attach(USBPort *port) -{ - USBDevice *dev = port->dev; - - assert(dev != NULL); - assert(dev->attached); - assert(dev->state == USB_STATE_NOTATTACHED); - usb_pick_speed(port); - port->ops->attach(port); - dev->state = USB_STATE_ATTACHED; - usb_device_handle_attach(dev); -} - -void usb_detach(USBPort *port) -{ - USBDevice *dev = port->dev; - - assert(dev != NULL); - assert(dev->state != USB_STATE_NOTATTACHED); - port->ops->detach(port); - dev->state = USB_STATE_NOTATTACHED; -} - -void usb_port_reset(USBPort *port) -{ - USBDevice *dev = port->dev; - - assert(dev != NULL); - usb_detach(port); - usb_attach(port); - usb_device_reset(dev); -} - -void usb_device_reset(USBDevice *dev) -{ - if (dev == NULL || !dev->attached) { - return; - } - dev->remote_wakeup = 0; - dev->addr = 0; - dev->state = USB_STATE_DEFAULT; - usb_device_handle_reset(dev); -} - -void usb_wakeup(USBEndpoint *ep, unsigned int stream) -{ - USBDevice *dev = ep->dev; - USBBus *bus = usb_bus_from_device(dev); - - if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { - dev->port->ops->wakeup(dev->port); - } - if (bus->ops->wakeup_endpoint) { - bus->ops->wakeup_endpoint(bus, ep, stream); - } -} - -/**********************/ - -/* generic USB device helpers (you are not forced to use them when - writing your USB device driver, but they help handling the - protocol) -*/ - -#define SETUP_STATE_IDLE 0 -#define SETUP_STATE_SETUP 1 -#define SETUP_STATE_DATA 2 -#define SETUP_STATE_ACK 3 -#define SETUP_STATE_PARAM 4 - -static void do_token_setup(USBDevice *s, USBPacket *p) -{ - int request, value, index; - - if (p->iov.size != 8) { - p->status = USB_RET_STALL; - return; - } - - usb_packet_copy(p, s->setup_buf, p->iov.size); - s->setup_index = 0; - p->actual_length = 0; - s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; - if (s->setup_len > sizeof(s->data_buf)) { - fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", - s->setup_len, sizeof(s->data_buf)); - p->status = USB_RET_STALL; - return; - } - - request = (s->setup_buf[0] << 8) | s->setup_buf[1]; - value = (s->setup_buf[3] << 8) | s->setup_buf[2]; - index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - - if (s->setup_buf[0] & USB_DIR_IN) { - usb_device_handle_control(s, p, request, value, index, - s->setup_len, s->data_buf); - if (p->status == USB_RET_ASYNC) { - s->setup_state = SETUP_STATE_SETUP; - } - if (p->status != USB_RET_SUCCESS) { - return; - } - - if (p->actual_length < s->setup_len) { - s->setup_len = p->actual_length; - } - s->setup_state = SETUP_STATE_DATA; - } else { - if (s->setup_len == 0) - s->setup_state = SETUP_STATE_ACK; - else - s->setup_state = SETUP_STATE_DATA; - } - - p->actual_length = 8; -} - -static void do_token_in(USBDevice *s, USBPacket *p) -{ - int request, value, index; - - assert(p->ep->nr == 0); - - request = (s->setup_buf[0] << 8) | s->setup_buf[1]; - value = (s->setup_buf[3] << 8) | s->setup_buf[2]; - index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - - switch(s->setup_state) { - case SETUP_STATE_ACK: - if (!(s->setup_buf[0] & USB_DIR_IN)) { - usb_device_handle_control(s, p, request, value, index, - s->setup_len, s->data_buf); - if (p->status == USB_RET_ASYNC) { - return; - } - s->setup_state = SETUP_STATE_IDLE; - p->actual_length = 0; - } - break; - - case SETUP_STATE_DATA: - if (s->setup_buf[0] & USB_DIR_IN) { - int len = s->setup_len - s->setup_index; - if (len > p->iov.size) { - len = p->iov.size; - } - usb_packet_copy(p, s->data_buf + s->setup_index, len); - s->setup_index += len; - if (s->setup_index >= s->setup_len) { - s->setup_state = SETUP_STATE_ACK; - } - return; - } - s->setup_state = SETUP_STATE_IDLE; - p->status = USB_RET_STALL; - break; - - default: - p->status = USB_RET_STALL; - } -} - -static void do_token_out(USBDevice *s, USBPacket *p) -{ - assert(p->ep->nr == 0); - - switch(s->setup_state) { - case SETUP_STATE_ACK: - if (s->setup_buf[0] & USB_DIR_IN) { - s->setup_state = SETUP_STATE_IDLE; - /* transfer OK */ - } else { - /* ignore additional output */ - } - break; - - case SETUP_STATE_DATA: - if (!(s->setup_buf[0] & USB_DIR_IN)) { - int len = s->setup_len - s->setup_index; - if (len > p->iov.size) { - len = p->iov.size; - } - usb_packet_copy(p, s->data_buf + s->setup_index, len); - s->setup_index += len; - if (s->setup_index >= s->setup_len) { - s->setup_state = SETUP_STATE_ACK; - } - return; - } - s->setup_state = SETUP_STATE_IDLE; - p->status = USB_RET_STALL; - break; - - default: - p->status = USB_RET_STALL; - } -} - -static void do_parameter(USBDevice *s, USBPacket *p) -{ - int i, request, value, index; - - for (i = 0; i < 8; i++) { - s->setup_buf[i] = p->parameter >> (i*8); - } - - s->setup_state = SETUP_STATE_PARAM; - s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; - s->setup_index = 0; - - request = (s->setup_buf[0] << 8) | s->setup_buf[1]; - value = (s->setup_buf[3] << 8) | s->setup_buf[2]; - index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - - if (s->setup_len > sizeof(s->data_buf)) { - fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", - s->setup_len, sizeof(s->data_buf)); - p->status = USB_RET_STALL; - return; - } - - if (p->pid == USB_TOKEN_OUT) { - usb_packet_copy(p, s->data_buf, s->setup_len); - } - - usb_device_handle_control(s, p, request, value, index, - s->setup_len, s->data_buf); - if (p->status == USB_RET_ASYNC) { - return; - } - - if (p->actual_length < s->setup_len) { - s->setup_len = p->actual_length; - } - if (p->pid == USB_TOKEN_IN) { - p->actual_length = 0; - usb_packet_copy(p, s->data_buf, s->setup_len); - } -} - -/* 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->status < 0) { - s->setup_state = SETUP_STATE_IDLE; - } - - switch (s->setup_state) { - case SETUP_STATE_SETUP: - if (p->actual_length < s->setup_len) { - s->setup_len = p->actual_length; - } - s->setup_state = SETUP_STATE_DATA; - p->actual_length = 8; - break; - - case SETUP_STATE_ACK: - s->setup_state = SETUP_STATE_IDLE; - p->actual_length = 0; - break; - - case SETUP_STATE_PARAM: - if (p->actual_length < s->setup_len) { - s->setup_len = p->actual_length; - } - if (p->pid == USB_TOKEN_IN) { - p->actual_length = 0; - usb_packet_copy(p, s->data_buf, s->setup_len); - } - break; - - default: - break; - } - usb_packet_complete(s, p); -} - -USBDevice *usb_find_device(USBPort *port, uint8_t addr) -{ - USBDevice *dev = port->dev; - - if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) { - return NULL; - } - if (dev->addr == addr) { - return dev; - } - return usb_device_find_device(dev, addr); -} - -static void usb_process_one(USBPacket *p) -{ - USBDevice *dev = p->ep->dev; - - /* - * Handlers expect status to be initialized to USB_RET_SUCCESS, but it - * can be USB_RET_NAK here from a previous usb_process_one() call, - * or USB_RET_ASYNC from going through usb_queue_one(). - */ - p->status = USB_RET_SUCCESS; - - if (p->ep->nr == 0) { - /* control pipe */ - if (p->parameter) { - do_parameter(dev, p); - return; - } - switch (p->pid) { - case USB_TOKEN_SETUP: - do_token_setup(dev, p); - break; - case USB_TOKEN_IN: - do_token_in(dev, p); - break; - case USB_TOKEN_OUT: - do_token_out(dev, p); - break; - default: - p->status = USB_RET_STALL; - } - } else { - /* data pipe */ - usb_device_handle_data(dev, p); - } -} - -static void usb_queue_one(USBPacket *p) -{ - usb_packet_set_state(p, USB_PACKET_QUEUED); - QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); - p->status = USB_RET_ASYNC; -} - -/* Hand over a packet to a device for processing. p->status == - USB_RET_ASYNC indicates the processing isn't finished yet, the - driver will call usb_packet_complete() when done processing it. */ -void usb_handle_packet(USBDevice *dev, USBPacket *p) -{ - if (dev == NULL) { - p->status = USB_RET_NODEV; - return; - } - assert(dev == p->ep->dev); - assert(dev->state == USB_STATE_DEFAULT); - usb_packet_check_state(p, USB_PACKET_SETUP); - assert(p->ep != NULL); - - /* Submitting a new packet clears halt */ - if (p->ep->halted) { - assert(QTAILQ_EMPTY(&p->ep->queue)); - p->ep->halted = false; - } - - if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline || p->stream) { - usb_process_one(p); - if (p->status == USB_RET_ASYNC) { - /* hcd drivers cannot handle async for isoc */ - assert(p->ep->type != USB_ENDPOINT_XFER_ISOC); - /* using async for interrupt packets breaks migration */ - assert(p->ep->type != USB_ENDPOINT_XFER_INT || - (dev->flags & (1 << USB_DEV_FLAG_IS_HOST))); - usb_packet_set_state(p, USB_PACKET_ASYNC); - QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); - } else if (p->status == USB_RET_ADD_TO_QUEUE) { - usb_queue_one(p); - } else { - /* - * When pipelining is enabled usb-devices must always return async, - * otherwise packets can complete out of order! - */ - assert(p->stream || !p->ep->pipeline || - QTAILQ_EMPTY(&p->ep->queue)); - if (p->status != USB_RET_NAK) { - usb_packet_set_state(p, USB_PACKET_COMPLETE); - } - } - } else { - usb_queue_one(p); - } -} - -void usb_packet_complete_one(USBDevice *dev, USBPacket *p) -{ - USBEndpoint *ep = p->ep; - - assert(p->stream || QTAILQ_FIRST(&ep->queue) == p); - assert(p->status != USB_RET_ASYNC && p->status != USB_RET_NAK); - - if (p->status != USB_RET_SUCCESS || - (p->short_not_ok && (p->actual_length < p->iov.size))) { - ep->halted = true; - } - usb_packet_set_state(p, USB_PACKET_COMPLETE); - QTAILQ_REMOVE(&ep->queue, p, queue); - dev->port->ops->complete(dev->port, p); -} - -/* 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) -{ - USBEndpoint *ep = p->ep; - - usb_packet_check_state(p, USB_PACKET_ASYNC); - usb_packet_complete_one(dev, p); - - while (!QTAILQ_EMPTY(&ep->queue)) { - p = QTAILQ_FIRST(&ep->queue); - if (ep->halted) { - /* Empty the queue on a halt */ - p->status = USB_RET_REMOVE_FROM_QUEUE; - dev->port->ops->complete(dev->port, p); - continue; - } - if (p->state == USB_PACKET_ASYNC) { - break; - } - usb_packet_check_state(p, USB_PACKET_QUEUED); - usb_process_one(p); - if (p->status == USB_RET_ASYNC) { - usb_packet_set_state(p, USB_PACKET_ASYNC); - break; - } - usb_packet_complete_one(ep->dev, p); - } -} - -/* 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) -{ - bool callback = (p->state == USB_PACKET_ASYNC); - assert(usb_packet_is_inflight(p)); - usb_packet_set_state(p, USB_PACKET_CANCELED); - QTAILQ_REMOVE(&p->ep->queue, p, queue); - if (callback) { - usb_device_cancel_packet(p->ep->dev, p); - } -} - - -void usb_packet_init(USBPacket *p) -{ - qemu_iovec_init(&p->iov, 1); -} - -static const char *usb_packet_state_name(USBPacketState state) -{ - static const char *name[] = { - [USB_PACKET_UNDEFINED] = "undef", - [USB_PACKET_SETUP] = "setup", - [USB_PACKET_QUEUED] = "queued", - [USB_PACKET_ASYNC] = "async", - [USB_PACKET_COMPLETE] = "complete", - [USB_PACKET_CANCELED] = "canceled", - }; - if (state < ARRAY_SIZE(name)) { - return name[state]; - } - return "INVALID"; -} - -void usb_packet_check_state(USBPacket *p, USBPacketState expected) -{ - USBDevice *dev; - USBBus *bus; - - if (p->state == expected) { - return; - } - dev = p->ep->dev; - bus = usb_bus_from_device(dev); - trace_usb_packet_state_fault(bus->busnr, dev->port->path, p->ep->nr, p, - usb_packet_state_name(p->state), - usb_packet_state_name(expected)); - assert(!"usb packet state check failed"); -} - -void usb_packet_set_state(USBPacket *p, USBPacketState state) -{ - if (p->ep) { - USBDevice *dev = p->ep->dev; - USBBus *bus = usb_bus_from_device(dev); - trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p, - usb_packet_state_name(p->state), - usb_packet_state_name(state)); - } else { - trace_usb_packet_state_change(-1, "", -1, p, - usb_packet_state_name(p->state), - usb_packet_state_name(state)); - } - p->state = state; -} - -void usb_packet_setup(USBPacket *p, int pid, - USBEndpoint *ep, unsigned int stream, - uint64_t id, bool short_not_ok, bool int_req) -{ - assert(!usb_packet_is_inflight(p)); - assert(p->iov.iov != NULL); - p->id = id; - p->pid = pid; - p->ep = ep; - p->stream = stream; - p->status = USB_RET_SUCCESS; - p->actual_length = 0; - p->parameter = 0; - p->short_not_ok = short_not_ok; - p->int_req = int_req; - p->combined = NULL; - qemu_iovec_reset(&p->iov); - usb_packet_set_state(p, USB_PACKET_SETUP); -} - -void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) -{ - qemu_iovec_add(&p->iov, ptr, len); -} - -void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) -{ - QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov; - - assert(p->actual_length >= 0); - assert(p->actual_length + bytes <= iov->size); - switch (p->pid) { - case USB_TOKEN_SETUP: - case USB_TOKEN_OUT: - iov_to_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes); - break; - case USB_TOKEN_IN: - iov_from_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes); - break; - default: - fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); - abort(); - } - p->actual_length += bytes; -} - -void usb_packet_skip(USBPacket *p, size_t bytes) -{ - QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov; - - assert(p->actual_length >= 0); - assert(p->actual_length + bytes <= iov->size); - if (p->pid == USB_TOKEN_IN) { - iov_memset(iov->iov, iov->niov, p->actual_length, 0, bytes); - } - p->actual_length += bytes; -} - -size_t usb_packet_size(USBPacket *p) -{ - return p->combined ? p->combined->iov.size : p->iov.size; -} - -void usb_packet_cleanup(USBPacket *p) -{ - assert(!usb_packet_is_inflight(p)); - qemu_iovec_destroy(&p->iov); -} - -void usb_ep_reset(USBDevice *dev) -{ - int ep; - - dev->ep_ctl.nr = 0; - dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; - dev->ep_ctl.ifnum = 0; - dev->ep_ctl.max_packet_size = 64; - dev->ep_ctl.max_streams = 0; - dev->ep_ctl.dev = dev; - dev->ep_ctl.pipeline = false; - for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { - dev->ep_in[ep].nr = ep + 1; - dev->ep_out[ep].nr = ep + 1; - dev->ep_in[ep].pid = USB_TOKEN_IN; - dev->ep_out[ep].pid = USB_TOKEN_OUT; - dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID; - dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID; - dev->ep_in[ep].ifnum = USB_INTERFACE_INVALID; - dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID; - dev->ep_in[ep].max_packet_size = 0; - dev->ep_out[ep].max_packet_size = 0; - dev->ep_in[ep].max_streams = 0; - dev->ep_out[ep].max_streams = 0; - dev->ep_in[ep].dev = dev; - dev->ep_out[ep].dev = dev; - dev->ep_in[ep].pipeline = false; - dev->ep_out[ep].pipeline = false; - } -} - -void usb_ep_init(USBDevice *dev) -{ - int ep; - - usb_ep_reset(dev); - QTAILQ_INIT(&dev->ep_ctl.queue); - for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { - QTAILQ_INIT(&dev->ep_in[ep].queue); - QTAILQ_INIT(&dev->ep_out[ep].queue); - } -} - -void usb_ep_dump(USBDevice *dev) -{ - static const char *tname[] = { - [USB_ENDPOINT_XFER_CONTROL] = "control", - [USB_ENDPOINT_XFER_ISOC] = "isoc", - [USB_ENDPOINT_XFER_BULK] = "bulk", - [USB_ENDPOINT_XFER_INT] = "int", - }; - int ifnum, ep, first; - - fprintf(stderr, "Device \"%s\", config %d\n", - dev->product_desc, dev->configuration); - for (ifnum = 0; ifnum < 16; ifnum++) { - first = 1; - for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { - if (dev->ep_in[ep].type != USB_ENDPOINT_XFER_INVALID && - dev->ep_in[ep].ifnum == ifnum) { - if (first) { - first = 0; - fprintf(stderr, " Interface %d, alternative %d\n", - ifnum, dev->altsetting[ifnum]); - } - fprintf(stderr, " Endpoint %d, IN, %s, %d max\n", ep, - tname[dev->ep_in[ep].type], - dev->ep_in[ep].max_packet_size); - } - if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID && - dev->ep_out[ep].ifnum == ifnum) { - if (first) { - first = 0; - fprintf(stderr, " Interface %d, alternative %d\n", - ifnum, dev->altsetting[ifnum]); - } - fprintf(stderr, " Endpoint %d, OUT, %s, %d max\n", ep, - tname[dev->ep_out[ep].type], - dev->ep_out[ep].max_packet_size); - } - } - } - fprintf(stderr, "--\n"); -} - -struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep) -{ - struct USBEndpoint *eps; - - if (dev == NULL) { - return NULL; - } - eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out; - if (ep == 0) { - return &dev->ep_ctl; - } - assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT); - assert(ep > 0 && ep <= USB_MAX_ENDPOINTS); - return eps + ep - 1; -} - -uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - return uep->type; -} - -void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - uep->type = type; -} - -void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - uep->ifnum = ifnum; -} - -void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, - uint16_t raw) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - int size, microframes; - - size = raw & 0x7ff; - switch ((raw >> 11) & 3) { - case 1: - microframes = 2; - break; - case 2: - microframes = 3; - break; - default: - microframes = 1; - break; - } - uep->max_packet_size = size * microframes; -} - -void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - int MaxStreams; - - MaxStreams = raw & 0x1f; - if (MaxStreams) { - uep->max_streams = 1 << MaxStreams; - } else { - uep->max_streams = 0; - } -} - -void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - uep->halted = halted; -} - -USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, - uint64_t id) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - USBPacket *p; - - QTAILQ_FOREACH(p, &uep->queue, queue) { - if (p->id == id) { - return p; - } - } - - return NULL; -} diff --git a/qemu/hw/usb/desc-msos.c b/qemu/hw/usb/desc-msos.c deleted file mode 100644 index 365291981..000000000 --- a/qemu/hw/usb/desc-msos.c +++ /dev/null @@ -1,239 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" - -/* - * Microsoft OS Descriptors - * - * Windows tries to fetch some special descriptors with informations - * specifically for windows. Presence is indicated using a special - * string @ index 0xee. There are two kinds of descriptors: - * - * compatid descriptor - * Used to bind drivers, if usb class isn't specific enougth. - * Used for PTP/MTP for example (both share the same usb class). - * - * properties descriptor - * Does carry registry entries. They show up in - * HLM\SYSTEM\CurrentControlSet\Enum\USB\<devid>\<serial>\Device Parameters - * - * Note that Windows caches the stuff it got in the registry, so when - * playing with this you have to delete registry subtrees to make - * windows query the device again: - * HLM\SYSTEM\CurrentControlSet\Control\usbflags - * HLM\SYSTEM\CurrentControlSet\Enum\USB - * Windows will complain it can't delete entries on the second one. - * It has deleted everything it had permissions too, which is enouth - * as this includes "Device Parameters". - * - * http://msdn.microsoft.com/en-us/library/windows/hardware/ff537430.aspx - * - */ - -/* ------------------------------------------------------------------ */ - -typedef struct msos_compat_hdr { - uint32_t dwLength; - uint8_t bcdVersion_lo; - uint8_t bcdVersion_hi; - uint8_t wIndex_lo; - uint8_t wIndex_hi; - uint8_t bCount; - uint8_t reserved[7]; -} QEMU_PACKED msos_compat_hdr; - -typedef struct msos_compat_func { - uint8_t bFirstInterfaceNumber; - uint8_t reserved_1; - char compatibleId[8]; - uint8_t subCompatibleId[8]; - uint8_t reserved_2[6]; -} QEMU_PACKED msos_compat_func; - -static int usb_desc_msos_compat(const USBDesc *desc, uint8_t *dest) -{ - msos_compat_hdr *hdr = (void *)dest; - msos_compat_func *func; - int length = sizeof(*hdr); - int count = 0; - - func = (void *)(dest + length); - func->bFirstInterfaceNumber = 0; - func->reserved_1 = 0x01; - if (desc->msos->CompatibleID) { - snprintf(func->compatibleId, sizeof(func->compatibleId), - "%s", desc->msos->CompatibleID); - } - length += sizeof(*func); - count++; - - hdr->dwLength = cpu_to_le32(length); - hdr->bcdVersion_lo = 0x00; - hdr->bcdVersion_hi = 0x01; - hdr->wIndex_lo = 0x04; - hdr->wIndex_hi = 0x00; - hdr->bCount = count; - return length; -} - -/* ------------------------------------------------------------------ */ - -typedef struct msos_prop_hdr { - uint32_t dwLength; - uint8_t bcdVersion_lo; - uint8_t bcdVersion_hi; - uint8_t wIndex_lo; - uint8_t wIndex_hi; - uint8_t wCount_lo; - uint8_t wCount_hi; -} QEMU_PACKED msos_prop_hdr; - -typedef struct msos_prop { - uint32_t dwLength; - uint32_t dwPropertyDataType; - uint8_t dwPropertyNameLength_lo; - uint8_t dwPropertyNameLength_hi; - uint8_t bPropertyName[]; -} QEMU_PACKED msos_prop; - -typedef struct msos_prop_data { - uint32_t dwPropertyDataLength; - uint8_t bPropertyData[]; -} QEMU_PACKED msos_prop_data; - -typedef enum msos_prop_type { - MSOS_REG_SZ = 1, - MSOS_REG_EXPAND_SZ = 2, - MSOS_REG_BINARY = 3, - MSOS_REG_DWORD_LE = 4, - MSOS_REG_DWORD_BE = 5, - MSOS_REG_LINK = 6, - MSOS_REG_MULTI_SZ = 7, -} msos_prop_type; - -static int usb_desc_msos_prop_name(struct msos_prop *prop, - const wchar_t *name) -{ - int length = wcslen(name) + 1; - int i; - - prop->dwPropertyNameLength_lo = usb_lo(length*2); - prop->dwPropertyNameLength_hi = usb_hi(length*2); - for (i = 0; i < length; i++) { - prop->bPropertyName[i*2] = usb_lo(name[i]); - prop->bPropertyName[i*2+1] = usb_hi(name[i]); - } - return length*2; -} - -static int usb_desc_msos_prop_str(uint8_t *dest, msos_prop_type type, - const wchar_t *name, const wchar_t *value) -{ - struct msos_prop *prop = (void *)dest; - struct msos_prop_data *data; - int length = sizeof(*prop); - int i, vlen = wcslen(value) + 1; - - prop->dwPropertyDataType = cpu_to_le32(type); - length += usb_desc_msos_prop_name(prop, name); - data = (void *)(dest + length); - - data->dwPropertyDataLength = cpu_to_le32(vlen*2); - length += sizeof(*prop); - - for (i = 0; i < vlen; i++) { - data->bPropertyData[i*2] = usb_lo(value[i]); - data->bPropertyData[i*2+1] = usb_hi(value[i]); - } - length += vlen*2; - - prop->dwLength = cpu_to_le32(length); - return length; -} - -static int usb_desc_msos_prop_dword(uint8_t *dest, const wchar_t *name, - uint32_t value) -{ - struct msos_prop *prop = (void *)dest; - struct msos_prop_data *data; - int length = sizeof(*prop); - - prop->dwPropertyDataType = cpu_to_le32(MSOS_REG_DWORD_LE); - length += usb_desc_msos_prop_name(prop, name); - data = (void *)(dest + length); - - data->dwPropertyDataLength = cpu_to_le32(4); - data->bPropertyData[0] = (value) & 0xff; - data->bPropertyData[1] = (value >> 8) & 0xff; - data->bPropertyData[2] = (value >> 16) & 0xff; - data->bPropertyData[3] = (value >> 24) & 0xff; - length += sizeof(*prop) + 4; - - prop->dwLength = cpu_to_le32(length); - return length; -} - -static int usb_desc_msos_prop(const USBDesc *desc, uint8_t *dest) -{ - msos_prop_hdr *hdr = (void *)dest; - int length = sizeof(*hdr); - int count = 0; - - if (desc->msos->Label) { - /* - * Given as example in the specs. Havn't figured yet where - * this label shows up in the windows gui. - */ - length += usb_desc_msos_prop_str(dest+length, MSOS_REG_SZ, - L"Label", desc->msos->Label); - count++; - } - - if (desc->msos->SelectiveSuspendEnabled) { - /* - * Signaling remote wakeup capability in the standard usb - * descriptors isn't enouth to make windows actually use it. - * This is the "Yes, we really mean it" registy entry to flip - * the switch in the windows drivers. - */ - length += usb_desc_msos_prop_dword(dest+length, - L"SelectiveSuspendEnabled", 1); - count++; - } - - hdr->dwLength = cpu_to_le32(length); - hdr->bcdVersion_lo = 0x00; - hdr->bcdVersion_hi = 0x01; - hdr->wIndex_lo = 0x05; - hdr->wIndex_hi = 0x00; - hdr->wCount_lo = usb_lo(count); - hdr->wCount_hi = usb_hi(count); - return length; -} - -/* ------------------------------------------------------------------ */ - -int usb_desc_msos(const USBDesc *desc, USBPacket *p, - int index, uint8_t *dest, size_t len) -{ - void *buf = g_malloc0(4096); - int length = 0; - - switch (index) { - case 0x0004: - length = usb_desc_msos_compat(desc, buf); - break; - case 0x0005: - length = usb_desc_msos_prop(desc, buf); - break; - } - - if (length > len) { - length = len; - } - memcpy(dest, buf, length); - g_free(buf); - - p->actual_length = length; - return 0; -} diff --git a/qemu/hw/usb/desc.c b/qemu/hw/usb/desc.c deleted file mode 100644 index adb026e43..000000000 --- a/qemu/hw/usb/desc.c +++ /dev/null @@ -1,804 +0,0 @@ -#include "qemu/osdep.h" - -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "trace.h" - -/* ------------------------------------------------------------------ */ - -int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, - bool msos, uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x12; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_DEVICE; - - if (msos && dev->bcdUSB < 0x0200) { - /* - * Version 2.0+ required for microsoft os descriptors to work. - * Done this way so msos-desc compat property will handle both - * the version and the new descriptors being present. - */ - d->u.device.bcdUSB_lo = usb_lo(0x0200); - d->u.device.bcdUSB_hi = usb_hi(0x0200); - } else { - d->u.device.bcdUSB_lo = usb_lo(dev->bcdUSB); - d->u.device.bcdUSB_hi = usb_hi(dev->bcdUSB); - } - d->u.device.bDeviceClass = dev->bDeviceClass; - d->u.device.bDeviceSubClass = dev->bDeviceSubClass; - d->u.device.bDeviceProtocol = dev->bDeviceProtocol; - d->u.device.bMaxPacketSize0 = dev->bMaxPacketSize0; - - d->u.device.idVendor_lo = usb_lo(id->idVendor); - d->u.device.idVendor_hi = usb_hi(id->idVendor); - d->u.device.idProduct_lo = usb_lo(id->idProduct); - d->u.device.idProduct_hi = usb_hi(id->idProduct); - d->u.device.bcdDevice_lo = usb_lo(id->bcdDevice); - d->u.device.bcdDevice_hi = usb_hi(id->bcdDevice); - d->u.device.iManufacturer = id->iManufacturer; - d->u.device.iProduct = id->iProduct; - d->u.device.iSerialNumber = id->iSerialNumber; - - d->u.device.bNumConfigurations = dev->bNumConfigurations; - - return bLength; -} - -int usb_desc_device_qualifier(const USBDescDevice *dev, - uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x0a; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_DEVICE_QUALIFIER; - - d->u.device_qualifier.bcdUSB_lo = usb_lo(dev->bcdUSB); - d->u.device_qualifier.bcdUSB_hi = usb_hi(dev->bcdUSB); - d->u.device_qualifier.bDeviceClass = dev->bDeviceClass; - d->u.device_qualifier.bDeviceSubClass = dev->bDeviceSubClass; - d->u.device_qualifier.bDeviceProtocol = dev->bDeviceProtocol; - d->u.device_qualifier.bMaxPacketSize0 = dev->bMaxPacketSize0; - d->u.device_qualifier.bNumConfigurations = dev->bNumConfigurations; - d->u.device_qualifier.bReserved = 0; - - return bLength; -} - -int usb_desc_config(const USBDescConfig *conf, int flags, - uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x09; - uint16_t wTotalLength = 0; - USBDescriptor *d = (void *)dest; - int i, rc; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_CONFIG; - - d->u.config.bNumInterfaces = conf->bNumInterfaces; - d->u.config.bConfigurationValue = conf->bConfigurationValue; - d->u.config.iConfiguration = conf->iConfiguration; - d->u.config.bmAttributes = conf->bmAttributes; - d->u.config.bMaxPower = conf->bMaxPower; - wTotalLength += bLength; - - /* handle grouped interfaces if any */ - for (i = 0; i < conf->nif_groups; i++) { - rc = usb_desc_iface_group(&(conf->if_groups[i]), flags, - 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, flags, - dest + wTotalLength, len - wTotalLength); - if (rc < 0) { - return rc; - } - wTotalLength += rc; - } - - d->u.config.wTotalLength_lo = usb_lo(wTotalLength); - d->u.config.wTotalLength_hi = usb_hi(wTotalLength); - return wTotalLength; -} - -int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags, - 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]), flags, dest + pos, len - pos); - if (rc < 0) { - return rc; - } - pos += rc; - } - - return pos; -} - -int usb_desc_iface(const USBDescIface *iface, int flags, - uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x09; - int i, rc, pos = 0; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_INTERFACE; - - d->u.interface.bInterfaceNumber = iface->bInterfaceNumber; - d->u.interface.bAlternateSetting = iface->bAlternateSetting; - d->u.interface.bNumEndpoints = iface->bNumEndpoints; - d->u.interface.bInterfaceClass = iface->bInterfaceClass; - d->u.interface.bInterfaceSubClass = iface->bInterfaceSubClass; - d->u.interface.bInterfaceProtocol = iface->bInterfaceProtocol; - d->u.interface.iInterface = iface->iInterface; - pos += bLength; - - for (i = 0; i < iface->ndesc; i++) { - rc = usb_desc_other(iface->descs + i, dest + pos, len - pos); - if (rc < 0) { - return rc; - } - pos += rc; - } - - for (i = 0; i < iface->bNumEndpoints; i++) { - rc = usb_desc_endpoint(iface->eps + i, flags, dest + pos, len - pos); - if (rc < 0) { - return rc; - } - pos += rc; - } - - return pos; -} - -int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, - uint8_t *dest, size_t len) -{ - uint8_t bLength = ep->is_audio ? 0x09 : 0x07; - uint8_t extralen = ep->extra ? ep->extra[0] : 0; - uint8_t superlen = (flags & USB_DESC_FLAG_SUPER) ? 0x06 : 0; - USBDescriptor *d = (void *)dest; - - if (len < bLength + extralen + superlen) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_ENDPOINT; - - d->u.endpoint.bEndpointAddress = ep->bEndpointAddress; - d->u.endpoint.bmAttributes = ep->bmAttributes; - d->u.endpoint.wMaxPacketSize_lo = usb_lo(ep->wMaxPacketSize); - d->u.endpoint.wMaxPacketSize_hi = usb_hi(ep->wMaxPacketSize); - d->u.endpoint.bInterval = ep->bInterval; - if (ep->is_audio) { - d->u.endpoint.bRefresh = ep->bRefresh; - d->u.endpoint.bSynchAddress = ep->bSynchAddress; - } - - if (superlen) { - USBDescriptor *d = (void *)(dest + bLength); - - d->bLength = 0x06; - d->bDescriptorType = USB_DT_ENDPOINT_COMPANION; - - d->u.super_endpoint.bMaxBurst = ep->bMaxBurst; - d->u.super_endpoint.bmAttributes = ep->bmAttributes_super; - d->u.super_endpoint.wBytesPerInterval_lo = - usb_lo(ep->wBytesPerInterval); - d->u.super_endpoint.wBytesPerInterval_hi = - usb_hi(ep->wBytesPerInterval); - } - - if (ep->extra) { - memcpy(dest + bLength + superlen, ep->extra, extralen); - } - - return bLength + extralen + superlen; -} - -int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len) -{ - int bLength = desc->length ? desc->length : desc->data[0]; - - if (len < bLength) { - return -1; - } - - memcpy(dest, desc->data, bLength); - return bLength; -} - -static int usb_desc_cap_usb2_ext(const USBDesc *desc, uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x07; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; - d->u.cap.bDevCapabilityType = USB_DEV_CAP_USB2_EXT; - - d->u.cap.u.usb2_ext.bmAttributes_1 = (1 << 1); /* LPM */ - d->u.cap.u.usb2_ext.bmAttributes_2 = 0; - d->u.cap.u.usb2_ext.bmAttributes_3 = 0; - d->u.cap.u.usb2_ext.bmAttributes_4 = 0; - - return bLength; -} - -static int usb_desc_cap_super(const USBDesc *desc, uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x0a; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; - d->u.cap.bDevCapabilityType = USB_DEV_CAP_SUPERSPEED; - - d->u.cap.u.super.bmAttributes = 0; - d->u.cap.u.super.wSpeedsSupported_lo = 0; - d->u.cap.u.super.wSpeedsSupported_hi = 0; - d->u.cap.u.super.bFunctionalitySupport = 0; - d->u.cap.u.super.bU1DevExitLat = 0x0a; - d->u.cap.u.super.wU2DevExitLat_lo = 0x20; - d->u.cap.u.super.wU2DevExitLat_hi = 0; - - if (desc->full) { - d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 1); - d->u.cap.u.super.bFunctionalitySupport = 1; - } - if (desc->high) { - d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 2); - if (!d->u.cap.u.super.bFunctionalitySupport) { - d->u.cap.u.super.bFunctionalitySupport = 2; - } - } - if (desc->super) { - d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 3); - if (!d->u.cap.u.super.bFunctionalitySupport) { - d->u.cap.u.super.bFunctionalitySupport = 3; - } - } - - return bLength; -} - -static int usb_desc_bos(const USBDesc *desc, uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x05; - uint16_t wTotalLength = 0; - uint8_t bNumDeviceCaps = 0; - USBDescriptor *d = (void *)dest; - int rc; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_BOS; - - wTotalLength += bLength; - - if (desc->high != NULL) { - rc = usb_desc_cap_usb2_ext(desc, dest + wTotalLength, - len - wTotalLength); - if (rc < 0) { - return rc; - } - wTotalLength += rc; - bNumDeviceCaps++; - } - - if (desc->super != NULL) { - rc = usb_desc_cap_super(desc, dest + wTotalLength, - len - wTotalLength); - if (rc < 0) { - return rc; - } - wTotalLength += rc; - bNumDeviceCaps++; - } - - d->u.bos.wTotalLength_lo = usb_lo(wTotalLength); - d->u.bos.wTotalLength_hi = usb_hi(wTotalLength); - d->u.bos.bNumDeviceCaps = bNumDeviceCaps; - return wTotalLength; -} - -/* ------------------------------------------------------------------ */ - -static void usb_desc_ep_init(USBDevice *dev) -{ - const USBDescIface *iface; - int i, e, pid, ep; - - usb_ep_init(dev); - for (i = 0; i < dev->ninterfaces; i++) { - iface = dev->ifaces[i]; - if (iface == NULL) { - continue; - } - for (e = 0; e < iface->bNumEndpoints; e++) { - pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ? - USB_TOKEN_IN : USB_TOKEN_OUT; - ep = iface->eps[e].bEndpointAddress & 0x0f; - usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03); - usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber); - usb_ep_set_max_packet_size(dev, pid, ep, - iface->eps[e].wMaxPacketSize); - usb_ep_set_max_streams(dev, pid, ep, - iface->eps[e].bmAttributes_super); - } - } -} - -static const USBDescIface *usb_desc_find_interface(USBDevice *dev, - int nif, int alt) -{ - const USBDescIface *iface; - int g, i; - - if (!dev->config) { - return NULL; - } - for (g = 0; g < dev->config->nif_groups; g++) { - for (i = 0; i < dev->config->if_groups[g].nif; i++) { - iface = &dev->config->if_groups[g].ifs[i]; - if (iface->bInterfaceNumber == nif && - iface->bAlternateSetting == alt) { - return iface; - } - } - } - for (i = 0; i < dev->config->nif; i++) { - iface = &dev->config->ifs[i]; - if (iface->bInterfaceNumber == nif && - iface->bAlternateSetting == alt) { - return iface; - } - } - return NULL; -} - -static int usb_desc_set_interface(USBDevice *dev, int index, int value) -{ - const USBDescIface *iface; - int old; - - iface = usb_desc_find_interface(dev, index, value); - if (iface == NULL) { - return -1; - } - - old = dev->altsetting[index]; - dev->altsetting[index] = value; - dev->ifaces[index] = iface; - usb_desc_ep_init(dev); - - if (old != value) { - usb_device_set_interface(dev, index, old, value); - } - return 0; -} - -static int usb_desc_set_config(USBDevice *dev, int value) -{ - int i; - - if (value == 0) { - dev->configuration = 0; - dev->ninterfaces = 0; - dev->config = NULL; - } else { - for (i = 0; i < dev->device->bNumConfigurations; i++) { - if (dev->device->confs[i].bConfigurationValue == value) { - dev->configuration = value; - dev->ninterfaces = dev->device->confs[i].bNumInterfaces; - dev->config = dev->device->confs + i; - assert(dev->ninterfaces <= USB_MAX_INTERFACES); - } - } - if (i < dev->device->bNumConfigurations) { - return -1; - } - } - - for (i = 0; i < dev->ninterfaces; i++) { - usb_desc_set_interface(dev, i, 0); - } - for (; i < USB_MAX_INTERFACES; i++) { - dev->altsetting[i] = 0; - dev->ifaces[i] = NULL; - } - - return 0; -} - -static void usb_desc_setdefaults(USBDevice *dev) -{ - const USBDesc *desc = usb_device_get_usb_desc(dev); - - assert(desc != NULL); - switch (dev->speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - dev->device = desc->full; - break; - case USB_SPEED_HIGH: - dev->device = desc->high; - break; - case USB_SPEED_SUPER: - dev->device = desc->super; - break; - } - usb_desc_set_config(dev, 0); -} - -void usb_desc_init(USBDevice *dev) -{ - const USBDesc *desc = usb_device_get_usb_desc(dev); - - assert(desc != NULL); - dev->speed = USB_SPEED_FULL; - dev->speedmask = 0; - if (desc->full) { - dev->speedmask |= USB_SPEED_MASK_FULL; - } - if (desc->high) { - dev->speedmask |= USB_SPEED_MASK_HIGH; - } - if (desc->super) { - dev->speedmask |= USB_SPEED_MASK_SUPER; - } - if (desc->msos && (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_ENABLE))) { - dev->flags |= (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE); - usb_desc_set_string(dev, 0xee, "MSFT100Q"); - } - usb_desc_setdefaults(dev); -} - -void usb_desc_attach(USBDevice *dev) -{ - usb_desc_setdefaults(dev); -} - -void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str) -{ - USBDescString *s; - - QLIST_FOREACH(s, &dev->strings, next) { - if (s->index == index) { - break; - } - } - if (s == NULL) { - s = g_malloc0(sizeof(*s)); - s->index = index; - QLIST_INSERT_HEAD(&dev->strings, s, next); - } - g_free(s->str); - s->str = g_strdup(str); -} - -/* - * This function creates a serial number for a usb device. - * The serial number should: - * (a) Be unique within the virtual machine. - * (b) Be constant, so you don't get a new one each - * time the guest is started. - * So we are using the physical location to generate a serial number - * from it. It has three pieces: First a fixed, device-specific - * prefix. Second the device path of the host controller (which is - * the pci address in most cases). Third the physical port path. - * Results in serial numbers like this: "314159-0000:00:1d.7-3". - */ -void usb_desc_create_serial(USBDevice *dev) -{ - DeviceState *hcd = dev->qdev.parent_bus->parent; - const USBDesc *desc = usb_device_get_usb_desc(dev); - int index = desc->id.iSerialNumber; - char serial[64]; - char *path; - int dst; - - if (dev->serial) { - /* 'serial' usb bus property has priority if present */ - usb_desc_set_string(dev, index, dev->serial); - return; - } - - assert(index != 0 && desc->str[index] != NULL); - dst = snprintf(serial, sizeof(serial), "%s", desc->str[index]); - path = qdev_get_dev_path(hcd); - if (path) { - dst += snprintf(serial+dst, sizeof(serial)-dst, "-%s", path); - } - dst += snprintf(serial+dst, sizeof(serial)-dst, "-%s", dev->port->path); - usb_desc_set_string(dev, index, serial); -} - -const char *usb_desc_get_string(USBDevice *dev, uint8_t index) -{ - USBDescString *s; - - QLIST_FOREACH(s, &dev->strings, next) { - if (s->index == index) { - return s->str; - } - } - return NULL; -} - -int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len) -{ - uint8_t bLength, pos, i; - const char *str; - - if (len < 4) { - return -1; - } - - if (index == 0) { - /* language ids */ - dest[0] = 4; - dest[1] = USB_DT_STRING; - dest[2] = 0x09; - dest[3] = 0x04; - return 4; - } - - str = usb_desc_get_string(dev, index); - if (str == NULL) { - str = usb_device_get_usb_desc(dev)->str[index]; - if (str == NULL) { - return 0; - } - } - - bLength = strlen(str) * 2 + 2; - dest[0] = bLength; - dest[1] = USB_DT_STRING; - i = 0; pos = 2; - while (pos+1 < bLength && pos+1 < len) { - dest[pos++] = str[i++]; - dest[pos++] = 0; - } - return pos; -} - -int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p, - int value, uint8_t *dest, size_t len) -{ - bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE)); - const USBDesc *desc = usb_device_get_usb_desc(dev); - const USBDescDevice *other_dev; - uint8_t buf[256]; - uint8_t type = value >> 8; - uint8_t index = value & 0xff; - int flags, ret = -1; - - if (dev->speed == USB_SPEED_HIGH) { - other_dev = usb_device_get_usb_desc(dev)->full; - } else { - other_dev = usb_device_get_usb_desc(dev)->high; - } - - flags = 0; - if (dev->device->bcdUSB >= 0x0300) { - flags |= USB_DESC_FLAG_SUPER; - } - - switch(type) { - case USB_DT_DEVICE: - ret = usb_desc_device(&desc->id, dev->device, msos, buf, sizeof(buf)); - trace_usb_desc_device(dev->addr, len, ret); - break; - case USB_DT_CONFIG: - if (index < dev->device->bNumConfigurations) { - ret = usb_desc_config(dev->device->confs + index, flags, - buf, sizeof(buf)); - } - trace_usb_desc_config(dev->addr, index, len, ret); - break; - case USB_DT_STRING: - ret = usb_desc_string(dev, index, buf, sizeof(buf)); - trace_usb_desc_string(dev->addr, index, len, ret); - break; - case USB_DT_DEVICE_QUALIFIER: - if (other_dev != NULL) { - ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf)); - } - trace_usb_desc_device_qualifier(dev->addr, len, ret); - break; - case USB_DT_OTHER_SPEED_CONFIG: - if (other_dev != NULL && index < other_dev->bNumConfigurations) { - ret = usb_desc_config(other_dev->confs + index, flags, - buf, sizeof(buf)); - buf[0x01] = USB_DT_OTHER_SPEED_CONFIG; - } - trace_usb_desc_other_speed_config(dev->addr, index, len, ret); - break; - case USB_DT_BOS: - ret = usb_desc_bos(desc, buf, sizeof(buf)); - trace_usb_desc_bos(dev->addr, len, ret); - break; - - case USB_DT_DEBUG: - /* ignore silently */ - break; - - default: - fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__, - dev->addr, type, len); - break; - } - - if (ret > 0) { - if (ret > len) { - ret = len; - } - memcpy(dest, buf, ret); - p->actual_length = ret; - ret = 0; - } - return ret; -} - -int usb_desc_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE)); - const USBDesc *desc = usb_device_get_usb_desc(dev); - int ret = -1; - - assert(desc != NULL); - switch(request) { - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - trace_usb_set_addr(dev->addr); - ret = 0; - break; - - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - ret = usb_desc_get_descriptor(dev, p, value, data, length); - break; - - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - /* - * 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; - p->actual_length = 1; - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = usb_desc_set_config(dev, value); - trace_usb_set_config(dev->addr, value, ret); - break; - - case DeviceRequest | USB_REQ_GET_STATUS: { - const USBDescConfig *config = dev->config ? - dev->config : &dev->device->confs[0]; - - data[0] = 0; - /* - * 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 & USB_CFG_ATT_SELFPOWER) { - data[0] |= 1 << USB_DEVICE_SELF_POWERED; - } - if (dev->remote_wakeup) { - data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; - } - data[1] = 0x00; - p->actual_length = 2; - ret = 0; - break; - } - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - ret = 0; - } - trace_usb_clear_device_feature(dev->addr, value, ret); - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - ret = 0; - } - trace_usb_set_device_feature(dev->addr, value, ret); - break; - - case InterfaceRequest | USB_REQ_GET_INTERFACE: - if (index < 0 || index >= dev->ninterfaces) { - break; - } - data[0] = dev->altsetting[index]; - p->actual_length = 1; - ret = 0; - break; - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - ret = usb_desc_set_interface(dev, index, value); - trace_usb_set_interface(dev->addr, index, value, ret); - break; - - case VendorDeviceRequest | 'Q': - if (msos) { - ret = usb_desc_msos(desc, p, index, data, length); - trace_usb_desc_msos(dev->addr, index, length, ret); - } - break; - case VendorInterfaceRequest | 'Q': - if (msos) { - ret = usb_desc_msos(desc, p, index, data, length); - trace_usb_desc_msos(dev->addr, index, length, ret); - } - break; - - } - return ret; -} diff --git a/qemu/hw/usb/desc.h b/qemu/hw/usb/desc.h deleted file mode 100644 index 4d81c68e0..000000000 --- a/qemu/hw/usb/desc.h +++ /dev/null @@ -1,244 +0,0 @@ -#ifndef QEMU_HW_USB_DESC_H -#define QEMU_HW_USB_DESC_H - -#include <wchar.h> - -/* binary representation */ -typedef struct USBDescriptor { - uint8_t bLength; - uint8_t bDescriptorType; - union { - struct { - uint8_t bcdUSB_lo; - uint8_t bcdUSB_hi; - uint8_t bDeviceClass; - uint8_t bDeviceSubClass; - uint8_t bDeviceProtocol; - uint8_t bMaxPacketSize0; - uint8_t idVendor_lo; - uint8_t idVendor_hi; - uint8_t idProduct_lo; - uint8_t idProduct_hi; - uint8_t bcdDevice_lo; - uint8_t bcdDevice_hi; - uint8_t iManufacturer; - uint8_t iProduct; - uint8_t iSerialNumber; - uint8_t bNumConfigurations; - } device; - struct { - uint8_t bcdUSB_lo; - uint8_t bcdUSB_hi; - uint8_t bDeviceClass; - uint8_t bDeviceSubClass; - uint8_t bDeviceProtocol; - uint8_t bMaxPacketSize0; - uint8_t bNumConfigurations; - uint8_t bReserved; - } device_qualifier; - struct { - uint8_t wTotalLength_lo; - uint8_t wTotalLength_hi; - uint8_t bNumInterfaces; - uint8_t bConfigurationValue; - uint8_t iConfiguration; - uint8_t bmAttributes; - uint8_t bMaxPower; - } config; - struct { - uint8_t bInterfaceNumber; - uint8_t bAlternateSetting; - uint8_t bNumEndpoints; - uint8_t bInterfaceClass; - uint8_t bInterfaceSubClass; - uint8_t bInterfaceProtocol; - uint8_t iInterface; - } interface; - struct { - uint8_t bEndpointAddress; - uint8_t bmAttributes; - uint8_t wMaxPacketSize_lo; - uint8_t wMaxPacketSize_hi; - uint8_t bInterval; - uint8_t bRefresh; /* only audio ep */ - uint8_t bSynchAddress; /* only audio ep */ - } endpoint; - struct { - uint8_t bMaxBurst; - uint8_t bmAttributes; - uint8_t wBytesPerInterval_lo; - uint8_t wBytesPerInterval_hi; - } super_endpoint; - struct { - uint8_t wTotalLength_lo; - uint8_t wTotalLength_hi; - uint8_t bNumDeviceCaps; - } bos; - struct { - uint8_t bDevCapabilityType; - union { - struct { - uint8_t bmAttributes_1; - uint8_t bmAttributes_2; - uint8_t bmAttributes_3; - uint8_t bmAttributes_4; - } usb2_ext; - struct { - uint8_t bmAttributes; - uint8_t wSpeedsSupported_lo; - uint8_t wSpeedsSupported_hi; - uint8_t bFunctionalitySupport; - uint8_t bU1DevExitLat; - uint8_t wU2DevExitLat_lo; - uint8_t wU2DevExitLat_hi; - } super; - } u; - } cap; - } u; -} QEMU_PACKED USBDescriptor; - -struct USBDescID { - uint16_t idVendor; - uint16_t idProduct; - uint16_t bcdDevice; - uint8_t iManufacturer; - uint8_t iProduct; - uint8_t iSerialNumber; -}; - -struct USBDescDevice { - uint16_t bcdUSB; - uint8_t bDeviceClass; - uint8_t bDeviceSubClass; - uint8_t bDeviceProtocol; - uint8_t bMaxPacketSize0; - uint8_t bNumConfigurations; - - const USBDescConfig *confs; -}; - -struct USBDescConfig { - uint8_t bNumInterfaces; - uint8_t bConfigurationValue; - uint8_t iConfiguration; - 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; -}; - -struct USBDescIface { - uint8_t bInterfaceNumber; - uint8_t bAlternateSetting; - uint8_t bNumEndpoints; - uint8_t bInterfaceClass; - uint8_t bInterfaceSubClass; - uint8_t bInterfaceProtocol; - uint8_t iInterface; - - uint8_t ndesc; - USBDescOther *descs; - USBDescEndpoint *eps; -}; - -struct USBDescEndpoint { - uint8_t bEndpointAddress; - uint8_t bmAttributes; - uint16_t wMaxPacketSize; - uint8_t bInterval; - uint8_t bRefresh; - uint8_t bSynchAddress; - - uint8_t is_audio; /* has bRefresh + bSynchAddress */ - uint8_t *extra; - - /* superspeed endpoint companion */ - uint8_t bMaxBurst; - uint8_t bmAttributes_super; - uint16_t wBytesPerInterval; -}; - -struct USBDescOther { - uint8_t length; - const uint8_t *data; -}; - -struct USBDescMSOS { - const char *CompatibleID; - const wchar_t *Label; - bool SelectiveSuspendEnabled; -}; - -typedef const char *USBDescStrings[256]; - -struct USBDesc { - USBDescID id; - const USBDescDevice *full; - const USBDescDevice *high; - const USBDescDevice *super; - const char* const *str; - const USBDescMSOS *msos; -}; - -#define USB_DESC_FLAG_SUPER (1 << 1) - -/* little helpers */ -static inline uint8_t usb_lo(uint16_t val) -{ - return val & 0xff; -} - -static inline uint8_t usb_hi(uint16_t val) -{ - return (val >> 8) & 0xff; -} - -/* generate usb packages from structs */ -int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, - bool msos, uint8_t *dest, size_t len); -int usb_desc_device_qualifier(const USBDescDevice *dev, - uint8_t *dest, size_t len); -int usb_desc_config(const USBDescConfig *conf, int flags, - uint8_t *dest, size_t len); -int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags, - uint8_t *dest, size_t len); -int usb_desc_iface(const USBDescIface *iface, int flags, - uint8_t *dest, size_t len); -int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, - uint8_t *dest, size_t len); -int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len); -int usb_desc_msos(const USBDesc *desc, USBPacket *p, - int index, uint8_t *dest, size_t len); - -/* control message emulation helpers */ -void usb_desc_init(USBDevice *dev); -void usb_desc_attach(USBDevice *dev); -void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str); -void usb_desc_create_serial(USBDevice *dev); -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, USBPacket *p, - int value, uint8_t *dest, size_t len); -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 */ diff --git a/qemu/hw/usb/dev-audio.c b/qemu/hw/usb/dev-audio.c deleted file mode 100644 index 87cab0a3d..000000000 --- a/qemu/hw/usb/dev-audio.c +++ /dev/null @@ -1,703 +0,0 @@ -/* - * QEMU USB audio device - * - * written by: - * H. Peter Anvin <hpa@linux.intel.com> - * Gerd Hoffmann <kraxel@redhat.com> - * - * lousely based on usb net device code which is: - * - * Copyright (c) 2006 Thomas Sailer - * Copyright (c) 2008 Andrzej Zaborowski - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "hw/hw.h" -#include "audio/audio.h" - -#define USBAUDIO_VENDOR_NUM 0x46f4 /* CRC16() of "QEMU" */ -#define USBAUDIO_PRODUCT_NUM 0x0002 - -#define DEV_CONFIG_VALUE 1 /* The one and only */ - -/* Descriptor subtypes for AC interfaces */ -#define DST_AC_HEADER 1 -#define DST_AC_INPUT_TERMINAL 2 -#define DST_AC_OUTPUT_TERMINAL 3 -#define DST_AC_FEATURE_UNIT 6 -/* Descriptor subtypes for AS interfaces */ -#define DST_AS_GENERAL 1 -#define DST_AS_FORMAT_TYPE 2 -/* Descriptor subtypes for endpoints */ -#define DST_EP_GENERAL 1 - -enum usb_audio_strings { - STRING_NULL, - STRING_MANUFACTURER, - STRING_PRODUCT, - STRING_SERIALNUMBER, - STRING_CONFIG, - STRING_USBAUDIO_CONTROL, - STRING_INPUT_TERMINAL, - STRING_FEATURE_UNIT, - STRING_OUTPUT_TERMINAL, - STRING_NULL_STREAM, - STRING_REAL_STREAM, -}; - -static const USBDescStrings usb_audio_stringtable = { - [STRING_MANUFACTURER] = "QEMU", - [STRING_PRODUCT] = "QEMU USB Audio", - [STRING_SERIALNUMBER] = "1", - [STRING_CONFIG] = "Audio Configuration", - [STRING_USBAUDIO_CONTROL] = "Audio Device", - [STRING_INPUT_TERMINAL] = "Audio Output Pipe", - [STRING_FEATURE_UNIT] = "Audio Output Volume Control", - [STRING_OUTPUT_TERMINAL] = "Audio Output Terminal", - [STRING_NULL_STREAM] = "Audio Output - Disabled", - [STRING_REAL_STREAM] = "Audio Output - 48 kHz Stereo", -}; - -#define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff) -#define U24(x) U16(x), (((x) >> 16) & 0xff) -#define U32(x) U24(x), (((x) >> 24) & 0xff) - -/* - * A Basic Audio Device uses these specific values - */ -#define USBAUDIO_PACKET_SIZE 192 -#define USBAUDIO_SAMPLE_RATE 48000 -#define USBAUDIO_PACKET_INTERVAL 1 - -static const USBDescIface desc_iface[] = { - { - .bInterfaceNumber = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, - .bInterfaceProtocol = 0x04, - .iInterface = STRING_USBAUDIO_CONTROL, - .ndesc = 4, - .descs = (USBDescOther[]) { - { - /* Headphone Class-Specific AC Interface Header Descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AC_HEADER, /* u8 bDescriptorSubtype */ - U16(0x0100), /* u16 bcdADC */ - U16(0x2b), /* u16 wTotalLength */ - 0x01, /* u8 bInCollection */ - 0x01, /* u8 baInterfaceNr */ - } - },{ - /* Generic Stereo Input Terminal ID1 Descriptor */ - .data = (uint8_t[]) { - 0x0c, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */ - 0x01, /* u8 bTerminalID */ - U16(0x0101), /* u16 wTerminalType */ - 0x00, /* u8 bAssocTerminal */ - 0x02, /* u16 bNrChannels */ - U16(0x0003), /* u16 wChannelConfig */ - 0x00, /* u8 iChannelNames */ - STRING_INPUT_TERMINAL, /* u8 iTerminal */ - } - },{ - /* Generic Stereo Feature Unit ID2 Descriptor */ - .data = (uint8_t[]) { - 0x0d, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */ - 0x02, /* u8 bUnitID */ - 0x01, /* u8 bSourceID */ - 0x02, /* u8 bControlSize */ - U16(0x0001), /* u16 bmaControls(0) */ - U16(0x0002), /* u16 bmaControls(1) */ - U16(0x0002), /* u16 bmaControls(2) */ - STRING_FEATURE_UNIT, /* u8 iFeature */ - } - },{ - /* Headphone Ouptut Terminal ID3 Descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */ - 0x03, /* u8 bUnitID */ - U16(0x0301), /* u16 wTerminalType (SPK) */ - 0x00, /* u8 bAssocTerminal */ - 0x02, /* u8 bSourceID */ - STRING_OUTPUT_TERMINAL, /* u8 iTerminal */ - } - } - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, - .iInterface = STRING_NULL_STREAM, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 1, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, - .iInterface = STRING_REAL_STREAM, - .ndesc = 2, - .descs = (USBDescOther[]) { - { - /* Headphone Class-specific AS General Interface Descriptor */ - .data = (uint8_t[]) { - 0x07, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AS_GENERAL, /* u8 bDescriptorSubtype */ - 0x01, /* u8 bTerminalLink */ - 0x00, /* u8 bDelay */ - 0x01, 0x00, /* u16 wFormatTag */ - } - },{ - /* Headphone Type I Format Type Descriptor */ - .data = (uint8_t[]) { - 0x0b, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ - 0x01, /* u8 bFormatType */ - 0x02, /* u8 bNrChannels */ - 0x02, /* u8 bSubFrameSize */ - 0x10, /* u8 bBitResolution */ - 0x01, /* u8 bSamFreqType */ - U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ - } - } - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | 0x01, - .bmAttributes = 0x0d, - .wMaxPacketSize = USBAUDIO_PACKET_SIZE, - .bInterval = 1, - .is_audio = 1, - /* Stereo Headphone Class-specific - AS Audio Data Endpoint Descriptor */ - .extra = (uint8_t[]) { - 0x07, /* u8 bLength */ - USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ - DST_EP_GENERAL, /* u8 bDescriptorSubtype */ - 0x00, /* u8 bmAttributes */ - 0x00, /* u8 bLockDelayUnits */ - U16(0x0000), /* u16 wLockDelay */ - }, - }, - } - } -}; - -static const USBDescDevice desc_device = { - .bcdUSB = 0x0100, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 2, - .bConfigurationValue = DEV_CONFIG_VALUE, - .iConfiguration = STRING_CONFIG, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .bMaxPower = 0x32, - .nif = ARRAY_SIZE(desc_iface), - .ifs = desc_iface, - }, - }, -}; - -static const USBDesc desc_audio = { - .id = { - .idVendor = USBAUDIO_VENDOR_NUM, - .idProduct = USBAUDIO_PRODUCT_NUM, - .bcdDevice = 0, - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, - .iSerialNumber = STRING_SERIALNUMBER, - }, - .full = &desc_device, - .str = usb_audio_stringtable, -}; - -/* - * A USB audio device supports an arbitrary number of alternate - * interface settings for each interface. Each corresponds to a block - * diagram of parameterized blocks. This can thus refer to things like - * number of channels, data rates, or in fact completely different - * block diagrams. Alternative setting 0 is always the null block diagram, - * which is used by a disabled device. - */ -enum usb_audio_altset { - ALTSET_OFF = 0x00, /* No endpoint */ - ALTSET_ON = 0x01, /* Single endpoint */ -}; - -/* - * Class-specific control requests - */ -#define CR_SET_CUR 0x01 -#define CR_GET_CUR 0x81 -#define CR_SET_MIN 0x02 -#define CR_GET_MIN 0x82 -#define CR_SET_MAX 0x03 -#define CR_GET_MAX 0x83 -#define CR_SET_RES 0x04 -#define CR_GET_RES 0x84 -#define CR_SET_MEM 0x05 -#define CR_GET_MEM 0x85 -#define CR_GET_STAT 0xff - -/* - * Feature Unit Control Selectors - */ -#define MUTE_CONTROL 0x01 -#define VOLUME_CONTROL 0x02 -#define BASS_CONTROL 0x03 -#define MID_CONTROL 0x04 -#define TREBLE_CONTROL 0x05 -#define GRAPHIC_EQUALIZER_CONTROL 0x06 -#define AUTOMATIC_GAIN_CONTROL 0x07 -#define DELAY_CONTROL 0x08 -#define BASS_BOOST_CONTROL 0x09 -#define LOUDNESS_CONTROL 0x0a - -/* - * buffering - */ - -struct streambuf { - uint8_t *data; - uint32_t size; - uint32_t prod; - uint32_t cons; -}; - -static void streambuf_init(struct streambuf *buf, uint32_t size) -{ - g_free(buf->data); - buf->size = size - (size % USBAUDIO_PACKET_SIZE); - buf->data = g_malloc(buf->size); - buf->prod = 0; - buf->cons = 0; -} - -static void streambuf_fini(struct streambuf *buf) -{ - g_free(buf->data); - buf->data = NULL; -} - -static int streambuf_put(struct streambuf *buf, USBPacket *p) -{ - uint32_t free = buf->size - (buf->prod - buf->cons); - - if (!free) { - return 0; - } - assert(free >= USBAUDIO_PACKET_SIZE); - usb_packet_copy(p, buf->data + (buf->prod % buf->size), - USBAUDIO_PACKET_SIZE); - buf->prod += USBAUDIO_PACKET_SIZE; - return USBAUDIO_PACKET_SIZE; -} - -static uint8_t *streambuf_get(struct streambuf *buf) -{ - uint32_t used = buf->prod - buf->cons; - uint8_t *data; - - if (!used) { - return NULL; - } - assert(used >= USBAUDIO_PACKET_SIZE); - data = buf->data + (buf->cons % buf->size); - buf->cons += USBAUDIO_PACKET_SIZE; - return data; -} - -typedef struct USBAudioState { - /* qemu interfaces */ - USBDevice dev; - QEMUSoundCard card; - - /* state */ - struct { - enum usb_audio_altset altset; - struct audsettings as; - SWVoiceOut *voice; - bool mute; - uint8_t vol[2]; - struct streambuf buf; - } out; - - /* properties */ - uint32_t debug; - uint32_t buffer; -} USBAudioState; - -#define TYPE_USB_AUDIO "usb-audio" -#define USB_AUDIO(obj) OBJECT_CHECK(USBAudioState, (obj), TYPE_USB_AUDIO) - -static void output_callback(void *opaque, int avail) -{ - USBAudioState *s = opaque; - uint8_t *data; - - for (;;) { - if (avail < USBAUDIO_PACKET_SIZE) { - return; - } - data = streambuf_get(&s->out.buf); - if (!data) { - return; - } - AUD_write(s->out.voice, data, USBAUDIO_PACKET_SIZE); - avail -= USBAUDIO_PACKET_SIZE; - } -} - -static int usb_audio_set_output_altset(USBAudioState *s, int altset) -{ - switch (altset) { - case ALTSET_OFF: - streambuf_init(&s->out.buf, s->buffer); - AUD_set_active_out(s->out.voice, false); - break; - case ALTSET_ON: - AUD_set_active_out(s->out.voice, true); - break; - default: - return -1; - } - - if (s->debug) { - fprintf(stderr, "usb-audio: set interface %d\n", altset); - } - s->out.altset = altset; - return 0; -} - -/* - * Note: we arbitrarily map the volume control range onto -inf..+8 dB - */ -#define ATTRIB_ID(cs, attrib, idif) \ - (((cs) << 24) | ((attrib) << 16) | (idif)) - -static int usb_audio_get_control(USBAudioState *s, uint8_t attrib, - uint16_t cscn, uint16_t idif, - int length, uint8_t *data) -{ - uint8_t cs = cscn >> 8; - uint8_t cn = cscn - 1; /* -1 for the non-present master control */ - uint32_t aid = ATTRIB_ID(cs, attrib, idif); - int ret = USB_RET_STALL; - - switch (aid) { - case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200): - data[0] = s->out.mute; - ret = 1; - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200): - if (cn < 2) { - uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000; - data[0] = vol; - data[1] = vol >> 8; - ret = 2; - } - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200): - if (cn < 2) { - data[0] = 0x01; - data[1] = 0x80; - ret = 2; - } - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200): - if (cn < 2) { - data[0] = 0x00; - data[1] = 0x08; - ret = 2; - } - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200): - if (cn < 2) { - data[0] = 0x88; - data[1] = 0x00; - ret = 2; - } - break; - } - - return ret; -} -static int usb_audio_set_control(USBAudioState *s, uint8_t attrib, - uint16_t cscn, uint16_t idif, - int length, uint8_t *data) -{ - uint8_t cs = cscn >> 8; - uint8_t cn = cscn - 1; /* -1 for the non-present master control */ - uint32_t aid = ATTRIB_ID(cs, attrib, idif); - int ret = USB_RET_STALL; - bool set_vol = false; - - switch (aid) { - case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200): - s->out.mute = data[0] & 1; - set_vol = true; - ret = 0; - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200): - if (cn < 2) { - uint16_t vol = data[0] + (data[1] << 8); - - if (s->debug) { - fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol); - } - - vol -= 0x8000; - vol = (vol * 255 + 0x4400) / 0x8800; - if (vol > 255) { - vol = 255; - } - - s->out.vol[cn] = vol; - set_vol = true; - ret = 0; - } - break; - } - - if (set_vol) { - if (s->debug) { - fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n", - s->out.mute, s->out.vol[0], s->out.vol[1]); - } - AUD_set_volume_out(s->out.voice, s->out.mute, - s->out.vol[0], s->out.vol[1]); - } - - return ret; -} - -static void usb_audio_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, - int length, uint8_t *data) -{ - USBAudioState *s = USB_AUDIO(dev); - int ret = 0; - - if (s->debug) { - fprintf(stderr, "usb-audio: control transaction: " - "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", - request, value, index, length); - } - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - case ClassInterfaceRequest | CR_GET_CUR: - case ClassInterfaceRequest | CR_GET_MIN: - case ClassInterfaceRequest | CR_GET_MAX: - case ClassInterfaceRequest | CR_GET_RES: - ret = usb_audio_get_control(s, request & 0xff, value, index, - length, data); - if (ret < 0) { - if (s->debug) { - fprintf(stderr, "usb-audio: fail: get control\n"); - } - goto fail; - } - p->actual_length = ret; - break; - - case ClassInterfaceOutRequest | CR_SET_CUR: - case ClassInterfaceOutRequest | CR_SET_MIN: - case ClassInterfaceOutRequest | CR_SET_MAX: - case ClassInterfaceOutRequest | CR_SET_RES: - ret = usb_audio_set_control(s, request & 0xff, value, index, - length, data); - if (ret < 0) { - if (s->debug) { - fprintf(stderr, "usb-audio: fail: set control\n"); - } - goto fail; - } - break; - - default: -fail: - if (s->debug) { - fprintf(stderr, "usb-audio: failed control transaction: " - "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", - request, value, index, length); - } - p->status = USB_RET_STALL; - break; - } -} - -static void usb_audio_set_interface(USBDevice *dev, int iface, - int old, int value) -{ - USBAudioState *s = USB_AUDIO(dev); - - if (iface == 1) { - usb_audio_set_output_altset(s, value); - } -} - -static void usb_audio_handle_reset(USBDevice *dev) -{ - USBAudioState *s = USB_AUDIO(dev); - - if (s->debug) { - fprintf(stderr, "usb-audio: reset\n"); - } - usb_audio_set_output_altset(s, ALTSET_OFF); -} - -static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p) -{ - if (s->out.altset == ALTSET_OFF) { - p->status = USB_RET_STALL; - return; - } - - streambuf_put(&s->out.buf, p); - if (p->actual_length < p->iov.size && s->debug > 1) { - fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n", - p->iov.size - p->actual_length); - } -} - -static void usb_audio_handle_data(USBDevice *dev, USBPacket *p) -{ - USBAudioState *s = (USBAudioState *) dev; - - if (p->pid == USB_TOKEN_OUT && p->ep->nr == 1) { - usb_audio_handle_dataout(s, p); - return; - } - - p->status = USB_RET_STALL; - if (s->debug) { - fprintf(stderr, "usb-audio: failed data transaction: " - "pid 0x%x ep 0x%x len 0x%zx\n", - p->pid, p->ep->nr, p->iov.size); - } -} - -static void usb_audio_handle_destroy(USBDevice *dev) -{ - USBAudioState *s = USB_AUDIO(dev); - - if (s->debug) { - fprintf(stderr, "usb-audio: destroy\n"); - } - - usb_audio_set_output_altset(s, ALTSET_OFF); - AUD_close_out(&s->card, s->out.voice); - AUD_remove_card(&s->card); - - streambuf_fini(&s->out.buf); -} - -static void usb_audio_realize(USBDevice *dev, Error **errp) -{ - USBAudioState *s = USB_AUDIO(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - s->dev.opaque = s; - AUD_register_card(TYPE_USB_AUDIO, &s->card); - - s->out.altset = ALTSET_OFF; - s->out.mute = false; - s->out.vol[0] = 240; /* 0 dB */ - s->out.vol[1] = 240; /* 0 dB */ - s->out.as.freq = USBAUDIO_SAMPLE_RATE; - s->out.as.nchannels = 2; - s->out.as.fmt = AUD_FMT_S16; - s->out.as.endianness = 0; - streambuf_init(&s->out.buf, s->buffer); - - s->out.voice = AUD_open_out(&s->card, s->out.voice, TYPE_USB_AUDIO, - s, output_callback, &s->out.as); - AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]); - AUD_set_active_out(s->out.voice, 0); -} - -static const VMStateDescription vmstate_usb_audio = { - .name = TYPE_USB_AUDIO, - .unmigratable = 1, -}; - -static Property usb_audio_properties[] = { - DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), - DEFINE_PROP_UINT32("buffer", USBAudioState, buffer, - 32 * USBAUDIO_PACKET_SIZE), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_audio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *k = USB_DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_usb_audio; - dc->props = usb_audio_properties; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - k->product_desc = "QEMU USB Audio Interface"; - k->usb_desc = &desc_audio; - k->realize = usb_audio_realize; - k->handle_reset = usb_audio_handle_reset; - k->handle_control = usb_audio_handle_control; - k->handle_data = usb_audio_handle_data; - k->handle_destroy = usb_audio_handle_destroy; - k->set_interface = usb_audio_set_interface; -} - -static const TypeInfo usb_audio_info = { - .name = TYPE_USB_AUDIO, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBAudioState), - .class_init = usb_audio_class_init, -}; - -static void usb_audio_register_types(void) -{ - type_register_static(&usb_audio_info); - usb_legacy_register(TYPE_USB_AUDIO, "audio", NULL); -} - -type_init(usb_audio_register_types) diff --git a/qemu/hw/usb/dev-bluetooth.c b/qemu/hw/usb/dev-bluetooth.c deleted file mode 100644 index 91a4a0b8b..000000000 --- a/qemu/hw/usb/dev-bluetooth.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - * QEMU Bluetooth HCI USB Transport Layer v1.0 - * - * Copyright (C) 2007 OpenMoko, Inc. - * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/error-report.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "sysemu/bt.h" -#include "hw/bt.h" - -struct USBBtState { - USBDevice dev; - struct HCIInfo *hci; - USBEndpoint *intr; - - int config; - -#define CFIFO_LEN_MASK 255 -#define DFIFO_LEN_MASK 4095 - struct usb_hci_in_fifo_s { - uint8_t data[(DFIFO_LEN_MASK + 1) * 2]; - struct { - uint8_t *data; - int len; - } fifo[CFIFO_LEN_MASK + 1]; - int dstart, dlen, dsize, start, len; - } evt, acl, sco; - - struct usb_hci_out_fifo_s { - uint8_t data[4096]; - int len; - } outcmd, outacl, outsco; -}; - -#define TYPE_USB_BT "usb-bt-dongle" -#define USB_BT(obj) OBJECT_CHECK(struct USBBtState, (obj), TYPE_USB_BT) - -#define USB_EVT_EP 1 -#define USB_ACL_EP 2 -#define USB_SCO_EP 3 - -enum { - STR_MANUFACTURER = 1, - STR_SERIALNUMBER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_SERIALNUMBER] = "1", -}; - -static const USBDescIface desc_iface_bluetooth[] = { - { - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | USB_EVT_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 0x10, - .bInterval = 0x02, - }, - { - .bEndpointAddress = USB_DIR_OUT | USB_ACL_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - .bInterval = 0x0a, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_ACL_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - .bInterval = 0x0a, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 1, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x09, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x09, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 2, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x11, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x11, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 3, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x19, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x19, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 4, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x21, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x21, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 5, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x31, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x31, - .bInterval = 0x01, - }, - }, - } -}; - -static const USBDescDevice desc_device_bluetooth = { - .bcdUSB = 0x0110, - .bDeviceClass = 0xe0, /* Wireless */ - .bDeviceSubClass = 0x01, /* Radio Frequency */ - .bDeviceProtocol = 0x01, /* Bluetooth */ - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 2, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .bMaxPower = 0, - .nif = ARRAY_SIZE(desc_iface_bluetooth), - .ifs = desc_iface_bluetooth, - }, - }, -}; - -static const USBDesc desc_bluetooth = { - .id = { - .idVendor = 0x0a12, - .idProduct = 0x0001, - .bcdDevice = 0x1958, - .iManufacturer = STR_MANUFACTURER, - .iProduct = 0, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_bluetooth, - .str = desc_strings, -}; - -static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo) -{ - fifo->dstart = 0; - fifo->dlen = 0; - fifo->dsize = DFIFO_LEN_MASK + 1; - fifo->start = 0; - fifo->len = 0; -} - -static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo, - const uint8_t *data, int len) -{ - int off = fifo->dstart + fifo->dlen; - uint8_t *buf; - - fifo->dlen += len; - if (off <= DFIFO_LEN_MASK) { - if (off + len > DFIFO_LEN_MASK + 1 && - (fifo->dsize = off + len) > (DFIFO_LEN_MASK + 1) * 2) { - fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); - exit(-1); - } - buf = fifo->data + off; - } else { - if (fifo->dlen > fifo->dsize) { - fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); - exit(-1); - } - buf = fifo->data + off - fifo->dsize; - } - - off = (fifo->start + fifo->len ++) & CFIFO_LEN_MASK; - fifo->fifo[off].data = memcpy(buf, data, len); - fifo->fifo[off].len = len; -} - -static inline void usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo, - USBPacket *p) -{ - int len; - - assert(fifo->len != 0); - - len = MIN(p->iov.size, fifo->fifo[fifo->start].len); - usb_packet_copy(p, fifo->fifo[fifo->start].data, len); - if (len == p->iov.size) { - fifo->fifo[fifo->start].len -= len; - fifo->fifo[fifo->start].data += len; - } else { - fifo->start ++; - fifo->start &= CFIFO_LEN_MASK; - fifo->len --; - } - - fifo->dstart += len; - fifo->dlen -= len; - if (fifo->dstart >= fifo->dsize) { - fifo->dstart = 0; - fifo->dsize = DFIFO_LEN_MASK + 1; - } -} - -static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s, - struct usb_hci_out_fifo_s *fifo, - void (*send)(struct HCIInfo *, const uint8_t *, int), - int (*complete)(const uint8_t *, int), - USBPacket *p) -{ - usb_packet_copy(p, fifo->data + fifo->len, p->iov.size); - fifo->len += p->iov.size; - if (complete(fifo->data, fifo->len)) { - send(s->hci, fifo->data, fifo->len); - fifo->len = 0; - } - - /* TODO: do we need to loop? */ -} - -static int usb_bt_hci_cmd_complete(const uint8_t *data, int len) -{ - len -= HCI_COMMAND_HDR_SIZE; - return len >= 0 && - len >= ((struct hci_command_hdr *) data)->plen; -} - -static int usb_bt_hci_acl_complete(const uint8_t *data, int len) -{ - len -= HCI_ACL_HDR_SIZE; - return len >= 0 && - len >= le16_to_cpu(((struct hci_acl_hdr *) data)->dlen); -} - -static int usb_bt_hci_sco_complete(const uint8_t *data, int len) -{ - len -= HCI_SCO_HDR_SIZE; - return len >= 0 && - len >= ((struct hci_sco_hdr *) data)->dlen; -} - -static void usb_bt_handle_reset(USBDevice *dev) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - - usb_bt_fifo_reset(&s->evt); - usb_bt_fifo_reset(&s->acl); - usb_bt_fifo_reset(&s->sco); - s->outcmd.len = 0; - s->outacl.len = 0; - s->outsco.len = 0; -} - -static void 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, p, request, value, index, length, data); - if (ret >= 0) { - switch (request) { - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - s->config = 0; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - s->config = 1; - usb_bt_fifo_reset(&s->evt); - usb_bt_fifo_reset(&s->acl); - usb_bt_fifo_reset(&s->sco); - break; - } - return; - } - - switch (request) { - case InterfaceRequest | USB_REQ_GET_STATUS: - case EndpointRequest | USB_REQ_GET_STATUS: - data[0] = 0x00; - data[1] = 0x00; - p->actual_length = 2; - break; - case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE: - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - goto fail; - case InterfaceOutRequest | USB_REQ_SET_FEATURE: - case EndpointOutRequest | USB_REQ_SET_FEATURE: - goto fail; - break; - case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8): - if (s->config) - usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send, - usb_bt_hci_cmd_complete, p); - break; - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_bt_handle_data(USBDevice *dev, USBPacket *p) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - - if (!s->config) - goto fail; - - switch (p->pid) { - case USB_TOKEN_IN: - switch (p->ep->nr) { - case USB_EVT_EP: - if (s->evt.len == 0) { - p->status = USB_RET_NAK; - break; - } - usb_bt_fifo_dequeue(&s->evt, p); - break; - - case USB_ACL_EP: - if (s->evt.len == 0) { - p->status = USB_RET_STALL; - break; - } - usb_bt_fifo_dequeue(&s->acl, p); - break; - - case USB_SCO_EP: - if (s->evt.len == 0) { - p->status = USB_RET_STALL; - break; - } - usb_bt_fifo_dequeue(&s->sco, p); - break; - - default: - goto fail; - } - break; - - case USB_TOKEN_OUT: - switch (p->ep->nr) { - case USB_ACL_EP: - usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, - usb_bt_hci_acl_complete, p); - break; - - case USB_SCO_EP: - usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send, - usb_bt_hci_sco_complete, p); - break; - - default: - goto fail; - } - break; - - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_bt_out_hci_packet_event(void *opaque, - const uint8_t *data, int len) -{ - struct USBBtState *s = (struct USBBtState *) opaque; - - if (s->evt.len == 0) { - usb_wakeup(s->intr, 0); - } - usb_bt_fifo_enqueue(&s->evt, data, len); -} - -static void usb_bt_out_hci_packet_acl(void *opaque, - const uint8_t *data, int len) -{ - struct USBBtState *s = (struct USBBtState *) opaque; - - usb_bt_fifo_enqueue(&s->acl, data, len); -} - -static void usb_bt_handle_destroy(USBDevice *dev) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - - s->hci->opaque = NULL; - s->hci->evt_recv = NULL; - s->hci->acl_recv = NULL; -} - -static void usb_bt_realize(USBDevice *dev, Error **errp) -{ - struct USBBtState *s = USB_BT(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - s->dev.opaque = s; - if (!s->hci) { - s->hci = bt_new_hci(qemu_find_bt_vlan(0)); - } - s->hci->opaque = s; - s->hci->evt_recv = usb_bt_out_hci_packet_event; - s->hci->acl_recv = usb_bt_out_hci_packet_acl; - usb_bt_handle_reset(&s->dev); - s->intr = usb_ep_get(dev, USB_TOKEN_IN, USB_EVT_EP); -} - -static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline) -{ - USBDevice *dev; - struct USBBtState *s; - HCIInfo *hci; - const char *name = TYPE_USB_BT; - - if (*cmdline) { - hci = hci_init(cmdline); - } else { - hci = bt_new_hci(qemu_find_bt_vlan(0)); - } - if (!hci) - return NULL; - - dev = usb_create(bus, name); - s = USB_BT(dev); - s->hci = hci; - return dev; -} - -static const VMStateDescription vmstate_usb_bt = { - .name = "usb-bt", - .unmigratable = 1, -}; - -static void usb_bt_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_bt_realize; - uc->product_desc = "QEMU BT dongle"; - uc->usb_desc = &desc_bluetooth; - uc->handle_reset = usb_bt_handle_reset; - uc->handle_control = usb_bt_handle_control; - uc->handle_data = usb_bt_handle_data; - uc->handle_destroy = usb_bt_handle_destroy; - dc->vmsd = &vmstate_usb_bt; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -static const TypeInfo bt_info = { - .name = TYPE_USB_BT, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(struct USBBtState), - .class_init = usb_bt_class_initfn, -}; - -static void usb_bt_register_types(void) -{ - type_register_static(&bt_info); - usb_legacy_register(TYPE_USB_BT, "bt", usb_bt_init); -} - -type_init(usb_bt_register_types) diff --git a/qemu/hw/usb/dev-hid.c b/qemu/hw/usb/dev-hid.c deleted file mode 100644 index 24d05f76f..000000000 --- a/qemu/hw/usb/dev-hid.c +++ /dev/null @@ -1,883 +0,0 @@ -/* - * QEMU USB HID devices - * - * Copyright (c) 2005 Fabrice Bellard - * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "ui/console.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "hw/input/hid.h" - -/* HID interface requests */ -#define GET_REPORT 0xa101 -#define GET_IDLE 0xa102 -#define GET_PROTOCOL 0xa103 -#define SET_REPORT 0x2109 -#define SET_IDLE 0x210a -#define SET_PROTOCOL 0x210b - -/* HID descriptor types */ -#define USB_DT_HID 0x21 -#define USB_DT_REPORT 0x22 -#define USB_DT_PHY 0x23 - -typedef struct USBHIDState { - USBDevice dev; - USBEndpoint *intr; - HIDState hid; - uint32_t usb_version; - char *display; - uint32_t head; -} USBHIDState; - -#define TYPE_USB_HID "usb-hid" -#define USB_HID(obj) OBJECT_CHECK(USBHIDState, (obj), TYPE_USB_HID) - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT_MOUSE, - STR_PRODUCT_TABLET, - STR_PRODUCT_KEYBOARD, - STR_SERIALNUMBER, - STR_CONFIG_MOUSE, - STR_CONFIG_TABLET, - STR_CONFIG_KEYBOARD, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT_MOUSE] = "QEMU USB Mouse", - [STR_PRODUCT_TABLET] = "QEMU USB Tablet", - [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard", - [STR_SERIALNUMBER] = "42", /* == remote wakeup works */ - [STR_CONFIG_MOUSE] = "HID Mouse", - [STR_CONFIG_TABLET] = "HID Tablet", - [STR_CONFIG_KEYBOARD] = "HID Keyboard", -}; - -static const USBDescIface desc_iface_mouse = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 52, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 4, - .bInterval = 0x0a, - }, - }, -}; - -static const USBDescIface desc_iface_mouse2 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 52, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 4, - .bInterval = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms */ - }, - }, -}; - -static const USBDescIface desc_iface_tablet = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 74, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 0x0a, - }, - }, -}; - -static const USBDescIface desc_iface_tablet2 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 74, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 4, /* 2 ^ (4-1) * 125 usecs = 1 ms */ - }, - }, -}; - -static const USBDescIface desc_iface_keyboard = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x01, /* keyboard */ - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x11, 0x01, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 0x3f, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 0x0a, - }, - }, -}; - -static const USBDescIface desc_iface_keyboard2 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x01, /* keyboard */ - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x11, 0x01, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 0x3f, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms */ - }, - }, -}; - -static const USBDescDevice desc_device_mouse = { - .bcdUSB = 0x0100, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_MOUSE, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_mouse, - }, - }, -}; - -static const USBDescDevice desc_device_mouse2 = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_MOUSE, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_mouse2, - }, - }, -}; - -static const USBDescDevice desc_device_tablet = { - .bcdUSB = 0x0100, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_TABLET, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_tablet, - }, - }, -}; - -static const USBDescDevice desc_device_tablet2 = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_TABLET, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_tablet2, - }, - }, -}; - -static const USBDescDevice desc_device_keyboard = { - .bcdUSB = 0x0100, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_KEYBOARD, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_keyboard, - }, - }, -}; - -static const USBDescDevice desc_device_keyboard2 = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_KEYBOARD, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_keyboard2, - }, - }, -}; - -static const USBDescMSOS desc_msos_suspend = { - .SelectiveSuspendEnabled = true, -}; - -static const USBDesc desc_mouse = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_MOUSE, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_mouse, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_mouse2 = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_MOUSE, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_mouse, - .high = &desc_device_mouse2, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_tablet = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_TABLET, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_tablet, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_tablet2 = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_TABLET, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_tablet, - .high = &desc_device_tablet2, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_keyboard = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_KEYBOARD, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_keyboard, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_keyboard2 = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_KEYBOARD, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_keyboard, - .high = &desc_device_keyboard2, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const uint8_t qemu_mouse_hid_report_descriptor[] = { - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x02, /* Usage (Mouse) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x09, 0x01, /* Usage (Pointer) */ - 0xa1, 0x00, /* Collection (Physical) */ - 0x05, 0x09, /* Usage Page (Button) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x03, /* Usage Maximum (3) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x95, 0x03, /* Report Count (3) */ - 0x75, 0x01, /* Report Size (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x05, /* Report Size (5) */ - 0x81, 0x01, /* Input (Constant) */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x30, /* Usage (X) */ - 0x09, 0x31, /* Usage (Y) */ - 0x09, 0x38, /* Usage (Wheel) */ - 0x15, 0x81, /* Logical Minimum (-0x7f) */ - 0x25, 0x7f, /* Logical Maximum (0x7f) */ - 0x75, 0x08, /* Report Size (8) */ - 0x95, 0x03, /* Report Count (3) */ - 0x81, 0x06, /* Input (Data, Variable, Relative) */ - 0xc0, /* End Collection */ - 0xc0, /* End Collection */ -}; - -static const uint8_t qemu_tablet_hid_report_descriptor[] = { - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x01, /* Usage (Pointer) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x09, 0x01, /* Usage (Pointer) */ - 0xa1, 0x00, /* Collection (Physical) */ - 0x05, 0x09, /* Usage Page (Button) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x03, /* Usage Maximum (3) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x95, 0x03, /* Report Count (3) */ - 0x75, 0x01, /* Report Size (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x05, /* Report Size (5) */ - 0x81, 0x01, /* Input (Constant) */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x30, /* Usage (X) */ - 0x09, 0x31, /* Usage (Y) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */ - 0x35, 0x00, /* Physical Minimum (0) */ - 0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */ - 0x75, 0x10, /* Report Size (16) */ - 0x95, 0x02, /* Report Count (2) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x38, /* Usage (Wheel) */ - 0x15, 0x81, /* Logical Minimum (-0x7f) */ - 0x25, 0x7f, /* Logical Maximum (0x7f) */ - 0x35, 0x00, /* Physical Minimum (same as logical) */ - 0x45, 0x00, /* Physical Maximum (same as logical) */ - 0x75, 0x08, /* Report Size (8) */ - 0x95, 0x01, /* Report Count (1) */ - 0x81, 0x06, /* Input (Data, Variable, Relative) */ - 0xc0, /* End Collection */ - 0xc0, /* End Collection */ -}; - -static const uint8_t qemu_keyboard_hid_report_descriptor[] = { - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x06, /* Usage (Keyboard) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x08, /* Report Count (8) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0xe0, /* Usage Minimum (224) */ - 0x29, 0xe7, /* Usage Maximum (231) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x08, /* Report Size (8) */ - 0x81, 0x01, /* Input (Constant) */ - 0x95, 0x05, /* Report Count (5) */ - 0x75, 0x01, /* Report Size (1) */ - 0x05, 0x08, /* Usage Page (LEDs) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x05, /* Usage Maximum (5) */ - 0x91, 0x02, /* Output (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x03, /* Report Size (3) */ - 0x91, 0x01, /* Output (Constant) */ - 0x95, 0x06, /* Report Count (6) */ - 0x75, 0x08, /* Report Size (8) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0xff, /* Logical Maximum (255) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0x00, /* Usage Minimum (0) */ - 0x29, 0xff, /* Usage Maximum (255) */ - 0x81, 0x00, /* Input (Data, Array) */ - 0xc0, /* End Collection */ -}; - -static void usb_hid_changed(HIDState *hs) -{ - USBHIDState *us = container_of(hs, USBHIDState, hid); - - usb_wakeup(us->intr, 0); -} - -static void usb_hid_handle_reset(USBDevice *dev) -{ - USBHIDState *us = USB_HID(dev); - - hid_reset(&us->hid); -} - -static void usb_hid_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - USBHIDState *us = USB_HID(dev); - HIDState *hs = &us->hid; - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - /* hid specific requests */ - case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: - switch (value >> 8) { - case 0x22: - if (hs->kind == HID_MOUSE) { - memcpy(data, qemu_mouse_hid_report_descriptor, - sizeof(qemu_mouse_hid_report_descriptor)); - p->actual_length = sizeof(qemu_mouse_hid_report_descriptor); - } else if (hs->kind == HID_TABLET) { - memcpy(data, qemu_tablet_hid_report_descriptor, - sizeof(qemu_tablet_hid_report_descriptor)); - p->actual_length = sizeof(qemu_tablet_hid_report_descriptor); - } else if (hs->kind == HID_KEYBOARD) { - memcpy(data, qemu_keyboard_hid_report_descriptor, - sizeof(qemu_keyboard_hid_report_descriptor)); - p->actual_length = sizeof(qemu_keyboard_hid_report_descriptor); - } - break; - default: - goto fail; - } - break; - case GET_REPORT: - if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { - p->actual_length = hid_pointer_poll(hs, data, length); - } else if (hs->kind == HID_KEYBOARD) { - p->actual_length = hid_keyboard_poll(hs, data, length); - } - break; - case SET_REPORT: - if (hs->kind == HID_KEYBOARD) { - p->actual_length = hid_keyboard_write(hs, data, length); - } else { - goto fail; - } - break; - case GET_PROTOCOL: - if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { - goto fail; - } - data[0] = hs->protocol; - p->actual_length = 1; - break; - case SET_PROTOCOL: - if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { - goto fail; - } - hs->protocol = value; - break; - case GET_IDLE: - data[0] = hs->idle; - p->actual_length = 1; - break; - case SET_IDLE: - hs->idle = (uint8_t) (value >> 8); - hid_set_next_idle(hs); - if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { - hid_pointer_activate(hs); - } - break; - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_hid_handle_data(USBDevice *dev, USBPacket *p) -{ - USBHIDState *us = USB_HID(dev); - HIDState *hs = &us->hid; - uint8_t buf[p->iov.size]; - int len = 0; - - switch (p->pid) { - case USB_TOKEN_IN: - if (p->ep->nr == 1) { - if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { - hid_pointer_activate(hs); - } - if (!hid_has_events(hs)) { - p->status = USB_RET_NAK; - return; - } - hid_set_next_idle(hs); - if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { - len = hid_pointer_poll(hs, buf, p->iov.size); - } else if (hs->kind == HID_KEYBOARD) { - len = hid_keyboard_poll(hs, buf, p->iov.size); - } - usb_packet_copy(p, buf, len); - } else { - goto fail; - } - break; - case USB_TOKEN_OUT: - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_hid_handle_destroy(USBDevice *dev) -{ - USBHIDState *us = USB_HID(dev); - - hid_free(&us->hid); -} - -static void usb_hid_initfn(USBDevice *dev, int kind, - const USBDesc *usb1, const USBDesc *usb2, - Error **errp) -{ - USBHIDState *us = USB_HID(dev); - switch (us->usb_version) { - case 1: - dev->usb_desc = usb1; - break; - case 2: - dev->usb_desc = usb2; - break; - default: - dev->usb_desc = NULL; - } - if (!dev->usb_desc) { - error_setg(errp, "Invalid usb version %d for usb hid device", - us->usb_version); - return; - } - - if (dev->serial) { - usb_desc_set_string(dev, STR_SERIALNUMBER, dev->serial); - } - usb_desc_init(dev); - us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); - hid_init(&us->hid, kind, usb_hid_changed); - if (us->display && us->hid.s) { - qemu_input_handler_bind(us->hid.s, us->display, us->head, NULL); - } -} - -static void usb_tablet_realize(USBDevice *dev, Error **errp) -{ - - usb_hid_initfn(dev, HID_TABLET, &desc_tablet, &desc_tablet2, errp); -} - -static void usb_mouse_realize(USBDevice *dev, Error **errp) -{ - usb_hid_initfn(dev, HID_MOUSE, &desc_mouse, &desc_mouse2, errp); -} - -static void usb_keyboard_realize(USBDevice *dev, Error **errp) -{ - usb_hid_initfn(dev, HID_KEYBOARD, &desc_keyboard, &desc_keyboard2, errp); -} - -static int usb_ptr_post_load(void *opaque, int version_id) -{ - USBHIDState *s = opaque; - - if (s->dev.remote_wakeup) { - hid_pointer_activate(&s->hid); - } - return 0; -} - -static const VMStateDescription vmstate_usb_ptr = { - .name = "usb-ptr", - .version_id = 1, - .minimum_version_id = 1, - .post_load = usb_ptr_post_load, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_HID_POINTER_DEVICE(hid, USBHIDState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_usb_kbd = { - .name = "usb-kbd", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_HID_KEYBOARD_DEVICE(hid, USBHIDState), - VMSTATE_END_OF_LIST() - } -}; - -static void usb_hid_class_initfn(ObjectClass *klass, void *data) -{ - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->handle_reset = usb_hid_handle_reset; - uc->handle_control = usb_hid_handle_control; - uc->handle_data = usb_hid_handle_data; - uc->handle_destroy = usb_hid_handle_destroy; - uc->handle_attach = usb_desc_attach; -} - -static const TypeInfo usb_hid_type_info = { - .name = TYPE_USB_HID, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBHIDState), - .abstract = true, - .class_init = usb_hid_class_initfn, -}; - -static Property usb_tablet_properties[] = { - DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), - DEFINE_PROP_STRING("display", USBHIDState, display), - DEFINE_PROP_UINT32("head", USBHIDState, head, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_tablet_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_tablet_realize; - uc->product_desc = "QEMU USB Tablet"; - dc->vmsd = &vmstate_usb_ptr; - dc->props = usb_tablet_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo usb_tablet_info = { - .name = "usb-tablet", - .parent = TYPE_USB_HID, - .class_init = usb_tablet_class_initfn, -}; - -static Property usb_mouse_properties[] = { - DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_mouse_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_mouse_realize; - uc->product_desc = "QEMU USB Mouse"; - dc->vmsd = &vmstate_usb_ptr; - dc->props = usb_mouse_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo usb_mouse_info = { - .name = "usb-mouse", - .parent = TYPE_USB_HID, - .class_init = usb_mouse_class_initfn, -}; - -static Property usb_keyboard_properties[] = { - DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), - DEFINE_PROP_STRING("display", USBHIDState, display), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_keyboard_realize; - uc->product_desc = "QEMU USB Keyboard"; - dc->vmsd = &vmstate_usb_kbd; - dc->props = usb_keyboard_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo usb_keyboard_info = { - .name = "usb-kbd", - .parent = TYPE_USB_HID, - .class_init = usb_keyboard_class_initfn, -}; - -static void usb_hid_register_types(void) -{ - type_register_static(&usb_hid_type_info); - type_register_static(&usb_tablet_info); - usb_legacy_register("usb-tablet", "tablet", NULL); - type_register_static(&usb_mouse_info); - usb_legacy_register("usb-mouse", "mouse", NULL); - type_register_static(&usb_keyboard_info); - usb_legacy_register("usb-kbd", "keyboard", NULL); -} - -type_init(usb_hid_register_types) diff --git a/qemu/hw/usb/dev-hub.c b/qemu/hw/usb/dev-hub.c deleted file mode 100644 index a33f21cb3..000000000 --- a/qemu/hw/usb/dev-hub.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - * QEMU USB HUB emulation - * - * Copyright (c) 2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "trace.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "qemu/error-report.h" - -#define NUM_PORTS 8 - -typedef struct USBHubPort { - USBPort port; - uint16_t wPortStatus; - uint16_t wPortChange; -} USBHubPort; - -typedef struct USBHubState { - USBDevice dev; - USBEndpoint *intr; - USBHubPort ports[NUM_PORTS]; -} USBHubState; - -#define TYPE_USB_HUB "usb-hub" -#define USB_HUB(obj) OBJECT_CHECK(USBHubState, (obj), TYPE_USB_HUB) - -#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) -#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) -#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) -#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) -#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) -#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) -#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) - -#define PORT_STAT_CONNECTION 0x0001 -#define PORT_STAT_ENABLE 0x0002 -#define PORT_STAT_SUSPEND 0x0004 -#define PORT_STAT_OVERCURRENT 0x0008 -#define PORT_STAT_RESET 0x0010 -#define PORT_STAT_POWER 0x0100 -#define PORT_STAT_LOW_SPEED 0x0200 -#define PORT_STAT_HIGH_SPEED 0x0400 -#define PORT_STAT_TEST 0x0800 -#define PORT_STAT_INDICATOR 0x1000 - -#define PORT_STAT_C_CONNECTION 0x0001 -#define PORT_STAT_C_ENABLE 0x0002 -#define PORT_STAT_C_SUSPEND 0x0004 -#define PORT_STAT_C_OVERCURRENT 0x0008 -#define PORT_STAT_C_RESET 0x0010 - -#define PORT_CONNECTION 0 -#define PORT_ENABLE 1 -#define PORT_SUSPEND 2 -#define PORT_OVERCURRENT 3 -#define PORT_RESET 4 -#define PORT_POWER 8 -#define PORT_LOWSPEED 9 -#define PORT_HIGHSPEED 10 -#define PORT_C_CONNECTION 16 -#define PORT_C_ENABLE 17 -#define PORT_C_SUSPEND 18 -#define PORT_C_OVERCURRENT 19 -#define PORT_C_RESET 20 -#define PORT_TEST 21 -#define PORT_INDICATOR 22 - -/* same as Linux kernel root hubs */ - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "QEMU USB Hub", - [STR_SERIALNUMBER] = "314159", -}; - -static const USBDescIface desc_iface_hub = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HUB, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8, - .bInterval = 0xff, - }, - } -}; - -static const USBDescDevice desc_device_hub = { - .bcdUSB = 0x0110, - .bDeviceClass = USB_CLASS_HUB, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER | - USB_CFG_ATT_WAKEUP, - .nif = 1, - .ifs = &desc_iface_hub, - }, - }, -}; - -static const USBDesc desc_hub = { - .id = { - .idVendor = 0x0409, - .idProduct = 0x55aa, - .bcdDevice = 0x0101, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_hub, - .str = desc_strings, -}; - -static const uint8_t qemu_hub_hub_descriptor[] = -{ - 0x00, /* u8 bLength; patched in later */ - 0x29, /* u8 bDescriptorType; Hub-descriptor */ - 0x00, /* u8 bNbrPorts; (patched later) */ - 0x0a, /* u16 wHubCharacteristics; */ - 0x00, /* (per-port OC, no power switching) */ - 0x01, /* u8 bPwrOn2pwrGood; 2ms */ - 0x00 /* u8 bHubContrCurrent; 0 mA */ - - /* DeviceRemovable and PortPwrCtrlMask patched in later */ -}; - -static void usb_hub_attach(USBPort *port1) -{ - USBHubState *s = port1->opaque; - USBHubPort *port = &s->ports[port1->index]; - - trace_usb_hub_attach(s->dev.addr, port1->index + 1); - port->wPortStatus |= PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->port.dev->speed == USB_SPEED_LOW) { - port->wPortStatus |= PORT_STAT_LOW_SPEED; - } else { - port->wPortStatus &= ~PORT_STAT_LOW_SPEED; - } - usb_wakeup(s->intr, 0); -} - -static void usb_hub_detach(USBPort *port1) -{ - USBHubState *s = port1->opaque; - USBHubPort *port = &s->ports[port1->index]; - - trace_usb_hub_detach(s->dev.addr, port1->index + 1); - usb_wakeup(s->intr, 0); - - /* Let upstream know the device on this port is gone */ - s->dev.port->ops->child_detach(s->dev.port, port1->dev); - - port->wPortStatus &= ~PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->wPortStatus & PORT_STAT_ENABLE) { - port->wPortStatus &= ~PORT_STAT_ENABLE; - port->wPortChange |= PORT_STAT_C_ENABLE; - } - usb_wakeup(s->intr, 0); -} - -static void usb_hub_child_detach(USBPort *port1, USBDevice *child) -{ - USBHubState *s = port1->opaque; - - /* Pass along upstream */ - s->dev.port->ops->child_detach(s->dev.port, child); -} - -static void usb_hub_wakeup(USBPort *port1) -{ - USBHubState *s = port1->opaque; - USBHubPort *port = &s->ports[port1->index]; - - if (port->wPortStatus & PORT_STAT_SUSPEND) { - port->wPortChange |= PORT_STAT_C_SUSPEND; - usb_wakeup(s->intr, 0); - } -} - -static void usb_hub_complete(USBPort *port, USBPacket *packet) -{ - USBHubState *s = port->opaque; - - /* - * Just pass it along upstream for now. - * - * If we ever implement usb 2.0 split transactions this will - * become a little more complicated ... - * - * Can't use usb_packet_complete() here because packet->owner is - * cleared already, go call the ->complete() callback directly - * instead. - */ - s->dev.port->ops->complete(s->dev.port, packet); -} - -static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr) -{ - USBHubState *s = USB_HUB(dev); - USBHubPort *port; - USBDevice *downstream; - int i; - - for (i = 0; i < NUM_PORTS; i++) { - port = &s->ports[i]; - if (!(port->wPortStatus & PORT_STAT_ENABLE)) { - continue; - } - downstream = usb_find_device(&port->port, addr); - if (downstream != NULL) { - return downstream; - } - } - return NULL; -} - -static void usb_hub_handle_reset(USBDevice *dev) -{ - USBHubState *s = USB_HUB(dev); - USBHubPort *port; - int i; - - trace_usb_hub_reset(s->dev.addr); - for (i = 0; i < NUM_PORTS; i++) { - port = s->ports + i; - port->wPortStatus = PORT_STAT_POWER; - port->wPortChange = 0; - if (port->port.dev && port->port.dev->attached) { - port->wPortStatus |= PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->port.dev->speed == USB_SPEED_LOW) { - port->wPortStatus |= PORT_STAT_LOW_SPEED; - } - } - } -} - -static const char *feature_name(int feature) -{ - static const char *name[] = { - [PORT_CONNECTION] = "connection", - [PORT_ENABLE] = "enable", - [PORT_SUSPEND] = "suspend", - [PORT_OVERCURRENT] = "overcurrent", - [PORT_RESET] = "reset", - [PORT_POWER] = "power", - [PORT_LOWSPEED] = "lowspeed", - [PORT_HIGHSPEED] = "highspeed", - [PORT_C_CONNECTION] = "change connection", - [PORT_C_ENABLE] = "change enable", - [PORT_C_SUSPEND] = "change suspend", - [PORT_C_OVERCURRENT] = "change overcurrent", - [PORT_C_RESET] = "change reset", - [PORT_TEST] = "test", - [PORT_INDICATOR] = "indicator", - }; - if (feature < 0 || feature >= ARRAY_SIZE(name)) { - return "?"; - } - return name[feature] ?: "?"; -} - -static void 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; - - trace_usb_hub_control(s->dev.addr, request, value, index, length); - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch(request) { - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == 0 && index != 0x81) { /* clear ep halt */ - goto fail; - } - break; - /* usb specific requests */ - case GetHubStatus: - data[0] = 0; - data[1] = 0; - data[2] = 0; - data[3] = 0; - p->actual_length = 4; - break; - case GetPortStatus: - { - unsigned int n = index - 1; - USBHubPort *port; - if (n >= NUM_PORTS) { - goto fail; - } - port = &s->ports[n]; - trace_usb_hub_get_port_status(s->dev.addr, index, - port->wPortStatus, - port->wPortChange); - data[0] = port->wPortStatus; - data[1] = port->wPortStatus >> 8; - data[2] = port->wPortChange; - data[3] = port->wPortChange >> 8; - p->actual_length = 4; - } - break; - case SetHubFeature: - case ClearHubFeature: - if (value != 0 && value != 1) { - goto fail; - } - break; - case SetPortFeature: - { - unsigned int n = index - 1; - USBHubPort *port; - USBDevice *dev; - - trace_usb_hub_set_port_feature(s->dev.addr, index, - feature_name(value)); - - if (n >= NUM_PORTS) { - goto fail; - } - port = &s->ports[n]; - dev = port->port.dev; - switch(value) { - case PORT_SUSPEND: - port->wPortStatus |= PORT_STAT_SUSPEND; - break; - case PORT_RESET: - if (dev && dev->attached) { - usb_device_reset(dev); - port->wPortChange |= PORT_STAT_C_RESET; - /* set enable bit */ - port->wPortStatus |= PORT_STAT_ENABLE; - usb_wakeup(s->intr, 0); - } - break; - case PORT_POWER: - break; - default: - goto fail; - } - } - break; - case ClearPortFeature: - { - unsigned int n = index - 1; - USBHubPort *port; - - trace_usb_hub_clear_port_feature(s->dev.addr, index, - feature_name(value)); - - if (n >= NUM_PORTS) { - goto fail; - } - port = &s->ports[n]; - switch(value) { - case PORT_ENABLE: - port->wPortStatus &= ~PORT_STAT_ENABLE; - break; - case PORT_C_ENABLE: - port->wPortChange &= ~PORT_STAT_C_ENABLE; - break; - case PORT_SUSPEND: - port->wPortStatus &= ~PORT_STAT_SUSPEND; - break; - case PORT_C_SUSPEND: - port->wPortChange &= ~PORT_STAT_C_SUSPEND; - break; - case PORT_C_CONNECTION: - port->wPortChange &= ~PORT_STAT_C_CONNECTION; - break; - case PORT_C_OVERCURRENT: - port->wPortChange &= ~PORT_STAT_C_OVERCURRENT; - break; - case PORT_C_RESET: - port->wPortChange &= ~PORT_STAT_C_RESET; - break; - default: - goto fail; - } - } - break; - case GetHubDescriptor: - { - unsigned int n, limit, var_hub_size = 0; - memcpy(data, qemu_hub_hub_descriptor, - sizeof(qemu_hub_hub_descriptor)); - data[2] = NUM_PORTS; - - /* fill DeviceRemovable bits */ - limit = ((NUM_PORTS + 1 + 7) / 8) + 7; - for (n = 7; n < limit; n++) { - data[n] = 0x00; - var_hub_size++; - } - - /* fill PortPwrCtrlMask bits */ - limit = limit + ((NUM_PORTS + 7) / 8); - for (;n < limit; n++) { - data[n] = 0xff; - var_hub_size++; - } - - p->actual_length = sizeof(qemu_hub_hub_descriptor) + var_hub_size; - data[0] = p->actual_length; - break; - } - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_hub_handle_data(USBDevice *dev, USBPacket *p) -{ - USBHubState *s = (USBHubState *)dev; - - switch(p->pid) { - case USB_TOKEN_IN: - if (p->ep->nr == 1) { - USBHubPort *port; - unsigned int status; - uint8_t buf[4]; - int i, n; - n = (NUM_PORTS + 1 + 7) / 8; - if (p->iov.size == 1) { /* FreeBSD workaround */ - n = 1; - } else if (n > p->iov.size) { - p->status = USB_RET_BABBLE; - return; - } - status = 0; - for(i = 0; i < NUM_PORTS; i++) { - port = &s->ports[i]; - if (port->wPortChange) - status |= (1 << (i + 1)); - } - if (status != 0) { - trace_usb_hub_status_report(s->dev.addr, status); - for(i = 0; i < n; i++) { - buf[i] = status >> (8 * i); - } - usb_packet_copy(p, buf, n); - } else { - p->status = USB_RET_NAK; /* usb11 11.13.1 */ - } - } else { - goto fail; - } - break; - case USB_TOKEN_OUT: - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_hub_handle_destroy(USBDevice *dev) -{ - USBHubState *s = (USBHubState *)dev; - int i; - - for (i = 0; i < NUM_PORTS; i++) { - usb_unregister_port(usb_bus_from_device(dev), - &s->ports[i].port); - } -} - -static USBPortOps usb_hub_port_ops = { - .attach = usb_hub_attach, - .detach = usb_hub_detach, - .child_detach = usb_hub_child_detach, - .wakeup = usb_hub_wakeup, - .complete = usb_hub_complete, -}; - -static void usb_hub_realize(USBDevice *dev, Error **errp) -{ - USBHubState *s = USB_HUB(dev); - USBHubPort *port; - int i; - - if (dev->port->hubcount == 5) { - error_setg(errp, "usb hub chain too deep"); - return; - } - - usb_desc_create_serial(dev); - usb_desc_init(dev); - s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); - for (i = 0; i < NUM_PORTS; i++) { - port = &s->ports[i]; - usb_register_port(usb_bus_from_device(dev), - &port->port, s, i, &usb_hub_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&port->port, dev->port, i+1); - } - usb_hub_handle_reset(dev); -} - -static const VMStateDescription vmstate_usb_hub_port = { - .name = "usb-hub-port", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(wPortStatus, USBHubPort), - VMSTATE_UINT16(wPortChange, USBHubPort), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_usb_hub = { - .name = "usb-hub", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, USBHubState), - VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0, - vmstate_usb_hub_port, USBHubPort), - VMSTATE_END_OF_LIST() - } -}; - -static void usb_hub_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_hub_realize; - uc->product_desc = "QEMU USB Hub"; - uc->usb_desc = &desc_hub; - uc->find_device = usb_hub_find_device; - uc->handle_reset = usb_hub_handle_reset; - uc->handle_control = usb_hub_handle_control; - uc->handle_data = usb_hub_handle_data; - uc->handle_destroy = usb_hub_handle_destroy; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->fw_name = "hub"; - dc->vmsd = &vmstate_usb_hub; -} - -static const TypeInfo hub_info = { - .name = TYPE_USB_HUB, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBHubState), - .class_init = usb_hub_class_initfn, -}; - -static void usb_hub_register_types(void) -{ - type_register_static(&hub_info); -} - -type_init(usb_hub_register_types) diff --git a/qemu/hw/usb/dev-mtp.c b/qemu/hw/usb/dev-mtp.c deleted file mode 100644 index bda84a64b..000000000 --- a/qemu/hw/usb/dev-mtp.c +++ /dev/null @@ -1,1414 +0,0 @@ -/* - * Media Transfer Protocol implementation, backed by host filesystem. - * - * Copyright Red Hat, Inc 2014 - * - * Author: - * Gerd Hoffmann <kraxel@redhat.com> - * - * This code is licensed under the GPL v2 or later. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include <wchar.h> -#include <dirent.h> - -#include <sys/statvfs.h> -#ifdef CONFIG_INOTIFY1 -#include <sys/inotify.h> -#include "qapi/error.h" -#include "qemu/main-loop.h" -#endif - -#include "qemu-common.h" -#include "qemu/iov.h" -#include "trace.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" - -/* ----------------------------------------------------------------------- */ - -enum mtp_container_type { - TYPE_COMMAND = 1, - TYPE_DATA = 2, - TYPE_RESPONSE = 3, - TYPE_EVENT = 4, -}; - -enum mtp_code { - /* command codes */ - CMD_GET_DEVICE_INFO = 0x1001, - CMD_OPEN_SESSION = 0x1002, - CMD_CLOSE_SESSION = 0x1003, - CMD_GET_STORAGE_IDS = 0x1004, - CMD_GET_STORAGE_INFO = 0x1005, - CMD_GET_NUM_OBJECTS = 0x1006, - CMD_GET_OBJECT_HANDLES = 0x1007, - CMD_GET_OBJECT_INFO = 0x1008, - CMD_GET_OBJECT = 0x1009, - CMD_GET_PARTIAL_OBJECT = 0x101b, - - /* response codes */ - RES_OK = 0x2001, - RES_GENERAL_ERROR = 0x2002, - RES_SESSION_NOT_OPEN = 0x2003, - RES_INVALID_TRANSACTION_ID = 0x2004, - RES_OPERATION_NOT_SUPPORTED = 0x2005, - RES_PARAMETER_NOT_SUPPORTED = 0x2006, - RES_INCOMPLETE_TRANSFER = 0x2007, - RES_INVALID_STORAGE_ID = 0x2008, - RES_INVALID_OBJECT_HANDLE = 0x2009, - RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014, - RES_INVALID_PARENT_OBJECT = 0x201a, - RES_INVALID_PARAMETER = 0x201d, - RES_SESSION_ALREADY_OPEN = 0x201e, - - /* format codes */ - FMT_UNDEFINED_OBJECT = 0x3000, - FMT_ASSOCIATION = 0x3001, - - /* event codes */ - EVT_OBJ_ADDED = 0x4002, - EVT_OBJ_REMOVED = 0x4003, - EVT_OBJ_INFO_CHANGED = 0x4007, -}; - -typedef struct { - uint32_t length; - uint16_t type; - uint16_t code; - uint32_t trans; -} QEMU_PACKED mtp_container; - -/* ----------------------------------------------------------------------- */ - -typedef struct MTPState MTPState; -typedef struct MTPControl MTPControl; -typedef struct MTPData MTPData; -typedef struct MTPObject MTPObject; - -enum { - EP_DATA_IN = 1, - EP_DATA_OUT, - EP_EVENT, -}; - -#ifdef CONFIG_INOTIFY1 -typedef struct MTPMonEntry MTPMonEntry; - -struct MTPMonEntry { - uint32_t event; - uint32_t handle; - - QTAILQ_ENTRY(MTPMonEntry) next; -}; -#endif - -struct MTPControl { - uint16_t code; - uint32_t trans; - int argc; - uint32_t argv[5]; -}; - -struct MTPData { - uint16_t code; - uint32_t trans; - uint32_t offset; - uint32_t length; - uint32_t alloc; - uint8_t *data; - bool first; - int fd; -}; - -struct MTPObject { - uint32_t handle; - uint16_t format; - char *name; - char *path; - struct stat stat; -#ifdef CONFIG_INOTIFY1 - /* inotify watch cookie */ - int watchfd; -#endif - MTPObject *parent; - uint32_t nchildren; - QLIST_HEAD(, MTPObject) children; - QLIST_ENTRY(MTPObject) list; - bool have_children; - QTAILQ_ENTRY(MTPObject) next; -}; - -struct MTPState { - USBDevice dev; - char *root; - char *desc; - uint32_t flags; - - MTPData *data_in; - MTPData *data_out; - MTPControl *result; - uint32_t session; - uint32_t next_handle; - - QTAILQ_HEAD(, MTPObject) objects; -#ifdef CONFIG_INOTIFY1 - /* inotify descriptor */ - int inotifyfd; - QTAILQ_HEAD(events, MTPMonEntry) events; -#endif -}; - -#define TYPE_USB_MTP "usb-mtp" -#define USB_MTP(obj) OBJECT_CHECK(MTPState, (obj), TYPE_USB_MTP) - -#define QEMU_STORAGE_ID 0x00010001 - -#define MTP_FLAG_WRITABLE 0 - -#define FLAG_SET(_mtp, _flag) ((_mtp)->flags & (1 << (_flag))) - -/* ----------------------------------------------------------------------- */ - -#define MTP_MANUFACTURER "QEMU" -#define MTP_PRODUCT "QEMU filesharing" - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, - STR_MTP, - STR_CONFIG_FULL, - STR_CONFIG_HIGH, - STR_CONFIG_SUPER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = MTP_MANUFACTURER, - [STR_PRODUCT] = MTP_PRODUCT, - [STR_SERIALNUMBER] = "34617", - [STR_MTP] = "MTP", - [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", - [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", - [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", -}; - -static const USBDescIface desc_iface_full = { - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = USB_CLASS_STILL_IMAGE, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01, - .iInterface = STR_MTP, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_IN | EP_EVENT, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 64, - .bInterval = 0x0a, - }, - } -}; - -static const USBDescDevice desc_device_full = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_FULL, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 2, - .nif = 1, - .ifs = &desc_iface_full, - }, - }, -}; - -static const USBDescIface desc_iface_high = { - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = USB_CLASS_STILL_IMAGE, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01, - .iInterface = STR_MTP, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - },{ - .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - },{ - .bEndpointAddress = USB_DIR_IN | EP_EVENT, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 64, - .bInterval = 0x0a, - }, - } -}; - -static const USBDescDevice desc_device_high = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_HIGH, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 2, - .nif = 1, - .ifs = &desc_iface_high, - }, - }, -}; - -static const USBDescMSOS desc_msos = { - .CompatibleID = "MTP", - .SelectiveSuspendEnabled = true, -}; - -static const USBDesc desc = { - .id = { - .idVendor = 0x46f4, /* CRC16() of "QEMU" */ - .idProduct = 0x0004, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_full, - .high = &desc_device_high, - .str = desc_strings, - .msos = &desc_msos, -}; - -/* ----------------------------------------------------------------------- */ - -static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, - MTPObject *parent, char *name) -{ - MTPObject *o = g_new0(MTPObject, 1); - - if (name[0] == '.') { - goto ignore; - } - - o->handle = handle; - o->parent = parent; - o->name = g_strdup(name); - if (parent == NULL) { - o->path = g_strdup(name); - } else { - o->path = g_strdup_printf("%s/%s", parent->path, name); - } - - if (lstat(o->path, &o->stat) != 0) { - goto ignore; - } - if (S_ISREG(o->stat.st_mode)) { - o->format = FMT_UNDEFINED_OBJECT; - } else if (S_ISDIR(o->stat.st_mode)) { - o->format = FMT_ASSOCIATION; - } else { - goto ignore; - } - - if (access(o->path, R_OK) != 0) { - goto ignore; - } - - trace_usb_mtp_object_alloc(s->dev.addr, o->handle, o->path); - - QTAILQ_INSERT_TAIL(&s->objects, o, next); - return o; - -ignore: - g_free(o->name); - g_free(o->path); - g_free(o); - return NULL; -} - -static void usb_mtp_object_free(MTPState *s, MTPObject *o) -{ - MTPObject *iter; - - if (!o) { - return; - } - - trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path); - - QTAILQ_REMOVE(&s->objects, o, next); - if (o->parent) { - QLIST_REMOVE(o, list); - o->parent->nchildren--; - } - - while (!QLIST_EMPTY(&o->children)) { - iter = QLIST_FIRST(&o->children); - usb_mtp_object_free(s, iter); - } - g_free(o->name); - g_free(o->path); - g_free(o); -} - -static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle) -{ - MTPObject *o; - - QTAILQ_FOREACH(o, &s->objects, next) { - if (o->handle == handle) { - return o; - } - } - return NULL; -} - -static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, - char *name) -{ - MTPObject *child = - usb_mtp_object_alloc(s, s->next_handle++, o, name); - - if (child) { - trace_usb_mtp_add_child(s->dev.addr, child->handle, child->path); - QLIST_INSERT_HEAD(&o->children, child, list); - o->nchildren++; - - if (child->format == FMT_ASSOCIATION) { - QLIST_INIT(&child->children); - } - } - - return child; -} - -#ifdef CONFIG_INOTIFY1 -static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, - char *name, int len) -{ - MTPObject *iter; - - QLIST_FOREACH(iter, &parent->children, list) { - if (strncmp(iter->name, name, len) == 0) { - return iter; - } - } - - return NULL; -} - -static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) -{ - MTPObject *iter; - - QTAILQ_FOREACH(iter, &s->objects, next) { - if (iter->watchfd == wd) { - return iter; - } - } - - return NULL; -} - -static void inotify_watchfn(void *arg) -{ - MTPState *s = arg; - ssize_t bytes; - /* From the man page: atleast one event can be read */ - int pos; - char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; - - for (;;) { - bytes = read(s->inotifyfd, buf, sizeof(buf)); - pos = 0; - - if (bytes <= 0) { - /* Better luck next time */ - return; - } - - /* - * TODO: Ignore initiator initiated events. - * For now we are good because the store is RO - */ - while (bytes > 0) { - char *p = buf + pos; - struct inotify_event *event = (struct inotify_event *)p; - int watchfd = 0; - uint32_t mask = event->mask & (IN_CREATE | IN_DELETE | - IN_MODIFY | IN_IGNORED); - MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd); - MTPMonEntry *entry = NULL; - MTPObject *o; - - pos = pos + sizeof(struct inotify_event) + event->len; - bytes = bytes - pos; - - if (!parent) { - continue; - } - - switch (mask) { - case IN_CREATE: - if (usb_mtp_object_lookup_name - (parent, event->name, event->len)) { - /* Duplicate create event */ - continue; - } - entry = g_new0(MTPMonEntry, 1); - entry->handle = s->next_handle; - entry->event = EVT_OBJ_ADDED; - o = usb_mtp_add_child(s, parent, event->name); - if (!o) { - g_free(entry); - continue; - } - o->watchfd = watchfd; - trace_usb_mtp_inotify_event(s->dev.addr, event->name, - event->mask, "Obj Added"); - break; - - case IN_DELETE: - /* - * The kernel issues a IN_IGNORED event - * when a dir containing a watchpoint is - * deleted, so we don't have to delete the - * watchpoint - */ - o = usb_mtp_object_lookup_name(parent, event->name, event->len); - if (!o) { - continue; - } - entry = g_new0(MTPMonEntry, 1); - entry->handle = o->handle; - entry->event = EVT_OBJ_REMOVED; - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - event->mask, "Obj Deleted"); - usb_mtp_object_free(s, o); - break; - - case IN_MODIFY: - o = usb_mtp_object_lookup_name(parent, event->name, event->len); - if (!o) { - continue; - } - entry = g_new0(MTPMonEntry, 1); - entry->handle = o->handle; - entry->event = EVT_OBJ_INFO_CHANGED; - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - event->mask, "Obj Modified"); - break; - - case IN_IGNORED: - o = usb_mtp_object_lookup_name(parent, event->name, event->len); - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - event->mask, "Obj ignored"); - break; - - default: - fprintf(stderr, "usb-mtp: failed to parse inotify event\n"); - continue; - } - - if (entry) { - QTAILQ_INSERT_HEAD(&s->events, entry, next); - } - } - } -} - -static int usb_mtp_inotify_init(MTPState *s) -{ - int fd; - - fd = inotify_init1(IN_NONBLOCK); - if (fd == -1) { - return 1; - } - - QTAILQ_INIT(&s->events); - s->inotifyfd = fd; - - qemu_set_fd_handler(fd, inotify_watchfn, NULL, s); - - return 0; -} - -static void usb_mtp_inotify_cleanup(MTPState *s) -{ - MTPMonEntry *e, *p; - - if (!s->inotifyfd) { - return; - } - - qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s); - close(s->inotifyfd); - - QTAILQ_FOREACH_SAFE(e, &s->events, next, p) { - QTAILQ_REMOVE(&s->events, e, next); - g_free(e); - } -} - -static int usb_mtp_add_watch(int inotifyfd, char *path) -{ - uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | - IN_ISDIR; - - return inotify_add_watch(inotifyfd, path, mask); -} -#endif - -static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) -{ - struct dirent *entry; - DIR *dir; - - if (o->have_children) { - return; - } - o->have_children = true; - - dir = opendir(o->path); - if (!dir) { - return; - } -#ifdef CONFIG_INOTIFY1 - int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path); - if (watchfd == -1) { - fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path); - } else { - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - 0, "Watch Added"); - o->watchfd = watchfd; - } -#endif - while ((entry = readdir(dir)) != NULL) { - usb_mtp_add_child(s, o, entry->d_name); - } - closedir(dir); -} - -/* ----------------------------------------------------------------------- */ - -static MTPData *usb_mtp_data_alloc(MTPControl *c) -{ - MTPData *data = g_new0(MTPData, 1); - - data->code = c->code; - data->trans = c->trans; - data->fd = -1; - data->first = true; - return data; -} - -static void usb_mtp_data_free(MTPData *data) -{ - if (data == NULL) { - return; - } - if (data->fd != -1) { - close(data->fd); - } - g_free(data->data); - g_free(data); -} - -static void usb_mtp_realloc(MTPData *data, uint32_t bytes) -{ - if (data->length + bytes <= data->alloc) { - return; - } - data->alloc = (data->length + bytes + 0xff) & ~0xff; - data->data = g_realloc(data->data, data->alloc); -} - -static void usb_mtp_add_u8(MTPData *data, uint8_t val) -{ - usb_mtp_realloc(data, 1); - data->data[data->length++] = val; -} - -static void usb_mtp_add_u16(MTPData *data, uint16_t val) -{ - usb_mtp_realloc(data, 2); - data->data[data->length++] = (val >> 0) & 0xff; - data->data[data->length++] = (val >> 8) & 0xff; -} - -static void usb_mtp_add_u32(MTPData *data, uint32_t val) -{ - usb_mtp_realloc(data, 4); - data->data[data->length++] = (val >> 0) & 0xff; - data->data[data->length++] = (val >> 8) & 0xff; - data->data[data->length++] = (val >> 16) & 0xff; - data->data[data->length++] = (val >> 24) & 0xff; -} - -static void usb_mtp_add_u64(MTPData *data, uint64_t val) -{ - usb_mtp_realloc(data, 8); - data->data[data->length++] = (val >> 0) & 0xff; - data->data[data->length++] = (val >> 8) & 0xff; - data->data[data->length++] = (val >> 16) & 0xff; - data->data[data->length++] = (val >> 24) & 0xff; - data->data[data->length++] = (val >> 32) & 0xff; - data->data[data->length++] = (val >> 40) & 0xff; - data->data[data->length++] = (val >> 48) & 0xff; - data->data[data->length++] = (val >> 56) & 0xff; -} - -static void usb_mtp_add_u16_array(MTPData *data, uint32_t len, - const uint16_t *vals) -{ - int i; - - usb_mtp_add_u32(data, len); - for (i = 0; i < len; i++) { - usb_mtp_add_u16(data, vals[i]); - } -} - -static void usb_mtp_add_u32_array(MTPData *data, uint32_t len, - const uint32_t *vals) -{ - int i; - - usb_mtp_add_u32(data, len); - for (i = 0; i < len; i++) { - usb_mtp_add_u32(data, vals[i]); - } -} - -static void usb_mtp_add_wstr(MTPData *data, const wchar_t *str) -{ - uint32_t len = wcslen(str); - int i; - - if (len > 0) { - len++; /* include terminating L'\0' */ - } - - usb_mtp_add_u8(data, len); - for (i = 0; i < len; i++) { - usb_mtp_add_u16(data, str[i]); - } -} - -static void usb_mtp_add_str(MTPData *data, const char *str) -{ - uint32_t len = strlen(str)+1; - wchar_t *wstr = g_new(wchar_t, len); - size_t ret; - - ret = mbstowcs(wstr, str, len); - if (ret == -1) { - usb_mtp_add_wstr(data, L"Oops"); - } else { - usb_mtp_add_wstr(data, wstr); - } - - g_free(wstr); -} - -static void usb_mtp_add_time(MTPData *data, time_t time) -{ - char buf[16]; - struct tm tm; - - gmtime_r(&time, &tm); - strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", &tm); - usb_mtp_add_str(data, buf); -} - -/* ----------------------------------------------------------------------- */ - -static void usb_mtp_queue_result(MTPState *s, uint16_t code, uint32_t trans, - int argc, uint32_t arg0, uint32_t arg1) -{ - MTPControl *c = g_new0(MTPControl, 1); - - c->code = code; - c->trans = trans; - c->argc = argc; - if (argc > 0) { - c->argv[0] = arg0; - } - if (argc > 1) { - c->argv[1] = arg1; - } - - assert(s->result == NULL); - s->result = c; -} - -/* ----------------------------------------------------------------------- */ - -static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c) -{ - static const uint16_t ops[] = { - CMD_GET_DEVICE_INFO, - CMD_OPEN_SESSION, - CMD_CLOSE_SESSION, - CMD_GET_STORAGE_IDS, - CMD_GET_STORAGE_INFO, - CMD_GET_NUM_OBJECTS, - CMD_GET_OBJECT_HANDLES, - CMD_GET_OBJECT_INFO, - CMD_GET_OBJECT, - CMD_GET_PARTIAL_OBJECT, - }; - static const uint16_t fmt[] = { - FMT_UNDEFINED_OBJECT, - FMT_ASSOCIATION, - }; - MTPData *d = usb_mtp_data_alloc(c); - - trace_usb_mtp_op_get_device_info(s->dev.addr); - - usb_mtp_add_u16(d, 100); - usb_mtp_add_u32(d, 0xffffffff); - usb_mtp_add_u16(d, 0x0101); - usb_mtp_add_wstr(d, L""); - usb_mtp_add_u16(d, 0x0000); - - usb_mtp_add_u16_array(d, ARRAY_SIZE(ops), ops); - usb_mtp_add_u16_array(d, 0, NULL); - usb_mtp_add_u16_array(d, 0, NULL); - usb_mtp_add_u16_array(d, 0, NULL); - usb_mtp_add_u16_array(d, ARRAY_SIZE(fmt), fmt); - - usb_mtp_add_wstr(d, L"" MTP_MANUFACTURER); - usb_mtp_add_wstr(d, L"" MTP_PRODUCT); - usb_mtp_add_wstr(d, L"0.1"); - usb_mtp_add_wstr(d, L"0123456789abcdef0123456789abcdef"); - - return d; -} - -static MTPData *usb_mtp_get_storage_ids(MTPState *s, MTPControl *c) -{ - static const uint32_t ids[] = { - QEMU_STORAGE_ID, - }; - MTPData *d = usb_mtp_data_alloc(c); - - trace_usb_mtp_op_get_storage_ids(s->dev.addr); - - usb_mtp_add_u32_array(d, ARRAY_SIZE(ids), ids); - - return d; -} - -static MTPData *usb_mtp_get_storage_info(MTPState *s, MTPControl *c) -{ - MTPData *d = usb_mtp_data_alloc(c); - struct statvfs buf; - int rc; - - trace_usb_mtp_op_get_storage_info(s->dev.addr); - - if (FLAG_SET(s, MTP_FLAG_WRITABLE)) { - usb_mtp_add_u16(d, 0x0003); - usb_mtp_add_u16(d, 0x0002); - usb_mtp_add_u16(d, 0x0000); - } else { - usb_mtp_add_u16(d, 0x0001); - usb_mtp_add_u16(d, 0x0002); - usb_mtp_add_u16(d, 0x0001); - } - - rc = statvfs(s->root, &buf); - if (rc == 0) { - usb_mtp_add_u64(d, (uint64_t)buf.f_frsize * buf.f_blocks); - usb_mtp_add_u64(d, (uint64_t)buf.f_bavail * buf.f_blocks); - usb_mtp_add_u32(d, buf.f_ffree); - } else { - usb_mtp_add_u64(d, 0xffffffff); - usb_mtp_add_u64(d, 0xffffffff); - usb_mtp_add_u32(d, 0xffffffff); - } - - usb_mtp_add_str(d, s->desc); - usb_mtp_add_wstr(d, L"123456789abcdef"); - return d; -} - -static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c, - MTPObject *o) -{ - MTPData *d = usb_mtp_data_alloc(c); - uint32_t i = 0, handles[o->nchildren]; - MTPObject *iter; - - trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path); - - QLIST_FOREACH(iter, &o->children, list) { - handles[i++] = iter->handle; - } - assert(i == o->nchildren); - usb_mtp_add_u32_array(d, o->nchildren, handles); - - return d; -} - -static MTPData *usb_mtp_get_object_info(MTPState *s, MTPControl *c, - MTPObject *o) -{ - MTPData *d = usb_mtp_data_alloc(c); - - trace_usb_mtp_op_get_object_info(s->dev.addr, o->handle, o->path); - - usb_mtp_add_u32(d, QEMU_STORAGE_ID); - usb_mtp_add_u16(d, o->format); - usb_mtp_add_u16(d, 0); - usb_mtp_add_u32(d, o->stat.st_size); - - usb_mtp_add_u16(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - - if (o->parent) { - usb_mtp_add_u32(d, o->parent->handle); - } else { - usb_mtp_add_u32(d, 0); - } - if (o->format == FMT_ASSOCIATION) { - usb_mtp_add_u16(d, 0x0001); - usb_mtp_add_u32(d, 0x00000001); - usb_mtp_add_u32(d, 0); - } else { - usb_mtp_add_u16(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - } - - usb_mtp_add_str(d, o->name); - usb_mtp_add_time(d, o->stat.st_ctime); - usb_mtp_add_time(d, o->stat.st_mtime); - usb_mtp_add_wstr(d, L""); - - return d; -} - -static MTPData *usb_mtp_get_object(MTPState *s, MTPControl *c, - MTPObject *o) -{ - MTPData *d = usb_mtp_data_alloc(c); - - trace_usb_mtp_op_get_object(s->dev.addr, o->handle, o->path); - - d->fd = open(o->path, O_RDONLY); - if (d->fd == -1) { - usb_mtp_data_free(d); - return NULL; - } - d->length = o->stat.st_size; - d->alloc = 512; - d->data = g_malloc(d->alloc); - return d; -} - -static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c, - MTPObject *o) -{ - MTPData *d = usb_mtp_data_alloc(c); - off_t offset; - - trace_usb_mtp_op_get_partial_object(s->dev.addr, o->handle, o->path, - c->argv[1], c->argv[2]); - - d->fd = open(o->path, O_RDONLY); - if (d->fd == -1) { - usb_mtp_data_free(d); - return NULL; - } - - offset = c->argv[1]; - if (offset > o->stat.st_size) { - offset = o->stat.st_size; - } - if (lseek(d->fd, offset, SEEK_SET) < 0) { - usb_mtp_data_free(d); - return NULL; - } - - d->length = c->argv[2]; - if (d->length > o->stat.st_size - offset) { - d->length = o->stat.st_size - offset; - } - - return d; -} - -static void usb_mtp_command(MTPState *s, MTPControl *c) -{ - MTPData *data_in = NULL; - MTPObject *o; - uint32_t nres = 0, res0 = 0; - - /* sanity checks */ - if (c->code >= CMD_CLOSE_SESSION && s->session == 0) { - usb_mtp_queue_result(s, RES_SESSION_NOT_OPEN, - c->trans, 0, 0, 0); - return; - } - - /* process commands */ - switch (c->code) { - case CMD_GET_DEVICE_INFO: - data_in = usb_mtp_get_device_info(s, c); - break; - case CMD_OPEN_SESSION: - if (s->session) { - usb_mtp_queue_result(s, RES_SESSION_ALREADY_OPEN, - c->trans, 1, s->session, 0); - return; - } - if (c->argv[0] == 0) { - usb_mtp_queue_result(s, RES_INVALID_PARAMETER, - c->trans, 0, 0, 0); - return; - } - trace_usb_mtp_op_open_session(s->dev.addr); - s->session = c->argv[0]; - usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root); -#ifdef CONFIG_INOTIFY1 - if (usb_mtp_inotify_init(s)) { - fprintf(stderr, "usb-mtp: file monitoring init failed\n"); - } -#endif - break; - case CMD_CLOSE_SESSION: - trace_usb_mtp_op_close_session(s->dev.addr); - s->session = 0; - s->next_handle = 0; -#ifdef CONFIG_INOTIFY1 - usb_mtp_inotify_cleanup(s); -#endif - usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); - assert(QTAILQ_EMPTY(&s->objects)); - break; - case CMD_GET_STORAGE_IDS: - data_in = usb_mtp_get_storage_ids(s, c); - break; - case CMD_GET_STORAGE_INFO: - if (c->argv[0] != QEMU_STORAGE_ID && - c->argv[0] != 0xffffffff) { - usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, - c->trans, 0, 0, 0); - return; - } - data_in = usb_mtp_get_storage_info(s, c); - break; - case CMD_GET_NUM_OBJECTS: - case CMD_GET_OBJECT_HANDLES: - if (c->argv[0] != QEMU_STORAGE_ID && - c->argv[0] != 0xffffffff) { - usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, - c->trans, 0, 0, 0); - return; - } - if (c->argv[1] != 0x00000000) { - usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, - c->trans, 0, 0, 0); - return; - } - if (c->argv[2] == 0x00000000 || - c->argv[2] == 0xffffffff) { - o = QTAILQ_FIRST(&s->objects); - } else { - o = usb_mtp_object_lookup(s, c->argv[2]); - } - if (o == NULL) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - if (o->format != FMT_ASSOCIATION) { - usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, - c->trans, 0, 0, 0); - return; - } - usb_mtp_object_readdir(s, o); - if (c->code == CMD_GET_NUM_OBJECTS) { - trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path); - nres = 1; - res0 = o->nchildren; - } else { - data_in = usb_mtp_get_object_handles(s, c, o); - } - break; - case CMD_GET_OBJECT_INFO: - o = usb_mtp_object_lookup(s, c->argv[0]); - if (o == NULL) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - data_in = usb_mtp_get_object_info(s, c, o); - break; - case CMD_GET_OBJECT: - o = usb_mtp_object_lookup(s, c->argv[0]); - if (o == NULL) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - if (o->format == FMT_ASSOCIATION) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - data_in = usb_mtp_get_object(s, c, o); - if (data_in == NULL) { - usb_mtp_queue_result(s, RES_GENERAL_ERROR, - c->trans, 0, 0, 0); - return; - } - break; - case CMD_GET_PARTIAL_OBJECT: - o = usb_mtp_object_lookup(s, c->argv[0]); - if (o == NULL) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - if (o->format == FMT_ASSOCIATION) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - data_in = usb_mtp_get_partial_object(s, c, o); - if (data_in == NULL) { - usb_mtp_queue_result(s, RES_GENERAL_ERROR, - c->trans, 0, 0, 0); - return; - } - nres = 1; - res0 = data_in->length; - break; - default: - trace_usb_mtp_op_unknown(s->dev.addr, c->code); - usb_mtp_queue_result(s, RES_OPERATION_NOT_SUPPORTED, - c->trans, 0, 0, 0); - return; - } - - /* return results on success */ - if (data_in) { - assert(s->data_in == NULL); - s->data_in = data_in; - } - usb_mtp_queue_result(s, RES_OK, c->trans, nres, res0, 0); -} - -/* ----------------------------------------------------------------------- */ - -static void usb_mtp_handle_reset(USBDevice *dev) -{ - MTPState *s = USB_MTP(dev); - - trace_usb_mtp_reset(s->dev.addr); - -#ifdef CONFIG_INOTIFY1 - usb_mtp_inotify_cleanup(s); -#endif - usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); - s->session = 0; - usb_mtp_data_free(s->data_in); - s->data_in = NULL; - usb_mtp_data_free(s->data_out); - s->data_out = NULL; - g_free(s->result); - s->result = NULL; -} - -static void usb_mtp_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, - int length, uint8_t *data) -{ - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - trace_usb_mtp_stall(dev->addr, "unknown control request"); - p->status = USB_RET_STALL; -} - -static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p) -{ - /* we don't use async packets, so this should never be called */ - fprintf(stderr, "%s\n", __func__); -} - -static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) -{ - MTPState *s = USB_MTP(dev); - MTPControl cmd; - mtp_container container; - uint32_t params[5]; - int i, rc; - - switch (p->ep->nr) { - case EP_DATA_IN: - if (s->data_out != NULL) { - /* guest bug */ - trace_usb_mtp_stall(s->dev.addr, "awaiting data-out"); - p->status = USB_RET_STALL; - return; - } - if (p->iov.size < sizeof(container)) { - trace_usb_mtp_stall(s->dev.addr, "packet too small"); - p->status = USB_RET_STALL; - return; - } - if (s->data_in != NULL) { - MTPData *d = s->data_in; - int dlen = d->length - d->offset; - if (d->first) { - trace_usb_mtp_data_in(s->dev.addr, d->trans, d->length); - container.length = cpu_to_le32(d->length + sizeof(container)); - container.type = cpu_to_le16(TYPE_DATA); - container.code = cpu_to_le16(d->code); - container.trans = cpu_to_le32(d->trans); - usb_packet_copy(p, &container, sizeof(container)); - d->first = false; - if (dlen > p->iov.size - sizeof(container)) { - dlen = p->iov.size - sizeof(container); - } - } else { - if (dlen > p->iov.size) { - dlen = p->iov.size; - } - } - if (d->fd == -1) { - usb_packet_copy(p, d->data + d->offset, dlen); - } else { - if (d->alloc < p->iov.size) { - d->alloc = p->iov.size; - d->data = g_realloc(d->data, d->alloc); - } - rc = read(d->fd, d->data, dlen); - if (rc != dlen) { - memset(d->data, 0, dlen); - s->result->code = RES_INCOMPLETE_TRANSFER; - } - usb_packet_copy(p, d->data, dlen); - } - d->offset += dlen; - if (d->offset == d->length) { - usb_mtp_data_free(s->data_in); - s->data_in = NULL; - } - } else if (s->result != NULL) { - MTPControl *r = s->result; - int length = sizeof(container) + r->argc * sizeof(uint32_t); - if (r->code == RES_OK) { - trace_usb_mtp_success(s->dev.addr, r->trans, - (r->argc > 0) ? r->argv[0] : 0, - (r->argc > 1) ? r->argv[1] : 0); - } else { - trace_usb_mtp_error(s->dev.addr, r->code, r->trans, - (r->argc > 0) ? r->argv[0] : 0, - (r->argc > 1) ? r->argv[1] : 0); - } - container.length = cpu_to_le32(length); - container.type = cpu_to_le16(TYPE_RESPONSE); - container.code = cpu_to_le16(r->code); - container.trans = cpu_to_le32(r->trans); - for (i = 0; i < r->argc; i++) { - params[i] = cpu_to_le32(r->argv[i]); - } - usb_packet_copy(p, &container, sizeof(container)); - usb_packet_copy(p, ¶ms, length - sizeof(container)); - g_free(s->result); - s->result = NULL; - } - break; - case EP_DATA_OUT: - if (p->iov.size < sizeof(container)) { - trace_usb_mtp_stall(s->dev.addr, "packet too small"); - p->status = USB_RET_STALL; - return; - } - usb_packet_copy(p, &container, sizeof(container)); - switch (le16_to_cpu(container.type)) { - case TYPE_COMMAND: - if (s->data_in || s->data_out || s->result) { - trace_usb_mtp_stall(s->dev.addr, "transaction inflight"); - p->status = USB_RET_STALL; - return; - } - cmd.code = le16_to_cpu(container.code); - cmd.argc = (le32_to_cpu(container.length) - sizeof(container)) - / sizeof(uint32_t); - cmd.trans = le32_to_cpu(container.trans); - if (cmd.argc > ARRAY_SIZE(cmd.argv)) { - cmd.argc = ARRAY_SIZE(cmd.argv); - } - if (p->iov.size < sizeof(container) + cmd.argc * sizeof(uint32_t)) { - trace_usb_mtp_stall(s->dev.addr, "packet too small"); - p->status = USB_RET_STALL; - return; - } - usb_packet_copy(p, ¶ms, cmd.argc * sizeof(uint32_t)); - for (i = 0; i < cmd.argc; i++) { - cmd.argv[i] = le32_to_cpu(params[i]); - } - trace_usb_mtp_command(s->dev.addr, cmd.code, cmd.trans, - (cmd.argc > 0) ? cmd.argv[0] : 0, - (cmd.argc > 1) ? cmd.argv[1] : 0, - (cmd.argc > 2) ? cmd.argv[2] : 0, - (cmd.argc > 3) ? cmd.argv[3] : 0, - (cmd.argc > 4) ? cmd.argv[4] : 0); - usb_mtp_command(s, &cmd); - break; - default: - /* not needed as long as the mtp device is read-only */ - p->status = USB_RET_STALL; - return; - } - break; - case EP_EVENT: -#ifdef CONFIG_INOTIFY1 - if (!QTAILQ_EMPTY(&s->events)) { - struct MTPMonEntry *e = QTAILQ_LAST(&s->events, events); - uint32_t handle; - int len = sizeof(container) + sizeof(uint32_t); - - if (p->iov.size < len) { - trace_usb_mtp_stall(s->dev.addr, - "packet too small to send event"); - p->status = USB_RET_STALL; - return; - } - - QTAILQ_REMOVE(&s->events, e, next); - container.length = cpu_to_le32(len); - container.type = cpu_to_le32(TYPE_EVENT); - container.code = cpu_to_le16(e->event); - container.trans = 0; /* no trans specific events */ - handle = cpu_to_le32(e->handle); - usb_packet_copy(p, &container, sizeof(container)); - usb_packet_copy(p, &handle, sizeof(uint32_t)); - g_free(e); - return; - } -#endif - p->status = USB_RET_NAK; - return; - default: - trace_usb_mtp_stall(s->dev.addr, "invalid endpoint"); - p->status = USB_RET_STALL; - return; - } - - if (p->actual_length == 0) { - trace_usb_mtp_nak(s->dev.addr, p->ep->nr); - p->status = USB_RET_NAK; - return; - } else { - trace_usb_mtp_xfer(s->dev.addr, p->ep->nr, p->actual_length, - p->iov.size); - return; - } -} - -static void usb_mtp_realize(USBDevice *dev, Error **errp) -{ - MTPState *s = USB_MTP(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - QTAILQ_INIT(&s->objects); - if (s->desc == NULL) { - if (s->root == NULL) { - error_setg(errp, "usb-mtp: x-root property must be configured"); - return; - } - s->desc = strrchr(s->root, '/'); - if (s->desc && s->desc[0]) { - s->desc = g_strdup(s->desc + 1); - } else { - s->desc = g_strdup("none"); - } - } -} - -static const VMStateDescription vmstate_usb_mtp = { - .name = "usb-mtp", - .unmigratable = 1, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, MTPState), - VMSTATE_END_OF_LIST() - } -}; - -static Property mtp_properties[] = { - DEFINE_PROP_STRING("x-root", MTPState, root), - DEFINE_PROP_STRING("desc", MTPState, desc), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_mtp_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_mtp_realize; - uc->product_desc = "QEMU USB MTP"; - uc->usb_desc = &desc; - uc->cancel_packet = usb_mtp_cancel_packet; - uc->handle_attach = usb_desc_attach; - uc->handle_reset = usb_mtp_handle_reset; - uc->handle_control = usb_mtp_handle_control; - uc->handle_data = usb_mtp_handle_data; - dc->fw_name = "mtp"; - dc->vmsd = &vmstate_usb_mtp; - dc->props = mtp_properties; -} - -static TypeInfo mtp_info = { - .name = TYPE_USB_MTP, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(MTPState), - .class_init = usb_mtp_class_initfn, -}; - -static void usb_mtp_register_types(void) -{ - type_register_static(&mtp_info); -} - -type_init(usb_mtp_register_types) diff --git a/qemu/hw/usb/dev-network.c b/qemu/hw/usb/dev-network.c deleted file mode 100644 index 74306b58e..000000000 --- a/qemu/hw/usb/dev-network.c +++ /dev/null @@ -1,1455 +0,0 @@ -/* - * QEMU USB Net devices - * - * Copyright (c) 2006 Thomas Sailer - * Copyright (c) 2008 Andrzej Zaborowski - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "net/net.h" -#include "qemu/error-report.h" -#include "qemu/queue.h" -#include "qemu/config-file.h" -#include "sysemu/sysemu.h" -#include "qemu/iov.h" -#include "qemu/cutils.h" - -/*#define TRAFFIC_DEBUG*/ -/* Thanks to NetChip Technologies for donating this product ID. - * It's for devices with only CDC Ethernet configurations. - */ -#define CDC_VENDOR_NUM 0x0525 /* NetChip */ -#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ -/* For hardware that can talk RNDIS and either of the above protocols, - * use this ID ... the windows INF files will know it. - */ -#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ -#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ - -enum usbstring_idx { - STRING_MANUFACTURER = 1, - STRING_PRODUCT, - STRING_ETHADDR, - STRING_DATA, - STRING_CONTROL, - STRING_RNDIS_CONTROL, - STRING_CDC, - STRING_SUBSET, - STRING_RNDIS, - STRING_SERIALNUMBER, -}; - -#define DEV_CONFIG_VALUE 1 /* CDC or a subset */ -#define DEV_RNDIS_CONFIG_VALUE 2 /* RNDIS; optional */ - -#define USB_CDC_SUBCLASS_ACM 0x02 -#define USB_CDC_SUBCLASS_ETHERNET 0x06 - -#define USB_CDC_PROTO_NONE 0 -#define USB_CDC_ACM_PROTO_VENDOR 0xff - -#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ -#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ -#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ -#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ -#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ - -#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 -#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 -#define USB_CDC_REQ_SET_LINE_CODING 0x20 -#define USB_CDC_REQ_GET_LINE_CODING 0x21 -#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 -#define USB_CDC_REQ_SEND_BREAK 0x23 -#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 -#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 -#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 -#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 -#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 - -#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ -#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ - -#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ - -static const USBDescStrings usb_net_stringtable = { - [STRING_MANUFACTURER] = "QEMU", - [STRING_PRODUCT] = "RNDIS/QEMU USB Network Device", - [STRING_ETHADDR] = "400102030405", - [STRING_DATA] = "QEMU USB Net Data Interface", - [STRING_CONTROL] = "QEMU USB Net Control Interface", - [STRING_RNDIS_CONTROL] = "QEMU USB Net RNDIS Control Interface", - [STRING_CDC] = "QEMU USB Net CDC", - [STRING_SUBSET] = "QEMU USB Net Subset", - [STRING_RNDIS] = "QEMU USB Net RNDIS", - [STRING_SERIALNUMBER] = "1", -}; - -static const USBDescIface desc_iface_rndis[] = { - { - /* RNDIS Control Interface */ - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, - .iInterface = STRING_RNDIS_CONTROL, - .ndesc = 4, - .descs = (USBDescOther[]) { - { - /* Header Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ - 0x10, 0x01, /* le16 bcdCDC */ - }, - },{ - /* Call Management Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bmCapabilities */ - 0x01, /* u8 bDataInterface */ - }, - },{ - /* ACM Descriptor */ - .data = (uint8_t[]) { - 0x04, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bmCapabilities */ - }, - },{ - /* Union Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bMasterInterface0 */ - 0x01, /* u8 bSlaveInterface0 */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = STATUS_BYTECOUNT, - .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, - }, - } - },{ - /* RNDIS Data Interface */ - .bInterfaceNumber = 1, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .iInterface = STRING_DATA, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - } - } - } -}; - -static const USBDescIface desc_iface_cdc[] = { - { - /* CDC Control Interface */ - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, - .bInterfaceProtocol = USB_CDC_PROTO_NONE, - .iInterface = STRING_CONTROL, - .ndesc = 3, - .descs = (USBDescOther[]) { - { - /* Header Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ - 0x10, 0x01, /* le16 bcdCDC */ - }, - },{ - /* Union Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bMasterInterface0 */ - 0x01, /* u8 bSlaveInterface0 */ - }, - },{ - /* Ethernet Descriptor */ - .data = (uint8_t[]) { - 0x0d, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */ - STRING_ETHADDR, /* u8 iMACAddress */ - 0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */ - ETH_FRAME_LEN & 0xff, - ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */ - 0x00, 0x00, /* le16 wNumberMCFilters */ - 0x00, /* u8 bNumberPowerFilters */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = STATUS_BYTECOUNT, - .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, - }, - } - },{ - /* CDC Data Interface (off) */ - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_CDC_DATA, - },{ - /* CDC Data Interface */ - .bInterfaceNumber = 1, - .bAlternateSetting = 1, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .iInterface = STRING_DATA, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - } - } - } -}; - -static const USBDescDevice desc_device_net = { - .bcdUSB = 0x0200, - .bDeviceClass = USB_CLASS_COMM, - .bMaxPacketSize0 = 0x40, - .bNumConfigurations = 2, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 2, - .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, - .iConfiguration = STRING_RNDIS, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .bMaxPower = 0x32, - .nif = ARRAY_SIZE(desc_iface_rndis), - .ifs = desc_iface_rndis, - },{ - .bNumInterfaces = 2, - .bConfigurationValue = DEV_CONFIG_VALUE, - .iConfiguration = STRING_CDC, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .bMaxPower = 0x32, - .nif = ARRAY_SIZE(desc_iface_cdc), - .ifs = desc_iface_cdc, - } - }, -}; - -static const USBDesc desc_net = { - .id = { - .idVendor = RNDIS_VENDOR_NUM, - .idProduct = RNDIS_PRODUCT_NUM, - .bcdDevice = 0, - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, - .iSerialNumber = STRING_SERIALNUMBER, - }, - .full = &desc_device_net, - .str = usb_net_stringtable, -}; - -/* - * RNDIS Definitions - in theory not specific to USB. - */ -#define RNDIS_MAXIMUM_FRAME_SIZE 1518 -#define RNDIS_MAX_TOTAL_SIZE 1558 - -/* Remote NDIS Versions */ -#define RNDIS_MAJOR_VERSION 1 -#define RNDIS_MINOR_VERSION 0 - -/* Status Values */ -#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ -#define RNDIS_STATUS_FAILURE 0xc0000001U /* Unspecified error */ -#define RNDIS_STATUS_INVALID_DATA 0xc0010015U /* Invalid data */ -#define RNDIS_STATUS_NOT_SUPPORTED 0xc00000bbU /* Unsupported request */ -#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000bU /* Device connected */ -#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000cU /* Device disconnected */ - -/* Message Set for Connectionless (802.3) Devices */ -enum { - RNDIS_PACKET_MSG = 1, - RNDIS_INITIALIZE_MSG = 2, /* Initialize device */ - RNDIS_HALT_MSG = 3, - RNDIS_QUERY_MSG = 4, - RNDIS_SET_MSG = 5, - RNDIS_RESET_MSG = 6, - RNDIS_INDICATE_STATUS_MSG = 7, - RNDIS_KEEPALIVE_MSG = 8, -}; - -/* Message completion */ -enum { - RNDIS_INITIALIZE_CMPLT = 0x80000002U, - RNDIS_QUERY_CMPLT = 0x80000004U, - RNDIS_SET_CMPLT = 0x80000005U, - RNDIS_RESET_CMPLT = 0x80000006U, - RNDIS_KEEPALIVE_CMPLT = 0x80000008U, -}; - -/* Device Flags */ -enum { - RNDIS_DF_CONNECTIONLESS = 1, - RNDIS_DF_CONNECTIONORIENTED = 2, -}; - -#define RNDIS_MEDIUM_802_3 0x00000000U - -/* from drivers/net/sk98lin/h/skgepnmi.h */ -#define OID_PNP_CAPABILITIES 0xfd010100 -#define OID_PNP_SET_POWER 0xfd010101 -#define OID_PNP_QUERY_POWER 0xfd010102 -#define OID_PNP_ADD_WAKE_UP_PATTERN 0xfd010103 -#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xfd010104 -#define OID_PNP_ENABLE_WAKE_UP 0xfd010106 - -typedef uint32_t le32; - -typedef struct rndis_init_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 MajorVersion; - le32 MinorVersion; - le32 MaxTransferSize; -} rndis_init_msg_type; - -typedef struct rndis_init_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 Status; - le32 MajorVersion; - le32 MinorVersion; - le32 DeviceFlags; - le32 Medium; - le32 MaxPacketsPerTransfer; - le32 MaxTransferSize; - le32 PacketAlignmentFactor; - le32 AFListOffset; - le32 AFListSize; -} rndis_init_cmplt_type; - -typedef struct rndis_halt_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; -} rndis_halt_msg_type; - -typedef struct rndis_query_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 OID; - le32 InformationBufferLength; - le32 InformationBufferOffset; - le32 DeviceVcHandle; -} rndis_query_msg_type; - -typedef struct rndis_query_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 Status; - le32 InformationBufferLength; - le32 InformationBufferOffset; -} rndis_query_cmplt_type; - -typedef struct rndis_set_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 OID; - le32 InformationBufferLength; - le32 InformationBufferOffset; - le32 DeviceVcHandle; -} rndis_set_msg_type; - -typedef struct rndis_set_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 Status; -} rndis_set_cmplt_type; - -typedef struct rndis_reset_msg_type { - le32 MessageType; - le32 MessageLength; - le32 Reserved; -} rndis_reset_msg_type; - -typedef struct rndis_reset_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 Status; - le32 AddressingReset; -} rndis_reset_cmplt_type; - -typedef struct rndis_indicate_status_msg_type { - le32 MessageType; - le32 MessageLength; - le32 Status; - le32 StatusBufferLength; - le32 StatusBufferOffset; -} rndis_indicate_status_msg_type; - -typedef struct rndis_keepalive_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; -} rndis_keepalive_msg_type; - -typedef struct rndis_keepalive_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 Status; -} rndis_keepalive_cmplt_type; - -struct rndis_packet_msg_type { - le32 MessageType; - le32 MessageLength; - le32 DataOffset; - le32 DataLength; - le32 OOBDataOffset; - le32 OOBDataLength; - le32 NumOOBDataElements; - le32 PerPacketInfoOffset; - le32 PerPacketInfoLength; - le32 VcHandle; - le32 Reserved; -}; - -struct rndis_config_parameter { - le32 ParameterNameOffset; - le32 ParameterNameLength; - le32 ParameterType; - le32 ParameterValueOffset; - le32 ParameterValueLength; -}; - -/* implementation specific */ -enum rndis_state -{ - RNDIS_UNINITIALIZED, - RNDIS_INITIALIZED, - RNDIS_DATA_INITIALIZED, -}; - -/* from ndis.h */ -enum ndis_oid { - /* Required Object IDs (OIDs) */ - OID_GEN_SUPPORTED_LIST = 0x00010101, - OID_GEN_HARDWARE_STATUS = 0x00010102, - OID_GEN_MEDIA_SUPPORTED = 0x00010103, - OID_GEN_MEDIA_IN_USE = 0x00010104, - OID_GEN_MAXIMUM_LOOKAHEAD = 0x00010105, - OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106, - OID_GEN_LINK_SPEED = 0x00010107, - OID_GEN_TRANSMIT_BUFFER_SPACE = 0x00010108, - OID_GEN_RECEIVE_BUFFER_SPACE = 0x00010109, - OID_GEN_TRANSMIT_BLOCK_SIZE = 0x0001010a, - OID_GEN_RECEIVE_BLOCK_SIZE = 0x0001010b, - OID_GEN_VENDOR_ID = 0x0001010c, - OID_GEN_VENDOR_DESCRIPTION = 0x0001010d, - OID_GEN_CURRENT_PACKET_FILTER = 0x0001010e, - OID_GEN_CURRENT_LOOKAHEAD = 0x0001010f, - OID_GEN_DRIVER_VERSION = 0x00010110, - OID_GEN_MAXIMUM_TOTAL_SIZE = 0x00010111, - OID_GEN_PROTOCOL_OPTIONS = 0x00010112, - OID_GEN_MAC_OPTIONS = 0x00010113, - OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114, - OID_GEN_MAXIMUM_SEND_PACKETS = 0x00010115, - OID_GEN_VENDOR_DRIVER_VERSION = 0x00010116, - OID_GEN_SUPPORTED_GUIDS = 0x00010117, - OID_GEN_NETWORK_LAYER_ADDRESSES = 0x00010118, - OID_GEN_TRANSPORT_HEADER_OFFSET = 0x00010119, - OID_GEN_MACHINE_NAME = 0x0001021a, - OID_GEN_RNDIS_CONFIG_PARAMETER = 0x0001021b, - OID_GEN_VLAN_ID = 0x0001021c, - - /* Optional OIDs */ - OID_GEN_MEDIA_CAPABILITIES = 0x00010201, - OID_GEN_PHYSICAL_MEDIUM = 0x00010202, - - /* Required statistics OIDs */ - OID_GEN_XMIT_OK = 0x00020101, - OID_GEN_RCV_OK = 0x00020102, - OID_GEN_XMIT_ERROR = 0x00020103, - OID_GEN_RCV_ERROR = 0x00020104, - OID_GEN_RCV_NO_BUFFER = 0x00020105, - - /* Optional statistics OIDs */ - OID_GEN_DIRECTED_BYTES_XMIT = 0x00020201, - OID_GEN_DIRECTED_FRAMES_XMIT = 0x00020202, - OID_GEN_MULTICAST_BYTES_XMIT = 0x00020203, - OID_GEN_MULTICAST_FRAMES_XMIT = 0x00020204, - OID_GEN_BROADCAST_BYTES_XMIT = 0x00020205, - OID_GEN_BROADCAST_FRAMES_XMIT = 0x00020206, - OID_GEN_DIRECTED_BYTES_RCV = 0x00020207, - OID_GEN_DIRECTED_FRAMES_RCV = 0x00020208, - OID_GEN_MULTICAST_BYTES_RCV = 0x00020209, - OID_GEN_MULTICAST_FRAMES_RCV = 0x0002020a, - OID_GEN_BROADCAST_BYTES_RCV = 0x0002020b, - OID_GEN_BROADCAST_FRAMES_RCV = 0x0002020c, - OID_GEN_RCV_CRC_ERROR = 0x0002020d, - OID_GEN_TRANSMIT_QUEUE_LENGTH = 0x0002020e, - OID_GEN_GET_TIME_CAPS = 0x0002020f, - OID_GEN_GET_NETCARD_TIME = 0x00020210, - OID_GEN_NETCARD_LOAD = 0x00020211, - OID_GEN_DEVICE_PROFILE = 0x00020212, - OID_GEN_INIT_TIME_MS = 0x00020213, - OID_GEN_RESET_COUNTS = 0x00020214, - OID_GEN_MEDIA_SENSE_COUNTS = 0x00020215, - OID_GEN_FRIENDLY_NAME = 0x00020216, - OID_GEN_MINIPORT_INFO = 0x00020217, - OID_GEN_RESET_VERIFY_PARAMETERS = 0x00020218, - - /* IEEE 802.3 (Ethernet) OIDs */ - OID_802_3_PERMANENT_ADDRESS = 0x01010101, - OID_802_3_CURRENT_ADDRESS = 0x01010102, - OID_802_3_MULTICAST_LIST = 0x01010103, - OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104, - OID_802_3_MAC_OPTIONS = 0x01010105, - OID_802_3_RCV_ERROR_ALIGNMENT = 0x01020101, - OID_802_3_XMIT_ONE_COLLISION = 0x01020102, - OID_802_3_XMIT_MORE_COLLISIONS = 0x01020103, - OID_802_3_XMIT_DEFERRED = 0x01020201, - OID_802_3_XMIT_MAX_COLLISIONS = 0x01020202, - OID_802_3_RCV_OVERRUN = 0x01020203, - OID_802_3_XMIT_UNDERRUN = 0x01020204, - OID_802_3_XMIT_HEARTBEAT_FAILURE = 0x01020205, - OID_802_3_XMIT_TIMES_CRS_LOST = 0x01020206, - OID_802_3_XMIT_LATE_COLLISIONS = 0x01020207, -}; - -static const uint32_t oid_supported_list[] = -{ - /* the general stuff */ - OID_GEN_SUPPORTED_LIST, - OID_GEN_HARDWARE_STATUS, - OID_GEN_MEDIA_SUPPORTED, - OID_GEN_MEDIA_IN_USE, - OID_GEN_MAXIMUM_FRAME_SIZE, - OID_GEN_LINK_SPEED, - OID_GEN_TRANSMIT_BLOCK_SIZE, - OID_GEN_RECEIVE_BLOCK_SIZE, - OID_GEN_VENDOR_ID, - OID_GEN_VENDOR_DESCRIPTION, - OID_GEN_VENDOR_DRIVER_VERSION, - OID_GEN_CURRENT_PACKET_FILTER, - OID_GEN_MAXIMUM_TOTAL_SIZE, - OID_GEN_MEDIA_CONNECT_STATUS, - OID_GEN_PHYSICAL_MEDIUM, - - /* the statistical stuff */ - OID_GEN_XMIT_OK, - OID_GEN_RCV_OK, - OID_GEN_XMIT_ERROR, - OID_GEN_RCV_ERROR, - OID_GEN_RCV_NO_BUFFER, - - /* IEEE 802.3 */ - /* the general stuff */ - OID_802_3_PERMANENT_ADDRESS, - OID_802_3_CURRENT_ADDRESS, - OID_802_3_MULTICAST_LIST, - OID_802_3_MAC_OPTIONS, - OID_802_3_MAXIMUM_LIST_SIZE, - - /* the statistical stuff */ - OID_802_3_RCV_ERROR_ALIGNMENT, - OID_802_3_XMIT_ONE_COLLISION, - OID_802_3_XMIT_MORE_COLLISIONS, -}; - -#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA (1 << 0) -#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED (1 << 1) -#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND (1 << 2) -#define NDIS_MAC_OPTION_NO_LOOPBACK (1 << 3) -#define NDIS_MAC_OPTION_FULL_DUPLEX (1 << 4) -#define NDIS_MAC_OPTION_EOTX_INDICATION (1 << 5) -#define NDIS_MAC_OPTION_8021P_PRIORITY (1 << 6) - -struct rndis_response { - QTAILQ_ENTRY(rndis_response) entries; - uint32_t length; - uint8_t buf[0]; -}; - -typedef struct USBNetState { - USBDevice dev; - - enum rndis_state rndis_state; - uint32_t medium; - uint32_t speed; - uint32_t media_state; - uint16_t filter; - uint32_t vendorid; - - unsigned int out_ptr; - uint8_t out_buf[2048]; - - unsigned int in_ptr, in_len; - uint8_t in_buf[2048]; - - USBEndpoint *intr; - - char usbstring_mac[13]; - NICState *nic; - NICConf conf; - QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp; -} USBNetState; - -#define TYPE_USB_NET "usb-net" -#define USB_NET(obj) OBJECT_CHECK(USBNetState, (obj), TYPE_USB_NET) - -static int is_rndis(USBNetState *s) -{ - return s->dev.config ? - s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE : 0; -} - -static int ndis_query(USBNetState *s, uint32_t oid, - uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf, - size_t outlen) -{ - unsigned int i; - - switch (oid) { - /* general oids (table 4-1) */ - /* mandatory */ - case OID_GEN_SUPPORTED_LIST: - for (i = 0; i < ARRAY_SIZE(oid_supported_list); i++) - ((le32 *) outbuf)[i] = cpu_to_le32(oid_supported_list[i]); - return sizeof(oid_supported_list); - - /* mandatory */ - case OID_GEN_HARDWARE_STATUS: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MEDIA_SUPPORTED: - *((le32 *) outbuf) = cpu_to_le32(s->medium); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MEDIA_IN_USE: - *((le32 *) outbuf) = cpu_to_le32(s->medium); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MAXIMUM_FRAME_SIZE: - *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_LINK_SPEED: - *((le32 *) outbuf) = cpu_to_le32(s->speed); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_TRANSMIT_BLOCK_SIZE: - *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_RECEIVE_BLOCK_SIZE: - *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_VENDOR_ID: - *((le32 *) outbuf) = cpu_to_le32(s->vendorid); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_VENDOR_DESCRIPTION: - pstrcpy((char *)outbuf, outlen, "QEMU USB RNDIS Net"); - return strlen((char *)outbuf) + 1; - - case OID_GEN_VENDOR_DRIVER_VERSION: - *((le32 *) outbuf) = cpu_to_le32(1); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_CURRENT_PACKET_FILTER: - *((le32 *) outbuf) = cpu_to_le32(s->filter); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MAXIMUM_TOTAL_SIZE: - *((le32 *) outbuf) = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MEDIA_CONNECT_STATUS: - *((le32 *) outbuf) = cpu_to_le32(s->media_state); - return sizeof(le32); - - case OID_GEN_PHYSICAL_MEDIUM: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - case OID_GEN_MAC_OPTIONS: - *((le32 *) outbuf) = cpu_to_le32( - NDIS_MAC_OPTION_RECEIVE_SERIALIZED | - NDIS_MAC_OPTION_FULL_DUPLEX); - return sizeof(le32); - - /* statistics OIDs (table 4-2) */ - /* mandatory */ - case OID_GEN_XMIT_OK: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_RCV_OK: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_XMIT_ERROR: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_RCV_ERROR: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_RCV_NO_BUFFER: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* ieee802.3 OIDs (table 4-3) */ - /* mandatory */ - case OID_802_3_PERMANENT_ADDRESS: - memcpy(outbuf, s->conf.macaddr.a, 6); - return 6; - - /* mandatory */ - case OID_802_3_CURRENT_ADDRESS: - memcpy(outbuf, s->conf.macaddr.a, 6); - return 6; - - /* mandatory */ - case OID_802_3_MULTICAST_LIST: - *((le32 *) outbuf) = cpu_to_le32(0xe0000000); - return sizeof(le32); - - /* mandatory */ - case OID_802_3_MAXIMUM_LIST_SIZE: - *((le32 *) outbuf) = cpu_to_le32(1); - return sizeof(le32); - - case OID_802_3_MAC_OPTIONS: - return 0; - - /* ieee802.3 statistics OIDs (table 4-4) */ - /* mandatory */ - case OID_802_3_RCV_ERROR_ALIGNMENT: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_802_3_XMIT_ONE_COLLISION: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_802_3_XMIT_MORE_COLLISIONS: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - default: - fprintf(stderr, "usbnet: unknown OID 0x%08x\n", oid); - return 0; - } - return -1; -} - -static int ndis_set(USBNetState *s, uint32_t oid, - uint8_t *inbuf, unsigned int inlen) -{ - switch (oid) { - case OID_GEN_CURRENT_PACKET_FILTER: - s->filter = le32_to_cpup((le32 *) inbuf); - if (s->filter) { - s->rndis_state = RNDIS_DATA_INITIALIZED; - } else { - s->rndis_state = RNDIS_INITIALIZED; - } - return 0; - - case OID_802_3_MULTICAST_LIST: - return 0; - } - return -1; -} - -static int rndis_get_response(USBNetState *s, uint8_t *buf) -{ - int ret = 0; - struct rndis_response *r = s->rndis_resp.tqh_first; - - if (!r) - return ret; - - QTAILQ_REMOVE(&s->rndis_resp, r, entries); - ret = r->length; - memcpy(buf, r->buf, r->length); - g_free(r); - - return ret; -} - -static void *rndis_queue_response(USBNetState *s, unsigned int length) -{ - struct rndis_response *r = - g_malloc0(sizeof(struct rndis_response) + length); - - if (QTAILQ_EMPTY(&s->rndis_resp)) { - usb_wakeup(s->intr, 0); - } - - QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries); - r->length = length; - - return &r->buf[0]; -} - -static void rndis_clear_responsequeue(USBNetState *s) -{ - struct rndis_response *r; - - while ((r = s->rndis_resp.tqh_first)) { - QTAILQ_REMOVE(&s->rndis_resp, r, entries); - g_free(r); - } -} - -static int rndis_init_response(USBNetState *s, rndis_init_msg_type *buf) -{ - rndis_init_cmplt_type *resp = - rndis_queue_response(s, sizeof(rndis_init_cmplt_type)); - - if (!resp) - return USB_RET_STALL; - - resp->MessageType = cpu_to_le32(RNDIS_INITIALIZE_CMPLT); - resp->MessageLength = cpu_to_le32(sizeof(rndis_init_cmplt_type)); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION); - resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION); - resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS); - resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3); - resp->MaxPacketsPerTransfer = cpu_to_le32(1); - resp->MaxTransferSize = cpu_to_le32(ETH_FRAME_LEN + - sizeof(struct rndis_packet_msg_type) + 22); - resp->PacketAlignmentFactor = cpu_to_le32(0); - resp->AFListOffset = cpu_to_le32(0); - resp->AFListSize = cpu_to_le32(0); - return 0; -} - -static int rndis_query_response(USBNetState *s, - rndis_query_msg_type *buf, unsigned int length) -{ - rndis_query_cmplt_type *resp; - /* oid_supported_list is the largest data reply */ - uint8_t infobuf[sizeof(oid_supported_list)]; - uint32_t bufoffs, buflen; - int infobuflen; - unsigned int resplen; - - bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; - buflen = le32_to_cpu(buf->InformationBufferLength); - if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { - return USB_RET_STALL; - } - - infobuflen = ndis_query(s, le32_to_cpu(buf->OID), - bufoffs + (uint8_t *) buf, buflen, infobuf, - sizeof(infobuf)); - resplen = sizeof(rndis_query_cmplt_type) + - ((infobuflen < 0) ? 0 : infobuflen); - resp = rndis_queue_response(s, resplen); - if (!resp) - return USB_RET_STALL; - - resp->MessageType = cpu_to_le32(RNDIS_QUERY_CMPLT); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->MessageLength = cpu_to_le32(resplen); - - if (infobuflen < 0) { - /* OID not supported */ - resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); - resp->InformationBufferLength = cpu_to_le32(0); - resp->InformationBufferOffset = cpu_to_le32(0); - return 0; - } - - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - resp->InformationBufferOffset = - cpu_to_le32(infobuflen ? sizeof(rndis_query_cmplt_type) - 8 : 0); - resp->InformationBufferLength = cpu_to_le32(infobuflen); - memcpy(resp + 1, infobuf, infobuflen); - - return 0; -} - -static int rndis_set_response(USBNetState *s, - rndis_set_msg_type *buf, unsigned int length) -{ - rndis_set_cmplt_type *resp = - rndis_queue_response(s, sizeof(rndis_set_cmplt_type)); - uint32_t bufoffs, buflen; - int ret; - - if (!resp) - return USB_RET_STALL; - - bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; - buflen = le32_to_cpu(buf->InformationBufferLength); - if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { - return USB_RET_STALL; - } - - ret = ndis_set(s, le32_to_cpu(buf->OID), - bufoffs + (uint8_t *) buf, buflen); - resp->MessageType = cpu_to_le32(RNDIS_SET_CMPLT); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->MessageLength = cpu_to_le32(sizeof(rndis_set_cmplt_type)); - if (ret < 0) { - /* OID not supported */ - resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); - return 0; - } - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - - return 0; -} - -static int rndis_reset_response(USBNetState *s, rndis_reset_msg_type *buf) -{ - rndis_reset_cmplt_type *resp = - rndis_queue_response(s, sizeof(rndis_reset_cmplt_type)); - - if (!resp) - return USB_RET_STALL; - - resp->MessageType = cpu_to_le32(RNDIS_RESET_CMPLT); - resp->MessageLength = cpu_to_le32(sizeof(rndis_reset_cmplt_type)); - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - resp->AddressingReset = cpu_to_le32(1); /* reset information */ - - return 0; -} - -static int rndis_keepalive_response(USBNetState *s, - rndis_keepalive_msg_type *buf) -{ - rndis_keepalive_cmplt_type *resp = - rndis_queue_response(s, sizeof(rndis_keepalive_cmplt_type)); - - if (!resp) - return USB_RET_STALL; - - resp->MessageType = cpu_to_le32(RNDIS_KEEPALIVE_CMPLT); - resp->MessageLength = cpu_to_le32(sizeof(rndis_keepalive_cmplt_type)); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - - return 0; -} - -/* Prepare to receive the next packet */ -static void usb_net_reset_in_buf(USBNetState *s) -{ - s->in_ptr = s->in_len = 0; - qemu_flush_queued_packets(qemu_get_queue(s->nic)); -} - -static int rndis_parse(USBNetState *s, uint8_t *data, int length) -{ - uint32_t msg_type; - le32 *tmp = (le32 *) data; - - msg_type = le32_to_cpup(tmp); - - switch (msg_type) { - case RNDIS_INITIALIZE_MSG: - s->rndis_state = RNDIS_INITIALIZED; - return rndis_init_response(s, (rndis_init_msg_type *) data); - - case RNDIS_HALT_MSG: - s->rndis_state = RNDIS_UNINITIALIZED; - return 0; - - case RNDIS_QUERY_MSG: - return rndis_query_response(s, (rndis_query_msg_type *) data, length); - - case RNDIS_SET_MSG: - return rndis_set_response(s, (rndis_set_msg_type *) data, length); - - case RNDIS_RESET_MSG: - rndis_clear_responsequeue(s); - s->out_ptr = 0; - usb_net_reset_in_buf(s); - return rndis_reset_response(s, (rndis_reset_msg_type *) data); - - case RNDIS_KEEPALIVE_MSG: - /* For USB: host does this every 5 seconds */ - return rndis_keepalive_response(s, (rndis_keepalive_msg_type *) data); - } - - return USB_RET_STALL; -} - -static void usb_net_handle_reset(USBDevice *dev) -{ -} - -static void 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, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch(request) { - case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (!is_rndis(s) || value || index != 0) { - goto fail; - } -#ifdef TRAFFIC_DEBUG - { - unsigned int i; - fprintf(stderr, "SEND_ENCAPSULATED_COMMAND:"); - for (i = 0; i < length; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", data[i]); - } - fprintf(stderr, "\n\n"); - } -#endif - ret = rndis_parse(s, data, length); - if (ret < 0) { - p->status = ret; - } - break; - - case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE: - if (!is_rndis(s) || value || index != 0) { - goto fail; - } - p->actual_length = rndis_get_response(s, data); - if (p->actual_length == 0) { - data[0] = 0; - p->actual_length = 1; - } -#ifdef TRAFFIC_DEBUG - { - unsigned int i; - fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:"); - for (i = 0; i < p->actual_length; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", data[i]); - } - fprintf(stderr, "\n\n"); - } -#endif - break; - - default: - fail: - fprintf(stderr, "usbnet: failed control transaction: " - "request 0x%x value 0x%x index 0x%x length 0x%x\n", - request, value, index, length); - p->status = USB_RET_STALL; - break; - } -} - -static void usb_net_handle_statusin(USBNetState *s, USBPacket *p) -{ - le32 buf[2]; - - if (p->iov.size < 8) { - p->status = USB_RET_STALL; - return; - } - - buf[0] = cpu_to_le32(1); - buf[1] = cpu_to_le32(0); - usb_packet_copy(p, buf, 8); - if (!s->rndis_resp.tqh_first) { - p->status = USB_RET_NAK; - } - -#ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: interrupt poll len %zu return %d", - p->iov.size, p->status); - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->status); -#endif -} - -static void usb_net_handle_datain(USBNetState *s, USBPacket *p) -{ - int len; - - if (s->in_ptr > s->in_len) { - usb_net_reset_in_buf(s); - p->status = USB_RET_NAK; - return; - } - if (!s->in_len) { - p->status = USB_RET_NAK; - return; - } - len = s->in_len - s->in_ptr; - if (len > p->iov.size) { - len = p->iov.size; - } - usb_packet_copy(p, &s->in_buf[s->in_ptr], len); - s->in_ptr += len; - if (s->in_ptr >= s->in_len && - (is_rndis(s) || (s->in_len & (64 - 1)) || !len)) { - /* no short packet necessary */ - usb_net_reset_in_buf(s); - } - -#ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, len); - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", len); -#endif -} - -static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) -{ - int sz = sizeof(s->out_buf) - s->out_ptr; - struct rndis_packet_msg_type *msg = - (struct rndis_packet_msg_type *) s->out_buf; - uint32_t len; - -#ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size); - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size); -#endif - - if (sz > p->iov.size) { - sz = p->iov.size; - } - usb_packet_copy(p, &s->out_buf[s->out_ptr], sz); - s->out_ptr += sz; - - if (!is_rndis(s)) { - if (p->iov.size < 64) { - qemu_send_packet(qemu_get_queue(s->nic), s->out_buf, s->out_ptr); - s->out_ptr = 0; - } - return; - } - len = le32_to_cpu(msg->MessageLength); - if (s->out_ptr < 8 || s->out_ptr < len) { - return; - } - if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) { - uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); - uint32_t size = le32_to_cpu(msg->DataLength); - if (offs < len && size < len && offs + size <= len) { - qemu_send_packet(qemu_get_queue(s->nic), s->out_buf + offs, size); - } - } - s->out_ptr -= len; - memmove(s->out_buf, &s->out_buf[len], s->out_ptr); -} - -static void usb_net_handle_data(USBDevice *dev, USBPacket *p) -{ - USBNetState *s = (USBNetState *) dev; - - switch(p->pid) { - case USB_TOKEN_IN: - switch (p->ep->nr) { - case 1: - usb_net_handle_statusin(s, p); - break; - - case 2: - usb_net_handle_datain(s, p); - break; - - default: - goto fail; - } - break; - - case USB_TOKEN_OUT: - switch (p->ep->nr) { - case 2: - usb_net_handle_dataout(s, p); - break; - - default: - goto fail; - } - break; - - default: - fail: - p->status = USB_RET_STALL; - break; - } - - if (p->status == USB_RET_STALL) { - fprintf(stderr, "usbnet: failed data transaction: " - "pid 0x%x ep 0x%x len 0x%zx\n", - p->pid, p->ep->nr, p->iov.size); - } -} - -static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - USBNetState *s = qemu_get_nic_opaque(nc); - uint8_t *in_buf = s->in_buf; - size_t total_size = size; - - if (!s->dev.config) { - return -1; - } - - if (is_rndis(s)) { - if (s->rndis_state != RNDIS_DATA_INITIALIZED) { - return -1; - } - total_size += sizeof(struct rndis_packet_msg_type); - } - if (total_size > sizeof(s->in_buf)) { - return -1; - } - - /* Only accept packet if input buffer is empty */ - if (s->in_len > 0) { - return 0; - } - - if (is_rndis(s)) { - struct rndis_packet_msg_type *msg; - - msg = (struct rndis_packet_msg_type *)in_buf; - memset(msg, 0, sizeof(struct rndis_packet_msg_type)); - msg->MessageType = cpu_to_le32(RNDIS_PACKET_MSG); - msg->MessageLength = cpu_to_le32(size + sizeof(*msg)); - msg->DataOffset = cpu_to_le32(sizeof(*msg) - 8); - msg->DataLength = cpu_to_le32(size); - /* msg->OOBDataOffset; - * msg->OOBDataLength; - * msg->NumOOBDataElements; - * msg->PerPacketInfoOffset; - * msg->PerPacketInfoLength; - * msg->VcHandle; - * msg->Reserved; - */ - in_buf += sizeof(*msg); - } - - memcpy(in_buf, buf, size); - s->in_len = total_size; - s->in_ptr = 0; - return size; -} - -static void usbnet_cleanup(NetClientState *nc) -{ - USBNetState *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - -static void usb_net_handle_destroy(USBDevice *dev) -{ - USBNetState *s = (USBNetState *) dev; - - /* TODO: remove the nd_table[] entry */ - rndis_clear_responsequeue(s); - qemu_del_nic(s->nic); -} - -static NetClientInfo net_usbnet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = usbnet_receive, - .cleanup = usbnet_cleanup, -}; - -static void usb_net_realize(USBDevice *dev, Error **errrp) -{ - USBNetState *s = USB_NET(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - - s->rndis_state = RNDIS_UNINITIALIZED; - QTAILQ_INIT(&s->rndis_resp); - - s->medium = 0; /* NDIS_MEDIUM_802_3 */ - s->speed = 1000000; /* 100MBps, in 100Bps units */ - s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */; - s->filter = 0; - s->vendorid = 0x1234; - s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_usbnet_info, &s->conf, - object_get_typename(OBJECT(s)), s->dev.qdev.id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - snprintf(s->usbstring_mac, sizeof(s->usbstring_mac), - "%02x%02x%02x%02x%02x%02x", - 0x40, - s->conf.macaddr.a[1], - s->conf.macaddr.a[2], - s->conf.macaddr.a[3], - s->conf.macaddr.a[4], - s->conf.macaddr.a[5]); - usb_desc_set_string(dev, STRING_ETHADDR, s->usbstring_mac); -} - -static void usb_net_instance_init(Object *obj) -{ - USBDevice *dev = USB_DEVICE(obj); - USBNetState *s = USB_NET(dev); - - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", "/ethernet-phy@0", - &dev->qdev, NULL); -} - -static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) -{ - Error *local_err = NULL; - USBDevice *dev; - QemuOpts *opts; - int idx; - - opts = qemu_opts_parse_noisily(qemu_find_opts("net"), cmdline, false); - if (!opts) { - return NULL; - } - qemu_opt_set(opts, "type", "nic", &error_abort); - qemu_opt_set(opts, "model", "usb", &error_abort); - - idx = net_client_init(opts, 0, &local_err); - if (local_err) { - error_report_err(local_err); - return NULL; - } - - dev = usb_create(bus, "usb-net"); - qdev_set_nic_properties(&dev->qdev, &nd_table[idx]); - return dev; -} - -static const VMStateDescription vmstate_usb_net = { - .name = "usb-net", - .unmigratable = 1, -}; - -static Property net_properties[] = { - DEFINE_NIC_PROPERTIES(USBNetState, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_net_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_net_realize; - uc->product_desc = "QEMU USB Network Interface"; - uc->usb_desc = &desc_net; - uc->handle_reset = usb_net_handle_reset; - uc->handle_control = usb_net_handle_control; - uc->handle_data = usb_net_handle_data; - uc->handle_destroy = usb_net_handle_destroy; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->fw_name = "network"; - dc->vmsd = &vmstate_usb_net; - dc->props = net_properties; -} - -static const TypeInfo net_info = { - .name = TYPE_USB_NET, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBNetState), - .class_init = usb_net_class_initfn, - .instance_init = usb_net_instance_init, -}; - -static void usb_net_register_types(void) -{ - type_register_static(&net_info); - usb_legacy_register(TYPE_USB_NET, "net", usb_net_init); -} - -type_init(usb_net_register_types) diff --git a/qemu/hw/usb/dev-serial.c b/qemu/hw/usb/dev-serial.c deleted file mode 100644 index ba8538e60..000000000 --- a/qemu/hw/usb/dev-serial.c +++ /dev/null @@ -1,652 +0,0 @@ -/* - * FTDI FT232BM Device emulation - * - * Copyright (c) 2006 CodeSourcery. - * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org> - * Written by Paul Brook, reused for FTDI by Samuel Thibault - * - * This code is licensed under the LGPL. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "qemu/cutils.h" -#include "qemu/error-report.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "sysemu/char.h" - -//#define DEBUG_Serial - -#ifdef DEBUG_Serial -#define DPRINTF(fmt, ...) \ -do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#define RECV_BUF 384 - -/* Commands */ -#define FTDI_RESET 0 -#define FTDI_SET_MDM_CTRL 1 -#define FTDI_SET_FLOW_CTRL 2 -#define FTDI_SET_BAUD 3 -#define FTDI_SET_DATA 4 -#define FTDI_GET_MDM_ST 5 -#define FTDI_SET_EVENT_CHR 6 -#define FTDI_SET_ERROR_CHR 7 -#define FTDI_SET_LATENCY 9 -#define FTDI_GET_LATENCY 10 - -#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) -#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) - -/* RESET */ - -#define FTDI_RESET_SIO 0 -#define FTDI_RESET_RX 1 -#define FTDI_RESET_TX 2 - -/* SET_MDM_CTRL */ - -#define FTDI_DTR 1 -#define FTDI_SET_DTR (FTDI_DTR << 8) -#define FTDI_RTS 2 -#define FTDI_SET_RTS (FTDI_RTS << 8) - -/* SET_FLOW_CTRL */ - -#define FTDI_RTS_CTS_HS 1 -#define FTDI_DTR_DSR_HS 2 -#define FTDI_XON_XOFF_HS 4 - -/* SET_DATA */ - -#define FTDI_PARITY (0x7 << 8) -#define FTDI_ODD (0x1 << 8) -#define FTDI_EVEN (0x2 << 8) -#define FTDI_MARK (0x3 << 8) -#define FTDI_SPACE (0x4 << 8) - -#define FTDI_STOP (0x3 << 11) -#define FTDI_STOP1 (0x0 << 11) -#define FTDI_STOP15 (0x1 << 11) -#define FTDI_STOP2 (0x2 << 11) - -/* GET_MDM_ST */ -/* TODO: should be sent every 40ms */ -#define FTDI_CTS (1<<4) // CTS line status -#define FTDI_DSR (1<<5) // DSR line status -#define FTDI_RI (1<<6) // RI line status -#define FTDI_RLSD (1<<7) // Receive Line Signal Detect - -/* Status */ - -#define FTDI_DR (1<<0) // Data Ready -#define FTDI_OE (1<<1) // Overrun Err -#define FTDI_PE (1<<2) // Parity Err -#define FTDI_FE (1<<3) // Framing Err -#define FTDI_BI (1<<4) // Break Interrupt -#define FTDI_THRE (1<<5) // Transmitter Holding Register -#define FTDI_TEMT (1<<6) // Transmitter Empty -#define FTDI_FIFO (1<<7) // Error in FIFO - -typedef struct { - USBDevice dev; - uint8_t recv_buf[RECV_BUF]; - uint16_t recv_ptr; - uint16_t recv_used; - uint8_t event_chr; - uint8_t error_chr; - uint8_t event_trigger; - QEMUSerialSetParams params; - int latency; /* ms */ - CharDriverState *cs; -} USBSerialState; - -#define TYPE_USB_SERIAL "usb-serial-dev" -#define USB_SERIAL_DEV(obj) OBJECT_CHECK(USBSerialState, (obj), TYPE_USB_SERIAL) - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT_SERIAL, - STR_PRODUCT_BRAILLE, - STR_SERIALNUMBER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL", - [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE", - [STR_SERIALNUMBER] = "1", -}; - -static const USBDescIface desc_iface0 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = 0xff, - .bInterfaceSubClass = 0xff, - .bInterfaceProtocol = 0xff, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - }, - } -}; - -static const USBDescDevice desc_device = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface0, - }, - }, -}; - -static const USBDesc desc_serial = { - .id = { - .idVendor = 0x0403, - .idProduct = 0x6001, - .bcdDevice = 0x0400, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_SERIAL, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device, - .str = desc_strings, -}; - -static const USBDesc desc_braille = { - .id = { - .idVendor = 0x0403, - .idProduct = 0xfe72, - .bcdDevice = 0x0400, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_BRAILLE, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device, - .str = desc_strings, -}; - -static void usb_serial_reset(USBSerialState *s) -{ - /* TODO: Set flow control to none */ - s->event_chr = 0x0d; - s->event_trigger = 0; - s->recv_ptr = 0; - s->recv_used = 0; - /* TODO: purge in char driver */ -} - -static void usb_serial_handle_reset(USBDevice *dev) -{ - USBSerialState *s = (USBSerialState *)dev; - - DPRINTF("Reset\n"); - - usb_serial_reset(s); - /* TODO: Reset char device, send BREAK? */ -} - -static uint8_t usb_get_modem_lines(USBSerialState *s) -{ - int flags; - uint8_t ret; - - if (qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) - return FTDI_CTS|FTDI_DSR|FTDI_RLSD; - - ret = 0; - if (flags & CHR_TIOCM_CTS) - ret |= FTDI_CTS; - if (flags & CHR_TIOCM_DSR) - ret |= FTDI_DSR; - if (flags & CHR_TIOCM_RI) - ret |= FTDI_RI; - if (flags & CHR_TIOCM_CAR) - ret |= FTDI_RLSD; - - return ret; -} - -static void 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, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - break; - - /* Class specific requests. */ - case DeviceOutVendor | FTDI_RESET: - switch (value) { - case FTDI_RESET_SIO: - usb_serial_reset(s); - break; - case FTDI_RESET_RX: - s->recv_ptr = 0; - s->recv_used = 0; - /* TODO: purge from char device */ - break; - case FTDI_RESET_TX: - /* TODO: purge from char device */ - break; - } - break; - case DeviceOutVendor | FTDI_SET_MDM_CTRL: - { - static int flags; - qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); - if (value & FTDI_SET_RTS) { - if (value & FTDI_RTS) - flags |= CHR_TIOCM_RTS; - else - flags &= ~CHR_TIOCM_RTS; - } - if (value & FTDI_SET_DTR) { - if (value & FTDI_DTR) - flags |= CHR_TIOCM_DTR; - else - flags &= ~CHR_TIOCM_DTR; - } - qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); - break; - } - case DeviceOutVendor | FTDI_SET_FLOW_CTRL: - /* TODO: ioctl */ - break; - case DeviceOutVendor | FTDI_SET_BAUD: { - static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 }; - int subdivisor8 = subdivisors8[((value & 0xc000) >> 14) - | ((index & 1) << 2)]; - int divisor = value & 0x3fff; - - /* chip special cases */ - if (divisor == 1 && subdivisor8 == 0) - subdivisor8 = 4; - if (divisor == 0 && subdivisor8 == 0) - divisor = 1; - - s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); - qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); - break; - } - case DeviceOutVendor | FTDI_SET_DATA: - switch (value & FTDI_PARITY) { - case 0: - s->params.parity = 'N'; - break; - case FTDI_ODD: - s->params.parity = 'O'; - break; - case FTDI_EVEN: - s->params.parity = 'E'; - break; - default: - DPRINTF("unsupported parity %d\n", value & FTDI_PARITY); - goto fail; - } - switch (value & FTDI_STOP) { - case FTDI_STOP1: - s->params.stop_bits = 1; - break; - case FTDI_STOP2: - s->params.stop_bits = 2; - break; - default: - DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP); - goto fail; - } - qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); - /* TODO: TX ON/OFF */ - break; - case DeviceInVendor | FTDI_GET_MDM_ST: - data[0] = usb_get_modem_lines(s) | 1; - data[1] = 0; - p->actual_length = 2; - break; - case DeviceOutVendor | FTDI_SET_EVENT_CHR: - /* TODO: handle it */ - s->event_chr = value; - break; - case DeviceOutVendor | FTDI_SET_ERROR_CHR: - /* TODO: handle it */ - s->error_chr = value; - break; - case DeviceOutVendor | FTDI_SET_LATENCY: - s->latency = value; - break; - case DeviceInVendor | FTDI_GET_LATENCY: - data[0] = s->latency; - p->actual_length = 1; - break; - default: - fail: - DPRINTF("got unsupported/bogus control %x, value %x\n", request, value); - p->status = USB_RET_STALL; - break; - } -} - -static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) -{ - USBSerialState *s = (USBSerialState *)dev; - uint8_t devep = p->ep->nr; - struct iovec *iov; - uint8_t header[2]; - int i, first_len, len; - - switch (p->pid) { - case USB_TOKEN_OUT: - if (devep != 2) - goto fail; - for (i = 0; i < p->iov.niov; i++) { - iov = p->iov.iov + i; - qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len); - } - p->actual_length = p->iov.size; - break; - - case USB_TOKEN_IN: - if (devep != 1) - goto fail; - first_len = RECV_BUF - s->recv_ptr; - len = p->iov.size; - if (len <= 2) { - p->status = USB_RET_NAK; - break; - } - header[0] = usb_get_modem_lines(s) | 1; - /* We do not have the uart details */ - /* handle serial break */ - if (s->event_trigger && s->event_trigger & FTDI_BI) { - s->event_trigger &= ~FTDI_BI; - header[1] = FTDI_BI; - usb_packet_copy(p, header, 2); - break; - } else { - header[1] = 0; - } - len -= 2; - if (len > s->recv_used) - len = s->recv_used; - if (!len) { - p->status = USB_RET_NAK; - break; - } - if (first_len > len) - first_len = len; - usb_packet_copy(p, header, 2); - usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); - if (len > first_len) - usb_packet_copy(p, s->recv_buf, len - first_len); - s->recv_used -= len; - s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; - break; - - default: - DPRINTF("Bad token\n"); - fail: - p->status = USB_RET_STALL; - break; - } -} - -static int usb_serial_can_read(void *opaque) -{ - USBSerialState *s = opaque; - - if (!s->dev.attached) { - return 0; - } - return RECV_BUF - s->recv_used; -} - -static void usb_serial_read(void *opaque, const uint8_t *buf, int size) -{ - USBSerialState *s = opaque; - int first_size, start; - - /* room in the buffer? */ - if (size > (RECV_BUF - s->recv_used)) - size = RECV_BUF - s->recv_used; - - start = s->recv_ptr + s->recv_used; - if (start < RECV_BUF) { - /* copy data to end of buffer */ - first_size = RECV_BUF - start; - if (first_size > size) - first_size = size; - - memcpy(s->recv_buf + start, buf, first_size); - - /* wrap around to front if needed */ - if (size > first_size) - memcpy(s->recv_buf, buf + first_size, size - first_size); - } else { - start -= RECV_BUF; - memcpy(s->recv_buf + start, buf, size); - } - s->recv_used += size; -} - -static void usb_serial_event(void *opaque, int event) -{ - USBSerialState *s = opaque; - - switch (event) { - case CHR_EVENT_BREAK: - s->event_trigger |= FTDI_BI; - break; - case CHR_EVENT_FOCUS: - break; - case CHR_EVENT_OPENED: - if (!s->dev.attached) { - usb_device_attach(&s->dev, &error_abort); - } - break; - case CHR_EVENT_CLOSED: - if (s->dev.attached) { - usb_device_detach(&s->dev); - } - break; - } -} - -static void usb_serial_realize(USBDevice *dev, Error **errp) -{ - USBSerialState *s = USB_SERIAL_DEV(dev); - Error *local_err = NULL; - - usb_desc_create_serial(dev); - usb_desc_init(dev); - dev->auto_attach = 0; - - if (!s->cs) { - error_setg(errp, "Property chardev is required"); - return; - } - - usb_check_attach(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read, - usb_serial_event, s); - usb_serial_handle_reset(dev); - - if (s->cs->be_open && !dev->attached) { - usb_device_attach(dev, &error_abort); - } -} - -static USBDevice *usb_serial_init(USBBus *bus, const char *filename) -{ - USBDevice *dev; - CharDriverState *cdrv; - uint32_t vendorid = 0, productid = 0; - char label[32]; - static int index; - - while (*filename && *filename != ':') { - const char *p; - char *e; - if (strstart(filename, "vendorid=", &p)) { - vendorid = strtol(p, &e, 16); - if (e == p || (*e && *e != ',' && *e != ':')) { - error_report("bogus vendor ID %s", p); - return NULL; - } - filename = e; - } else if (strstart(filename, "productid=", &p)) { - productid = strtol(p, &e, 16); - if (e == p || (*e && *e != ',' && *e != ':')) { - error_report("bogus product ID %s", p); - return NULL; - } - filename = e; - } else { - error_report("unrecognized serial USB option %s", filename); - return NULL; - } - while(*filename == ',') - filename++; - } - if (!*filename) { - error_report("character device specification needed"); - return NULL; - } - filename++; - - snprintf(label, sizeof(label), "usbserial%d", index++); - cdrv = qemu_chr_new(label, filename, NULL); - if (!cdrv) - return NULL; - - dev = usb_create(bus, "usb-serial"); - qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); - if (vendorid) - qdev_prop_set_uint16(&dev->qdev, "vendorid", vendorid); - if (productid) - qdev_prop_set_uint16(&dev->qdev, "productid", productid); - return dev; -} - -static USBDevice *usb_braille_init(USBBus *bus, const char *unused) -{ - USBDevice *dev; - CharDriverState *cdrv; - - cdrv = qemu_chr_new("braille", "braille", NULL); - if (!cdrv) - return NULL; - - dev = usb_create(bus, "usb-braille"); - qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); - return dev; -} - -static const VMStateDescription vmstate_usb_serial = { - .name = "usb-serial", - .unmigratable = 1, -}; - -static Property serial_properties[] = { - DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_serial_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_serial_realize; - uc->handle_reset = usb_serial_handle_reset; - uc->handle_control = usb_serial_handle_control; - uc->handle_data = usb_serial_handle_data; - dc->vmsd = &vmstate_usb_serial; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo usb_serial_dev_type_info = { - .name = TYPE_USB_SERIAL, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBSerialState), - .abstract = true, - .class_init = usb_serial_dev_class_init, -}; - -static void usb_serial_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->product_desc = "QEMU USB Serial"; - uc->usb_desc = &desc_serial; - dc->props = serial_properties; -} - -static const TypeInfo serial_info = { - .name = "usb-serial", - .parent = TYPE_USB_SERIAL, - .class_init = usb_serial_class_initfn, -}; - -static Property braille_properties[] = { - DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_braille_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->product_desc = "QEMU USB Braille"; - uc->usb_desc = &desc_braille; - dc->props = braille_properties; -} - -static const TypeInfo braille_info = { - .name = "usb-braille", - .parent = TYPE_USB_SERIAL, - .class_init = usb_braille_class_initfn, -}; - -static void usb_serial_register_types(void) -{ - type_register_static(&usb_serial_dev_type_info); - type_register_static(&serial_info); - usb_legacy_register("usb-serial", "serial", usb_serial_init); - type_register_static(&braille_info); - usb_legacy_register("usb-braille", "braille", usb_braille_init); -} - -type_init(usb_serial_register_types) diff --git a/qemu/hw/usb/dev-smartcard-reader.c b/qemu/hw/usb/dev-smartcard-reader.c deleted file mode 100644 index af4b85135..000000000 --- a/qemu/hw/usb/dev-smartcard-reader.c +++ /dev/null @@ -1,1507 +0,0 @@ -/* - * Copyright (C) 2011 Red Hat, Inc. - * - * CCID Device emulation - * - * Written by Alon Levy, with contributions from Robert Relyea. - * - * Based on usb-serial.c, see its copyright and attributions below. - * - * This work is licensed under the terms of the GNU GPL, version 2.1 or later. - * See the COPYING file in the top-level directory. - * ------- (original copyright & attribution for usb-serial.c below) -------- - * Copyright (c) 2006 CodeSourcery. - * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org> - * Written by Paul Brook, reused for FTDI by Samuel Thibault, - */ - -/* - * References: - * - * CCID Specification Revision 1.1 April 22nd 2005 - * "Universal Serial Bus, Device Class: Smart Card" - * Specification for Integrated Circuit(s) Cards Interface Devices - * - * Endianness note: from the spec (1.3) - * "Fields that are larger than a byte are stored in little endian" - * - * KNOWN BUGS - * 1. remove/insert can sometimes result in removed state instead of inserted. - * This is a result of the following: - * symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This can happen - * when a short packet is sent, as seen in uhci-usb.c, resulting from a urb - * from the guest requesting SPD and us returning a smaller packet. - * Not sure which messages trigger this. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "qemu/error-report.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" - -#include "ccid.h" - -#define DPRINTF(s, lvl, fmt, ...) \ -do { \ - if (lvl <= s->debug) { \ - printf("usb-ccid: " fmt , ## __VA_ARGS__); \ - } \ -} while (0) - -#define D_WARN 1 -#define D_INFO 2 -#define D_MORE_INFO 3 -#define D_VERBOSE 4 - -#define CCID_DEV_NAME "usb-ccid" -#define USB_CCID_DEV(obj) OBJECT_CHECK(USBCCIDState, (obj), CCID_DEV_NAME) -/* - * The two options for variable sized buffers: - * make them constant size, for large enough constant, - * or handle the migration complexity - VMState doesn't handle this case. - * sizes are expected never to be exceeded, unless guest misbehaves. - */ -#define BULK_OUT_DATA_SIZE 65536 -#define PENDING_ANSWERS_NUM 128 - -#define BULK_IN_BUF_SIZE 384 -#define BULK_IN_PENDING_NUM 8 - -#define CCID_MAX_PACKET_SIZE 64 - -#define CCID_CONTROL_ABORT 0x1 -#define CCID_CONTROL_GET_CLOCK_FREQUENCIES 0x2 -#define CCID_CONTROL_GET_DATA_RATES 0x3 - -#define CCID_PRODUCT_DESCRIPTION "QEMU USB CCID" -#define CCID_VENDOR_DESCRIPTION "QEMU" -#define CCID_INTERFACE_NAME "CCID Interface" -#define CCID_SERIAL_NUMBER_STRING "1" -/* - * Using Gemplus Vendor and Product id - * Effect on various drivers: - * usbccid.sys (winxp, others untested) is a class driver so it doesn't care. - * linux has a number of class drivers, but openct filters based on - * vendor/product (/etc/openct.conf under fedora), hence Gemplus. - */ -#define CCID_VENDOR_ID 0x08e6 -#define CCID_PRODUCT_ID 0x4433 -#define CCID_DEVICE_VERSION 0x0000 - -/* - * BULK_OUT messages from PC to Reader - * Defined in CCID Rev 1.1 6.1 (page 26) - */ -#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn 0x62 -#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff 0x63 -#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus 0x65 -#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock 0x6f -#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters 0x6c -#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters 0x6d -#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters 0x61 -#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape 0x6b -#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock 0x6e -#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU 0x6a -#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure 0x69 -#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical 0x71 -#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort 0x72 -#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73 - -/* - * BULK_IN messages from Reader to PC - * Defined in CCID Rev 1.1 6.2 (page 48) - */ -#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock 0x80 -#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus 0x81 -#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters 0x82 -#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape 0x83 -#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84 - -/* - * INTERRUPT_IN messages from Reader to PC - * Defined in CCID Rev 1.1 6.3 (page 56) - */ -#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange 0x50 -#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError 0x51 - -/* - * Endpoints for CCID - addresses are up to us to decide. - * To support slot insertion and removal we must have an interrupt in ep - * in addition we need a bulk in and bulk out ep - * 5.2, page 20 - */ -#define CCID_INT_IN_EP 1 -#define CCID_BULK_IN_EP 2 -#define CCID_BULK_OUT_EP 3 - -/* bmSlotICCState masks */ -#define SLOT_0_STATE_MASK 1 -#define SLOT_0_CHANGED_MASK 2 - -/* Status codes that go in bStatus (see 6.2.6) */ -enum { - ICC_STATUS_PRESENT_ACTIVE = 0, - ICC_STATUS_PRESENT_INACTIVE, - ICC_STATUS_NOT_PRESENT -}; - -enum { - COMMAND_STATUS_NO_ERROR = 0, - COMMAND_STATUS_FAILED, - COMMAND_STATUS_TIME_EXTENSION_REQUIRED -}; - -/* Error codes that go in bError (see 6.2.6) */ -enum { - ERROR_CMD_NOT_SUPPORTED = 0, - ERROR_CMD_ABORTED = -1, - ERROR_ICC_MUTE = -2, - ERROR_XFR_PARITY_ERROR = -3, - ERROR_XFR_OVERRUN = -4, - ERROR_HW_ERROR = -5, -}; - -/* 6.2.6 RDR_to_PC_SlotStatus definitions */ -enum { - CLOCK_STATUS_RUNNING = 0, - /* - * 0 - Clock Running, 1 - Clock stopped in State L, 2 - H, - * 3 - unknown state. rest are RFU - */ -}; - -typedef struct QEMU_PACKED CCID_Header { - uint8_t bMessageType; - uint32_t dwLength; - uint8_t bSlot; - uint8_t bSeq; -} CCID_Header; - -typedef struct QEMU_PACKED CCID_BULK_IN { - CCID_Header hdr; - uint8_t bStatus; /* Only used in BULK_IN */ - uint8_t bError; /* Only used in BULK_IN */ -} CCID_BULK_IN; - -typedef struct QEMU_PACKED CCID_SlotStatus { - CCID_BULK_IN b; - uint8_t bClockStatus; -} CCID_SlotStatus; - -typedef struct QEMU_PACKED CCID_T0ProtocolDataStructure { - uint8_t bmFindexDindex; - uint8_t bmTCCKST0; - uint8_t bGuardTimeT0; - uint8_t bWaitingIntegerT0; - uint8_t bClockStop; -} CCID_T0ProtocolDataStructure; - -typedef struct QEMU_PACKED CCID_T1ProtocolDataStructure { - uint8_t bmFindexDindex; - uint8_t bmTCCKST1; - uint8_t bGuardTimeT1; - uint8_t bWaitingIntegerT1; - uint8_t bClockStop; - uint8_t bIFSC; - uint8_t bNadValue; -} CCID_T1ProtocolDataStructure; - -typedef union CCID_ProtocolDataStructure { - CCID_T0ProtocolDataStructure t0; - CCID_T1ProtocolDataStructure t1; - uint8_t data[7]; /* must be = max(sizeof(t0), sizeof(t1)) */ -} CCID_ProtocolDataStructure; - -typedef struct QEMU_PACKED CCID_Parameter { - CCID_BULK_IN b; - uint8_t bProtocolNum; - CCID_ProtocolDataStructure abProtocolDataStructure; -} CCID_Parameter; - -typedef struct QEMU_PACKED CCID_DataBlock { - CCID_BULK_IN b; - uint8_t bChainParameter; - uint8_t abData[0]; -} CCID_DataBlock; - -/* 6.1.4 PC_to_RDR_XfrBlock */ -typedef struct QEMU_PACKED CCID_XferBlock { - CCID_Header hdr; - uint8_t bBWI; /* Block Waiting Timeout */ - uint16_t wLevelParameter; /* XXX currently unused */ - uint8_t abData[0]; -} CCID_XferBlock; - -typedef struct QEMU_PACKED CCID_IccPowerOn { - CCID_Header hdr; - uint8_t bPowerSelect; - uint16_t abRFU; -} CCID_IccPowerOn; - -typedef struct QEMU_PACKED CCID_IccPowerOff { - CCID_Header hdr; - uint16_t abRFU; -} CCID_IccPowerOff; - -typedef struct QEMU_PACKED CCID_SetParameters { - CCID_Header hdr; - uint8_t bProtocolNum; - uint16_t abRFU; - CCID_ProtocolDataStructure abProtocolDataStructure; -} CCID_SetParameters; - -typedef struct CCID_Notify_Slot_Change { - uint8_t bMessageType; /* CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange */ - uint8_t bmSlotICCState; -} CCID_Notify_Slot_Change; - -/* used for DataBlock response to XferBlock */ -typedef struct Answer { - uint8_t slot; - uint8_t seq; -} Answer; - -/* pending BULK_IN messages */ -typedef struct BulkIn { - uint8_t data[BULK_IN_BUF_SIZE]; - uint32_t len; - uint32_t pos; -} BulkIn; - -enum { - MIGRATION_NONE, - MIGRATION_MIGRATED, -}; - -typedef struct CCIDBus { - BusState qbus; -} CCIDBus; - -/* - * powered - defaults to true, changed by PowerOn/PowerOff messages - */ -typedef struct USBCCIDState { - USBDevice dev; - USBEndpoint *intr; - USBEndpoint *bulk; - CCIDBus bus; - CCIDCardState *card; - BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */ - uint32_t bulk_in_pending_start; - uint32_t bulk_in_pending_end; /* first free */ - uint32_t bulk_in_pending_num; - BulkIn *current_bulk_in; - uint8_t bulk_out_data[BULK_OUT_DATA_SIZE]; - uint32_t bulk_out_pos; - uint64_t last_answer_error; - Answer pending_answers[PENDING_ANSWERS_NUM]; - uint32_t pending_answers_start; - uint32_t pending_answers_end; - uint32_t pending_answers_num; - uint8_t bError; - uint8_t bmCommandStatus; - uint8_t bProtocolNum; - CCID_ProtocolDataStructure abProtocolDataStructure; - uint32_t ulProtocolDataStructureSize; - uint32_t state_vmstate; - uint32_t migration_target_ip; - uint16_t migration_target_port; - uint8_t migration_state; - uint8_t bmSlotICCState; - uint8_t powered; - uint8_t notify_slot_change; - uint8_t debug; -} USBCCIDState; - -/* - * CCID Spec chapter 4: CCID uses a standard device descriptor per Chapter 9, - * "USB Device Framework", section 9.6.1, in the Universal Serial Bus - * Specification. - * - * This device implemented based on the spec and with an Athena Smart Card - * Reader as reference: - * 0dc3:1004 Athena Smartcard Solutions, Inc. - */ - -static const uint8_t qemu_ccid_descriptor[] = { - /* Smart Card Device Class Descriptor */ - 0x36, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; Functional */ - 0x10, 0x01, /* u16 bcdCCID; CCID Specification Release Number. */ - 0x00, /* - * u8 bMaxSlotIndex; The index of the highest available - * slot on this device. All slots are consecutive starting - * at 00h. - */ - 0x07, /* u8 bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */ - - 0x00, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ - 0x01, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ - /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */ - 0xa0, 0x0f, 0x00, 0x00, - /* u32 dwMaximumClock; */ - 0x00, 0x00, 0x01, 0x00, - 0x00, /* u8 bNumClockSupported; * - * 0 means just the default and max. */ - /* u32 dwDataRate ;bps. 9600 == 00002580h */ - 0x80, 0x25, 0x00, 0x00, - /* u32 dwMaxDataRate ; 11520 bps == 0001C200h */ - 0x00, 0xC2, 0x01, 0x00, - 0x00, /* u8 bNumDataRatesSupported; 00 means all rates between - * default and max */ - /* u32 dwMaxIFSD; * - * maximum IFSD supported by CCID for protocol * - * T=1 (Maximum seen from various cards) */ - 0xfe, 0x00, 0x00, 0x00, - /* u32 dwSyncProtocols; 1 - 2-wire, 2 - 3-wire, 4 - I2C */ - 0x00, 0x00, 0x00, 0x00, - /* u32 dwMechanical; 0 - no special characteristics. */ - 0x00, 0x00, 0x00, 0x00, - /* - * u32 dwFeatures; - * 0 - No special characteristics - * + 2 Automatic parameter configuration based on ATR data - * + 4 Automatic activation of ICC on inserting - * + 8 Automatic ICC voltage selection - * + 10 Automatic ICC clock frequency change - * + 20 Automatic baud rate change - * + 40 Automatic parameters negotiation made by the CCID - * + 80 automatic PPS made by the CCID - * 100 CCID can set ICC in clock stop mode - * 200 NAD value other then 00 accepted (T=1 protocol) - * + 400 Automatic IFSD exchange as first exchange (T=1) - * One of the following only: - * + 10000 TPDU level exchanges with CCID - * 20000 Short APDU level exchange with CCID - * 40000 Short and Extended APDU level exchange with CCID - * - * 100000 USB Wake up signaling supported on card - * insertion and removal. Must set bit 5 in bmAttributes - * in Configuration descriptor if 100000 is set. - */ - 0xfe, 0x04, 0x01, 0x00, - /* - * u32 dwMaxCCIDMessageLength; For extended APDU in - * [261 + 10 , 65544 + 10]. Otherwise the minimum is - * wMaxPacketSize of the Bulk-OUT endpoint - */ - 0x12, 0x00, 0x01, 0x00, - 0xFF, /* - * u8 bClassGetResponse; Significant only for CCID that - * offers an APDU level for exchanges. Indicates the - * default class value used by the CCID when it sends a - * Get Response command to perform the transportation of - * an APDU by T=0 protocol - * FFh indicates that the CCID echos the class of the APDU. - */ - 0xFF, /* - * u8 bClassEnvelope; EAPDU only. Envelope command for - * T=0 - */ - 0x00, 0x00, /* - * u16 wLcdLayout; XXYY Number of lines (XX) and chars per - * line for LCD display used for PIN entry. 0000 - no LCD - */ - 0x01, /* - * u8 bPINSupport; 01h PIN Verification, - * 02h PIN Modification - */ - 0x01, /* u8 bMaxCCIDBusySlots; */ -}; - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, - STR_INTERFACE, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "QEMU USB CCID", - [STR_SERIALNUMBER] = "1", - [STR_INTERFACE] = "CCID Interface", -}; - -static const USBDescIface desc_iface0 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = USB_CLASS_CSCID, - .bInterfaceSubClass = USB_SUBCLASS_UNDEFINED, - .bInterfaceProtocol = 0x00, - .iInterface = STR_INTERFACE, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* smartcard descriptor */ - .data = qemu_ccid_descriptor, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | CCID_INT_IN_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .bInterval = 255, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_IN | CCID_BULK_IN_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_OUT | CCID_BULK_OUT_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - }, - } -}; - -static const USBDescDevice desc_device = { - .bcdUSB = 0x0110, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER | - USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface0, - }, - }, -}; - -static const USBDesc desc_ccid = { - .id = { - .idVendor = CCID_VENDOR_ID, - .idProduct = CCID_PRODUCT_ID, - .bcdDevice = CCID_DEVICE_VERSION, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device, - .str = desc_strings, -}; - -static const uint8_t *ccid_card_get_atr(CCIDCardState *card, uint32_t *len) -{ - CCIDCardClass *cc = CCID_CARD_GET_CLASS(card); - - if (cc->get_atr) { - return cc->get_atr(card, len); - } - return NULL; -} - -static void ccid_card_apdu_from_guest(CCIDCardState *card, - const uint8_t *apdu, - uint32_t len) -{ - CCIDCardClass *cc = CCID_CARD_GET_CLASS(card); - - if (cc->apdu_from_guest) { - cc->apdu_from_guest(card, apdu, len); - } -} - -static int ccid_card_exitfn(CCIDCardState *card) -{ - CCIDCardClass *cc = CCID_CARD_GET_CLASS(card); - - if (cc->exitfn) { - return cc->exitfn(card); - } - return 0; -} - -static int ccid_card_initfn(CCIDCardState *card) -{ - CCIDCardClass *cc = CCID_CARD_GET_CLASS(card); - - if (cc->initfn) { - return cc->initfn(card); - } - return 0; -} - -static bool ccid_has_pending_answers(USBCCIDState *s) -{ - return s->pending_answers_num > 0; -} - -static void ccid_clear_pending_answers(USBCCIDState *s) -{ - s->pending_answers_num = 0; - s->pending_answers_start = 0; - s->pending_answers_end = 0; -} - -static void ccid_print_pending_answers(USBCCIDState *s) -{ - Answer *answer; - int i, count; - - DPRINTF(s, D_VERBOSE, "usb-ccid: pending answers:"); - if (!ccid_has_pending_answers(s)) { - DPRINTF(s, D_VERBOSE, " empty\n"); - return; - } - for (i = s->pending_answers_start, count = s->pending_answers_num ; - count > 0; count--, i++) { - answer = &s->pending_answers[i % PENDING_ANSWERS_NUM]; - if (count == 1) { - DPRINTF(s, D_VERBOSE, "%d:%d\n", answer->slot, answer->seq); - } else { - DPRINTF(s, D_VERBOSE, "%d:%d,", answer->slot, answer->seq); - } - } -} - -static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr) -{ - Answer *answer; - - assert(s->pending_answers_num < PENDING_ANSWERS_NUM); - s->pending_answers_num++; - answer = - &s->pending_answers[(s->pending_answers_end++) % PENDING_ANSWERS_NUM]; - answer->slot = hdr->bSlot; - answer->seq = hdr->bSeq; - ccid_print_pending_answers(s); -} - -static void ccid_remove_pending_answer(USBCCIDState *s, - uint8_t *slot, uint8_t *seq) -{ - Answer *answer; - - assert(s->pending_answers_num > 0); - s->pending_answers_num--; - answer = - &s->pending_answers[(s->pending_answers_start++) % PENDING_ANSWERS_NUM]; - *slot = answer->slot; - *seq = answer->seq; - ccid_print_pending_answers(s); -} - -static void ccid_bulk_in_clear(USBCCIDState *s) -{ - s->bulk_in_pending_start = 0; - s->bulk_in_pending_end = 0; - s->bulk_in_pending_num = 0; -} - -static void ccid_bulk_in_release(USBCCIDState *s) -{ - assert(s->current_bulk_in != NULL); - s->current_bulk_in->pos = 0; - s->current_bulk_in = NULL; -} - -static void ccid_bulk_in_get(USBCCIDState *s) -{ - if (s->current_bulk_in != NULL || s->bulk_in_pending_num == 0) { - return; - } - assert(s->bulk_in_pending_num > 0); - s->bulk_in_pending_num--; - s->current_bulk_in = - &s->bulk_in_pending[(s->bulk_in_pending_start++) % BULK_IN_PENDING_NUM]; -} - -static void *ccid_reserve_recv_buf(USBCCIDState *s, uint16_t len) -{ - BulkIn *bulk_in; - - DPRINTF(s, D_VERBOSE, "%s: QUEUE: reserve %d bytes\n", __func__, len); - - /* look for an existing element */ - if (len > BULK_IN_BUF_SIZE) { - DPRINTF(s, D_WARN, "usb-ccid.c: %s: len larger then max (%d>%d). " - "discarding message.\n", - __func__, len, BULK_IN_BUF_SIZE); - return NULL; - } - if (s->bulk_in_pending_num >= BULK_IN_PENDING_NUM) { - DPRINTF(s, D_WARN, "usb-ccid.c: %s: No free bulk_in buffers. " - "discarding message.\n", __func__); - return NULL; - } - bulk_in = - &s->bulk_in_pending[(s->bulk_in_pending_end++) % BULK_IN_PENDING_NUM]; - s->bulk_in_pending_num++; - bulk_in->len = len; - return bulk_in->data; -} - -static void ccid_reset(USBCCIDState *s) -{ - ccid_bulk_in_clear(s); - ccid_clear_pending_answers(s); -} - -static void ccid_detach(USBCCIDState *s) -{ - ccid_reset(s); -} - -static void ccid_handle_reset(USBDevice *dev) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - - DPRINTF(s, 1, "Reset\n"); - - ccid_reset(s); -} - -static const char *ccid_control_to_str(USBCCIDState *s, int request) -{ - switch (request) { - /* generic - should be factored out if there are other debugees */ - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - return "(generic) set address"; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - return "(generic) get descriptor"; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - return "(generic) get configuration"; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - return "(generic) set configuration"; - case DeviceRequest | USB_REQ_GET_STATUS: - return "(generic) get status"; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - return "(generic) clear feature"; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - return "(generic) set_feature"; - case InterfaceRequest | USB_REQ_GET_INTERFACE: - return "(generic) get interface"; - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - return "(generic) set interface"; - /* class requests */ - case ClassInterfaceOutRequest | CCID_CONTROL_ABORT: - return "ABORT"; - case ClassInterfaceRequest | CCID_CONTROL_GET_CLOCK_FREQUENCIES: - return "GET_CLOCK_FREQUENCIES"; - case ClassInterfaceRequest | CCID_CONTROL_GET_DATA_RATES: - return "GET_DATA_RATES"; - } - return "unknown"; -} - -static void ccid_handle_control(USBDevice *dev, USBPacket *p, int request, - int value, int index, int length, uint8_t *data) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - int ret; - - DPRINTF(s, 1, "%s: got control %s (%x), value %x\n", __func__, - ccid_control_to_str(s, request), request, value); - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - /* Class specific requests. */ - case ClassInterfaceOutRequest | CCID_CONTROL_ABORT: - DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n"); - p->status = USB_RET_STALL; - break; - case ClassInterfaceRequest | CCID_CONTROL_GET_CLOCK_FREQUENCIES: - DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n"); - p->status = USB_RET_STALL; - break; - case ClassInterfaceRequest | CCID_CONTROL_GET_DATA_RATES: - DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n"); - p->status = USB_RET_STALL; - break; - default: - DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n", - request, value); - p->status = USB_RET_STALL; - break; - } -} - -static bool ccid_card_inserted(USBCCIDState *s) -{ - return s->bmSlotICCState & SLOT_0_STATE_MASK; -} - -static uint8_t ccid_card_status(USBCCIDState *s) -{ - return ccid_card_inserted(s) - ? (s->powered ? - ICC_STATUS_PRESENT_ACTIVE - : ICC_STATUS_PRESENT_INACTIVE - ) - : ICC_STATUS_NOT_PRESENT; -} - -static uint8_t ccid_calc_status(USBCCIDState *s) -{ - /* - * page 55, 6.2.6, calculation of bStatus from bmICCStatus and - * bmCommandStatus - */ - uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus << 6); - DPRINTF(s, D_VERBOSE, "%s: status = %d\n", __func__, ret); - return ret; -} - -static void ccid_reset_error_status(USBCCIDState *s) -{ - s->bError = ERROR_CMD_NOT_SUPPORTED; - s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; -} - -static void ccid_write_slot_status(USBCCIDState *s, CCID_Header *recv) -{ - CCID_SlotStatus *h = ccid_reserve_recv_buf(s, sizeof(CCID_SlotStatus)); - if (h == NULL) { - return; - } - h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus; - h->b.hdr.dwLength = 0; - h->b.hdr.bSlot = recv->bSlot; - h->b.hdr.bSeq = recv->bSeq; - h->b.bStatus = ccid_calc_status(s); - h->b.bError = s->bError; - h->bClockStatus = CLOCK_STATUS_RUNNING; - ccid_reset_error_status(s); - usb_wakeup(s->bulk, 0); -} - -static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv) -{ - CCID_Parameter *h; - uint32_t len = s->ulProtocolDataStructureSize; - - h = ccid_reserve_recv_buf(s, sizeof(CCID_Parameter) + len); - if (h == NULL) { - return; - } - h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_Parameters; - h->b.hdr.dwLength = 0; - h->b.hdr.bSlot = recv->bSlot; - h->b.hdr.bSeq = recv->bSeq; - h->b.bStatus = ccid_calc_status(s); - h->b.bError = s->bError; - h->bProtocolNum = s->bProtocolNum; - h->abProtocolDataStructure = s->abProtocolDataStructure; - ccid_reset_error_status(s); - usb_wakeup(s->bulk, 0); -} - -static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq, - const uint8_t *data, uint32_t len) -{ - CCID_DataBlock *p = ccid_reserve_recv_buf(s, sizeof(*p) + len); - - if (p == NULL) { - return; - } - p->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock; - p->b.hdr.dwLength = cpu_to_le32(len); - p->b.hdr.bSlot = slot; - p->b.hdr.bSeq = seq; - p->b.bStatus = ccid_calc_status(s); - p->b.bError = s->bError; - if (p->b.bError) { - DPRINTF(s, D_VERBOSE, "error %d\n", p->b.bError); - } - memcpy(p->abData, data, len); - ccid_reset_error_status(s); - usb_wakeup(s->bulk, 0); -} - -static void ccid_report_error_failed(USBCCIDState *s, uint8_t error) -{ - s->bmCommandStatus = COMMAND_STATUS_FAILED; - s->bError = error; -} - -static void ccid_write_data_block_answer(USBCCIDState *s, - const uint8_t *data, uint32_t len) -{ - uint8_t seq; - uint8_t slot; - - if (!ccid_has_pending_answers(s)) { - DPRINTF(s, D_WARN, "error: no pending answer to return to guest\n"); - ccid_report_error_failed(s, ERROR_ICC_MUTE); - return; - } - ccid_remove_pending_answer(s, &slot, &seq); - ccid_write_data_block(s, slot, seq, data, len); -} - -static uint8_t atr_get_protocol_num(const uint8_t *atr, uint32_t len) -{ - int i; - - if (len < 2 || !(atr[1] & 0x80)) { - /* too short or TD1 not included */ - return 0; /* T=0, default */ - } - i = 1 + !!(atr[1] & 0x10) + !!(atr[1] & 0x20) + !!(atr[1] & 0x40); - i += !!(atr[1] & 0x80); - return atr[i] & 0x0f; -} - -static void ccid_write_data_block_atr(USBCCIDState *s, CCID_Header *recv) -{ - const uint8_t *atr = NULL; - uint32_t len = 0; - uint8_t atr_protocol_num; - CCID_T0ProtocolDataStructure *t0 = &s->abProtocolDataStructure.t0; - CCID_T1ProtocolDataStructure *t1 = &s->abProtocolDataStructure.t1; - - if (s->card) { - atr = ccid_card_get_atr(s->card, &len); - } - atr_protocol_num = atr_get_protocol_num(atr, len); - DPRINTF(s, D_VERBOSE, "%s: atr contains protocol=%d\n", __func__, - atr_protocol_num); - /* set parameters from ATR - see spec page 109 */ - s->bProtocolNum = (atr_protocol_num <= 1 ? atr_protocol_num - : s->bProtocolNum); - switch (atr_protocol_num) { - case 0: - /* TODO: unimplemented ATR T0 parameters */ - t0->bmFindexDindex = 0; - t0->bmTCCKST0 = 0; - t0->bGuardTimeT0 = 0; - t0->bWaitingIntegerT0 = 0; - t0->bClockStop = 0; - break; - case 1: - /* TODO: unimplemented ATR T1 parameters */ - t1->bmFindexDindex = 0; - t1->bmTCCKST1 = 0; - t1->bGuardTimeT1 = 0; - t1->bWaitingIntegerT1 = 0; - t1->bClockStop = 0; - t1->bIFSC = 0; - t1->bNadValue = 0; - break; - default: - DPRINTF(s, D_WARN, "%s: error: unsupported ATR protocol %d\n", - __func__, atr_protocol_num); - } - ccid_write_data_block(s, recv->bSlot, recv->bSeq, atr, len); -} - -static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv) -{ - CCID_SetParameters *ph = (CCID_SetParameters *) recv; - uint32_t protocol_num = ph->bProtocolNum & 3; - - if (protocol_num != 0 && protocol_num != 1) { - ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED); - return; - } - s->bProtocolNum = protocol_num; - s->abProtocolDataStructure = ph->abProtocolDataStructure; -} - -/* - * must be 5 bytes for T=0, 7 bytes for T=1 - * See page 52 - */ -static const CCID_ProtocolDataStructure defaultProtocolDataStructure = { - .t1 = { - .bmFindexDindex = 0x77, - .bmTCCKST1 = 0x00, - .bGuardTimeT1 = 0x00, - .bWaitingIntegerT1 = 0x00, - .bClockStop = 0x00, - .bIFSC = 0xfe, - .bNadValue = 0x00, - } -}; - -static void ccid_reset_parameters(USBCCIDState *s) -{ - s->bProtocolNum = 0; /* T=0 */ - s->abProtocolDataStructure = defaultProtocolDataStructure; -} - -/* NOTE: only a single slot is supported (SLOT_0) */ -static void ccid_on_slot_change(USBCCIDState *s, bool full) -{ - /* RDR_to_PC_NotifySlotChange, 6.3.1 page 56 */ - uint8_t current = s->bmSlotICCState; - if (full) { - s->bmSlotICCState |= SLOT_0_STATE_MASK; - } else { - s->bmSlotICCState &= ~SLOT_0_STATE_MASK; - } - if (current != s->bmSlotICCState) { - s->bmSlotICCState |= SLOT_0_CHANGED_MASK; - } - s->notify_slot_change = true; - usb_wakeup(s->intr, 0); -} - -static void ccid_write_data_block_error( - USBCCIDState *s, uint8_t slot, uint8_t seq) -{ - ccid_write_data_block(s, slot, seq, NULL, 0); -} - -static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv) -{ - uint32_t len; - - if (ccid_card_status(s) != ICC_STATUS_PRESENT_ACTIVE) { - DPRINTF(s, 1, - "usb-ccid: not sending apdu to client, no card connected\n"); - ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq); - return; - } - len = le32_to_cpu(recv->hdr.dwLength); - DPRINTF(s, 1, "%s: seq %d, len %d\n", __func__, - recv->hdr.bSeq, len); - ccid_add_pending_answer(s, (CCID_Header *)recv); - if (s->card) { - ccid_card_apdu_from_guest(s->card, recv->abData, len); - } else { - DPRINTF(s, D_WARN, "warning: discarded apdu\n"); - } -} - -static const char *ccid_message_type_to_str(uint8_t type) -{ - switch (type) { - case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn: return "IccPowerOn"; - case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff: return "IccPowerOff"; - case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus: return "GetSlotStatus"; - case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock: return "XfrBlock"; - case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters: return "GetParameters"; - case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters: return "ResetParameters"; - case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters: return "SetParameters"; - case CCID_MESSAGE_TYPE_PC_to_RDR_Escape: return "Escape"; - case CCID_MESSAGE_TYPE_PC_to_RDR_IccClock: return "IccClock"; - case CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU: return "T0APDU"; - case CCID_MESSAGE_TYPE_PC_to_RDR_Secure: return "Secure"; - case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical: return "Mechanical"; - case CCID_MESSAGE_TYPE_PC_to_RDR_Abort: return "Abort"; - case CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency: - return "SetDataRateAndClockFrequency"; - } - return "unknown"; -} - -static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) -{ - CCID_Header *ccid_header; - - if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { - p->status = USB_RET_STALL; - return; - } - ccid_header = (CCID_Header *)s->bulk_out_data; - usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size); - s->bulk_out_pos += p->iov.size; - if (p->iov.size == CCID_MAX_PACKET_SIZE) { - DPRINTF(s, D_VERBOSE, - "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n", - p->iov.size, ccid_header->dwLength); - return; - } - if (s->bulk_out_pos < 10) { - DPRINTF(s, 1, - "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n", - __func__); - } else { - DPRINTF(s, D_MORE_INFO, "%s %x %s\n", __func__, - ccid_header->bMessageType, - ccid_message_type_to_str(ccid_header->bMessageType)); - switch (ccid_header->bMessageType) { - case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus: - ccid_write_slot_status(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn: - DPRINTF(s, 1, "%s: PowerOn: %d\n", __func__, - ((CCID_IccPowerOn *)(ccid_header))->bPowerSelect); - s->powered = true; - if (!ccid_card_inserted(s)) { - ccid_report_error_failed(s, ERROR_ICC_MUTE); - } - /* atr is written regardless of error. */ - ccid_write_data_block_atr(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff: - ccid_reset_error_status(s); - s->powered = false; - ccid_write_slot_status(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock: - ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters: - ccid_reset_error_status(s); - ccid_set_parameters(s, ccid_header); - ccid_write_parameters(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters: - ccid_reset_error_status(s); - ccid_reset_parameters(s); - ccid_write_parameters(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters: - ccid_reset_error_status(s); - ccid_write_parameters(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical: - ccid_report_error_failed(s, 0); - ccid_write_slot_status(s, ccid_header); - break; - default: - DPRINTF(s, 1, - "handle_data: ERROR: unhandled message type %Xh\n", - ccid_header->bMessageType); - /* - * The caller is expecting the device to respond, tell it we - * don't support the operation. - */ - ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED); - ccid_write_slot_status(s, ccid_header); - break; - } - } - s->bulk_out_pos = 0; -} - -static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) -{ - int len = 0; - - ccid_bulk_in_get(s); - if (s->current_bulk_in != NULL) { - len = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, - p->iov.size); - usb_packet_copy(p, s->current_bulk_in->data + - s->current_bulk_in->pos, len); - s->current_bulk_in->pos += len; - if (s->current_bulk_in->pos == s->current_bulk_in->len) { - ccid_bulk_in_release(s); - } - } else { - /* return when device has no data - usb 2.0 spec Table 8-4 */ - p->status = USB_RET_NAK; - } - if (len) { - DPRINTF(s, D_MORE_INFO, - "%s: %zd/%d req/act to guest (BULK_IN)\n", - __func__, p->iov.size, len); - } - if (len < p->iov.size) { - DPRINTF(s, 1, - "%s: returning short (EREMOTEIO) %d < %zd\n", - __func__, len, p->iov.size); - } -} - -static void ccid_handle_data(USBDevice *dev, USBPacket *p) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - uint8_t buf[2]; - - switch (p->pid) { - case USB_TOKEN_OUT: - ccid_handle_bulk_out(s, p); - break; - - case USB_TOKEN_IN: - switch (p->ep->nr) { - case CCID_BULK_IN_EP: - ccid_bulk_in_copy_to_guest(s, p); - break; - case CCID_INT_IN_EP: - if (s->notify_slot_change) { - /* page 56, RDR_to_PC_NotifySlotChange */ - buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; - buf[1] = s->bmSlotICCState; - usb_packet_copy(p, buf, 2); - s->notify_slot_change = false; - s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK; - DPRINTF(s, D_INFO, - "handle_data: int_in: notify_slot_change %X, " - "requested len %zd\n", - s->bmSlotICCState, p->iov.size); - } else { - p->status = USB_RET_NAK; - } - break; - default: - DPRINTF(s, 1, "Bad endpoint\n"); - p->status = USB_RET_STALL; - break; - } - break; - default: - DPRINTF(s, 1, "Bad token\n"); - p->status = USB_RET_STALL; - break; - } -} - -static void ccid_handle_destroy(USBDevice *dev) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - - ccid_bulk_in_clear(s); -} - -static void ccid_flush_pending_answers(USBCCIDState *s) -{ - while (ccid_has_pending_answers(s)) { - ccid_write_data_block_answer(s, NULL, 0); - } -} - -static Answer *ccid_peek_next_answer(USBCCIDState *s) -{ - return s->pending_answers_num == 0 - ? NULL - : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM]; -} - -static Property ccid_props[] = { - DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -#define TYPE_CCID_BUS "ccid-bus" -#define CCID_BUS(obj) OBJECT_CHECK(CCIDBus, (obj), TYPE_CCID_BUS) - -static const TypeInfo ccid_bus_info = { - .name = TYPE_CCID_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(CCIDBus), -}; - -void ccid_card_send_apdu_to_guest(CCIDCardState *card, - uint8_t *apdu, uint32_t len) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - Answer *answer; - - if (!ccid_has_pending_answers(s)) { - DPRINTF(s, 1, "CCID ERROR: got an APDU without pending answers\n"); - return; - } - s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; - answer = ccid_peek_next_answer(s); - if (answer == NULL) { - DPRINTF(s, D_WARN, "%s: error: unexpected lack of answer\n", __func__); - ccid_report_error_failed(s, ERROR_HW_ERROR); - return; - } - DPRINTF(s, 1, "APDU returned to guest %d (answer seq %d, slot %d)\n", - len, answer->seq, answer->slot); - ccid_write_data_block_answer(s, apdu, len); -} - -void ccid_card_card_removed(CCIDCardState *card) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - ccid_on_slot_change(s, false); - ccid_flush_pending_answers(s); - ccid_reset(s); -} - -int ccid_card_ccid_attach(CCIDCardState *card) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - DPRINTF(s, 1, "CCID Attach\n"); - if (s->migration_state == MIGRATION_MIGRATED) { - s->migration_state = MIGRATION_NONE; - } - return 0; -} - -void ccid_card_ccid_detach(CCIDCardState *card) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - DPRINTF(s, 1, "CCID Detach\n"); - if (ccid_card_inserted(s)) { - ccid_on_slot_change(s, false); - } - ccid_detach(s); -} - -void ccid_card_card_error(CCIDCardState *card, uint64_t error) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - s->bmCommandStatus = COMMAND_STATUS_FAILED; - s->last_answer_error = error; - DPRINTF(s, 1, "VSC_Error: %" PRIX64 "\n", s->last_answer_error); - /* TODO: these errors should be more verbose and propagated to the guest.*/ - /* - * We flush all pending answers on CardRemove message in ccid-card-passthru, - * so check that first to not trigger abort - */ - if (ccid_has_pending_answers(s)) { - ccid_write_data_block_answer(s, NULL, 0); - } -} - -void ccid_card_card_inserted(CCIDCardState *card) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; - ccid_flush_pending_answers(s); - ccid_on_slot_change(s, true); -} - -static int ccid_card_exit(DeviceState *qdev) -{ - int ret = 0; - CCIDCardState *card = CCID_CARD(qdev); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - if (ccid_card_inserted(s)) { - ccid_card_card_removed(card); - } - ret = ccid_card_exitfn(card); - s->card = NULL; - return ret; -} - -static int ccid_card_init(DeviceState *qdev) -{ - CCIDCardState *card = CCID_CARD(qdev); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - int ret = 0; - - if (card->slot != 0) { - error_report("Warning: usb-ccid supports one slot, can't add %d", - card->slot); - return -1; - } - if (s->card != NULL) { - error_report("Warning: usb-ccid card already full, not adding"); - return -1; - } - ret = ccid_card_initfn(card); - if (ret == 0) { - s->card = card; - } - return ret; -} - -static void ccid_realize(USBDevice *dev, Error **errp) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - qbus_create_inplace(&s->bus, sizeof(s->bus), TYPE_CCID_BUS, DEVICE(dev), - NULL); - qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(dev), &error_abort); - s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP); - s->bulk = usb_ep_get(dev, USB_TOKEN_IN, CCID_BULK_IN_EP); - s->card = NULL; - s->migration_state = MIGRATION_NONE; - s->migration_target_ip = 0; - s->migration_target_port = 0; - s->dev.speed = USB_SPEED_FULL; - s->dev.speedmask = USB_SPEED_MASK_FULL; - s->notify_slot_change = false; - s->powered = true; - s->pending_answers_num = 0; - s->last_answer_error = 0; - s->bulk_in_pending_start = 0; - s->bulk_in_pending_end = 0; - s->current_bulk_in = NULL; - ccid_reset_error_status(s); - s->bulk_out_pos = 0; - ccid_reset_parameters(s); - ccid_reset(s); - s->debug = parse_debug_env("QEMU_CCID_DEBUG", D_VERBOSE, s->debug); -} - -static int ccid_post_load(void *opaque, int version_id) -{ - USBCCIDState *s = opaque; - - /* - * This must be done after usb_device_attach, which sets state to ATTACHED, - * while it must be DEFAULT in order to accept packets (like it is after - * reset, but reset will reset our addr and call our reset handler which - * may change state, and we don't want to do that when migrating). - */ - s->dev.state = s->state_vmstate; - return 0; -} - -static void ccid_pre_save(void *opaque) -{ - USBCCIDState *s = opaque; - - s->state_vmstate = s->dev.state; - if (s->dev.attached) { - /* - * Migrating an open device, ignore reconnection CHR_EVENT to avoid an - * erroneous detach. - */ - s->migration_state = MIGRATION_MIGRATED; - } -} - -static VMStateDescription bulk_in_vmstate = { - .name = "CCID BulkIn state", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(data, BulkIn), - VMSTATE_UINT32(len, BulkIn), - VMSTATE_UINT32(pos, BulkIn), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription answer_vmstate = { - .name = "CCID Answer state", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(slot, Answer), - VMSTATE_UINT8(seq, Answer), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription usb_device_vmstate = { - .name = "usb_device", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(addr, USBDevice), - VMSTATE_BUFFER(setup_buf, USBDevice), - VMSTATE_BUFFER(data_buf, USBDevice), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription ccid_vmstate = { - .name = "usb-ccid", - .version_id = 1, - .minimum_version_id = 1, - .post_load = ccid_post_load, - .pre_save = ccid_pre_save, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(dev, USBCCIDState, 1, usb_device_vmstate, USBDevice), - VMSTATE_UINT8(debug, USBCCIDState), - VMSTATE_BUFFER(bulk_out_data, USBCCIDState), - VMSTATE_UINT32(bulk_out_pos, USBCCIDState), - VMSTATE_UINT8(bmSlotICCState, USBCCIDState), - VMSTATE_UINT8(powered, USBCCIDState), - VMSTATE_UINT8(notify_slot_change, USBCCIDState), - VMSTATE_UINT64(last_answer_error, USBCCIDState), - VMSTATE_UINT8(bError, USBCCIDState), - VMSTATE_UINT8(bmCommandStatus, USBCCIDState), - VMSTATE_UINT8(bProtocolNum, USBCCIDState), - VMSTATE_BUFFER(abProtocolDataStructure.data, USBCCIDState), - VMSTATE_UINT32(ulProtocolDataStructureSize, USBCCIDState), - VMSTATE_STRUCT_ARRAY(bulk_in_pending, USBCCIDState, - BULK_IN_PENDING_NUM, 1, bulk_in_vmstate, BulkIn), - VMSTATE_UINT32(bulk_in_pending_start, USBCCIDState), - VMSTATE_UINT32(bulk_in_pending_end, USBCCIDState), - VMSTATE_STRUCT_ARRAY(pending_answers, USBCCIDState, - PENDING_ANSWERS_NUM, 1, answer_vmstate, Answer), - VMSTATE_UINT32(pending_answers_num, USBCCIDState), - VMSTATE_UINT8(migration_state, USBCCIDState), - VMSTATE_UINT32(state_vmstate, USBCCIDState), - VMSTATE_END_OF_LIST() - } -}; - -static Property ccid_properties[] = { - DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ccid_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - uc->realize = ccid_realize; - uc->product_desc = "QEMU USB CCID"; - uc->usb_desc = &desc_ccid; - uc->handle_reset = ccid_handle_reset; - uc->handle_control = ccid_handle_control; - uc->handle_data = ccid_handle_data; - uc->handle_destroy = ccid_handle_destroy; - dc->desc = "CCID Rev 1.1 smartcard reader"; - dc->vmsd = &ccid_vmstate; - dc->props = ccid_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - hc->unplug = qdev_simple_device_unplug_cb; -} - -static const TypeInfo ccid_info = { - .name = CCID_DEV_NAME, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBCCIDState), - .class_init = ccid_class_initfn, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static void ccid_card_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->bus_type = TYPE_CCID_BUS; - k->init = ccid_card_init; - k->exit = ccid_card_exit; - k->props = ccid_props; -} - -static const TypeInfo ccid_card_type_info = { - .name = TYPE_CCID_CARD, - .parent = TYPE_DEVICE, - .instance_size = sizeof(CCIDCardState), - .abstract = true, - .class_size = sizeof(CCIDCardClass), - .class_init = ccid_card_class_init, -}; - -static void ccid_register_types(void) -{ - type_register_static(&ccid_bus_info); - type_register_static(&ccid_card_type_info); - type_register_static(&ccid_info); - usb_legacy_register(CCID_DEV_NAME, "ccid", NULL); -} - -type_init(ccid_register_types) diff --git a/qemu/hw/usb/dev-storage.c b/qemu/hw/usb/dev-storage.c deleted file mode 100644 index 248a58045..000000000 --- a/qemu/hw/usb/dev-storage.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * USB Mass Storage Device emulation - * - * Copyright (c) 2006 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the LGPL. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "qemu/error-report.h" -#include "qemu/option.h" -#include "qemu/config-file.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "hw/scsi/scsi.h" -#include "ui/console.h" -#include "monitor/monitor.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "qapi/visitor.h" -#include "qemu/cutils.h" - -//#define DEBUG_MSD - -#ifdef DEBUG_MSD -#define DPRINTF(fmt, ...) \ -do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -/* USB requests. */ -#define MassStorageReset 0xff -#define GetMaxLun 0xfe - -enum USBMSDMode { - USB_MSDM_CBW, /* Command Block. */ - USB_MSDM_DATAOUT, /* Transfer data to device. */ - USB_MSDM_DATAIN, /* Transfer data from device. */ - USB_MSDM_CSW /* Command Status. */ -}; - -struct usb_msd_csw { - uint32_t sig; - uint32_t tag; - uint32_t residue; - uint8_t status; -}; - -typedef struct { - USBDevice dev; - enum USBMSDMode mode; - uint32_t scsi_off; - uint32_t scsi_len; - uint32_t data_len; - struct usb_msd_csw csw; - SCSIRequest *req; - SCSIBus bus; - /* For async completion. */ - USBPacket *packet; - /* usb-storage only */ - BlockConf conf; - uint32_t removable; - SCSIDevice *scsi_dev; -} MSDState; - -#define TYPE_USB_STORAGE "usb-storage-dev" -#define USB_STORAGE_DEV(obj) OBJECT_CHECK(MSDState, (obj), TYPE_USB_STORAGE) - -struct usb_msd_cbw { - uint32_t sig; - uint32_t tag; - uint32_t data_len; - uint8_t flags; - uint8_t lun; - uint8_t cmd_len; - uint8_t cmd[16]; -}; - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, - STR_CONFIG_FULL, - STR_CONFIG_HIGH, - STR_CONFIG_SUPER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "QEMU USB HARDDRIVE", - [STR_SERIALNUMBER] = "1", - [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", - [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", - [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", -}; - -static const USBDescIface desc_iface_full = { - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x50, /* Bulk */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - }, - } -}; - -static const USBDescDevice desc_device_full = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_FULL, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_full, - }, - }, -}; - -static const USBDescIface desc_iface_high = { - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x50, /* Bulk */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - }, - } -}; - -static const USBDescDevice desc_device_high = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_HIGH, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_high, - }, - }, -}; - -static const USBDescIface desc_iface_super = { - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x50, /* Bulk */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - }, - } -}; - -static const USBDescDevice desc_device_super = { - .bcdUSB = 0x0300, - .bMaxPacketSize0 = 9, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_SUPER, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_super, - }, - }, -}; - -static const USBDesc desc = { - .id = { - .idVendor = 0x46f4, /* CRC16() of "QEMU" */ - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_full, - .high = &desc_device_high, - .super = &desc_device_super, - .str = desc_strings, -}; - -static void usb_msd_copy_data(MSDState *s, USBPacket *p) -{ - uint32_t len; - len = p->iov.size - p->actual_length; - if (len > s->scsi_len) - len = s->scsi_len; - usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len); - s->scsi_len -= len; - s->scsi_off += len; - s->data_len -= len; - if (s->scsi_len == 0 || s->data_len == 0) { - scsi_req_continue(s->req); - } -} - -static void usb_msd_send_status(MSDState *s, USBPacket *p) -{ - int len; - - DPRINTF("Command status %d tag 0x%x, len %zd\n", - s->csw.status, le32_to_cpu(s->csw.tag), p->iov.size); - - assert(s->csw.sig == cpu_to_le32(0x53425355)); - len = MIN(sizeof(s->csw), p->iov.size); - usb_packet_copy(p, &s->csw, len); - memset(&s->csw, 0, sizeof(s->csw)); -} - -static void usb_msd_packet_complete(MSDState *s) -{ - USBPacket *p = s->packet; - - /* Set s->packet to NULL before calling usb_packet_complete - because another request may be issued before - usb_packet_complete returns. */ - DPRINTF("Packet complete %p\n", p); - s->packet = NULL; - usb_packet_complete(&s->dev, p); -} - -static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) -{ - MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); - USBPacket *p = s->packet; - - assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); - s->scsi_len = len; - s->scsi_off = 0; - if (p) { - usb_msd_copy_data(s, p); - p = s->packet; - if (p && p->actual_length == p->iov.size) { - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_msd_packet_complete(s); - } - } -} - -static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t resid) -{ - MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); - USBPacket *p = s->packet; - - DPRINTF("Command complete %d tag 0x%x\n", status, req->tag); - - s->csw.sig = cpu_to_le32(0x53425355); - s->csw.tag = cpu_to_le32(req->tag); - s->csw.residue = cpu_to_le32(s->data_len); - s->csw.status = status != 0; - - if (s->packet) { - if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) { - /* A deferred packet with no write data remaining must be - the status read packet. */ - usb_msd_send_status(s, p); - s->mode = USB_MSDM_CBW; - } else if (s->mode == USB_MSDM_CSW) { - usb_msd_send_status(s, p); - s->mode = USB_MSDM_CBW; - } else { - if (s->data_len) { - int len = (p->iov.size - p->actual_length); - usb_packet_skip(p, len); - s->data_len -= len; - } - if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - } - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_msd_packet_complete(s); - } else if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - scsi_req_unref(req); - s->req = NULL; -} - -static void usb_msd_request_cancelled(SCSIRequest *req) -{ - MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); - - if (req == s->req) { - scsi_req_unref(s->req); - s->req = NULL; - s->scsi_len = 0; - } -} - -static void usb_msd_handle_reset(USBDevice *dev) -{ - MSDState *s = (MSDState *)dev; - - DPRINTF("Reset\n"); - if (s->req) { - scsi_req_cancel(s->req); - } - assert(s->req == NULL); - - if (s->packet) { - s->packet->status = USB_RET_STALL; - usb_msd_packet_complete(s); - } - - s->mode = USB_MSDM_CBW; -} - -static void usb_msd_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - MSDState *s = (MSDState *)dev; - SCSIDevice *scsi_dev; - int ret, maxlun; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - break; - /* Class specific requests. */ - case ClassInterfaceOutRequest | MassStorageReset: - /* Reset state ready for the next CBW. */ - s->mode = USB_MSDM_CBW; - break; - case ClassInterfaceRequest | GetMaxLun: - maxlun = 0; - for (;;) { - scsi_dev = scsi_device_find(&s->bus, 0, 0, maxlun+1); - if (scsi_dev == NULL) { - break; - } - if (scsi_dev->lun != maxlun+1) { - break; - } - maxlun++; - } - DPRINTF("MaxLun %d\n", maxlun); - data[0] = maxlun; - p->actual_length = 1; - break; - default: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p) -{ - MSDState *s = USB_STORAGE_DEV(dev); - - assert(s->packet == p); - s->packet = NULL; - - if (s->req) { - scsi_req_cancel(s->req); - } -} - -static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) -{ - MSDState *s = (MSDState *)dev; - uint32_t tag; - struct usb_msd_cbw cbw; - uint8_t devep = p->ep->nr; - SCSIDevice *scsi_dev; - uint32_t len; - - switch (p->pid) { - case USB_TOKEN_OUT: - if (devep != 2) - goto fail; - - switch (s->mode) { - case USB_MSDM_CBW: - if (p->iov.size != 31) { - error_report("usb-msd: Bad CBW size"); - goto fail; - } - usb_packet_copy(p, &cbw, 31); - if (le32_to_cpu(cbw.sig) != 0x43425355) { - error_report("usb-msd: Bad signature %08x", - le32_to_cpu(cbw.sig)); - goto fail; - } - DPRINTF("Command on LUN %d\n", cbw.lun); - scsi_dev = scsi_device_find(&s->bus, 0, 0, cbw.lun); - if (scsi_dev == NULL) { - error_report("usb-msd: Bad LUN %d", cbw.lun); - goto fail; - } - tag = le32_to_cpu(cbw.tag); - s->data_len = le32_to_cpu(cbw.data_len); - if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } else if (cbw.flags & 0x80) { - s->mode = USB_MSDM_DATAIN; - } else { - s->mode = USB_MSDM_DATAOUT; - } - DPRINTF("Command tag 0x%x flags %08x len %d data %d\n", - tag, cbw.flags, cbw.cmd_len, s->data_len); - assert(le32_to_cpu(s->csw.residue) == 0); - s->scsi_len = 0; - s->req = scsi_req_new(scsi_dev, tag, cbw.lun, cbw.cmd, NULL); -#ifdef DEBUG_MSD - scsi_req_print(s->req); -#endif - len = scsi_req_enqueue(s->req); - if (len) { - scsi_req_continue(s->req); - } - break; - - case USB_MSDM_DATAOUT: - DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len); - if (p->iov.size > s->data_len) { - goto fail; - } - - if (s->scsi_len) { - usb_msd_copy_data(s, p); - } - if (le32_to_cpu(s->csw.residue)) { - int len = p->iov.size - p->actual_length; - if (len) { - usb_packet_skip(p, len); - s->data_len -= len; - if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - } - } - if (p->actual_length < p->iov.size) { - DPRINTF("Deferring packet %p [wait data-out]\n", p); - s->packet = p; - p->status = USB_RET_ASYNC; - } - break; - - default: - DPRINTF("Unexpected write (len %zd)\n", p->iov.size); - goto fail; - } - break; - - case USB_TOKEN_IN: - if (devep != 1) - goto fail; - - switch (s->mode) { - case USB_MSDM_DATAOUT: - if (s->data_len != 0 || p->iov.size < 13) { - goto fail; - } - /* Waiting for SCSI write to complete. */ - s->packet = p; - p->status = USB_RET_ASYNC; - break; - - case USB_MSDM_CSW: - if (p->iov.size < 13) { - goto fail; - } - - if (s->req) { - /* still in flight */ - DPRINTF("Deferring packet %p [wait status]\n", p); - s->packet = p; - p->status = USB_RET_ASYNC; - } else { - usb_msd_send_status(s, p); - s->mode = USB_MSDM_CBW; - } - break; - - case USB_MSDM_DATAIN: - DPRINTF("Data in %zd/%d, scsi_len %d\n", - p->iov.size, s->data_len, s->scsi_len); - if (s->scsi_len) { - usb_msd_copy_data(s, p); - } - if (le32_to_cpu(s->csw.residue)) { - int len = p->iov.size - p->actual_length; - if (len) { - usb_packet_skip(p, len); - s->data_len -= len; - if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - } - } - if (p->actual_length < p->iov.size) { - DPRINTF("Deferring packet %p [wait data-in]\n", p); - s->packet = p; - p->status = USB_RET_ASYNC; - } - break; - - default: - DPRINTF("Unexpected read (len %zd)\n", p->iov.size); - goto fail; - } - break; - - default: - DPRINTF("Bad token\n"); - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_msd_password_cb(void *opaque, int err) -{ - MSDState *s = opaque; - Error *local_err = NULL; - - if (!err) { - usb_device_attach(&s->dev, &local_err); - } - - if (local_err) { - error_report_err(local_err); - qdev_unplug(&s->dev.qdev, NULL); - } -} - -static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req) -{ - MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); - - /* nothing to load, just store req in our state struct */ - assert(s->req == NULL); - scsi_req_ref(req); - s->req = req; - return NULL; -} - -static const struct SCSIBusInfo usb_msd_scsi_info_storage = { - .tcq = false, - .max_target = 0, - .max_lun = 0, - - .transfer_data = usb_msd_transfer_data, - .complete = usb_msd_command_complete, - .cancel = usb_msd_request_cancelled, - .load_request = usb_msd_load_request, -}; - -static const struct SCSIBusInfo usb_msd_scsi_info_bot = { - .tcq = false, - .max_target = 0, - .max_lun = 15, - - .transfer_data = usb_msd_transfer_data, - .complete = usb_msd_command_complete, - .cancel = usb_msd_request_cancelled, - .load_request = usb_msd_load_request, -}; - -static void usb_msd_realize_storage(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - BlockBackend *blk = s->conf.blk; - SCSIDevice *scsi_dev; - Error *err = NULL; - - if (!blk) { - error_setg(errp, "drive property not set"); - return; - } - - if (blk_bs(blk)) { - bdrv_add_key(blk_bs(blk), NULL, &err); - if (err) { - if (monitor_cur_is_qmp()) { - error_propagate(errp, err); - return; - } - error_free(err); - err = NULL; - if (cur_mon) { - monitor_read_bdrv_key_start(cur_mon, blk_bs(blk), - usb_msd_password_cb, s); - s->dev.auto_attach = 0; - } else { - autostart = 0; - } - } - } - - blkconf_serial(&s->conf, &dev->serial); - blkconf_blocksizes(&s->conf); - - /* - * Hack alert: this pretends to be a block device, but it's really - * a SCSI bus that can serve only a single device, which it - * creates automatically. But first it needs to detach from its - * blockdev, or else scsi_bus_legacy_add_drive() dies when it - * attaches again. - * - * The hack is probably a bad idea. - */ - blk_detach_dev(blk, &s->dev.qdev); - s->conf.blk = NULL; - - usb_desc_create_serial(dev); - usb_desc_init(dev); - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &usb_msd_scsi_info_storage, NULL); - scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, - s->conf.bootindex, dev->serial, - &err); - if (!scsi_dev) { - error_propagate(errp, err); - return; - } - usb_msd_handle_reset(dev); - s->scsi_dev = scsi_dev; -} - -static void usb_msd_realize_bot(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &usb_msd_scsi_info_bot, NULL); - usb_msd_handle_reset(dev); -} - -static USBDevice *usb_msd_init(USBBus *bus, const char *filename) -{ - static int nr=0; - Error *err = NULL; - char id[8]; - QemuOpts *opts; - DriveInfo *dinfo; - USBDevice *dev; - const char *p1; - char fmt[32]; - - /* parse -usbdevice disk: syntax into drive opts */ - do { - snprintf(id, sizeof(id), "usb%d", nr++); - opts = qemu_opts_create(qemu_find_opts("drive"), id, 1, NULL); - } while (!opts); - - p1 = strchr(filename, ':'); - if (p1++) { - const char *p2; - - if (strstart(filename, "format=", &p2)) { - int len = MIN(p1 - p2, sizeof(fmt)); - pstrcpy(fmt, len, p2); - qemu_opt_set(opts, "format", fmt, &error_abort); - } else if (*filename != ':') { - error_report("unrecognized USB mass-storage option %s", filename); - return NULL; - } - filename = p1; - } - if (!*filename) { - error_report("block device specification needed"); - return NULL; - } - qemu_opt_set(opts, "file", filename, &error_abort); - qemu_opt_set(opts, "if", "none", &error_abort); - - /* create host drive */ - dinfo = drive_new(opts, 0); - if (!dinfo) { - qemu_opts_del(opts); - return NULL; - } - - /* create guest device */ - dev = usb_create(bus, "usb-storage"); - qdev_prop_set_drive(&dev->qdev, "drive", blk_by_legacy_dinfo(dinfo), - &err); - if (err) { - error_report_err(err); - object_unparent(OBJECT(dev)); - return NULL; - } - return dev; -} - -static const VMStateDescription vmstate_usb_msd = { - .name = "usb-storage", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, MSDState), - VMSTATE_UINT32(mode, MSDState), - VMSTATE_UINT32(scsi_len, MSDState), - VMSTATE_UINT32(scsi_off, MSDState), - VMSTATE_UINT32(data_len, MSDState), - VMSTATE_UINT32(csw.sig, MSDState), - VMSTATE_UINT32(csw.tag, MSDState), - VMSTATE_UINT32(csw.residue, MSDState), - VMSTATE_UINT8(csw.status, MSDState), - VMSTATE_END_OF_LIST() - } -}; - -static Property msd_properties[] = { - DEFINE_BLOCK_PROPERTIES(MSDState, conf), - DEFINE_PROP_BIT("removable", MSDState, removable, 0, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_msd_class_initfn_common(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->product_desc = "QEMU USB MSD"; - uc->usb_desc = &desc; - uc->cancel_packet = usb_msd_cancel_io; - uc->handle_attach = usb_desc_attach; - uc->handle_reset = usb_msd_handle_reset; - uc->handle_control = usb_msd_handle_control; - uc->handle_data = usb_msd_handle_data; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->fw_name = "storage"; - dc->vmsd = &vmstate_usb_msd; -} - -static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_msd_realize_storage; - dc->props = msd_properties; -} - -static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - USBDevice *dev = USB_DEVICE(obj); - MSDState *s = USB_STORAGE_DEV(dev); - - visit_type_int32(v, name, &s->conf.bootindex, errp); -} - -static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - USBDevice *dev = USB_DEVICE(obj); - MSDState *s = USB_STORAGE_DEV(dev); - int32_t boot_index; - Error *local_err = NULL; - - visit_type_int32(v, name, &boot_index, &local_err); - if (local_err) { - goto out; - } - /* check whether bootindex is present in fw_boot_order list */ - check_boot_index(boot_index, &local_err); - if (local_err) { - goto out; - } - /* change bootindex to a new one */ - s->conf.bootindex = boot_index; - - if (s->scsi_dev) { - object_property_set_int(OBJECT(s->scsi_dev), boot_index, "bootindex", - &error_abort); - } - -out: - if (local_err) { - error_propagate(errp, local_err); - } -} - -static const TypeInfo usb_storage_dev_type_info = { - .name = TYPE_USB_STORAGE, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(MSDState), - .abstract = true, - .class_init = usb_msd_class_initfn_common, -}; - -static void usb_msd_instance_init(Object *obj) -{ - object_property_add(obj, "bootindex", "int32", - usb_msd_get_bootindex, - usb_msd_set_bootindex, NULL, NULL, NULL); - object_property_set_int(obj, -1, "bootindex", NULL); -} - -static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data) -{ - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - uc->realize = usb_msd_realize_bot; - dc->hotpluggable = false; -} - -static const TypeInfo msd_info = { - .name = "usb-storage", - .parent = TYPE_USB_STORAGE, - .class_init = usb_msd_class_initfn_storage, - .instance_init = usb_msd_instance_init, -}; - -static const TypeInfo bot_info = { - .name = "usb-bot", - .parent = TYPE_USB_STORAGE, - .class_init = usb_msd_class_initfn_bot, -}; - -static void usb_msd_register_types(void) -{ - type_register_static(&usb_storage_dev_type_info); - type_register_static(&msd_info); - type_register_static(&bot_info); - usb_legacy_register("usb-storage", "disk", usb_msd_init); -} - -type_init(usb_msd_register_types) diff --git a/qemu/hw/usb/dev-uas.c b/qemu/hw/usb/dev-uas.c deleted file mode 100644 index 0678b1b05..000000000 --- a/qemu/hw/usb/dev-uas.c +++ /dev/null @@ -1,961 +0,0 @@ -/* - * UAS (USB Attached SCSI) emulation - * - * Copyright Red Hat, Inc. 2012 - * - * Author: Gerd Hoffmann <kraxel@redhat.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/option.h" -#include "qemu/config-file.h" -#include "trace.h" -#include "qemu/error-report.h" - -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" - -/* --------------------------------------------------------------------- */ - -#define UAS_UI_COMMAND 0x01 -#define UAS_UI_SENSE 0x03 -#define UAS_UI_RESPONSE 0x04 -#define UAS_UI_TASK_MGMT 0x05 -#define UAS_UI_READ_READY 0x06 -#define UAS_UI_WRITE_READY 0x07 - -#define UAS_RC_TMF_COMPLETE 0x00 -#define UAS_RC_INVALID_INFO_UNIT 0x02 -#define UAS_RC_TMF_NOT_SUPPORTED 0x04 -#define UAS_RC_TMF_FAILED 0x05 -#define UAS_RC_TMF_SUCCEEDED 0x08 -#define UAS_RC_INCORRECT_LUN 0x09 -#define UAS_RC_OVERLAPPED_TAG 0x0a - -#define UAS_TMF_ABORT_TASK 0x01 -#define UAS_TMF_ABORT_TASK_SET 0x02 -#define UAS_TMF_CLEAR_TASK_SET 0x04 -#define UAS_TMF_LOGICAL_UNIT_RESET 0x08 -#define UAS_TMF_I_T_NEXUS_RESET 0x10 -#define UAS_TMF_CLEAR_ACA 0x40 -#define UAS_TMF_QUERY_TASK 0x80 -#define UAS_TMF_QUERY_TASK_SET 0x81 -#define UAS_TMF_QUERY_ASYNC_EVENT 0x82 - -#define UAS_PIPE_ID_COMMAND 0x01 -#define UAS_PIPE_ID_STATUS 0x02 -#define UAS_PIPE_ID_DATA_IN 0x03 -#define UAS_PIPE_ID_DATA_OUT 0x04 - -typedef struct { - uint8_t id; - uint8_t reserved; - uint16_t tag; -} QEMU_PACKED uas_iu_header; - -typedef struct { - uint8_t prio_taskattr; /* 6:3 priority, 2:0 task attribute */ - uint8_t reserved_1; - uint8_t add_cdb_length; /* 7:2 additional adb length (dwords) */ - uint8_t reserved_2; - uint64_t lun; - uint8_t cdb[16]; - uint8_t add_cdb[]; -} QEMU_PACKED uas_iu_command; - -typedef struct { - uint16_t status_qualifier; - uint8_t status; - uint8_t reserved[7]; - uint16_t sense_length; - uint8_t sense_data[18]; -} QEMU_PACKED uas_iu_sense; - -typedef struct { - uint8_t add_response_info[3]; - uint8_t response_code; -} QEMU_PACKED uas_iu_response; - -typedef struct { - uint8_t function; - uint8_t reserved; - uint16_t task_tag; - uint64_t lun; -} QEMU_PACKED uas_iu_task_mgmt; - -typedef struct { - uas_iu_header hdr; - union { - uas_iu_command command; - uas_iu_sense sense; - uas_iu_task_mgmt task; - uas_iu_response response; - }; -} QEMU_PACKED uas_iu; - -/* --------------------------------------------------------------------- */ - -#define UAS_STREAM_BM_ATTR 4 -#define UAS_MAX_STREAMS (1 << UAS_STREAM_BM_ATTR) - -typedef struct UASDevice UASDevice; -typedef struct UASRequest UASRequest; -typedef struct UASStatus UASStatus; - -struct UASDevice { - USBDevice dev; - SCSIBus bus; - QEMUBH *status_bh; - QTAILQ_HEAD(, UASStatus) results; - QTAILQ_HEAD(, UASRequest) requests; - - /* properties */ - uint32_t requestlog; - - /* usb 2.0 only */ - USBPacket *status2; - UASRequest *datain2; - UASRequest *dataout2; - - /* usb 3.0 only */ - USBPacket *data3[UAS_MAX_STREAMS + 1]; - USBPacket *status3[UAS_MAX_STREAMS + 1]; -}; - -#define TYPE_USB_UAS "usb-uas" -#define USB_UAS(obj) OBJECT_CHECK(UASDevice, (obj), TYPE_USB_UAS) - -struct UASRequest { - uint16_t tag; - uint64_t lun; - UASDevice *uas; - SCSIDevice *dev; - SCSIRequest *req; - USBPacket *data; - bool data_async; - bool active; - bool complete; - uint32_t buf_off; - uint32_t buf_size; - uint32_t data_off; - uint32_t data_size; - QTAILQ_ENTRY(UASRequest) next; -}; - -struct UASStatus { - uint32_t stream; - uas_iu status; - uint32_t length; - QTAILQ_ENTRY(UASStatus) next; -}; - -/* --------------------------------------------------------------------- */ - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, - STR_CONFIG_HIGH, - STR_CONFIG_SUPER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "USB Attached SCSI HBA", - [STR_SERIALNUMBER] = "27842", - [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", - [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", -}; - -static const USBDescIface desc_iface_high = { - .bInterfaceNumber = 0, - .bNumEndpoints = 4, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x62, /* UAS */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_COMMAND, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_STATUS, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_DATA_IN, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_DATA_OUT, - 0x00, /* u8 bReserved */ - }, - }, - } -}; - -static const USBDescIface desc_iface_super = { - .bInterfaceNumber = 0, - .bNumEndpoints = 4, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x62, /* UAS */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_COMMAND, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - .bmAttributes_super = UAS_STREAM_BM_ATTR, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_STATUS, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - .bmAttributes_super = UAS_STREAM_BM_ATTR, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_DATA_IN, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - .bmAttributes_super = UAS_STREAM_BM_ATTR, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_DATA_OUT, - 0x00, /* u8 bReserved */ - }, - }, - } -}; - -static const USBDescDevice desc_device_high = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_HIGH, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_high, - }, - }, -}; - -static const USBDescDevice desc_device_super = { - .bcdUSB = 0x0300, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_SUPER, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_super, - }, - }, -}; - -static const USBDesc desc = { - .id = { - .idVendor = 0x46f4, /* CRC16() of "QEMU" */ - .idProduct = 0x0003, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .high = &desc_device_high, - .super = &desc_device_super, - .str = desc_strings, -}; - -/* --------------------------------------------------------------------- */ - -static bool uas_using_streams(UASDevice *uas) -{ - return uas->dev.speed == USB_SPEED_SUPER; -} - -/* --------------------------------------------------------------------- */ - -static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag) -{ - UASStatus *st = g_new0(UASStatus, 1); - - st->status.hdr.id = id; - st->status.hdr.tag = cpu_to_be16(tag); - st->length = sizeof(uas_iu_header); - if (uas_using_streams(uas)) { - st->stream = tag; - } - return st; -} - -static void usb_uas_send_status_bh(void *opaque) -{ - UASDevice *uas = opaque; - UASStatus *st; - USBPacket *p; - - while ((st = QTAILQ_FIRST(&uas->results)) != NULL) { - if (uas_using_streams(uas)) { - p = uas->status3[st->stream]; - uas->status3[st->stream] = NULL; - } else { - p = uas->status2; - uas->status2 = NULL; - } - if (p == NULL) { - break; - } - - usb_packet_copy(p, &st->status, st->length); - QTAILQ_REMOVE(&uas->results, st, next); - g_free(st); - - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_packet_complete(&uas->dev, p); - } -} - -static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) -{ - USBPacket *p = uas_using_streams(uas) ? - uas->status3[st->stream] : uas->status2; - - st->length += length; - QTAILQ_INSERT_TAIL(&uas->results, st, next); - if (p) { - /* - * Just schedule bh make sure any in-flight data transaction - * is finished before completing (sending) the status packet. - */ - qemu_bh_schedule(uas->status_bh); - } else { - USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN, - UAS_PIPE_ID_STATUS); - usb_wakeup(ep, st->stream); - } -} - -static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, uint8_t code) -{ - UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag); - - trace_usb_uas_response(uas->dev.addr, tag, code); - st->status.response.response_code = code; - usb_uas_queue_status(uas, st, sizeof(uas_iu_response)); -} - -static void usb_uas_queue_sense(UASRequest *req, uint8_t status) -{ - UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_SENSE, req->tag); - int len, slen = 0; - - trace_usb_uas_sense(req->uas->dev.addr, req->tag, status); - st->status.sense.status = status; - st->status.sense.status_qualifier = cpu_to_be16(0); - if (status != GOOD) { - slen = scsi_req_get_sense(req->req, st->status.sense.sense_data, - sizeof(st->status.sense.sense_data)); - st->status.sense.sense_length = cpu_to_be16(slen); - } - len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen; - usb_uas_queue_status(req->uas, st, len); -} - -static void usb_uas_queue_fake_sense(UASDevice *uas, uint16_t tag, - struct SCSISense sense) -{ - UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_SENSE, tag); - int len, slen = 0; - - st->status.sense.status = CHECK_CONDITION; - st->status.sense.status_qualifier = cpu_to_be16(0); - st->status.sense.sense_data[0] = 0x70; - st->status.sense.sense_data[2] = sense.key; - st->status.sense.sense_data[7] = 10; - st->status.sense.sense_data[12] = sense.asc; - st->status.sense.sense_data[13] = sense.ascq; - slen = 18; - len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen; - usb_uas_queue_status(uas, st, len); -} - -static void usb_uas_queue_read_ready(UASRequest *req) -{ - UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY, - req->tag); - - trace_usb_uas_read_ready(req->uas->dev.addr, req->tag); - usb_uas_queue_status(req->uas, st, 0); -} - -static void usb_uas_queue_write_ready(UASRequest *req) -{ - UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_WRITE_READY, - req->tag); - - trace_usb_uas_write_ready(req->uas->dev.addr, req->tag); - usb_uas_queue_status(req->uas, st, 0); -} - -/* --------------------------------------------------------------------- */ - -static int usb_uas_get_lun(uint64_t lun64) -{ - return (lun64 >> 48) & 0xff; -} - -static SCSIDevice *usb_uas_get_dev(UASDevice *uas, uint64_t lun64) -{ - if ((lun64 >> 56) != 0x00) { - return NULL; - } - return scsi_device_find(&uas->bus, 0, 0, usb_uas_get_lun(lun64)); -} - -static void usb_uas_complete_data_packet(UASRequest *req) -{ - USBPacket *p; - - if (!req->data_async) { - return; - } - p = req->data; - req->data = NULL; - req->data_async = false; - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_packet_complete(&req->uas->dev, p); -} - -static void usb_uas_copy_data(UASRequest *req) -{ - uint32_t length; - - length = MIN(req->buf_size - req->buf_off, - req->data->iov.size - req->data->actual_length); - trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length, - req->data->actual_length, req->data->iov.size, - req->buf_off, req->buf_size); - usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off, - length); - req->buf_off += length; - req->data_off += length; - - if (req->data->actual_length == req->data->iov.size) { - usb_uas_complete_data_packet(req); - } - if (req->buf_size && req->buf_off == req->buf_size) { - req->buf_off = 0; - req->buf_size = 0; - scsi_req_continue(req->req); - } -} - -static void usb_uas_start_next_transfer(UASDevice *uas) -{ - UASRequest *req; - - if (uas_using_streams(uas)) { - return; - } - - QTAILQ_FOREACH(req, &uas->requests, next) { - if (req->active || req->complete) { - continue; - } - if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) { - uas->datain2 = req; - usb_uas_queue_read_ready(req); - req->active = true; - return; - } - if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) { - uas->dataout2 = req; - usb_uas_queue_write_ready(req); - req->active = true; - return; - } - } -} - -static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_iu *iu) -{ - UASRequest *req; - - req = g_new0(UASRequest, 1); - req->uas = uas; - req->tag = be16_to_cpu(iu->hdr.tag); - req->lun = be64_to_cpu(iu->command.lun); - req->dev = usb_uas_get_dev(req->uas, req->lun); - return req; -} - -static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv) -{ - UASRequest *req = priv; - UASDevice *uas = req->uas; - - if (req == uas->datain2) { - uas->datain2 = NULL; - } - if (req == uas->dataout2) { - uas->dataout2 = NULL; - } - QTAILQ_REMOVE(&uas->requests, req, next); - g_free(req); - usb_uas_start_next_transfer(uas); -} - -static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag) -{ - UASRequest *req; - - QTAILQ_FOREACH(req, &uas->requests, next) { - if (req->tag == tag) { - return req; - } - } - return NULL; -} - -static void usb_uas_scsi_transfer_data(SCSIRequest *r, uint32_t len) -{ - UASRequest *req = r->hba_private; - - trace_usb_uas_scsi_data(req->uas->dev.addr, req->tag, len); - req->buf_off = 0; - req->buf_size = len; - if (req->data) { - usb_uas_copy_data(req); - } else { - usb_uas_start_next_transfer(req->uas); - } -} - -static void usb_uas_scsi_command_complete(SCSIRequest *r, - uint32_t status, size_t resid) -{ - UASRequest *req = r->hba_private; - - trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid); - req->complete = true; - if (req->data) { - usb_uas_complete_data_packet(req); - } - usb_uas_queue_sense(req, status); - scsi_req_unref(req->req); -} - -static void usb_uas_scsi_request_cancelled(SCSIRequest *r) -{ - UASRequest *req = r->hba_private; - - /* FIXME: queue notification to status pipe? */ - scsi_req_unref(req->req); -} - -static const struct SCSIBusInfo usb_uas_scsi_info = { - .tcq = true, - .max_target = 0, - .max_lun = 255, - - .transfer_data = usb_uas_scsi_transfer_data, - .complete = usb_uas_scsi_command_complete, - .cancel = usb_uas_scsi_request_cancelled, - .free_request = usb_uas_scsi_free_request, -}; - -/* --------------------------------------------------------------------- */ - -static void usb_uas_handle_reset(USBDevice *dev) -{ - UASDevice *uas = USB_UAS(dev); - UASRequest *req, *nreq; - UASStatus *st, *nst; - - trace_usb_uas_reset(dev->addr); - QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) { - scsi_req_cancel(req->req); - } - QTAILQ_FOREACH_SAFE(st, &uas->results, next, nst) { - QTAILQ_REMOVE(&uas->results, st, next); - g_free(st); - } -} - -static void usb_uas_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - error_report("%s: unhandled control request", __func__); - p->status = USB_RET_STALL; -} - -static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) -{ - UASDevice *uas = USB_UAS(dev); - UASRequest *req, *nreq; - int i; - - if (uas->status2 == p) { - uas->status2 = NULL; - qemu_bh_cancel(uas->status_bh); - return; - } - if (uas_using_streams(uas)) { - for (i = 0; i <= UAS_MAX_STREAMS; i++) { - if (uas->status3[i] == p) { - uas->status3[i] = NULL; - return; - } - if (uas->data3[i] == p) { - uas->data3[i] = NULL; - return; - } - } - } - QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) { - if (req->data == p) { - req->data = NULL; - return; - } - } - assert(!"canceled usb packet not found"); -} - -static void usb_uas_command(UASDevice *uas, uas_iu *iu) -{ - UASRequest *req; - uint32_t len; - uint16_t tag = be16_to_cpu(iu->hdr.tag); - - if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { - goto invalid_tag; - } - req = usb_uas_find_request(uas, tag); - if (req) { - goto overlapped_tag; - } - req = usb_uas_alloc_request(uas, iu); - if (req->dev == NULL) { - goto bad_target; - } - - trace_usb_uas_command(uas->dev.addr, req->tag, - usb_uas_get_lun(req->lun), - req->lun >> 32, req->lun & 0xffffffff); - QTAILQ_INSERT_TAIL(&uas->requests, req, next); - if (uas_using_streams(uas) && uas->data3[req->tag] != NULL) { - req->data = uas->data3[req->tag]; - req->data_async = true; - uas->data3[req->tag] = NULL; - } - - req->req = scsi_req_new(req->dev, req->tag, - usb_uas_get_lun(req->lun), - iu->command.cdb, req); - if (uas->requestlog) { - scsi_req_print(req->req); - } - len = scsi_req_enqueue(req->req); - if (len) { - req->data_size = len; - scsi_req_continue(req->req); - } - return; - -invalid_tag: - usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG); - return; - -overlapped_tag: - usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS); - return; - -bad_target: - usb_uas_queue_fake_sense(uas, tag, sense_code_LUN_NOT_SUPPORTED); - g_free(req); -} - -static void usb_uas_task(UASDevice *uas, uas_iu *iu) -{ - uint16_t tag = be16_to_cpu(iu->hdr.tag); - uint64_t lun64 = be64_to_cpu(iu->task.lun); - SCSIDevice *dev = usb_uas_get_dev(uas, lun64); - int lun = usb_uas_get_lun(lun64); - UASRequest *req; - uint16_t task_tag; - - if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { - goto invalid_tag; - } - req = usb_uas_find_request(uas, be16_to_cpu(iu->hdr.tag)); - if (req) { - goto overlapped_tag; - } - if (dev == NULL) { - goto incorrect_lun; - } - - switch (iu->task.function) { - case UAS_TMF_ABORT_TASK: - task_tag = be16_to_cpu(iu->task.task_tag); - trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag); - req = usb_uas_find_request(uas, task_tag); - if (req && req->dev == dev) { - scsi_req_cancel(req->req); - } - usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); - break; - - case UAS_TMF_LOGICAL_UNIT_RESET: - trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun); - qdev_reset_all(&dev->qdev); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); - break; - - default: - trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, iu->task.function); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED); - break; - } - return; - -invalid_tag: - usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT); - return; - -overlapped_tag: - usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG); - return; - -incorrect_lun: - usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN); -} - -static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) -{ - UASDevice *uas = USB_UAS(dev); - uas_iu iu; - UASStatus *st; - UASRequest *req; - int length; - - switch (p->ep->nr) { - case UAS_PIPE_ID_COMMAND: - length = MIN(sizeof(iu), p->iov.size); - usb_packet_copy(p, &iu, length); - switch (iu.hdr.id) { - case UAS_UI_COMMAND: - usb_uas_command(uas, &iu); - break; - case UAS_UI_TASK_MGMT: - usb_uas_task(uas, &iu); - break; - default: - error_report("%s: unknown command iu: id 0x%x", - __func__, iu.hdr.id); - p->status = USB_RET_STALL; - break; - } - break; - case UAS_PIPE_ID_STATUS: - if (p->stream) { - QTAILQ_FOREACH(st, &uas->results, next) { - if (st->stream == p->stream) { - break; - } - } - if (st == NULL) { - assert(uas->status3[p->stream] == NULL); - uas->status3[p->stream] = p; - p->status = USB_RET_ASYNC; - break; - } - } else { - st = QTAILQ_FIRST(&uas->results); - if (st == NULL) { - assert(uas->status2 == NULL); - uas->status2 = p; - p->status = USB_RET_ASYNC; - break; - } - } - usb_packet_copy(p, &st->status, st->length); - QTAILQ_REMOVE(&uas->results, st, next); - g_free(st); - break; - case UAS_PIPE_ID_DATA_IN: - case UAS_PIPE_ID_DATA_OUT: - if (p->stream) { - req = usb_uas_find_request(uas, p->stream); - } else { - req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) - ? uas->datain2 : uas->dataout2; - } - if (req == NULL) { - if (p->stream) { - assert(uas->data3[p->stream] == NULL); - uas->data3[p->stream] = p; - p->status = USB_RET_ASYNC; - break; - } else { - error_report("%s: no inflight request", __func__); - p->status = USB_RET_STALL; - break; - } - } - scsi_req_ref(req->req); - req->data = p; - usb_uas_copy_data(req); - if (p->actual_length == p->iov.size || req->complete) { - req->data = NULL; - } else { - req->data_async = true; - p->status = USB_RET_ASYNC; - } - scsi_req_unref(req->req); - usb_uas_start_next_transfer(uas); - break; - default: - error_report("%s: invalid endpoint %d", __func__, p->ep->nr); - p->status = USB_RET_STALL; - break; - } -} - -static void usb_uas_handle_destroy(USBDevice *dev) -{ - UASDevice *uas = USB_UAS(dev); - - qemu_bh_delete(uas->status_bh); -} - -static void usb_uas_realize(USBDevice *dev, Error **errp) -{ - UASDevice *uas = USB_UAS(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - - QTAILQ_INIT(&uas->results); - QTAILQ_INIT(&uas->requests); - uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas); - - scsi_bus_new(&uas->bus, sizeof(uas->bus), DEVICE(dev), - &usb_uas_scsi_info, NULL); -} - -static const VMStateDescription vmstate_usb_uas = { - .name = "usb-uas", - .unmigratable = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, UASDevice), - VMSTATE_END_OF_LIST() - } -}; - -static Property uas_properties[] = { - DEFINE_PROP_UINT32("log-scsi-req", UASDevice, requestlog, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_uas_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_uas_realize; - uc->product_desc = desc_strings[STR_PRODUCT]; - uc->usb_desc = &desc; - uc->cancel_packet = usb_uas_cancel_io; - uc->handle_attach = usb_desc_attach; - uc->handle_reset = usb_uas_handle_reset; - uc->handle_control = usb_uas_handle_control; - uc->handle_data = usb_uas_handle_data; - uc->handle_destroy = usb_uas_handle_destroy; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->fw_name = "storage"; - dc->vmsd = &vmstate_usb_uas; - dc->props = uas_properties; -} - -static const TypeInfo uas_info = { - .name = TYPE_USB_UAS, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(UASDevice), - .class_init = usb_uas_class_initfn, -}; - -static void usb_uas_register_types(void) -{ - type_register_static(&uas_info); -} - -type_init(usb_uas_register_types) diff --git a/qemu/hw/usb/dev-wacom.c b/qemu/hw/usb/dev-wacom.c deleted file mode 100644 index c4702dbba..000000000 --- a/qemu/hw/usb/dev-wacom.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Wacom PenPartner USB tablet emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Author: Andrzej Zaborowski <balrog@zabor.org> - * - * Based on hw/usb-hid.c: - * Copyright (c) 2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "ui/console.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" - -/* Interface requests */ -#define WACOM_GET_REPORT 0x2101 -#define WACOM_SET_REPORT 0x2109 - -/* HID interface requests */ -#define HID_GET_REPORT 0xa101 -#define HID_GET_IDLE 0xa102 -#define HID_GET_PROTOCOL 0xa103 -#define HID_SET_IDLE 0x210a -#define HID_SET_PROTOCOL 0x210b - -typedef struct USBWacomState { - USBDevice dev; - USBEndpoint *intr; - QEMUPutMouseEntry *eh_entry; - int dx, dy, dz, buttons_state; - int x, y; - int mouse_grabbed; - enum { - WACOM_MODE_HID = 1, - WACOM_MODE_WACOM = 2, - } mode; - uint8_t idle; - int changed; -} USBWacomState; - -#define TYPE_USB_WACOM "usb-wacom-tablet" -#define USB_WACOM(obj) OBJECT_CHECK(USBWacomState, (obj), TYPE_USB_WACOM) - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "Wacom PenPartner", - [STR_SERIALNUMBER] = "1", -}; - -static const USBDescIface desc_iface_wacom = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - 0x21, /* u8 bDescriptorType */ - 0x01, 0x10, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type: Report */ - 0x6e, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 0x0a, - }, - }, -}; - -static const USBDescDevice desc_device_wacom = { - .bcdUSB = 0x0110, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE, - .bMaxPower = 40, - .nif = 1, - .ifs = &desc_iface_wacom, - }, - }, -}; - -static const USBDesc desc_wacom = { - .id = { - .idVendor = 0x056a, - .idProduct = 0x0000, - .bcdDevice = 0x4210, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_wacom, - .str = desc_strings, -}; - -static void usb_mouse_event(void *opaque, - int dx1, int dy1, int dz1, int buttons_state) -{ - USBWacomState *s = opaque; - - s->dx += dx1; - s->dy += dy1; - s->dz += dz1; - s->buttons_state = buttons_state; - s->changed = 1; - usb_wakeup(s->intr, 0); -} - -static void usb_wacom_event(void *opaque, - int x, int y, int dz, int buttons_state) -{ - USBWacomState *s = opaque; - - /* scale to Penpartner resolution */ - s->x = (x * 5040 / 0x7FFF); - s->y = (y * 3780 / 0x7FFF); - s->dz += dz; - s->buttons_state = buttons_state; - s->changed = 1; - usb_wakeup(s->intr, 0); -} - -static inline int int_clamp(int val, int vmin, int vmax) -{ - if (val < vmin) - return vmin; - else if (val > vmax) - return vmax; - else - return val; -} - -static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len) -{ - int dx, dy, dz, b, l; - - if (!s->mouse_grabbed) { - s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0, - "QEMU PenPartner tablet"); - qemu_activate_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 1; - } - - dx = int_clamp(s->dx, -128, 127); - dy = int_clamp(s->dy, -128, 127); - dz = int_clamp(s->dz, -128, 127); - - s->dx -= dx; - s->dy -= dy; - s->dz -= dz; - - b = 0; - if (s->buttons_state & MOUSE_EVENT_LBUTTON) - b |= 0x01; - if (s->buttons_state & MOUSE_EVENT_RBUTTON) - b |= 0x02; - if (s->buttons_state & MOUSE_EVENT_MBUTTON) - b |= 0x04; - - buf[0] = b; - buf[1] = dx; - buf[2] = dy; - l = 3; - if (len >= 4) { - buf[3] = dz; - l = 4; - } - return l; -} - -static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len) -{ - int b; - - if (!s->mouse_grabbed) { - s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1, - "QEMU PenPartner tablet"); - qemu_activate_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 1; - } - - b = 0; - if (s->buttons_state & MOUSE_EVENT_LBUTTON) - b |= 0x01; - if (s->buttons_state & MOUSE_EVENT_RBUTTON) - b |= 0x40; - if (s->buttons_state & MOUSE_EVENT_MBUTTON) - b |= 0x20; /* eraser */ - - if (len < 7) - return 0; - - buf[0] = s->mode; - buf[5] = 0x00 | (b & 0xf0); - buf[1] = s->x & 0xff; - buf[2] = s->x >> 8; - buf[3] = s->y & 0xff; - buf[4] = s->y >> 8; - if (b & 0x3f) { - buf[6] = 0; - } else { - buf[6] = (unsigned char) -127; - } - - return 7; -} - -static void usb_wacom_handle_reset(USBDevice *dev) -{ - USBWacomState *s = (USBWacomState *) dev; - - s->dx = 0; - s->dy = 0; - s->dz = 0; - s->x = 0; - s->y = 0; - s->buttons_state = 0; - s->mode = WACOM_MODE_HID; -} - -static void 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, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - case WACOM_SET_REPORT: - if (s->mouse_grabbed) { - qemu_remove_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 0; - } - s->mode = data[0]; - break; - case WACOM_GET_REPORT: - data[0] = 0; - data[1] = s->mode; - p->actual_length = 2; - break; - /* USB HID requests */ - case HID_GET_REPORT: - if (s->mode == WACOM_MODE_HID) - p->actual_length = usb_mouse_poll(s, data, length); - else if (s->mode == WACOM_MODE_WACOM) - p->actual_length = usb_wacom_poll(s, data, length); - break; - case HID_GET_IDLE: - data[0] = s->idle; - p->actual_length = 1; - break; - case HID_SET_IDLE: - s->idle = (uint8_t) (value >> 8); - break; - default: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_wacom_handle_data(USBDevice *dev, USBPacket *p) -{ - USBWacomState *s = (USBWacomState *) dev; - uint8_t buf[p->iov.size]; - int len = 0; - - switch (p->pid) { - case USB_TOKEN_IN: - if (p->ep->nr == 1) { - if (!(s->changed || s->idle)) { - p->status = USB_RET_NAK; - return; - } - s->changed = 0; - if (s->mode == WACOM_MODE_HID) - len = usb_mouse_poll(s, buf, p->iov.size); - else if (s->mode == WACOM_MODE_WACOM) - len = usb_wacom_poll(s, buf, p->iov.size); - usb_packet_copy(p, buf, len); - break; - } - /* Fall through. */ - case USB_TOKEN_OUT: - default: - p->status = USB_RET_STALL; - } -} - -static void usb_wacom_handle_destroy(USBDevice *dev) -{ - USBWacomState *s = (USBWacomState *) dev; - - if (s->mouse_grabbed) { - qemu_remove_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 0; - } -} - -static void usb_wacom_realize(USBDevice *dev, Error **errp) -{ - USBWacomState *s = USB_WACOM(dev); - usb_desc_create_serial(dev); - usb_desc_init(dev); - s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); - s->changed = 1; -} - -static const VMStateDescription vmstate_usb_wacom = { - .name = "usb-wacom", - .unmigratable = 1, -}; - -static void usb_wacom_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->product_desc = "QEMU PenPartner Tablet"; - uc->usb_desc = &desc_wacom; - uc->realize = usb_wacom_realize; - uc->handle_reset = usb_wacom_handle_reset; - uc->handle_control = usb_wacom_handle_control; - uc->handle_data = usb_wacom_handle_data; - uc->handle_destroy = usb_wacom_handle_destroy; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->desc = "QEMU PenPartner Tablet"; - dc->vmsd = &vmstate_usb_wacom; -} - -static const TypeInfo wacom_info = { - .name = TYPE_USB_WACOM, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBWacomState), - .class_init = usb_wacom_class_init, -}; - -static void usb_wacom_register_types(void) -{ - type_register_static(&wacom_info); - usb_legacy_register(TYPE_USB_WACOM, "wacom-tablet", NULL); -} - -type_init(usb_wacom_register_types) diff --git a/qemu/hw/usb/hcd-ehci-pci.c b/qemu/hw/usb/hcd-ehci-pci.c deleted file mode 100644 index 56577051e..000000000 --- a/qemu/hw/usb/hcd-ehci-pci.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * QEMU USB EHCI Emulation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "hw/usb/hcd-ehci.h" -#include "qemu/range.h" - -typedef struct EHCIPCIInfo { - const char *name; - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision; - bool companion; -} EHCIPCIInfo; - -static void usb_ehci_pci_realize(PCIDevice *dev, Error **errp) -{ - EHCIPCIState *i = PCI_EHCI(dev); - EHCIState *s = &i->ehci; - uint8_t *pci_conf = dev->config; - - pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); - - /* capabilities pointer */ - pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); - /* pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); */ - - pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */ - pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); - pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); - - /* pci_conf[0x50] = 0x01; *//* power management caps */ - - pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); /* release # (2.1.4) */ - pci_set_byte(&pci_conf[0x61], 0x20); /* frame length adjustment (2.1.5) */ - pci_set_word(&pci_conf[0x62], 0x00); /* port wake up capability (2.1.6) */ - - pci_conf[0x64] = 0x00; - pci_conf[0x65] = 0x00; - pci_conf[0x66] = 0x00; - pci_conf[0x67] = 0x00; - pci_conf[0x68] = 0x01; - pci_conf[0x69] = 0x00; - pci_conf[0x6a] = 0x00; - pci_conf[0x6b] = 0x00; /* USBLEGSUP */ - pci_conf[0x6c] = 0x00; - pci_conf[0x6d] = 0x00; - pci_conf[0x6e] = 0x00; - pci_conf[0x6f] = 0xc0; /* USBLEFCTLSTS */ - - s->irq = pci_allocate_irq(dev); - s->as = pci_get_address_space(dev); - - usb_ehci_realize(s, DEVICE(dev), NULL); - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); -} - -static void usb_ehci_pci_init(Object *obj) -{ - DeviceClass *dc = OBJECT_GET_CLASS(DeviceClass, obj, TYPE_DEVICE); - EHCIPCIState *i = PCI_EHCI(obj); - EHCIState *s = &i->ehci; - - s->caps[0x09] = 0x68; /* EECP */ - - s->capsbase = 0x00; - s->opregbase = 0x20; - s->portscbase = 0x44; - s->portnr = NB_PORTS; - - if (!dc->hotpluggable) { - s->companion_enable = true; - } - - usb_ehci_init(s, DEVICE(obj)); -} - -static void usb_ehci_pci_exit(PCIDevice *dev) -{ - EHCIPCIState *i = PCI_EHCI(dev); - EHCIState *s = &i->ehci; - - usb_ehci_unrealize(s, DEVICE(dev), NULL); - - g_free(s->irq); - s->irq = NULL; -} - -static void usb_ehci_pci_reset(DeviceState *dev) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - EHCIPCIState *i = PCI_EHCI(pci_dev); - EHCIState *s = &i->ehci; - - ehci_reset(s); -} - -static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr, - uint32_t val, int l) -{ - EHCIPCIState *i = PCI_EHCI(dev); - bool busmaster; - - pci_default_write_config(dev, addr, val, l); - - if (!range_covers_byte(addr, l, PCI_COMMAND)) { - return; - } - busmaster = pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_MASTER; - i->ehci.as = busmaster ? pci_get_address_space(dev) : &address_space_memory; -} - -static Property ehci_pci_properties[] = { - DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_ehci_pci = { - .name = "ehci", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState), - VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState), - VMSTATE_END_OF_LIST() - } -}; - -static void ehci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = usb_ehci_pci_realize; - k->exit = usb_ehci_pci_exit; - k->class_id = PCI_CLASS_SERIAL_USB; - k->config_write = usb_ehci_pci_write_config; - dc->vmsd = &vmstate_ehci_pci; - dc->props = ehci_pci_properties; - dc->reset = usb_ehci_pci_reset; -} - -static const TypeInfo ehci_pci_type_info = { - .name = TYPE_PCI_EHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EHCIPCIState), - .instance_init = usb_ehci_pci_init, - .abstract = true, - .class_init = ehci_class_init, -}; - -static void ehci_data_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - EHCIPCIInfo *i = data; - - k->vendor_id = i->vendor_id; - k->device_id = i->device_id; - k->revision = i->revision; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - if (i->companion) { - dc->hotpluggable = false; - } -} - -static struct EHCIPCIInfo ehci_pci_info[] = { - { - .name = "usb-ehci", - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ - .revision = 0x10, - },{ - .name = "ich9-usb-ehci1", /* 00:1d.7 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, - .revision = 0x03, - .companion = true, - },{ - .name = "ich9-usb-ehci2", /* 00:1a.7 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI2, - .revision = 0x03, - .companion = true, - } -}; - -static void ehci_pci_register_types(void) -{ - TypeInfo ehci_type_info = { - .parent = TYPE_PCI_EHCI, - .class_init = ehci_data_class_init, - }; - int i; - - type_register_static(&ehci_pci_type_info); - - for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) { - ehci_type_info.name = ehci_pci_info[i].name; - ehci_type_info.class_data = ehci_pci_info + i; - type_register(&ehci_type_info); - } -} - -type_init(ehci_pci_register_types) - -struct ehci_companions { - const char *name; - int func; - int port; -}; - -static const struct ehci_companions ich9_1d[] = { - { .name = "ich9-usb-uhci1", .func = 0, .port = 0 }, - { .name = "ich9-usb-uhci2", .func = 1, .port = 2 }, - { .name = "ich9-usb-uhci3", .func = 2, .port = 4 }, -}; - -static const struct ehci_companions ich9_1a[] = { - { .name = "ich9-usb-uhci4", .func = 0, .port = 0 }, - { .name = "ich9-usb-uhci5", .func = 1, .port = 2 }, - { .name = "ich9-usb-uhci6", .func = 2, .port = 4 }, -}; - -int ehci_create_ich9_with_companions(PCIBus *bus, int slot) -{ - const struct ehci_companions *comp; - PCIDevice *ehci, *uhci; - BusState *usbbus; - const char *name; - int i; - - switch (slot) { - case 0x1d: - name = "ich9-usb-ehci1"; - comp = ich9_1d; - break; - case 0x1a: - name = "ich9-usb-ehci2"; - comp = ich9_1a; - break; - default: - return -1; - } - - ehci = pci_create_multifunction(bus, PCI_DEVFN(slot, 7), true, name); - qdev_init_nofail(&ehci->qdev); - usbbus = QLIST_FIRST(&ehci->qdev.child_bus); - - for (i = 0; i < 3; i++) { - uhci = pci_create_multifunction(bus, PCI_DEVFN(slot, comp[i].func), - true, comp[i].name); - qdev_prop_set_string(&uhci->qdev, "masterbus", usbbus->name); - qdev_prop_set_uint32(&uhci->qdev, "firstport", comp[i].port); - qdev_init_nofail(&uhci->qdev); - } - return 0; -} diff --git a/qemu/hw/usb/hcd-ehci-sysbus.c b/qemu/hw/usb/hcd-ehci-sysbus.c deleted file mode 100644 index 6c20604d0..000000000 --- a/qemu/hw/usb/hcd-ehci-sysbus.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * QEMU USB EHCI Emulation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "hw/usb/hcd-ehci.h" - -static const VMStateDescription vmstate_ehci_sysbus = { - .name = "ehci-sysbus", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(ehci, EHCISysBusState, 2, vmstate_ehci, EHCIState), - VMSTATE_END_OF_LIST() - } -}; - -static Property ehci_sysbus_properties[] = { - DEFINE_PROP_UINT32("maxframes", EHCISysBusState, ehci.maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_ehci_sysbus_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *d = SYS_BUS_DEVICE(dev); - EHCISysBusState *i = SYS_BUS_EHCI(dev); - EHCIState *s = &i->ehci; - - usb_ehci_realize(s, dev, errp); - sysbus_init_irq(d, &s->irq); -} - -static void usb_ehci_sysbus_reset(DeviceState *dev) -{ - SysBusDevice *d = SYS_BUS_DEVICE(dev); - EHCISysBusState *i = SYS_BUS_EHCI(d); - EHCIState *s = &i->ehci; - - ehci_reset(s); -} - -static void ehci_sysbus_init(Object *obj) -{ - SysBusDevice *d = SYS_BUS_DEVICE(obj); - EHCISysBusState *i = SYS_BUS_EHCI(obj); - SysBusEHCIClass *sec = SYS_BUS_EHCI_GET_CLASS(obj); - EHCIState *s = &i->ehci; - - s->capsbase = sec->capsbase; - s->opregbase = sec->opregbase; - s->portscbase = sec->portscbase; - s->portnr = sec->portnr; - s->as = &address_space_memory; - - usb_ehci_init(s, DEVICE(obj)); - sysbus_init_mmio(d, &s->mem); -} - -static void ehci_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass); - - sec->portscbase = 0x44; - sec->portnr = NB_PORTS; - - dc->realize = usb_ehci_sysbus_realize; - dc->vmsd = &vmstate_ehci_sysbus; - dc->props = ehci_sysbus_properties; - dc->reset = usb_ehci_sysbus_reset; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo ehci_type_info = { - .name = TYPE_SYS_BUS_EHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(EHCISysBusState), - .instance_init = ehci_sysbus_init, - .abstract = true, - .class_init = ehci_sysbus_class_init, - .class_size = sizeof(SysBusEHCIClass), -}; - -static void ehci_xlnx_class_init(ObjectClass *oc, void *data) -{ - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - set_bit(DEVICE_CATEGORY_USB, dc->categories); - sec->capsbase = 0x100; - sec->opregbase = 0x140; -} - -static const TypeInfo ehci_xlnx_type_info = { - .name = "xlnx,ps7-usb", - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_xlnx_class_init, -}; - -static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) -{ - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - sec->capsbase = 0x0; - sec->opregbase = 0x10; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo ehci_exynos4210_type_info = { - .name = TYPE_EXYNOS4210_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_exynos4210_class_init, -}; - -static void ehci_tegra2_class_init(ObjectClass *oc, void *data) -{ - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - sec->capsbase = 0x100; - sec->opregbase = 0x140; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo ehci_tegra2_type_info = { - .name = TYPE_TEGRA2_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_tegra2_class_init, -}; - -/* - * Faraday FUSBH200 USB 2.0 EHCI - */ - -/** - * FUSBH200EHCIRegs: - * @FUSBH200_REG_EOF_ASTR: EOF/Async. Sleep Timer Register - * @FUSBH200_REG_BMCSR: Bus Monitor Control/Status Register - */ -enum FUSBH200EHCIRegs { - FUSBH200_REG_EOF_ASTR = 0x34, - FUSBH200_REG_BMCSR = 0x40, -}; - -static uint64_t fusbh200_ehci_read(void *opaque, hwaddr addr, unsigned size) -{ - EHCIState *s = opaque; - hwaddr off = s->opregbase + s->portscbase + 4 * s->portnr + addr; - - switch (off) { - case FUSBH200_REG_EOF_ASTR: - return 0x00000041; - case FUSBH200_REG_BMCSR: - /* High-Speed, VBUS valid, interrupt level-high active */ - return (2 << 9) | (1 << 8) | (1 << 3); - } - - return 0; -} - -static void fusbh200_ehci_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ -} - -static const MemoryRegionOps fusbh200_ehci_mmio_ops = { - .read = fusbh200_ehci_read, - .write = fusbh200_ehci_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void fusbh200_ehci_init(Object *obj) -{ - EHCISysBusState *i = SYS_BUS_EHCI(obj); - FUSBH200EHCIState *f = FUSBH200_EHCI(obj); - EHCIState *s = &i->ehci; - - memory_region_init_io(&f->mem_vendor, OBJECT(f), &fusbh200_ehci_mmio_ops, s, - "fusbh200", 0x4c); - memory_region_add_subregion(&s->mem, - s->opregbase + s->portscbase + 4 * s->portnr, - &f->mem_vendor); -} - -static void fusbh200_ehci_class_init(ObjectClass *oc, void *data) -{ - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - sec->capsbase = 0x0; - sec->opregbase = 0x10; - sec->portscbase = 0x20; - sec->portnr = 1; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo ehci_fusbh200_type_info = { - .name = TYPE_FUSBH200_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .instance_size = sizeof(FUSBH200EHCIState), - .instance_init = fusbh200_ehci_init, - .class_init = fusbh200_ehci_class_init, -}; - -static void ehci_sysbus_register_types(void) -{ - type_register_static(&ehci_type_info); - type_register_static(&ehci_xlnx_type_info); - type_register_static(&ehci_exynos4210_type_info); - type_register_static(&ehci_tegra2_type_info); - type_register_static(&ehci_fusbh200_type_info); -} - -type_init(ehci_sysbus_register_types) diff --git a/qemu/hw/usb/hcd-ehci.c b/qemu/hw/usb/hcd-ehci.c deleted file mode 100644 index 43a8f7abc..000000000 --- a/qemu/hw/usb/hcd-ehci.c +++ /dev/null @@ -1,2549 +0,0 @@ -/* - * QEMU USB EHCI Emulation - * - * Copyright(c) 2008 Emutex Ltd. (address@hidden) - * Copyright(c) 2011-2012 Red Hat, Inc. - * - * Red Hat Authors: - * Gerd Hoffmann <kraxel@redhat.com> - * Hans de Goede <hdegoede@redhat.com> - * - * EHCI project was started by Mark Burkley, with contributions by - * Niels de Vos. David S. Ahern continued working on it. Kevin Wolf, - * Jan Kiszka and Vincent Palatin contributed bugfixes. - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/usb/ehci-regs.h" -#include "hw/usb/hcd-ehci.h" -#include "trace.h" - -#define FRAME_TIMER_FREQ 1000 -#define FRAME_TIMER_NS (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ) -#define UFRAME_TIMER_NS (FRAME_TIMER_NS / 8) - -#define NB_MAXINTRATE 8 // Max rate at which controller issues ints -#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction -#define MAX_QH 100 // Max allowable queue heads in a chain -#define MIN_UFR_PER_TICK 24 /* Min frames to process when catching up */ -#define PERIODIC_ACTIVE 512 /* Micro-frames */ - -/* Internal periodic / asynchronous schedule state machine states - */ -typedef enum { - EST_INACTIVE = 1000, - EST_ACTIVE, - EST_EXECUTING, - EST_SLEEPING, - /* The following states are internal to the state machine function - */ - EST_WAITLISTHEAD, - EST_FETCHENTRY, - EST_FETCHQH, - EST_FETCHITD, - EST_FETCHSITD, - EST_ADVANCEQUEUE, - EST_FETCHQTD, - EST_EXECUTE, - EST_WRITEBACK, - EST_HORIZONTALQH -} EHCI_STATES; - -/* macros for accessing fields within next link pointer entry */ -#define NLPTR_GET(x) ((x) & 0xffffffe0) -#define NLPTR_TYPE_GET(x) (((x) >> 1) & 3) -#define NLPTR_TBIT(x) ((x) & 1) // 1=invalid, 0=valid - -/* link pointer types */ -#define NLPTR_TYPE_ITD 0 // isoc xfer descriptor -#define NLPTR_TYPE_QH 1 // queue head -#define NLPTR_TYPE_STITD 2 // split xaction, isoc xfer descriptor -#define NLPTR_TYPE_FSTN 3 // frame span traversal node - -#define SET_LAST_RUN_CLOCK(s) \ - (s)->last_run_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - -/* nifty macros from Arnon's EHCI version */ -#define get_field(data, field) \ - (((data) & field##_MASK) >> field##_SH) - -#define set_field(data, newval, field) do { \ - uint32_t val = *data; \ - val &= ~ field##_MASK; \ - val |= ((newval) << field##_SH) & field##_MASK; \ - *data = val; \ - } while(0) - -static const char *ehci_state_names[] = { - [EST_INACTIVE] = "INACTIVE", - [EST_ACTIVE] = "ACTIVE", - [EST_EXECUTING] = "EXECUTING", - [EST_SLEEPING] = "SLEEPING", - [EST_WAITLISTHEAD] = "WAITLISTHEAD", - [EST_FETCHENTRY] = "FETCH ENTRY", - [EST_FETCHQH] = "FETCH QH", - [EST_FETCHITD] = "FETCH ITD", - [EST_ADVANCEQUEUE] = "ADVANCEQUEUE", - [EST_FETCHQTD] = "FETCH QTD", - [EST_EXECUTE] = "EXECUTE", - [EST_WRITEBACK] = "WRITEBACK", - [EST_HORIZONTALQH] = "HORIZONTALQH", -}; - -static const char *ehci_mmio_names[] = { - [USBCMD] = "USBCMD", - [USBSTS] = "USBSTS", - [USBINTR] = "USBINTR", - [FRINDEX] = "FRINDEX", - [PERIODICLISTBASE] = "P-LIST BASE", - [ASYNCLISTADDR] = "A-LIST ADDR", - [CONFIGFLAG] = "CONFIGFLAG", -}; - -static int ehci_state_executing(EHCIQueue *q); -static int ehci_state_writeback(EHCIQueue *q); -static int ehci_state_advqueue(EHCIQueue *q); -static int ehci_fill_queue(EHCIPacket *p); -static void ehci_free_packet(EHCIPacket *p); - -static const char *nr2str(const char **n, size_t len, uint32_t nr) -{ - if (nr < len && n[nr] != NULL) { - return n[nr]; - } else { - return "unknown"; - } -} - -static const char *state2str(uint32_t state) -{ - return nr2str(ehci_state_names, ARRAY_SIZE(ehci_state_names), state); -} - -static const char *addr2str(hwaddr addr) -{ - return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); -} - -static void ehci_trace_usbsts(uint32_t mask, int state) -{ - /* interrupts */ - if (mask & USBSTS_INT) { - trace_usb_ehci_usbsts("INT", state); - } - if (mask & USBSTS_ERRINT) { - trace_usb_ehci_usbsts("ERRINT", state); - } - if (mask & USBSTS_PCD) { - trace_usb_ehci_usbsts("PCD", state); - } - if (mask & USBSTS_FLR) { - trace_usb_ehci_usbsts("FLR", state); - } - if (mask & USBSTS_HSE) { - trace_usb_ehci_usbsts("HSE", state); - } - if (mask & USBSTS_IAA) { - trace_usb_ehci_usbsts("IAA", state); - } - - /* status */ - if (mask & USBSTS_HALT) { - trace_usb_ehci_usbsts("HALT", state); - } - if (mask & USBSTS_REC) { - trace_usb_ehci_usbsts("REC", state); - } - if (mask & USBSTS_PSS) { - trace_usb_ehci_usbsts("PSS", state); - } - if (mask & USBSTS_ASS) { - trace_usb_ehci_usbsts("ASS", state); - } -} - -static inline void ehci_set_usbsts(EHCIState *s, int mask) -{ - if ((s->usbsts & mask) == mask) { - return; - } - ehci_trace_usbsts(mask, 1); - s->usbsts |= mask; -} - -static inline void ehci_clear_usbsts(EHCIState *s, int mask) -{ - if ((s->usbsts & mask) == 0) { - return; - } - ehci_trace_usbsts(mask, 0); - s->usbsts &= ~mask; -} - -/* update irq line */ -static inline void ehci_update_irq(EHCIState *s) -{ - int level = 0; - - if ((s->usbsts & USBINTR_MASK) & s->usbintr) { - level = 1; - } - - trace_usb_ehci_irq(level, s->frindex, s->usbsts, s->usbintr); - qemu_set_irq(s->irq, level); -} - -/* flag interrupt condition */ -static inline void ehci_raise_irq(EHCIState *s, int intr) -{ - if (intr & (USBSTS_PCD | USBSTS_FLR | USBSTS_HSE)) { - s->usbsts |= intr; - ehci_update_irq(s); - } else { - s->usbsts_pending |= intr; - } -} - -/* - * Commit pending interrupts (added via ehci_raise_irq), - * at the rate allowed by "Interrupt Threshold Control". - */ -static inline void ehci_commit_irq(EHCIState *s) -{ - uint32_t itc; - - if (!s->usbsts_pending) { - return; - } - if (s->usbsts_frindex > s->frindex) { - return; - } - - itc = (s->usbcmd >> 16) & 0xff; - s->usbsts |= s->usbsts_pending; - s->usbsts_pending = 0; - s->usbsts_frindex = s->frindex + itc; - ehci_update_irq(s); -} - -static void ehci_update_halt(EHCIState *s) -{ - if (s->usbcmd & USBCMD_RUNSTOP) { - ehci_clear_usbsts(s, USBSTS_HALT); - } else { - if (s->astate == EST_INACTIVE && s->pstate == EST_INACTIVE) { - ehci_set_usbsts(s, USBSTS_HALT); - } - } -} - -static void ehci_set_state(EHCIState *s, int async, int state) -{ - if (async) { - trace_usb_ehci_state("async", state2str(state)); - s->astate = state; - if (s->astate == EST_INACTIVE) { - ehci_clear_usbsts(s, USBSTS_ASS); - ehci_update_halt(s); - } else { - ehci_set_usbsts(s, USBSTS_ASS); - } - } else { - trace_usb_ehci_state("periodic", state2str(state)); - s->pstate = state; - if (s->pstate == EST_INACTIVE) { - ehci_clear_usbsts(s, USBSTS_PSS); - ehci_update_halt(s); - } else { - ehci_set_usbsts(s, USBSTS_PSS); - } - } -} - -static int ehci_get_state(EHCIState *s, int async) -{ - return async ? s->astate : s->pstate; -} - -static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr) -{ - if (async) { - s->a_fetch_addr = addr; - } else { - s->p_fetch_addr = addr; - } -} - -static int ehci_get_fetch_addr(EHCIState *s, int async) -{ - return async ? s->a_fetch_addr : s->p_fetch_addr; -} - -static void ehci_trace_qh(EHCIQueue *q, hwaddr addr, EHCIqh *qh) -{ - /* need three here due to argument count limits */ - trace_usb_ehci_qh_ptrs(q, addr, qh->next, - qh->current_qtd, qh->next_qtd, qh->altnext_qtd); - trace_usb_ehci_qh_fields(addr, - get_field(qh->epchar, QH_EPCHAR_RL), - get_field(qh->epchar, QH_EPCHAR_MPLEN), - get_field(qh->epchar, QH_EPCHAR_EPS), - get_field(qh->epchar, QH_EPCHAR_EP), - get_field(qh->epchar, QH_EPCHAR_DEVADDR)); - trace_usb_ehci_qh_bits(addr, - (bool)(qh->epchar & QH_EPCHAR_C), - (bool)(qh->epchar & QH_EPCHAR_H), - (bool)(qh->epchar & QH_EPCHAR_DTC), - (bool)(qh->epchar & QH_EPCHAR_I)); -} - -static void ehci_trace_qtd(EHCIQueue *q, hwaddr addr, EHCIqtd *qtd) -{ - /* need three here due to argument count limits */ - trace_usb_ehci_qtd_ptrs(q, addr, qtd->next, qtd->altnext); - trace_usb_ehci_qtd_fields(addr, - get_field(qtd->token, QTD_TOKEN_TBYTES), - get_field(qtd->token, QTD_TOKEN_CPAGE), - get_field(qtd->token, QTD_TOKEN_CERR), - get_field(qtd->token, QTD_TOKEN_PID)); - trace_usb_ehci_qtd_bits(addr, - (bool)(qtd->token & QTD_TOKEN_IOC), - (bool)(qtd->token & QTD_TOKEN_ACTIVE), - (bool)(qtd->token & QTD_TOKEN_HALT), - (bool)(qtd->token & QTD_TOKEN_BABBLE), - (bool)(qtd->token & QTD_TOKEN_XACTERR)); -} - -static void ehci_trace_itd(EHCIState *s, hwaddr addr, EHCIitd *itd) -{ - trace_usb_ehci_itd(addr, itd->next, - get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT), - get_field(itd->bufptr[2], ITD_BUFPTR_MULT), - get_field(itd->bufptr[0], ITD_BUFPTR_EP), - get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR)); -} - -static void ehci_trace_sitd(EHCIState *s, hwaddr addr, - EHCIsitd *sitd) -{ - trace_usb_ehci_sitd(addr, sitd->next, - (bool)(sitd->results & SITD_RESULTS_ACTIVE)); -} - -static void ehci_trace_guest_bug(EHCIState *s, const char *message) -{ - trace_usb_ehci_guest_bug(message); - fprintf(stderr, "ehci warning: %s\n", message); -} - -static inline bool ehci_enabled(EHCIState *s) -{ - return s->usbcmd & USBCMD_RUNSTOP; -} - -static inline bool ehci_async_enabled(EHCIState *s) -{ - return ehci_enabled(s) && (s->usbcmd & USBCMD_ASE); -} - -static inline bool ehci_periodic_enabled(EHCIState *s) -{ - return ehci_enabled(s) && (s->usbcmd & USBCMD_PSE); -} - -/* Get an array of dwords from main memory */ -static inline int get_dwords(EHCIState *ehci, uint32_t addr, - uint32_t *buf, int num) -{ - int i; - - if (!ehci->as) { - ehci_raise_irq(ehci, USBSTS_HSE); - ehci->usbcmd &= ~USBCMD_RUNSTOP; - trace_usb_ehci_dma_error(); - return -1; - } - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - dma_memory_read(ehci->as, addr, buf, sizeof(*buf)); - *buf = le32_to_cpu(*buf); - } - - return num; -} - -/* Put an array of dwords in to main memory */ -static inline int put_dwords(EHCIState *ehci, uint32_t addr, - uint32_t *buf, int num) -{ - int i; - - if (!ehci->as) { - ehci_raise_irq(ehci, USBSTS_HSE); - ehci->usbcmd &= ~USBCMD_RUNSTOP; - trace_usb_ehci_dma_error(); - return -1; - } - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - uint32_t tmp = cpu_to_le32(*buf); - dma_memory_write(ehci->as, addr, &tmp, sizeof(tmp)); - } - - return num; -} - -static int ehci_get_pid(EHCIqtd *qtd) -{ - switch (get_field(qtd->token, QTD_TOKEN_PID)) { - case 0: - return USB_TOKEN_OUT; - case 1: - return USB_TOKEN_IN; - case 2: - return USB_TOKEN_SETUP; - default: - fprintf(stderr, "bad token\n"); - return 0; - } -} - -static bool ehci_verify_qh(EHCIQueue *q, EHCIqh *qh) -{ - uint32_t devaddr = get_field(qh->epchar, QH_EPCHAR_DEVADDR); - uint32_t endp = get_field(qh->epchar, QH_EPCHAR_EP); - if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) || - (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) || - (qh->current_qtd != q->qh.current_qtd) || - (q->async && qh->next_qtd != q->qh.next_qtd) || - (memcmp(&qh->altnext_qtd, &q->qh.altnext_qtd, - 7 * sizeof(uint32_t)) != 0) || - (q->dev != NULL && q->dev->addr != devaddr)) { - return false; - } else { - return true; - } -} - -static bool ehci_verify_qtd(EHCIPacket *p, EHCIqtd *qtd) -{ - if (p->qtdaddr != p->queue->qtdaddr || - (p->queue->async && !NLPTR_TBIT(p->qtd.next) && - (p->qtd.next != qtd->next)) || - (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd->altnext)) || - p->qtd.token != qtd->token || - p->qtd.bufptr[0] != qtd->bufptr[0]) { - return false; - } else { - return true; - } -} - -static bool ehci_verify_pid(EHCIQueue *q, EHCIqtd *qtd) -{ - int ep = get_field(q->qh.epchar, QH_EPCHAR_EP); - int pid = ehci_get_pid(qtd); - - /* Note the pid changing is normal for ep 0 (the control ep) */ - if (q->last_pid && ep != 0 && pid != q->last_pid) { - return false; - } else { - return true; - } -} - -/* Finish executing and writeback a packet outside of the regular - fetchqh -> fetchqtd -> execute -> writeback cycle */ -static void ehci_writeback_async_complete_packet(EHCIPacket *p) -{ - EHCIQueue *q = p->queue; - EHCIqtd qtd; - EHCIqh qh; - int state; - - /* Verify the qh + qtd, like we do when going through fetchqh & fetchqtd */ - get_dwords(q->ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2); - get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), - (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); - if (!ehci_verify_qh(q, &qh) || !ehci_verify_qtd(p, &qtd)) { - p->async = EHCI_ASYNC_INITIALIZED; - ehci_free_packet(p); - return; - } - - state = ehci_get_state(q->ehci, q->async); - ehci_state_executing(q); - ehci_state_writeback(q); /* Frees the packet! */ - if (!(q->qh.token & QTD_TOKEN_HALT)) { - ehci_state_advqueue(q); - } - ehci_set_state(q->ehci, q->async, state); -} - -/* packet management */ - -static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) -{ - EHCIPacket *p; - - p = g_new0(EHCIPacket, 1); - p->queue = q; - usb_packet_init(&p->packet); - QTAILQ_INSERT_TAIL(&q->packets, p, next); - trace_usb_ehci_packet_action(p->queue, p, "alloc"); - return p; -} - -static void ehci_free_packet(EHCIPacket *p) -{ - if (p->async == EHCI_ASYNC_FINISHED && - !(p->queue->qh.token & QTD_TOKEN_HALT)) { - ehci_writeback_async_complete_packet(p); - return; - } - trace_usb_ehci_packet_action(p->queue, p, "free"); - if (p->async == EHCI_ASYNC_INFLIGHT) { - usb_cancel_packet(&p->packet); - } - if (p->async == EHCI_ASYNC_FINISHED && - p->packet.status == USB_RET_SUCCESS) { - fprintf(stderr, - "EHCI: Dropping completed packet from halted %s ep %02X\n", - (p->pid == USB_TOKEN_IN) ? "in" : "out", - get_field(p->queue->qh.epchar, QH_EPCHAR_EP)); - } - if (p->async != EHCI_ASYNC_NONE) { - usb_packet_unmap(&p->packet, &p->sgl); - qemu_sglist_destroy(&p->sgl); - } - QTAILQ_REMOVE(&p->queue->packets, p, next); - usb_packet_cleanup(&p->packet); - g_free(p); -} - -/* queue management */ - -static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - EHCIQueue *q; - - q = g_malloc0(sizeof(*q)); - q->ehci = ehci; - q->qhaddr = addr; - q->async = async; - QTAILQ_INIT(&q->packets); - QTAILQ_INSERT_HEAD(head, q, next); - trace_usb_ehci_queue_action(q, "alloc"); - return q; -} - -static void ehci_queue_stopped(EHCIQueue *q) -{ - int endp = get_field(q->qh.epchar, QH_EPCHAR_EP); - - if (!q->last_pid || !q->dev) { - return; - } - - usb_device_ep_stopped(q->dev, usb_ep_get(q->dev, q->last_pid, endp)); -} - -static int ehci_cancel_queue(EHCIQueue *q) -{ - EHCIPacket *p; - int packets = 0; - - p = QTAILQ_FIRST(&q->packets); - if (p == NULL) { - goto leave; - } - - trace_usb_ehci_queue_action(q, "cancel"); - do { - ehci_free_packet(p); - packets++; - } while ((p = QTAILQ_FIRST(&q->packets)) != NULL); - -leave: - ehci_queue_stopped(q); - return packets; -} - -static int ehci_reset_queue(EHCIQueue *q) -{ - int packets; - - trace_usb_ehci_queue_action(q, "reset"); - packets = ehci_cancel_queue(q); - q->dev = NULL; - q->qtdaddr = 0; - q->last_pid = 0; - return packets; -} - -static void ehci_free_queue(EHCIQueue *q, const char *warn) -{ - EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues; - int cancelled; - - trace_usb_ehci_queue_action(q, "free"); - cancelled = ehci_cancel_queue(q); - if (warn && cancelled > 0) { - ehci_trace_guest_bug(q->ehci, warn); - } - QTAILQ_REMOVE(head, q, next); - g_free(q); -} - -static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, - int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - EHCIQueue *q; - - QTAILQ_FOREACH(q, head, next) { - if (addr == q->qhaddr) { - return q; - } - } - return NULL; -} - -static void ehci_queues_rip_unused(EHCIState *ehci, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - const char *warn = async ? "guest unlinked busy QH" : NULL; - uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; - EHCIQueue *q, *tmp; - - QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - if (q->seen) { - q->seen = 0; - q->ts = ehci->last_run_ns; - continue; - } - if (ehci->last_run_ns < q->ts + maxage) { - continue; - } - ehci_free_queue(q, warn); - } -} - -static void ehci_queues_rip_unseen(EHCIState *ehci, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - EHCIQueue *q, *tmp; - - QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - if (!q->seen) { - ehci_free_queue(q, NULL); - } - } -} - -static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - EHCIQueue *q, *tmp; - - QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - if (q->dev != dev) { - continue; - } - ehci_free_queue(q, NULL); - } -} - -static void ehci_queues_rip_all(EHCIState *ehci, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - const char *warn = async ? "guest stopped busy async schedule" : NULL; - EHCIQueue *q, *tmp; - - QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - ehci_free_queue(q, warn); - } -} - -/* Attach or detach a device on root hub */ - -static void ehci_attach(USBPort *port) -{ - EHCIState *s = port->opaque; - uint32_t *portsc = &s->portsc[port->index]; - const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci"; - - trace_usb_ehci_port_attach(port->index, owner, port->dev->product_desc); - - if (*portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - companion->dev = port->dev; - companion->ops->attach(companion); - return; - } - - *portsc |= PORTSC_CONNECT; - *portsc |= PORTSC_CSC; - - ehci_raise_irq(s, USBSTS_PCD); -} - -static void ehci_detach(USBPort *port) -{ - EHCIState *s = port->opaque; - uint32_t *portsc = &s->portsc[port->index]; - const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci"; - - trace_usb_ehci_port_detach(port->index, owner); - - if (*portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - companion->ops->detach(companion); - companion->dev = NULL; - /* - * EHCI spec 4.2.2: "When a disconnect occurs... On the event, - * the port ownership is returned immediately to the EHCI controller." - */ - *portsc &= ~PORTSC_POWNER; - return; - } - - ehci_queues_rip_device(s, port->dev, 0); - ehci_queues_rip_device(s, port->dev, 1); - - *portsc &= ~(PORTSC_CONNECT|PORTSC_PED|PORTSC_SUSPEND); - *portsc |= PORTSC_CSC; - - ehci_raise_irq(s, USBSTS_PCD); -} - -static void ehci_child_detach(USBPort *port, USBDevice *child) -{ - EHCIState *s = port->opaque; - uint32_t portsc = s->portsc[port->index]; - - if (portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - companion->ops->child_detach(companion, child); - return; - } - - ehci_queues_rip_device(s, child, 0); - ehci_queues_rip_device(s, child, 1); -} - -static void ehci_wakeup(USBPort *port) -{ - EHCIState *s = port->opaque; - uint32_t *portsc = &s->portsc[port->index]; - - if (*portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - if (companion->ops->wakeup) { - companion->ops->wakeup(companion); - } - return; - } - - if (*portsc & PORTSC_SUSPEND) { - trace_usb_ehci_port_wakeup(port->index); - *portsc |= PORTSC_FPRES; - ehci_raise_irq(s, USBSTS_PCD); - } - - qemu_bh_schedule(s->async_bh); -} - -static void ehci_register_companion(USBBus *bus, USBPort *ports[], - uint32_t portcount, uint32_t firstport, - Error **errp) -{ - EHCIState *s = container_of(bus, EHCIState, bus); - uint32_t i; - - if (firstport + portcount > NB_PORTS) { - error_setg(errp, "firstport must be between 0 and %u", - NB_PORTS - portcount); - return; - } - - for (i = 0; i < portcount; i++) { - if (s->companion_ports[firstport + i]) { - error_setg(errp, "firstport %u asks for ports %u-%u," - " but port %u has a companion assigned already", - firstport, firstport, firstport + portcount - 1, - firstport + i); - return; - } - } - - for (i = 0; i < portcount; i++) { - s->companion_ports[firstport + i] = ports[i]; - s->ports[firstport + i].speedmask |= - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL; - /* Ensure devs attached before the initial reset go to the companion */ - s->portsc[firstport + i] = PORTSC_POWNER; - } - - s->companion_count++; - s->caps[0x05] = (s->companion_count << 4) | portcount; -} - -static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, - unsigned int stream) -{ - EHCIState *s = container_of(bus, EHCIState, bus); - uint32_t portsc = s->portsc[ep->dev->port->index]; - - if (portsc & PORTSC_POWNER) { - return; - } - - s->periodic_sched_active = PERIODIC_ACTIVE; - qemu_bh_schedule(s->async_bh); -} - -static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr) -{ - USBDevice *dev; - USBPort *port; - int i; - - for (i = 0; i < NB_PORTS; i++) { - port = &ehci->ports[i]; - if (!(ehci->portsc[i] & PORTSC_PED)) { - DPRINTF("Port %d not enabled\n", i); - continue; - } - dev = usb_find_device(port, addr); - if (dev != NULL) { - return dev; - } - } - return NULL; -} - -/* 4.1 host controller initialization */ -void ehci_reset(void *opaque) -{ - EHCIState *s = opaque; - int i; - USBDevice *devs[NB_PORTS]; - - trace_usb_ehci_reset(); - - /* - * Do the detach before touching portsc, so that it correctly gets send to - * us or to our companion based on PORTSC_POWNER before the reset. - */ - for(i = 0; i < NB_PORTS; i++) { - devs[i] = s->ports[i].dev; - if (devs[i] && devs[i]->attached) { - usb_detach(&s->ports[i]); - } - } - - memset(&s->opreg, 0x00, sizeof(s->opreg)); - memset(&s->portsc, 0x00, sizeof(s->portsc)); - - s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; - s->usbsts = USBSTS_HALT; - s->usbsts_pending = 0; - s->usbsts_frindex = 0; - ehci_update_irq(s); - - s->astate = EST_INACTIVE; - s->pstate = EST_INACTIVE; - - for(i = 0; i < NB_PORTS; i++) { - if (s->companion_ports[i]) { - s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; - } else { - s->portsc[i] = PORTSC_PPOWER; - } - if (devs[i] && devs[i]->attached) { - usb_attach(&s->ports[i]); - usb_device_reset(devs[i]); - } - } - ehci_queues_rip_all(s, 0); - ehci_queues_rip_all(s, 1); - timer_del(s->frame_timer); - qemu_bh_cancel(s->async_bh); -} - -static uint64_t ehci_caps_read(void *ptr, hwaddr addr, - unsigned size) -{ - EHCIState *s = ptr; - return s->caps[addr]; -} - -static void ehci_caps_write(void *ptr, hwaddr addr, - uint64_t val, unsigned size) -{ -} - -static uint64_t ehci_opreg_read(void *ptr, hwaddr addr, - unsigned size) -{ - EHCIState *s = ptr; - uint32_t val; - - switch (addr) { - case FRINDEX: - /* Round down to mult of 8, else it can go backwards on migration */ - val = s->frindex & ~7; - break; - default: - val = s->opreg[addr >> 2]; - } - - trace_usb_ehci_opreg_read(addr + s->opregbase, addr2str(addr), val); - return val; -} - -static uint64_t ehci_port_read(void *ptr, hwaddr addr, - unsigned size) -{ - EHCIState *s = ptr; - uint32_t val; - - val = s->portsc[addr >> 2]; - trace_usb_ehci_portsc_read(addr + s->portscbase, addr >> 2, val); - return val; -} - -static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) -{ - USBDevice *dev = s->ports[port].dev; - uint32_t *portsc = &s->portsc[port]; - uint32_t orig; - - if (s->companion_ports[port] == NULL) - return; - - owner = owner & PORTSC_POWNER; - orig = *portsc & PORTSC_POWNER; - - if (!(owner ^ orig)) { - return; - } - - if (dev && dev->attached) { - usb_detach(&s->ports[port]); - } - - *portsc &= ~PORTSC_POWNER; - *portsc |= owner; - - if (dev && dev->attached) { - usb_attach(&s->ports[port]); - } -} - -static void ehci_port_write(void *ptr, hwaddr addr, - uint64_t val, unsigned size) -{ - EHCIState *s = ptr; - int port = addr >> 2; - uint32_t *portsc = &s->portsc[port]; - uint32_t old = *portsc; - USBDevice *dev = s->ports[port].dev; - - trace_usb_ehci_portsc_write(addr + s->portscbase, addr >> 2, val); - - /* Clear rwc bits */ - *portsc &= ~(val & PORTSC_RWC_MASK); - /* The guest may clear, but not set the PED bit */ - *portsc &= val | ~PORTSC_PED; - /* POWNER is masked out by RO_MASK as it is RO when we've no companion */ - handle_port_owner_write(s, port, val); - /* And finally apply RO_MASK */ - val &= PORTSC_RO_MASK; - - if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { - trace_usb_ehci_port_reset(port, 1); - } - - if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { - trace_usb_ehci_port_reset(port, 0); - if (dev && dev->attached) { - usb_port_reset(&s->ports[port]); - *portsc &= ~PORTSC_CSC; - } - - /* - * Table 2.16 Set the enable bit(and enable bit change) to indicate - * to SW that this port has a high speed device attached - */ - if (dev && dev->attached && (dev->speedmask & USB_SPEED_MASK_HIGH)) { - val |= PORTSC_PED; - } - } - - if ((val & PORTSC_SUSPEND) && !(*portsc & PORTSC_SUSPEND)) { - trace_usb_ehci_port_suspend(port); - } - if (!(val & PORTSC_FPRES) && (*portsc & PORTSC_FPRES)) { - trace_usb_ehci_port_resume(port); - val &= ~PORTSC_SUSPEND; - } - - *portsc &= ~PORTSC_RO_MASK; - *portsc |= val; - trace_usb_ehci_portsc_change(addr + s->portscbase, addr >> 2, *portsc, old); -} - -static void ehci_opreg_write(void *ptr, hwaddr addr, - uint64_t val, unsigned size) -{ - EHCIState *s = ptr; - uint32_t *mmio = s->opreg + (addr >> 2); - uint32_t old = *mmio; - int i; - - trace_usb_ehci_opreg_write(addr + s->opregbase, addr2str(addr), val); - - switch (addr) { - case USBCMD: - if (val & USBCMD_HCRESET) { - ehci_reset(s); - val = s->usbcmd; - break; - } - - /* not supporting dynamic frame list size at the moment */ - if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { - fprintf(stderr, "attempt to set frame list size -- value %d\n", - (int)val & USBCMD_FLS); - val &= ~USBCMD_FLS; - } - - if (val & USBCMD_IAAD) { - /* - * Process IAAD immediately, otherwise the Linux IAAD watchdog may - * trigger and re-use a qh without us seeing the unlink. - */ - s->async_stepdown = 0; - qemu_bh_schedule(s->async_bh); - trace_usb_ehci_doorbell_ring(); - } - - if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) != - ((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) { - if (s->pstate == EST_INACTIVE) { - SET_LAST_RUN_CLOCK(s); - } - s->usbcmd = val; /* Set usbcmd for ehci_update_halt() */ - ehci_update_halt(s); - s->async_stepdown = 0; - qemu_bh_schedule(s->async_bh); - } - break; - - case USBSTS: - val &= USBSTS_RO_MASK; // bits 6 through 31 are RO - ehci_clear_usbsts(s, val); // bits 0 through 5 are R/WC - val = s->usbsts; - ehci_update_irq(s); - break; - - case USBINTR: - val &= USBINTR_MASK; - if (ehci_enabled(s) && (USBSTS_FLR & val)) { - qemu_bh_schedule(s->async_bh); - } - break; - - case FRINDEX: - val &= 0x00003fff; /* frindex is 14bits */ - s->usbsts_frindex = val; - break; - - case CONFIGFLAG: - val &= 0x1; - if (val) { - for(i = 0; i < NB_PORTS; i++) - handle_port_owner_write(s, i, 0); - } - break; - - case PERIODICLISTBASE: - if (ehci_periodic_enabled(s)) { - fprintf(stderr, - "ehci: PERIODIC list base register set while periodic schedule\n" - " is enabled and HC is enabled\n"); - } - break; - - case ASYNCLISTADDR: - if (ehci_async_enabled(s)) { - fprintf(stderr, - "ehci: ASYNC list address register set while async schedule\n" - " is enabled and HC is enabled\n"); - } - break; - } - - *mmio = val; - trace_usb_ehci_opreg_change(addr + s->opregbase, addr2str(addr), - *mmio, old); -} - -/* - * Write the qh back to guest physical memory. This step isn't - * in the EHCI spec but we need to do it since we don't share - * physical memory with our guest VM. - * - * The first three dwords are read-only for the EHCI, so skip them - * when writing back the qh. - */ -static void ehci_flush_qh(EHCIQueue *q) -{ - uint32_t *qh = (uint32_t *) &q->qh; - uint32_t dwords = sizeof(EHCIqh) >> 2; - uint32_t addr = NLPTR_GET(q->qhaddr); - - put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); -} - -// 4.10.2 - -static int ehci_qh_do_overlay(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - int i; - int dtoggle; - int ping; - int eps; - int reload; - - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - - // remember values in fields to preserve in qh after overlay - - dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE; - ping = q->qh.token & QTD_TOKEN_PING; - - q->qh.current_qtd = p->qtdaddr; - q->qh.next_qtd = p->qtd.next; - q->qh.altnext_qtd = p->qtd.altnext; - q->qh.token = p->qtd.token; - - - eps = get_field(q->qh.epchar, QH_EPCHAR_EPS); - if (eps == EHCI_QH_EPS_HIGH) { - q->qh.token &= ~QTD_TOKEN_PING; - q->qh.token |= ping; - } - - reload = get_field(q->qh.epchar, QH_EPCHAR_RL); - set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT); - - for (i = 0; i < 5; i++) { - q->qh.bufptr[i] = p->qtd.bufptr[i]; - } - - if (!(q->qh.epchar & QH_EPCHAR_DTC)) { - // preserve QH DT bit - q->qh.token &= ~QTD_TOKEN_DTOGGLE; - q->qh.token |= dtoggle; - } - - q->qh.bufptr[1] &= ~BUFPTR_CPROGMASK_MASK; - q->qh.bufptr[2] &= ~BUFPTR_FRAMETAG_MASK; - - ehci_flush_qh(q); - - return 0; -} - -static int ehci_init_transfer(EHCIPacket *p) -{ - uint32_t cpage, offset, bytes, plen; - dma_addr_t page; - - cpage = get_field(p->qtd.token, QTD_TOKEN_CPAGE); - bytes = get_field(p->qtd.token, QTD_TOKEN_TBYTES); - offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK; - qemu_sglist_init(&p->sgl, p->queue->ehci->device, 5, p->queue->ehci->as); - - while (bytes > 0) { - if (cpage > 4) { - fprintf(stderr, "cpage out of range (%d)\n", cpage); - return -1; - } - - page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK; - page += offset; - plen = bytes; - if (plen > 4096 - offset) { - plen = 4096 - offset; - offset = 0; - cpage++; - } - - qemu_sglist_add(&p->sgl, page, plen); - bytes -= plen; - } - return 0; -} - -static void ehci_finish_transfer(EHCIQueue *q, int len) -{ - uint32_t cpage, offset; - - if (len > 0) { - /* update cpage & offset */ - cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); - offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; - - offset += len; - cpage += offset >> QTD_BUFPTR_SH; - offset &= ~QTD_BUFPTR_MASK; - - set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE); - q->qh.bufptr[0] &= QTD_BUFPTR_MASK; - q->qh.bufptr[0] |= offset; - } -} - -static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) -{ - EHCIPacket *p; - EHCIState *s = port->opaque; - uint32_t portsc = s->portsc[port->index]; - - if (portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - companion->ops->complete(companion, packet); - return; - } - - p = container_of(packet, EHCIPacket, packet); - assert(p->async == EHCI_ASYNC_INFLIGHT); - - if (packet->status == USB_RET_REMOVE_FROM_QUEUE) { - trace_usb_ehci_packet_action(p->queue, p, "remove"); - ehci_free_packet(p); - return; - } - - trace_usb_ehci_packet_action(p->queue, p, "wakeup"); - p->async = EHCI_ASYNC_FINISHED; - - if (!p->queue->async) { - s->periodic_sched_active = PERIODIC_ACTIVE; - } - qemu_bh_schedule(s->async_bh); -} - -static void ehci_execute_complete(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - uint32_t tbytes; - - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - assert(p->async == EHCI_ASYNC_INITIALIZED || - p->async == EHCI_ASYNC_FINISHED); - - DPRINTF("execute_complete: qhaddr 0x%x, next 0x%x, qtdaddr 0x%x, " - "status %d, actual_length %d\n", - q->qhaddr, q->qh.next, q->qtdaddr, - p->packet.status, p->packet.actual_length); - - switch (p->packet.status) { - case USB_RET_SUCCESS: - break; - case USB_RET_IOERROR: - case USB_RET_NODEV: - q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); - set_field(&q->qh.token, 0, QTD_TOKEN_CERR); - ehci_raise_irq(q->ehci, USBSTS_ERRINT); - break; - case USB_RET_STALL: - q->qh.token |= QTD_TOKEN_HALT; - ehci_raise_irq(q->ehci, USBSTS_ERRINT); - break; - case USB_RET_NAK: - set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT); - return; /* We're not done yet with this transaction */ - case USB_RET_BABBLE: - q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); - ehci_raise_irq(q->ehci, USBSTS_ERRINT); - break; - default: - /* should not be triggerable */ - fprintf(stderr, "USB invalid response %d\n", p->packet.status); - g_assert_not_reached(); - break; - } - - /* TODO check 4.12 for splits */ - tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES); - if (tbytes && p->pid == USB_TOKEN_IN) { - tbytes -= p->packet.actual_length; - if (tbytes) { - /* 4.15.1.2 must raise int on a short input packet */ - ehci_raise_irq(q->ehci, USBSTS_INT); - if (q->async) { - q->ehci->int_req_by_async = true; - } - } - } else { - tbytes = 0; - } - DPRINTF("updating tbytes to %d\n", tbytes); - set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES); - - ehci_finish_transfer(q, p->packet.actual_length); - usb_packet_unmap(&p->packet, &p->sgl); - qemu_sglist_destroy(&p->sgl); - p->async = EHCI_ASYNC_NONE; - - q->qh.token ^= QTD_TOKEN_DTOGGLE; - q->qh.token &= ~QTD_TOKEN_ACTIVE; - - if (q->qh.token & QTD_TOKEN_IOC) { - ehci_raise_irq(q->ehci, USBSTS_INT); - if (q->async) { - q->ehci->int_req_by_async = true; - } - } -} - -/* 4.10.3 returns "again" */ -static int ehci_execute(EHCIPacket *p, const char *action) -{ - USBEndpoint *ep; - int endp; - bool spd; - - assert(p->async == EHCI_ASYNC_NONE || - p->async == EHCI_ASYNC_INITIALIZED); - - if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) { - fprintf(stderr, "Attempting to execute inactive qtd\n"); - return -1; - } - - if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) { - ehci_trace_guest_bug(p->queue->ehci, - "guest requested more bytes than allowed"); - return -1; - } - - if (!ehci_verify_pid(p->queue, &p->qtd)) { - ehci_queue_stopped(p->queue); /* Mark the ep in the prev dir stopped */ - } - p->pid = ehci_get_pid(&p->qtd); - p->queue->last_pid = p->pid; - endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); - ep = usb_ep_get(p->queue->dev, p->pid, endp); - - if (p->async == EHCI_ASYNC_NONE) { - if (ehci_init_transfer(p) != 0) { - return -1; - } - - spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0); - usb_packet_setup(&p->packet, p->pid, ep, 0, p->qtdaddr, spd, - (p->qtd.token & QTD_TOKEN_IOC) != 0); - usb_packet_map(&p->packet, &p->sgl); - p->async = EHCI_ASYNC_INITIALIZED; - } - - trace_usb_ehci_packet_action(p->queue, p, action); - usb_handle_packet(p->queue->dev, &p->packet); - DPRINTF("submit: qh 0x%x next 0x%x qtd 0x%x pid 0x%x len %zd endp 0x%x " - "status %d actual_length %d\n", p->queue->qhaddr, p->qtd.next, - p->qtdaddr, p->pid, p->packet.iov.size, endp, p->packet.status, - p->packet.actual_length); - - if (p->packet.actual_length > BUFF_SIZE) { - fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n"); - return -1; - } - - return 1; -} - -/* 4.7.2 - */ - -static int ehci_process_itd(EHCIState *ehci, - EHCIitd *itd, - uint32_t addr) -{ - USBDevice *dev; - USBEndpoint *ep; - uint32_t i, len, pid, dir, devaddr, endp; - uint32_t pg, off, ptr1, ptr2, max, mult; - - ehci->periodic_sched_active = PERIODIC_ACTIVE; - - dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); - devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR); - endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP); - max = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT); - mult = get_field(itd->bufptr[2], ITD_BUFPTR_MULT); - - for(i = 0; i < 8; i++) { - if (itd->transact[i] & ITD_XACT_ACTIVE) { - pg = get_field(itd->transact[i], ITD_XACT_PGSEL); - off = itd->transact[i] & ITD_XACT_OFFSET_MASK; - len = get_field(itd->transact[i], ITD_XACT_LENGTH); - - if (len > max * mult) { - len = max * mult; - } - if (len > BUFF_SIZE || pg > 6) { - return -1; - } - - ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); - qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as); - if (off + len > 4096) { - /* transfer crosses page border */ - if (pg == 6) { - return -1; /* avoid page pg + 1 */ - } - ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK); - uint32_t len2 = off + len - 4096; - uint32_t len1 = len - len2; - qemu_sglist_add(&ehci->isgl, ptr1 + off, len1); - qemu_sglist_add(&ehci->isgl, ptr2, len2); - } else { - qemu_sglist_add(&ehci->isgl, ptr1 + off, len); - } - - pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT; - - dev = ehci_find_device(ehci, devaddr); - ep = usb_ep_get(dev, pid, endp); - if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) { - usb_packet_setup(&ehci->ipacket, pid, ep, 0, addr, false, - (itd->transact[i] & ITD_XACT_IOC) != 0); - usb_packet_map(&ehci->ipacket, &ehci->isgl); - usb_handle_packet(dev, &ehci->ipacket); - usb_packet_unmap(&ehci->ipacket, &ehci->isgl); - } else { - DPRINTF("ISOCH: attempt to addess non-iso endpoint\n"); - ehci->ipacket.status = USB_RET_NAK; - ehci->ipacket.actual_length = 0; - } - qemu_sglist_destroy(&ehci->isgl); - - switch (ehci->ipacket.status) { - case USB_RET_SUCCESS: - break; - default: - fprintf(stderr, "Unexpected iso usb result: %d\n", - ehci->ipacket.status); - /* Fall through */ - case USB_RET_IOERROR: - case USB_RET_NODEV: - /* 3.3.2: XACTERR is only allowed on IN transactions */ - if (dir) { - itd->transact[i] |= ITD_XACT_XACTERR; - ehci_raise_irq(ehci, USBSTS_ERRINT); - } - break; - case USB_RET_BABBLE: - itd->transact[i] |= ITD_XACT_BABBLE; - ehci_raise_irq(ehci, USBSTS_ERRINT); - break; - case USB_RET_NAK: - /* no data for us, so do a zero-length transfer */ - ehci->ipacket.actual_length = 0; - break; - } - if (!dir) { - set_field(&itd->transact[i], len - ehci->ipacket.actual_length, - ITD_XACT_LENGTH); /* OUT */ - } else { - set_field(&itd->transact[i], ehci->ipacket.actual_length, - ITD_XACT_LENGTH); /* IN */ - } - if (itd->transact[i] & ITD_XACT_IOC) { - ehci_raise_irq(ehci, USBSTS_INT); - } - itd->transact[i] &= ~ITD_XACT_ACTIVE; - } - } - return 0; -} - - -/* This state is the entry point for asynchronous schedule - * processing. Entry here consitutes a EHCI start event state (4.8.5) - */ -static int ehci_state_waitlisthead(EHCIState *ehci, int async) -{ - EHCIqh qh; - int i = 0; - int again = 0; - uint32_t entry = ehci->asynclistaddr; - - /* set reclamation flag at start event (4.8.6) */ - if (async) { - ehci_set_usbsts(ehci, USBSTS_REC); - } - - ehci_queues_rip_unused(ehci, async); - - /* Find the head of the list (4.9.1.1) */ - for(i = 0; i < MAX_QH; i++) { - if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh, - sizeof(EHCIqh) >> 2) < 0) { - return 0; - } - ehci_trace_qh(NULL, NLPTR_GET(entry), &qh); - - if (qh.epchar & QH_EPCHAR_H) { - if (async) { - entry |= (NLPTR_TYPE_QH << 1); - } - - ehci_set_fetch_addr(ehci, async, entry); - ehci_set_state(ehci, async, EST_FETCHENTRY); - again = 1; - goto out; - } - - entry = qh.next; - if (entry == ehci->asynclistaddr) { - break; - } - } - - /* no head found for list. */ - - ehci_set_state(ehci, async, EST_ACTIVE); - -out: - return again; -} - - -/* This state is the entry point for periodic schedule processing as - * well as being a continuation state for async processing. - */ -static int ehci_state_fetchentry(EHCIState *ehci, int async) -{ - int again = 0; - uint32_t entry = ehci_get_fetch_addr(ehci, async); - - if (NLPTR_TBIT(entry)) { - ehci_set_state(ehci, async, EST_ACTIVE); - goto out; - } - - /* section 4.8, only QH in async schedule */ - if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) { - fprintf(stderr, "non queue head request in async schedule\n"); - return -1; - } - - switch (NLPTR_TYPE_GET(entry)) { - case NLPTR_TYPE_QH: - ehci_set_state(ehci, async, EST_FETCHQH); - again = 1; - break; - - case NLPTR_TYPE_ITD: - ehci_set_state(ehci, async, EST_FETCHITD); - again = 1; - break; - - case NLPTR_TYPE_STITD: - ehci_set_state(ehci, async, EST_FETCHSITD); - again = 1; - break; - - default: - /* TODO: handle FSTN type */ - fprintf(stderr, "FETCHENTRY: entry at %X is of type %d " - "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry)); - return -1; - } - -out: - return again; -} - -static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) -{ - uint32_t entry; - EHCIQueue *q; - EHCIqh qh; - - entry = ehci_get_fetch_addr(ehci, async); - q = ehci_find_queue_by_qh(ehci, entry, async); - if (q == NULL) { - q = ehci_alloc_queue(ehci, entry, async); - } - - q->seen++; - if (q->seen > 1) { - /* we are going in circles -- stop processing */ - ehci_set_state(ehci, async, EST_ACTIVE); - q = NULL; - goto out; - } - - if (get_dwords(ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2) < 0) { - q = NULL; - goto out; - } - ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh); - - /* - * The overlay area of the qh should never be changed by the guest, - * except when idle, in which case the reset is a nop. - */ - if (!ehci_verify_qh(q, &qh)) { - if (ehci_reset_queue(q) > 0) { - ehci_trace_guest_bug(ehci, "guest updated active QH"); - } - } - q->qh = qh; - - q->transact_ctr = get_field(q->qh.epcap, QH_EPCAP_MULT); - if (q->transact_ctr == 0) { /* Guest bug in some versions of windows */ - q->transact_ctr = 4; - } - - if (q->dev == NULL) { - q->dev = ehci_find_device(q->ehci, - get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)); - } - - if (async && (q->qh.epchar & QH_EPCHAR_H)) { - - /* EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */ - if (ehci->usbsts & USBSTS_REC) { - ehci_clear_usbsts(ehci, USBSTS_REC); - } else { - DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset" - " - done processing\n", q->qhaddr); - ehci_set_state(ehci, async, EST_ACTIVE); - q = NULL; - goto out; - } - } - -#if EHCI_DEBUG - if (q->qhaddr != q->qh.next) { - DPRINTF("FETCHQH: QH 0x%08x (h %x halt %x active %x) next 0x%08x\n", - q->qhaddr, - q->qh.epchar & QH_EPCHAR_H, - q->qh.token & QTD_TOKEN_HALT, - q->qh.token & QTD_TOKEN_ACTIVE, - q->qh.next); - } -#endif - - if (q->qh.token & QTD_TOKEN_HALT) { - ehci_set_state(ehci, async, EST_HORIZONTALQH); - - } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && - (NLPTR_TBIT(q->qh.current_qtd) == 0)) { - q->qtdaddr = q->qh.current_qtd; - ehci_set_state(ehci, async, EST_FETCHQTD); - - } else { - /* EHCI spec version 1.0 Section 4.10.2 */ - ehci_set_state(ehci, async, EST_ADVANCEQUEUE); - } - -out: - return q; -} - -static int ehci_state_fetchitd(EHCIState *ehci, int async) -{ - uint32_t entry; - EHCIitd itd; - - assert(!async); - entry = ehci_get_fetch_addr(ehci, async); - - if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2) < 0) { - return -1; - } - ehci_trace_itd(ehci, entry, &itd); - - if (ehci_process_itd(ehci, &itd, entry) != 0) { - return -1; - } - - put_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2); - ehci_set_fetch_addr(ehci, async, itd.next); - ehci_set_state(ehci, async, EST_FETCHENTRY); - - return 1; -} - -static int ehci_state_fetchsitd(EHCIState *ehci, int async) -{ - uint32_t entry; - EHCIsitd sitd; - - assert(!async); - entry = ehci_get_fetch_addr(ehci, async); - - if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd, - sizeof(EHCIsitd) >> 2) < 0) { - return 0; - } - ehci_trace_sitd(ehci, entry, &sitd); - - if (!(sitd.results & SITD_RESULTS_ACTIVE)) { - /* siTD is not active, nothing to do */; - } else { - /* TODO: split transfers are not implemented */ - fprintf(stderr, "WARNING: Skipping active siTD\n"); - } - - ehci_set_fetch_addr(ehci, async, sitd.next); - ehci_set_state(ehci, async, EST_FETCHENTRY); - return 1; -} - -/* Section 4.10.2 - paragraph 3 */ -static int ehci_state_advqueue(EHCIQueue *q) -{ -#if 0 - /* TO-DO: 4.10.2 - paragraph 2 - * if I-bit is set to 1 and QH is not active - * go to horizontal QH - */ - if (I-bit set) { - ehci_set_state(ehci, async, EST_HORIZONTALQH); - goto out; - } -#endif - - /* - * want data and alt-next qTD is valid - */ - if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && - (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) { - q->qtdaddr = q->qh.altnext_qtd; - ehci_set_state(q->ehci, q->async, EST_FETCHQTD); - - /* - * next qTD is valid - */ - } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) { - q->qtdaddr = q->qh.next_qtd; - ehci_set_state(q->ehci, q->async, EST_FETCHQTD); - - /* - * no valid qTD, try next QH - */ - } else { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - } - - return 1; -} - -/* Section 4.10.2 - paragraph 4 */ -static int ehci_state_fetchqtd(EHCIQueue *q) -{ - EHCIqtd qtd; - EHCIPacket *p; - int again = 1; - - if (get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd, - sizeof(EHCIqtd) >> 2) < 0) { - return 0; - } - ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); - - p = QTAILQ_FIRST(&q->packets); - if (p != NULL) { - if (!ehci_verify_qtd(p, &qtd)) { - ehci_cancel_queue(q); - if (qtd.token & QTD_TOKEN_ACTIVE) { - ehci_trace_guest_bug(q->ehci, "guest updated active qTD"); - } - p = NULL; - } else { - p->qtd = qtd; - ehci_qh_do_overlay(q); - } - } - - if (!(qtd.token & QTD_TOKEN_ACTIVE)) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - } else if (p != NULL) { - switch (p->async) { - case EHCI_ASYNC_NONE: - case EHCI_ASYNC_INITIALIZED: - /* Not yet executed (MULT), or previously nacked (int) packet */ - ehci_set_state(q->ehci, q->async, EST_EXECUTE); - break; - case EHCI_ASYNC_INFLIGHT: - /* Check if the guest has added new tds to the queue */ - again = ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)); - /* Unfinished async handled packet, go horizontal */ - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - break; - case EHCI_ASYNC_FINISHED: - /* Complete executing of the packet */ - ehci_set_state(q->ehci, q->async, EST_EXECUTING); - break; - } - } else { - p = ehci_alloc_packet(q); - p->qtdaddr = q->qtdaddr; - p->qtd = qtd; - ehci_set_state(q->ehci, q->async, EST_EXECUTE); - } - - return again; -} - -static int ehci_state_horizqh(EHCIQueue *q) -{ - int again = 0; - - if (ehci_get_fetch_addr(q->ehci, q->async) != q->qh.next) { - ehci_set_fetch_addr(q->ehci, q->async, q->qh.next); - ehci_set_state(q->ehci, q->async, EST_FETCHENTRY); - again = 1; - } else { - ehci_set_state(q->ehci, q->async, EST_ACTIVE); - } - - return again; -} - -/* Returns "again" */ -static int ehci_fill_queue(EHCIPacket *p) -{ - USBEndpoint *ep = p->packet.ep; - EHCIQueue *q = p->queue; - EHCIqtd qtd = p->qtd; - uint32_t qtdaddr; - - for (;;) { - if (NLPTR_TBIT(qtd.next) != 0) { - break; - } - qtdaddr = qtd.next; - /* - * Detect circular td lists, Windows creates these, counting on the - * active bit going low after execution to make the queue stop. - */ - QTAILQ_FOREACH(p, &q->packets, next) { - if (p->qtdaddr == qtdaddr) { - goto leave; - } - } - if (get_dwords(q->ehci, NLPTR_GET(qtdaddr), - (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2) < 0) { - return -1; - } - ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd); - if (!(qtd.token & QTD_TOKEN_ACTIVE)) { - break; - } - if (!ehci_verify_pid(q, &qtd)) { - ehci_trace_guest_bug(q->ehci, "guest queued token with wrong pid"); - break; - } - p = ehci_alloc_packet(q); - p->qtdaddr = qtdaddr; - p->qtd = qtd; - if (ehci_execute(p, "queue") == -1) { - return -1; - } - assert(p->packet.status == USB_RET_ASYNC); - p->async = EHCI_ASYNC_INFLIGHT; - } -leave: - usb_device_flush_ep_queue(ep->dev, ep); - return 1; -} - -static int ehci_state_execute(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - int again = 0; - - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - - if (ehci_qh_do_overlay(q) != 0) { - return -1; - } - - // TODO verify enough time remains in the uframe as in 4.4.1.1 - // TODO write back ptr to async list when done or out of time - - /* 4.10.3, bottom of page 82, go horizontal on transaction counter == 0 */ - if (!q->async && q->transact_ctr == 0) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = 1; - goto out; - } - - if (q->async) { - ehci_set_usbsts(q->ehci, USBSTS_REC); - } - - again = ehci_execute(p, "process"); - if (again == -1) { - goto out; - } - if (p->packet.status == USB_RET_ASYNC) { - ehci_flush_qh(q); - trace_usb_ehci_packet_action(p->queue, p, "async"); - p->async = EHCI_ASYNC_INFLIGHT; - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - if (q->async) { - again = ehci_fill_queue(p); - } else { - again = 1; - } - goto out; - } - - ehci_set_state(q->ehci, q->async, EST_EXECUTING); - again = 1; - -out: - return again; -} - -static int ehci_state_executing(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - - ehci_execute_complete(q); - - /* 4.10.3 */ - if (!q->async && q->transact_ctr > 0) { - q->transact_ctr--; - } - - /* 4.10.5 */ - if (p->packet.status == USB_RET_NAK) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - } else { - ehci_set_state(q->ehci, q->async, EST_WRITEBACK); - } - - ehci_flush_qh(q); - return 1; -} - - -static int ehci_state_writeback(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - uint32_t *qtd, addr; - int again = 0; - - /* Write back the QTD from the QH area */ - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - - ehci_trace_qtd(q, NLPTR_GET(p->qtdaddr), (EHCIqtd *) &q->qh.next_qtd); - qtd = (uint32_t *) &q->qh.next_qtd; - addr = NLPTR_GET(p->qtdaddr); - put_dwords(q->ehci, addr + 2 * sizeof(uint32_t), qtd + 2, 2); - ehci_free_packet(p); - - /* - * EHCI specs say go horizontal here. - * - * We can also advance the queue here for performance reasons. We - * need to take care to only take that shortcut in case we've - * processed the qtd just written back without errors, i.e. halt - * bit is clear. - */ - if (q->qh.token & QTD_TOKEN_HALT) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = 1; - } else { - ehci_set_state(q->ehci, q->async, EST_ADVANCEQUEUE); - again = 1; - } - return again; -} - -/* - * This is the state machine that is common to both async and periodic - */ - -static void ehci_advance_state(EHCIState *ehci, int async) -{ - EHCIQueue *q = NULL; - int itd_count = 0; - int again; - - do { - switch(ehci_get_state(ehci, async)) { - case EST_WAITLISTHEAD: - again = ehci_state_waitlisthead(ehci, async); - break; - - case EST_FETCHENTRY: - again = ehci_state_fetchentry(ehci, async); - break; - - case EST_FETCHQH: - q = ehci_state_fetchqh(ehci, async); - if (q != NULL) { - assert(q->async == async); - again = 1; - } else { - again = 0; - } - break; - - case EST_FETCHITD: - again = ehci_state_fetchitd(ehci, async); - itd_count++; - break; - - case EST_FETCHSITD: - again = ehci_state_fetchsitd(ehci, async); - itd_count++; - break; - - case EST_ADVANCEQUEUE: - assert(q != NULL); - again = ehci_state_advqueue(q); - break; - - case EST_FETCHQTD: - assert(q != NULL); - again = ehci_state_fetchqtd(q); - break; - - case EST_HORIZONTALQH: - assert(q != NULL); - again = ehci_state_horizqh(q); - break; - - case EST_EXECUTE: - assert(q != NULL); - again = ehci_state_execute(q); - if (async) { - ehci->async_stepdown = 0; - } - break; - - case EST_EXECUTING: - assert(q != NULL); - if (async) { - ehci->async_stepdown = 0; - } - again = ehci_state_executing(q); - break; - - case EST_WRITEBACK: - assert(q != NULL); - again = ehci_state_writeback(q); - if (!async) { - ehci->periodic_sched_active = PERIODIC_ACTIVE; - } - break; - - default: - fprintf(stderr, "Bad state!\n"); - again = -1; - g_assert_not_reached(); - break; - } - - if (again < 0 || itd_count > 16) { - /* TODO: notify guest (raise HSE irq?) */ - fprintf(stderr, "processing error - resetting ehci HC\n"); - ehci_reset(ehci); - again = 0; - } - } - while (again); -} - -static void ehci_advance_async_state(EHCIState *ehci) -{ - const int async = 1; - - switch(ehci_get_state(ehci, async)) { - case EST_INACTIVE: - if (!ehci_async_enabled(ehci)) { - break; - } - ehci_set_state(ehci, async, EST_ACTIVE); - // No break, fall through to ACTIVE - - case EST_ACTIVE: - if (!ehci_async_enabled(ehci)) { - ehci_queues_rip_all(ehci, async); - ehci_set_state(ehci, async, EST_INACTIVE); - break; - } - - /* make sure guest has acknowledged the doorbell interrupt */ - /* TO-DO: is this really needed? */ - if (ehci->usbsts & USBSTS_IAA) { - DPRINTF("IAA status bit still set.\n"); - break; - } - - /* check that address register has been set */ - if (ehci->asynclistaddr == 0) { - break; - } - - ehci_set_state(ehci, async, EST_WAITLISTHEAD); - ehci_advance_state(ehci, async); - - /* If the doorbell is set, the guest wants to make a change to the - * schedule. The host controller needs to release cached data. - * (section 4.8.2) - */ - if (ehci->usbcmd & USBCMD_IAAD) { - /* Remove all unseen qhs from the async qhs queue */ - ehci_queues_rip_unseen(ehci, async); - trace_usb_ehci_doorbell_ack(); - ehci->usbcmd &= ~USBCMD_IAAD; - ehci_raise_irq(ehci, USBSTS_IAA); - } - break; - - default: - /* this should only be due to a developer mistake */ - fprintf(stderr, "ehci: Bad asynchronous state %d. " - "Resetting to active\n", ehci->astate); - g_assert_not_reached(); - } -} - -static void ehci_advance_periodic_state(EHCIState *ehci) -{ - uint32_t entry; - uint32_t list; - const int async = 0; - - // 4.6 - - switch(ehci_get_state(ehci, async)) { - case EST_INACTIVE: - if (!(ehci->frindex & 7) && ehci_periodic_enabled(ehci)) { - ehci_set_state(ehci, async, EST_ACTIVE); - // No break, fall through to ACTIVE - } else - break; - - case EST_ACTIVE: - if (!(ehci->frindex & 7) && !ehci_periodic_enabled(ehci)) { - ehci_queues_rip_all(ehci, async); - ehci_set_state(ehci, async, EST_INACTIVE); - break; - } - - list = ehci->periodiclistbase & 0xfffff000; - /* check that register has been set */ - if (list == 0) { - break; - } - list |= ((ehci->frindex & 0x1ff8) >> 1); - - if (get_dwords(ehci, list, &entry, 1) < 0) { - break; - } - - DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", - ehci->frindex / 8, list, entry); - ehci_set_fetch_addr(ehci, async,entry); - ehci_set_state(ehci, async, EST_FETCHENTRY); - ehci_advance_state(ehci, async); - ehci_queues_rip_unused(ehci, async); - break; - - default: - /* this should only be due to a developer mistake */ - fprintf(stderr, "ehci: Bad periodic state %d. " - "Resetting to active\n", ehci->pstate); - g_assert_not_reached(); - } -} - -static void ehci_update_frindex(EHCIState *ehci, int uframes) -{ - int i; - - if (!ehci_enabled(ehci) && ehci->pstate == EST_INACTIVE) { - return; - } - - for (i = 0; i < uframes; i++) { - ehci->frindex++; - - if (ehci->frindex == 0x00002000) { - ehci_raise_irq(ehci, USBSTS_FLR); - } - - if (ehci->frindex == 0x00004000) { - ehci_raise_irq(ehci, USBSTS_FLR); - ehci->frindex = 0; - if (ehci->usbsts_frindex >= 0x00004000) { - ehci->usbsts_frindex -= 0x00004000; - } else { - ehci->usbsts_frindex = 0; - } - } - } -} - -static void ehci_frame_timer(void *opaque) -{ - EHCIState *ehci = opaque; - int need_timer = 0; - int64_t expire_time, t_now; - uint64_t ns_elapsed; - int uframes, skipped_uframes; - int i; - - t_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ns_elapsed = t_now - ehci->last_run_ns; - uframes = ns_elapsed / UFRAME_TIMER_NS; - - if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) { - need_timer++; - - if (uframes > (ehci->maxframes * 8)) { - skipped_uframes = uframes - (ehci->maxframes * 8); - ehci_update_frindex(ehci, skipped_uframes); - ehci->last_run_ns += UFRAME_TIMER_NS * skipped_uframes; - uframes -= skipped_uframes; - DPRINTF("WARNING - EHCI skipped %d uframes\n", skipped_uframes); - } - - for (i = 0; i < uframes; i++) { - /* - * If we're running behind schedule, we should not catch up - * too fast, as that will make some guests unhappy: - * 1) We must process a minimum of MIN_UFR_PER_TICK frames, - * otherwise we will never catch up - * 2) Process frames until the guest has requested an irq (IOC) - */ - if (i >= MIN_UFR_PER_TICK) { - ehci_commit_irq(ehci); - if ((ehci->usbsts & USBINTR_MASK) & ehci->usbintr) { - break; - } - } - if (ehci->periodic_sched_active) { - ehci->periodic_sched_active--; - } - ehci_update_frindex(ehci, 1); - if ((ehci->frindex & 7) == 0) { - ehci_advance_periodic_state(ehci); - } - ehci->last_run_ns += UFRAME_TIMER_NS; - } - } else { - ehci->periodic_sched_active = 0; - ehci_update_frindex(ehci, uframes); - ehci->last_run_ns += UFRAME_TIMER_NS * uframes; - } - - if (ehci->periodic_sched_active) { - ehci->async_stepdown = 0; - } else if (ehci->async_stepdown < ehci->maxframes / 2) { - ehci->async_stepdown++; - } - - /* Async is not inside loop since it executes everything it can once - * called - */ - if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) { - need_timer++; - ehci_advance_async_state(ehci); - } - - ehci_commit_irq(ehci); - if (ehci->usbsts_pending) { - need_timer++; - ehci->async_stepdown = 0; - } - - if (ehci_enabled(ehci) && (ehci->usbintr & USBSTS_FLR)) { - need_timer++; - } - - if (need_timer) { - /* If we've raised int, we speed up the timer, so that we quickly - * notice any new packets queued up in response */ - if (ehci->int_req_by_async && (ehci->usbsts & USBSTS_INT)) { - expire_time = t_now + - NANOSECONDS_PER_SECOND / (FRAME_TIMER_FREQ * 4); - ehci->int_req_by_async = false; - } else { - expire_time = t_now + (NANOSECONDS_PER_SECOND - * (ehci->async_stepdown+1) / FRAME_TIMER_FREQ); - } - timer_mod(ehci->frame_timer, expire_time); - } -} - -static const MemoryRegionOps ehci_mmio_caps_ops = { - .read = ehci_caps_read, - .write = ehci_caps_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps ehci_mmio_opreg_ops = { - .read = ehci_opreg_read, - .write = ehci_opreg_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps ehci_mmio_port_ops = { - .read = ehci_port_read, - .write = ehci_port_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static USBPortOps ehci_port_ops = { - .attach = ehci_attach, - .detach = ehci_detach, - .child_detach = ehci_child_detach, - .wakeup = ehci_wakeup, - .complete = ehci_async_complete_packet, -}; - -static USBBusOps ehci_bus_ops_companion = { - .register_companion = ehci_register_companion, - .wakeup_endpoint = ehci_wakeup_endpoint, -}; -static USBBusOps ehci_bus_ops_standalone = { - .wakeup_endpoint = ehci_wakeup_endpoint, -}; - -static void usb_ehci_pre_save(void *opaque) -{ - EHCIState *ehci = opaque; - uint32_t new_frindex; - - /* Round down frindex to a multiple of 8 for migration compatibility */ - new_frindex = ehci->frindex & ~7; - ehci->last_run_ns -= (ehci->frindex - new_frindex) * UFRAME_TIMER_NS; - ehci->frindex = new_frindex; -} - -static int usb_ehci_post_load(void *opaque, int version_id) -{ - EHCIState *s = opaque; - int i; - - for (i = 0; i < NB_PORTS; i++) { - USBPort *companion = s->companion_ports[i]; - if (companion == NULL) { - continue; - } - if (s->portsc[i] & PORTSC_POWNER) { - companion->dev = s->ports[i].dev; - } else { - companion->dev = NULL; - } - } - - return 0; -} - -static void usb_ehci_vm_state_change(void *opaque, int running, RunState state) -{ - EHCIState *ehci = opaque; - - /* - * We don't migrate the EHCIQueue-s, instead we rebuild them for the - * schedule in guest memory. We must do the rebuilt ASAP, so that - * USB-devices which have async handled packages have a packet in the - * ep queue to match the completion with. - */ - if (state == RUN_STATE_RUNNING) { - ehci_advance_async_state(ehci); - } - - /* - * The schedule rebuilt from guest memory could cause the migration dest - * to miss a QH unlink, and fail to cancel packets, since the unlinked QH - * will never have existed on the destination. Therefor we must flush the - * async schedule on savevm to catch any not yet noticed unlinks. - */ - if (state == RUN_STATE_SAVE_VM) { - ehci_advance_async_state(ehci); - ehci_queues_rip_unseen(ehci, 1); - } -} - -const VMStateDescription vmstate_ehci = { - .name = "ehci-core", - .version_id = 2, - .minimum_version_id = 1, - .pre_save = usb_ehci_pre_save, - .post_load = usb_ehci_post_load, - .fields = (VMStateField[]) { - /* mmio registers */ - VMSTATE_UINT32(usbcmd, EHCIState), - VMSTATE_UINT32(usbsts, EHCIState), - VMSTATE_UINT32_V(usbsts_pending, EHCIState, 2), - VMSTATE_UINT32_V(usbsts_frindex, EHCIState, 2), - VMSTATE_UINT32(usbintr, EHCIState), - VMSTATE_UINT32(frindex, EHCIState), - VMSTATE_UINT32(ctrldssegment, EHCIState), - VMSTATE_UINT32(periodiclistbase, EHCIState), - VMSTATE_UINT32(asynclistaddr, EHCIState), - VMSTATE_UINT32(configflag, EHCIState), - VMSTATE_UINT32(portsc[0], EHCIState), - VMSTATE_UINT32(portsc[1], EHCIState), - VMSTATE_UINT32(portsc[2], EHCIState), - VMSTATE_UINT32(portsc[3], EHCIState), - VMSTATE_UINT32(portsc[4], EHCIState), - VMSTATE_UINT32(portsc[5], EHCIState), - /* frame timer */ - VMSTATE_TIMER_PTR(frame_timer, EHCIState), - VMSTATE_UINT64(last_run_ns, EHCIState), - VMSTATE_UINT32(async_stepdown, EHCIState), - /* schedule state */ - VMSTATE_UINT32(astate, EHCIState), - VMSTATE_UINT32(pstate, EHCIState), - VMSTATE_UINT32(a_fetch_addr, EHCIState), - VMSTATE_UINT32(p_fetch_addr, EHCIState), - VMSTATE_END_OF_LIST() - } -}; - -void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) -{ - int i; - - if (s->portnr > NB_PORTS) { - error_setg(errp, "Too many ports! Max. port number is %d.", - NB_PORTS); - return; - } - - usb_bus_new(&s->bus, sizeof(s->bus), s->companion_enable ? - &ehci_bus_ops_companion : &ehci_bus_ops_standalone, dev); - for (i = 0; i < s->portnr; i++) { - usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops, - USB_SPEED_MASK_HIGH); - s->ports[i].dev = 0; - } - - s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ehci_frame_timer, s); - s->async_bh = qemu_bh_new(ehci_frame_timer, s); - s->device = dev; - - s->vmstate = qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); -} - -void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp) -{ - trace_usb_ehci_unrealize(); - - if (s->frame_timer) { - timer_del(s->frame_timer); - timer_free(s->frame_timer); - s->frame_timer = NULL; - } - if (s->async_bh) { - qemu_bh_delete(s->async_bh); - } - - ehci_queues_rip_all(s, 0); - ehci_queues_rip_all(s, 1); - - memory_region_del_subregion(&s->mem, &s->mem_caps); - memory_region_del_subregion(&s->mem, &s->mem_opreg); - memory_region_del_subregion(&s->mem, &s->mem_ports); - - usb_bus_release(&s->bus); - - if (s->vmstate) { - qemu_del_vm_change_state_handler(s->vmstate); - } -} - -void usb_ehci_init(EHCIState *s, DeviceState *dev) -{ - /* 2.2 host controller interface version */ - s->caps[0x00] = (uint8_t)(s->opregbase - s->capsbase); - s->caps[0x01] = 0x00; - s->caps[0x02] = 0x00; - s->caps[0x03] = 0x01; /* HC version */ - s->caps[0x04] = s->portnr; /* Number of downstream ports */ - s->caps[0x05] = 0x00; /* No companion ports at present */ - s->caps[0x06] = 0x00; - s->caps[0x07] = 0x00; - s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */ - s->caps[0x0a] = 0x00; - s->caps[0x0b] = 0x00; - - QTAILQ_INIT(&s->aqueues); - QTAILQ_INIT(&s->pqueues); - usb_packet_init(&s->ipacket); - - memory_region_init(&s->mem, OBJECT(dev), "ehci", MMIO_SIZE); - memory_region_init_io(&s->mem_caps, OBJECT(dev), &ehci_mmio_caps_ops, s, - "capabilities", CAPA_SIZE); - memory_region_init_io(&s->mem_opreg, OBJECT(dev), &ehci_mmio_opreg_ops, s, - "operational", s->portscbase); - memory_region_init_io(&s->mem_ports, OBJECT(dev), &ehci_mmio_port_ops, s, - "ports", 4 * s->portnr); - - memory_region_add_subregion(&s->mem, s->capsbase, &s->mem_caps); - memory_region_add_subregion(&s->mem, s->opregbase, &s->mem_opreg); - memory_region_add_subregion(&s->mem, s->opregbase + s->portscbase, - &s->mem_ports); -} - -/* - * vim: expandtab ts=4 - */ diff --git a/qemu/hw/usb/hcd-ehci.h b/qemu/hw/usb/hcd-ehci.h deleted file mode 100644 index 30218423c..000000000 --- a/qemu/hw/usb/hcd-ehci.h +++ /dev/null @@ -1,383 +0,0 @@ -/* - * QEMU USB EHCI Emulation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or(at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#ifndef HW_USB_EHCI_H -#define HW_USB_EHCI_H 1 - -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" -#include "hw/pci/pci.h" -#include "hw/sysbus.h" - -#ifndef EHCI_DEBUG -#define EHCI_DEBUG 0 -#endif - -#if EHCI_DEBUG -#define DPRINTF printf -#else -#define DPRINTF(...) -#endif - -#define MMIO_SIZE 0x1000 -#define CAPA_SIZE 0x10 - -#define NB_PORTS 6 /* Max. Number of downstream ports */ - -typedef struct EHCIPacket EHCIPacket; -typedef struct EHCIQueue EHCIQueue; -typedef struct EHCIState EHCIState; - -/* EHCI spec version 1.0 Section 3.3 - */ -typedef struct EHCIitd { - uint32_t next; - - uint32_t transact[8]; -#define ITD_XACT_ACTIVE (1 << 31) -#define ITD_XACT_DBERROR (1 << 30) -#define ITD_XACT_BABBLE (1 << 29) -#define ITD_XACT_XACTERR (1 << 28) -#define ITD_XACT_LENGTH_MASK 0x0fff0000 -#define ITD_XACT_LENGTH_SH 16 -#define ITD_XACT_IOC (1 << 15) -#define ITD_XACT_PGSEL_MASK 0x00007000 -#define ITD_XACT_PGSEL_SH 12 -#define ITD_XACT_OFFSET_MASK 0x00000fff - - uint32_t bufptr[7]; -#define ITD_BUFPTR_MASK 0xfffff000 -#define ITD_BUFPTR_SH 12 -#define ITD_BUFPTR_EP_MASK 0x00000f00 -#define ITD_BUFPTR_EP_SH 8 -#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f -#define ITD_BUFPTR_DEVADDR_SH 0 -#define ITD_BUFPTR_DIRECTION (1 << 11) -#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff -#define ITD_BUFPTR_MAXPKT_SH 0 -#define ITD_BUFPTR_MULT_MASK 0x00000003 -#define ITD_BUFPTR_MULT_SH 0 -} EHCIitd; - -/* EHCI spec version 1.0 Section 3.4 - */ -typedef struct EHCIsitd { - uint32_t next; /* Standard next link pointer */ - uint32_t epchar; -#define SITD_EPCHAR_IO (1 << 31) -#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000 -#define SITD_EPCHAR_PORTNUM_SH 24 -#define SITD_EPCHAR_HUBADD_MASK 0x007f0000 -#define SITD_EPCHAR_HUBADDR_SH 16 -#define SITD_EPCHAR_EPNUM_MASK 0x00000f00 -#define SITD_EPCHAR_EPNUM_SH 8 -#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f - - uint32_t uframe; -#define SITD_UFRAME_CMASK_MASK 0x0000ff00 -#define SITD_UFRAME_CMASK_SH 8 -#define SITD_UFRAME_SMASK_MASK 0x000000ff - - uint32_t results; -#define SITD_RESULTS_IOC (1 << 31) -#define SITD_RESULTS_PGSEL (1 << 30) -#define SITD_RESULTS_TBYTES_MASK 0x03ff0000 -#define SITD_RESULTS_TYBYTES_SH 16 -#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00 -#define SITD_RESULTS_CPROGMASK_SH 8 -#define SITD_RESULTS_ACTIVE (1 << 7) -#define SITD_RESULTS_ERR (1 << 6) -#define SITD_RESULTS_DBERR (1 << 5) -#define SITD_RESULTS_BABBLE (1 << 4) -#define SITD_RESULTS_XACTERR (1 << 3) -#define SITD_RESULTS_MISSEDUF (1 << 2) -#define SITD_RESULTS_SPLITXSTATE (1 << 1) - - uint32_t bufptr[2]; -#define SITD_BUFPTR_MASK 0xfffff000 -#define SITD_BUFPTR_CURROFF_MASK 0x00000fff -#define SITD_BUFPTR_TPOS_MASK 0x00000018 -#define SITD_BUFPTR_TPOS_SH 3 -#define SITD_BUFPTR_TCNT_MASK 0x00000007 - - uint32_t backptr; /* Standard next link pointer */ -} EHCIsitd; - -/* EHCI spec version 1.0 Section 3.5 - */ -typedef struct EHCIqtd { - uint32_t next; /* Standard next link pointer */ - uint32_t altnext; /* Standard next link pointer */ - uint32_t token; -#define QTD_TOKEN_DTOGGLE (1 << 31) -#define QTD_TOKEN_TBYTES_MASK 0x7fff0000 -#define QTD_TOKEN_TBYTES_SH 16 -#define QTD_TOKEN_IOC (1 << 15) -#define QTD_TOKEN_CPAGE_MASK 0x00007000 -#define QTD_TOKEN_CPAGE_SH 12 -#define QTD_TOKEN_CERR_MASK 0x00000c00 -#define QTD_TOKEN_CERR_SH 10 -#define QTD_TOKEN_PID_MASK 0x00000300 -#define QTD_TOKEN_PID_SH 8 -#define QTD_TOKEN_ACTIVE (1 << 7) -#define QTD_TOKEN_HALT (1 << 6) -#define QTD_TOKEN_DBERR (1 << 5) -#define QTD_TOKEN_BABBLE (1 << 4) -#define QTD_TOKEN_XACTERR (1 << 3) -#define QTD_TOKEN_MISSEDUF (1 << 2) -#define QTD_TOKEN_SPLITXSTATE (1 << 1) -#define QTD_TOKEN_PING (1 << 0) - - uint32_t bufptr[5]; /* Standard buffer pointer */ -#define QTD_BUFPTR_MASK 0xfffff000 -#define QTD_BUFPTR_SH 12 -} EHCIqtd; - -/* EHCI spec version 1.0 Section 3.6 - */ -typedef struct EHCIqh { - uint32_t next; /* Standard next link pointer */ - - /* endpoint characteristics */ - uint32_t epchar; -#define QH_EPCHAR_RL_MASK 0xf0000000 -#define QH_EPCHAR_RL_SH 28 -#define QH_EPCHAR_C (1 << 27) -#define QH_EPCHAR_MPLEN_MASK 0x07FF0000 -#define QH_EPCHAR_MPLEN_SH 16 -#define QH_EPCHAR_H (1 << 15) -#define QH_EPCHAR_DTC (1 << 14) -#define QH_EPCHAR_EPS_MASK 0x00003000 -#define QH_EPCHAR_EPS_SH 12 -#define EHCI_QH_EPS_FULL 0 -#define EHCI_QH_EPS_LOW 1 -#define EHCI_QH_EPS_HIGH 2 -#define EHCI_QH_EPS_RESERVED 3 - -#define QH_EPCHAR_EP_MASK 0x00000f00 -#define QH_EPCHAR_EP_SH 8 -#define QH_EPCHAR_I (1 << 7) -#define QH_EPCHAR_DEVADDR_MASK 0x0000007f -#define QH_EPCHAR_DEVADDR_SH 0 - - /* endpoint capabilities */ - uint32_t epcap; -#define QH_EPCAP_MULT_MASK 0xc0000000 -#define QH_EPCAP_MULT_SH 30 -#define QH_EPCAP_PORTNUM_MASK 0x3f800000 -#define QH_EPCAP_PORTNUM_SH 23 -#define QH_EPCAP_HUBADDR_MASK 0x007f0000 -#define QH_EPCAP_HUBADDR_SH 16 -#define QH_EPCAP_CMASK_MASK 0x0000ff00 -#define QH_EPCAP_CMASK_SH 8 -#define QH_EPCAP_SMASK_MASK 0x000000ff -#define QH_EPCAP_SMASK_SH 0 - - uint32_t current_qtd; /* Standard next link pointer */ - uint32_t next_qtd; /* Standard next link pointer */ - uint32_t altnext_qtd; -#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e -#define QH_ALTNEXT_NAKCNT_SH 1 - - uint32_t token; /* Same as QTD token */ - uint32_t bufptr[5]; /* Standard buffer pointer */ -#define BUFPTR_CPROGMASK_MASK 0x000000ff -#define BUFPTR_FRAMETAG_MASK 0x0000001f -#define BUFPTR_SBYTES_MASK 0x00000fe0 -#define BUFPTR_SBYTES_SH 5 -} EHCIqh; - -/* EHCI spec version 1.0 Section 3.7 - */ -typedef struct EHCIfstn { - uint32_t next; /* Standard next link pointer */ - uint32_t backptr; /* Standard next link pointer */ -} EHCIfstn; - -enum async_state { - EHCI_ASYNC_NONE = 0, - EHCI_ASYNC_INITIALIZED, - EHCI_ASYNC_INFLIGHT, - EHCI_ASYNC_FINISHED, -}; - -struct EHCIPacket { - EHCIQueue *queue; - QTAILQ_ENTRY(EHCIPacket) next; - - EHCIqtd qtd; /* copy of current QTD (being worked on) */ - uint32_t qtdaddr; /* address QTD read from */ - - USBPacket packet; - QEMUSGList sgl; - int pid; - enum async_state async; -}; - -struct EHCIQueue { - EHCIState *ehci; - QTAILQ_ENTRY(EHCIQueue) next; - uint32_t seen; - uint64_t ts; - int async; - int transact_ctr; - - /* cached data from guest - needs to be flushed - * when guest removes an entry (doorbell, handshake sequence) - */ - EHCIqh qh; /* copy of current QH (being worked on) */ - uint32_t qhaddr; /* address QH read from */ - uint32_t qtdaddr; /* address QTD read from */ - int last_pid; /* pid of last packet executed */ - USBDevice *dev; - QTAILQ_HEAD(pkts_head, EHCIPacket) packets; -}; - -typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; - -struct EHCIState { - USBBus bus; - DeviceState *device; - qemu_irq irq; - MemoryRegion mem; - AddressSpace *as; - MemoryRegion mem_caps; - MemoryRegion mem_opreg; - MemoryRegion mem_ports; - int companion_count; - bool companion_enable; - uint16_t capsbase; - uint16_t opregbase; - uint16_t portscbase; - uint16_t portnr; - - /* properties */ - uint32_t maxframes; - - /* - * EHCI spec version 1.0 Section 2.3 - * Host Controller Operational Registers - */ - uint8_t caps[CAPA_SIZE]; - union { - uint32_t opreg[0x44/sizeof(uint32_t)]; - struct { - uint32_t usbcmd; - uint32_t usbsts; - uint32_t usbintr; - uint32_t frindex; - uint32_t ctrldssegment; - uint32_t periodiclistbase; - uint32_t asynclistaddr; - uint32_t notused[9]; - uint32_t configflag; - }; - }; - uint32_t portsc[NB_PORTS]; - - /* - * Internal states, shadow registers, etc - */ - QEMUTimer *frame_timer; - QEMUBH *async_bh; - uint32_t astate; /* Current state in asynchronous schedule */ - uint32_t pstate; /* Current state in periodic schedule */ - USBPort ports[NB_PORTS]; - USBPort *companion_ports[NB_PORTS]; - uint32_t usbsts_pending; - uint32_t usbsts_frindex; - EHCIQueueHead aqueues; - EHCIQueueHead pqueues; - - /* which address to look at next */ - uint32_t a_fetch_addr; - uint32_t p_fetch_addr; - - USBPacket ipacket; - QEMUSGList isgl; - - uint64_t last_run_ns; - uint32_t async_stepdown; - uint32_t periodic_sched_active; - bool int_req_by_async; - VMChangeStateEntry *vmstate; -}; - -extern const VMStateDescription vmstate_ehci; - -void usb_ehci_init(EHCIState *s, DeviceState *dev); -void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp); -void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp); -void ehci_reset(void *opaque); - -#define TYPE_PCI_EHCI "pci-ehci-usb" -#define PCI_EHCI(obj) OBJECT_CHECK(EHCIPCIState, (obj), TYPE_PCI_EHCI) - -typedef struct EHCIPCIState { - /*< private >*/ - PCIDevice pcidev; - /*< public >*/ - - EHCIState ehci; -} EHCIPCIState; - - -#define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb" -#define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" -#define TYPE_TEGRA2_EHCI "tegra2-ehci-usb" -#define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb" - -#define SYS_BUS_EHCI(obj) \ - OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI) -#define SYS_BUS_EHCI_CLASS(class) \ - OBJECT_CLASS_CHECK(SysBusEHCIClass, (class), TYPE_SYS_BUS_EHCI) -#define SYS_BUS_EHCI_GET_CLASS(obj) \ - OBJECT_GET_CLASS(SysBusEHCIClass, (obj), TYPE_SYS_BUS_EHCI) - -typedef struct EHCISysBusState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - EHCIState ehci; -} EHCISysBusState; - -typedef struct SysBusEHCIClass { - /*< private >*/ - SysBusDeviceClass parent_class; - /*< public >*/ - - uint16_t capsbase; - uint16_t opregbase; - uint16_t portscbase; - uint16_t portnr; -} SysBusEHCIClass; - -#define FUSBH200_EHCI(obj) \ - OBJECT_CHECK(FUSBH200EHCIState, (obj), TYPE_FUSBH200_EHCI) - -typedef struct FUSBH200EHCIState { - /*< private >*/ - EHCISysBusState parent_obj; - /*< public >*/ - - MemoryRegion mem_vendor; -} FUSBH200EHCIState; - -#endif diff --git a/qemu/hw/usb/hcd-musb.c b/qemu/hw/usb/hcd-musb.c deleted file mode 100644 index 27d9d0bd8..000000000 --- a/qemu/hw/usb/hcd-musb.c +++ /dev/null @@ -1,1553 +0,0 @@ -/* - * "Inventra" High-speed Dual-Role Controller (MUSB-HDRC), Mentor Graphics, - * USB2.0 OTG compliant core used in various chips. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski <andrew@openedhand.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - * - * Only host-mode and non-DMA accesses are currently supported. - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/irq.h" -#include "hw/hw.h" - -/* Common USB registers */ -#define MUSB_HDRC_FADDR 0x00 /* 8-bit */ -#define MUSB_HDRC_POWER 0x01 /* 8-bit */ - -#define MUSB_HDRC_INTRTX 0x02 /* 16-bit */ -#define MUSB_HDRC_INTRRX 0x04 -#define MUSB_HDRC_INTRTXE 0x06 -#define MUSB_HDRC_INTRRXE 0x08 -#define MUSB_HDRC_INTRUSB 0x0a /* 8 bit */ -#define MUSB_HDRC_INTRUSBE 0x0b /* 8 bit */ -#define MUSB_HDRC_FRAME 0x0c /* 16-bit */ -#define MUSB_HDRC_INDEX 0x0e /* 8 bit */ -#define MUSB_HDRC_TESTMODE 0x0f /* 8 bit */ - -/* Per-EP registers in indexed mode */ -#define MUSB_HDRC_EP_IDX 0x10 /* 8-bit */ - -/* EP FIFOs */ -#define MUSB_HDRC_FIFO 0x20 - -/* Additional Control Registers */ -#define MUSB_HDRC_DEVCTL 0x60 /* 8 bit */ - -/* These are indexed */ -#define MUSB_HDRC_TXFIFOSZ 0x62 /* 8 bit (see masks) */ -#define MUSB_HDRC_RXFIFOSZ 0x63 /* 8 bit (see masks) */ -#define MUSB_HDRC_TXFIFOADDR 0x64 /* 16 bit offset shifted right 3 */ -#define MUSB_HDRC_RXFIFOADDR 0x66 /* 16 bit offset shifted right 3 */ - -/* Some more registers */ -#define MUSB_HDRC_VCTRL 0x68 /* 8 bit */ -#define MUSB_HDRC_HWVERS 0x6c /* 8 bit */ - -/* Added in HDRC 1.9(?) & MHDRC 1.4 */ -/* ULPI pass-through */ -#define MUSB_HDRC_ULPI_VBUSCTL 0x70 -#define MUSB_HDRC_ULPI_REGDATA 0x74 -#define MUSB_HDRC_ULPI_REGADDR 0x75 -#define MUSB_HDRC_ULPI_REGCTL 0x76 - -/* Extended config & PHY control */ -#define MUSB_HDRC_ENDCOUNT 0x78 /* 8 bit */ -#define MUSB_HDRC_DMARAMCFG 0x79 /* 8 bit */ -#define MUSB_HDRC_PHYWAIT 0x7a /* 8 bit */ -#define MUSB_HDRC_PHYVPLEN 0x7b /* 8 bit */ -#define MUSB_HDRC_HS_EOF1 0x7c /* 8 bit, units of 546.1 us */ -#define MUSB_HDRC_FS_EOF1 0x7d /* 8 bit, units of 533.3 ns */ -#define MUSB_HDRC_LS_EOF1 0x7e /* 8 bit, units of 1.067 us */ - -/* Per-EP BUSCTL registers */ -#define MUSB_HDRC_BUSCTL 0x80 - -/* Per-EP registers in flat mode */ -#define MUSB_HDRC_EP 0x100 - -/* offsets to registers in flat model */ -#define MUSB_HDRC_TXMAXP 0x00 /* 16 bit apparently */ -#define MUSB_HDRC_TXCSR 0x02 /* 16 bit apparently */ -#define MUSB_HDRC_CSR0 MUSB_HDRC_TXCSR /* re-used for EP0 */ -#define MUSB_HDRC_RXMAXP 0x04 /* 16 bit apparently */ -#define MUSB_HDRC_RXCSR 0x06 /* 16 bit apparently */ -#define MUSB_HDRC_RXCOUNT 0x08 /* 16 bit apparently */ -#define MUSB_HDRC_COUNT0 MUSB_HDRC_RXCOUNT /* re-used for EP0 */ -#define MUSB_HDRC_TXTYPE 0x0a /* 8 bit apparently */ -#define MUSB_HDRC_TYPE0 MUSB_HDRC_TXTYPE /* re-used for EP0 */ -#define MUSB_HDRC_TXINTERVAL 0x0b /* 8 bit apparently */ -#define MUSB_HDRC_NAKLIMIT0 MUSB_HDRC_TXINTERVAL /* re-used for EP0 */ -#define MUSB_HDRC_RXTYPE 0x0c /* 8 bit apparently */ -#define MUSB_HDRC_RXINTERVAL 0x0d /* 8 bit apparently */ -#define MUSB_HDRC_FIFOSIZE 0x0f /* 8 bit apparently */ -#define MUSB_HDRC_CONFIGDATA MGC_O_HDRC_FIFOSIZE /* re-used for EP0 */ - -/* "Bus control" registers */ -#define MUSB_HDRC_TXFUNCADDR 0x00 -#define MUSB_HDRC_TXHUBADDR 0x02 -#define MUSB_HDRC_TXHUBPORT 0x03 - -#define MUSB_HDRC_RXFUNCADDR 0x04 -#define MUSB_HDRC_RXHUBADDR 0x06 -#define MUSB_HDRC_RXHUBPORT 0x07 - -/* - * MUSBHDRC Register bit masks - */ - -/* POWER */ -#define MGC_M_POWER_ISOUPDATE 0x80 -#define MGC_M_POWER_SOFTCONN 0x40 -#define MGC_M_POWER_HSENAB 0x20 -#define MGC_M_POWER_HSMODE 0x10 -#define MGC_M_POWER_RESET 0x08 -#define MGC_M_POWER_RESUME 0x04 -#define MGC_M_POWER_SUSPENDM 0x02 -#define MGC_M_POWER_ENSUSPEND 0x01 - -/* INTRUSB */ -#define MGC_M_INTR_SUSPEND 0x01 -#define MGC_M_INTR_RESUME 0x02 -#define MGC_M_INTR_RESET 0x04 -#define MGC_M_INTR_BABBLE 0x04 -#define MGC_M_INTR_SOF 0x08 -#define MGC_M_INTR_CONNECT 0x10 -#define MGC_M_INTR_DISCONNECT 0x20 -#define MGC_M_INTR_SESSREQ 0x40 -#define MGC_M_INTR_VBUSERROR 0x80 /* FOR SESSION END */ -#define MGC_M_INTR_EP0 0x01 /* FOR EP0 INTERRUPT */ - -/* DEVCTL */ -#define MGC_M_DEVCTL_BDEVICE 0x80 -#define MGC_M_DEVCTL_FSDEV 0x40 -#define MGC_M_DEVCTL_LSDEV 0x20 -#define MGC_M_DEVCTL_VBUS 0x18 -#define MGC_S_DEVCTL_VBUS 3 -#define MGC_M_DEVCTL_HM 0x04 -#define MGC_M_DEVCTL_HR 0x02 -#define MGC_M_DEVCTL_SESSION 0x01 - -/* TESTMODE */ -#define MGC_M_TEST_FORCE_HOST 0x80 -#define MGC_M_TEST_FIFO_ACCESS 0x40 -#define MGC_M_TEST_FORCE_FS 0x20 -#define MGC_M_TEST_FORCE_HS 0x10 -#define MGC_M_TEST_PACKET 0x08 -#define MGC_M_TEST_K 0x04 -#define MGC_M_TEST_J 0x02 -#define MGC_M_TEST_SE0_NAK 0x01 - -/* CSR0 */ -#define MGC_M_CSR0_FLUSHFIFO 0x0100 -#define MGC_M_CSR0_TXPKTRDY 0x0002 -#define MGC_M_CSR0_RXPKTRDY 0x0001 - -/* CSR0 in Peripheral mode */ -#define MGC_M_CSR0_P_SVDSETUPEND 0x0080 -#define MGC_M_CSR0_P_SVDRXPKTRDY 0x0040 -#define MGC_M_CSR0_P_SENDSTALL 0x0020 -#define MGC_M_CSR0_P_SETUPEND 0x0010 -#define MGC_M_CSR0_P_DATAEND 0x0008 -#define MGC_M_CSR0_P_SENTSTALL 0x0004 - -/* CSR0 in Host mode */ -#define MGC_M_CSR0_H_NO_PING 0x0800 -#define MGC_M_CSR0_H_WR_DATATOGGLE 0x0400 /* set to allow setting: */ -#define MGC_M_CSR0_H_DATATOGGLE 0x0200 /* data toggle control */ -#define MGC_M_CSR0_H_NAKTIMEOUT 0x0080 -#define MGC_M_CSR0_H_STATUSPKT 0x0040 -#define MGC_M_CSR0_H_REQPKT 0x0020 -#define MGC_M_CSR0_H_ERROR 0x0010 -#define MGC_M_CSR0_H_SETUPPKT 0x0008 -#define MGC_M_CSR0_H_RXSTALL 0x0004 - -/* CONFIGDATA */ -#define MGC_M_CONFIGDATA_MPRXE 0x80 /* auto bulk pkt combining */ -#define MGC_M_CONFIGDATA_MPTXE 0x40 /* auto bulk pkt splitting */ -#define MGC_M_CONFIGDATA_BIGENDIAN 0x20 -#define MGC_M_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ -#define MGC_M_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ -#define MGC_M_CONFIGDATA_DYNFIFO 0x04 /* dynamic FIFO sizing */ -#define MGC_M_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ -#define MGC_M_CONFIGDATA_UTMIDW 0x01 /* Width, 0 => 8b, 1 => 16b */ - -/* TXCSR in Peripheral and Host mode */ -#define MGC_M_TXCSR_AUTOSET 0x8000 -#define MGC_M_TXCSR_ISO 0x4000 -#define MGC_M_TXCSR_MODE 0x2000 -#define MGC_M_TXCSR_DMAENAB 0x1000 -#define MGC_M_TXCSR_FRCDATATOG 0x0800 -#define MGC_M_TXCSR_DMAMODE 0x0400 -#define MGC_M_TXCSR_CLRDATATOG 0x0040 -#define MGC_M_TXCSR_FLUSHFIFO 0x0008 -#define MGC_M_TXCSR_FIFONOTEMPTY 0x0002 -#define MGC_M_TXCSR_TXPKTRDY 0x0001 - -/* TXCSR in Peripheral mode */ -#define MGC_M_TXCSR_P_INCOMPTX 0x0080 -#define MGC_M_TXCSR_P_SENTSTALL 0x0020 -#define MGC_M_TXCSR_P_SENDSTALL 0x0010 -#define MGC_M_TXCSR_P_UNDERRUN 0x0004 - -/* TXCSR in Host mode */ -#define MGC_M_TXCSR_H_WR_DATATOGGLE 0x0200 -#define MGC_M_TXCSR_H_DATATOGGLE 0x0100 -#define MGC_M_TXCSR_H_NAKTIMEOUT 0x0080 -#define MGC_M_TXCSR_H_RXSTALL 0x0020 -#define MGC_M_TXCSR_H_ERROR 0x0004 - -/* RXCSR in Peripheral and Host mode */ -#define MGC_M_RXCSR_AUTOCLEAR 0x8000 -#define MGC_M_RXCSR_DMAENAB 0x2000 -#define MGC_M_RXCSR_DISNYET 0x1000 -#define MGC_M_RXCSR_DMAMODE 0x0800 -#define MGC_M_RXCSR_INCOMPRX 0x0100 -#define MGC_M_RXCSR_CLRDATATOG 0x0080 -#define MGC_M_RXCSR_FLUSHFIFO 0x0010 -#define MGC_M_RXCSR_DATAERROR 0x0008 -#define MGC_M_RXCSR_FIFOFULL 0x0002 -#define MGC_M_RXCSR_RXPKTRDY 0x0001 - -/* RXCSR in Peripheral mode */ -#define MGC_M_RXCSR_P_ISO 0x4000 -#define MGC_M_RXCSR_P_SENTSTALL 0x0040 -#define MGC_M_RXCSR_P_SENDSTALL 0x0020 -#define MGC_M_RXCSR_P_OVERRUN 0x0004 - -/* RXCSR in Host mode */ -#define MGC_M_RXCSR_H_AUTOREQ 0x4000 -#define MGC_M_RXCSR_H_WR_DATATOGGLE 0x0400 -#define MGC_M_RXCSR_H_DATATOGGLE 0x0200 -#define MGC_M_RXCSR_H_RXSTALL 0x0040 -#define MGC_M_RXCSR_H_REQPKT 0x0020 -#define MGC_M_RXCSR_H_ERROR 0x0004 - -/* HUBADDR */ -#define MGC_M_HUBADDR_MULTI_TT 0x80 - -/* ULPI: Added in HDRC 1.9(?) & MHDRC 1.4 */ -#define MGC_M_ULPI_VBCTL_USEEXTVBUSIND 0x02 -#define MGC_M_ULPI_VBCTL_USEEXTVBUS 0x01 -#define MGC_M_ULPI_REGCTL_INT_ENABLE 0x08 -#define MGC_M_ULPI_REGCTL_READNOTWRITE 0x04 -#define MGC_M_ULPI_REGCTL_COMPLETE 0x02 -#define MGC_M_ULPI_REGCTL_REG 0x01 - -/* #define MUSB_DEBUG */ - -#ifdef MUSB_DEBUG -#define TRACE(fmt,...) fprintf(stderr, "%s@%d: " fmt "\n", __FUNCTION__, \ - __LINE__, ##__VA_ARGS__) -#else -#define TRACE(...) -#endif - - -static void musb_attach(USBPort *port); -static void musb_detach(USBPort *port); -static void musb_child_detach(USBPort *port, USBDevice *child); -static void musb_schedule_cb(USBPort *port, USBPacket *p); -static void musb_async_cancel_device(MUSBState *s, USBDevice *dev); - -static USBPortOps musb_port_ops = { - .attach = musb_attach, - .detach = musb_detach, - .child_detach = musb_child_detach, - .complete = musb_schedule_cb, -}; - -static USBBusOps musb_bus_ops = { -}; - -typedef struct MUSBPacket MUSBPacket; -typedef struct MUSBEndPoint MUSBEndPoint; - -struct MUSBPacket { - USBPacket p; - MUSBEndPoint *ep; - int dir; -}; - -struct MUSBEndPoint { - uint16_t faddr[2]; - uint8_t haddr[2]; - uint8_t hport[2]; - uint16_t csr[2]; - uint16_t maxp[2]; - uint16_t rxcount; - uint8_t type[2]; - uint8_t interval[2]; - uint8_t config; - uint8_t fifosize; - int timeout[2]; /* Always in microframes */ - - uint8_t *buf[2]; - int fifolen[2]; - int fifostart[2]; - int fifoaddr[2]; - MUSBPacket packey[2]; - int status[2]; - int ext_size[2]; - - /* For callbacks' use */ - int epnum; - int interrupt[2]; - MUSBState *musb; - USBCallback *delayed_cb[2]; - QEMUTimer *intv_timer[2]; -}; - -struct MUSBState { - qemu_irq irqs[musb_irq_max]; - USBBus bus; - USBPort port; - - int idx; - uint8_t devctl; - uint8_t power; - uint8_t faddr; - - uint8_t intr; - uint8_t mask; - uint16_t tx_intr; - uint16_t tx_mask; - uint16_t rx_intr; - uint16_t rx_mask; - - int setup_len; - int session; - - uint8_t buf[0x8000]; - - /* Duplicating the world since 2008!... probably we should have 32 - * logical, single endpoints instead. */ - MUSBEndPoint ep[16]; -}; - -void musb_reset(MUSBState *s) -{ - int i; - - s->faddr = 0x00; - s->devctl = 0; - s->power = MGC_M_POWER_HSENAB; - s->tx_intr = 0x0000; - s->rx_intr = 0x0000; - s->tx_mask = 0xffff; - s->rx_mask = 0xffff; - s->intr = 0x00; - s->mask = 0x06; - s->idx = 0; - - s->setup_len = 0; - s->session = 0; - memset(s->buf, 0, sizeof(s->buf)); - - /* TODO: _DW */ - s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO; - for (i = 0; i < 16; i ++) { - s->ep[i].fifosize = 64; - s->ep[i].maxp[0] = 0x40; - s->ep[i].maxp[1] = 0x40; - s->ep[i].musb = s; - s->ep[i].epnum = i; - usb_packet_init(&s->ep[i].packey[0].p); - usb_packet_init(&s->ep[i].packey[1].p); - } -} - -struct MUSBState *musb_init(DeviceState *parent_device, int gpio_base) -{ - MUSBState *s = g_malloc0(sizeof(*s)); - int i; - - for (i = 0; i < musb_irq_max; i++) { - s->irqs[i] = qdev_get_gpio_in(parent_device, gpio_base + i); - } - - musb_reset(s); - - usb_bus_new(&s->bus, sizeof(s->bus), &musb_bus_ops, parent_device); - usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - - return s; -} - -static void musb_vbus_set(MUSBState *s, int level) -{ - if (level) - s->devctl |= 3 << MGC_S_DEVCTL_VBUS; - else - s->devctl &= ~MGC_M_DEVCTL_VBUS; - - qemu_set_irq(s->irqs[musb_set_vbus], level); -} - -static void musb_intr_set(MUSBState *s, int line, int level) -{ - if (!level) { - s->intr &= ~(1 << line); - qemu_irq_lower(s->irqs[line]); - } else if (s->mask & (1 << line)) { - s->intr |= 1 << line; - qemu_irq_raise(s->irqs[line]); - } -} - -static void musb_tx_intr_set(MUSBState *s, int line, int level) -{ - if (!level) { - s->tx_intr &= ~(1 << line); - if (!s->tx_intr) - qemu_irq_lower(s->irqs[musb_irq_tx]); - } else if (s->tx_mask & (1 << line)) { - s->tx_intr |= 1 << line; - qemu_irq_raise(s->irqs[musb_irq_tx]); - } -} - -static void musb_rx_intr_set(MUSBState *s, int line, int level) -{ - if (line) { - if (!level) { - s->rx_intr &= ~(1 << line); - if (!s->rx_intr) - qemu_irq_lower(s->irqs[musb_irq_rx]); - } else if (s->rx_mask & (1 << line)) { - s->rx_intr |= 1 << line; - qemu_irq_raise(s->irqs[musb_irq_rx]); - } - } else - musb_tx_intr_set(s, line, level); -} - -uint32_t musb_core_intr_get(MUSBState *s) -{ - return (s->rx_intr << 15) | s->tx_intr; -} - -void musb_core_intr_clear(MUSBState *s, uint32_t mask) -{ - if (s->rx_intr) { - s->rx_intr &= mask >> 15; - if (!s->rx_intr) - qemu_irq_lower(s->irqs[musb_irq_rx]); - } - - if (s->tx_intr) { - s->tx_intr &= mask & 0xffff; - if (!s->tx_intr) - qemu_irq_lower(s->irqs[musb_irq_tx]); - } -} - -void musb_set_size(MUSBState *s, int epnum, int size, int is_tx) -{ - s->ep[epnum].ext_size[!is_tx] = size; - s->ep[epnum].fifostart[0] = 0; - s->ep[epnum].fifostart[1] = 0; - s->ep[epnum].fifolen[0] = 0; - s->ep[epnum].fifolen[1] = 0; -} - -static void musb_session_update(MUSBState *s, int prev_dev, int prev_sess) -{ - int detect_prev = prev_dev && prev_sess; - int detect = !!s->port.dev && s->session; - - if (detect && !detect_prev) { - /* Let's skip the ID pin sense and VBUS sense formalities and - * and signal a successful SRP directly. This should work at least - * for the Linux driver stack. */ - musb_intr_set(s, musb_irq_connect, 1); - - if (s->port.dev->speed == USB_SPEED_LOW) { - s->devctl &= ~MGC_M_DEVCTL_FSDEV; - s->devctl |= MGC_M_DEVCTL_LSDEV; - } else { - s->devctl |= MGC_M_DEVCTL_FSDEV; - s->devctl &= ~MGC_M_DEVCTL_LSDEV; - } - - /* A-mode? */ - s->devctl &= ~MGC_M_DEVCTL_BDEVICE; - - /* Host-mode bit? */ - s->devctl |= MGC_M_DEVCTL_HM; -#if 1 - musb_vbus_set(s, 1); -#endif - } else if (!detect && detect_prev) { -#if 1 - musb_vbus_set(s, 0); -#endif - } -} - -/* Attach or detach a device on our only port. */ -static void musb_attach(USBPort *port) -{ - MUSBState *s = (MUSBState *) port->opaque; - - musb_intr_set(s, musb_irq_vbus_request, 1); - musb_session_update(s, 0, s->session); -} - -static void musb_detach(USBPort *port) -{ - MUSBState *s = (MUSBState *) port->opaque; - - musb_async_cancel_device(s, port->dev); - - musb_intr_set(s, musb_irq_disconnect, 1); - musb_session_update(s, 1, s->session); -} - -static void musb_child_detach(USBPort *port, USBDevice *child) -{ - MUSBState *s = (MUSBState *) port->opaque; - - musb_async_cancel_device(s, child); -} - -static void musb_cb_tick0(void *opaque) -{ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - - ep->delayed_cb[0](&ep->packey[0].p, opaque); -} - -static void musb_cb_tick1(void *opaque) -{ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - - ep->delayed_cb[1](&ep->packey[1].p, opaque); -} - -#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) - -static void musb_schedule_cb(USBPort *port, USBPacket *packey) -{ - MUSBPacket *p = container_of(packey, MUSBPacket, p); - MUSBEndPoint *ep = p->ep; - int dir = p->dir; - int timeout = 0; - - if (ep->status[dir] == USB_RET_NAK) - timeout = ep->timeout[dir]; - else if (ep->interrupt[dir]) - timeout = 8; - else { - musb_cb_tick(ep); - return; - } - - if (!ep->intv_timer[dir]) - ep->intv_timer[dir] = timer_new_ns(QEMU_CLOCK_VIRTUAL, musb_cb_tick, ep); - - timer_mod(ep->intv_timer[dir], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(timeout, NANOSECONDS_PER_SECOND, 8000)); -} - -static int musb_timeout(int ttype, int speed, int val) -{ -#if 1 - return val << 3; -#endif - - switch (ttype) { - case USB_ENDPOINT_XFER_CONTROL: - if (val < 2) - return 0; - else if (speed == USB_SPEED_HIGH) - return 1 << (val - 1); - else - return 8 << (val - 1); - - case USB_ENDPOINT_XFER_INT: - if (speed == USB_SPEED_HIGH) - if (val < 2) - return 0; - else - return 1 << (val - 1); - else - return val << 3; - - case USB_ENDPOINT_XFER_BULK: - case USB_ENDPOINT_XFER_ISOC: - if (val < 2) - return 0; - else if (speed == USB_SPEED_HIGH) - return 1 << (val - 1); - else - return 8 << (val - 1); - /* TODO: what with low-speed Bulk and Isochronous? */ - } - - hw_error("bad interval\n"); -} - -static void musb_packet(MUSBState *s, MUSBEndPoint *ep, - int epnum, int pid, int len, USBCallback cb, int dir) -{ - USBDevice *dev; - USBEndpoint *uep; - int idx = epnum && dir; - int id; - int ttype; - - /* ep->type[0,1] contains: - * in bits 7:6 the speed (0 - invalid, 1 - high, 2 - full, 3 - slow) - * in bits 5:4 the transfer type (BULK / INT) - * in bits 3:0 the EP num - */ - ttype = epnum ? (ep->type[idx] >> 4) & 3 : 0; - - ep->timeout[dir] = musb_timeout(ttype, - ep->type[idx] >> 6, ep->interval[idx]); - ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; - ep->delayed_cb[dir] = cb; - - /* A wild guess on the FADDR semantics... */ - dev = usb_find_device(&s->port, ep->faddr[idx]); - uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); - id = pid; - if (uep) { - id |= (dev->addr << 16) | (uep->nr << 8); - } - usb_packet_setup(&ep->packey[dir].p, pid, uep, 0, id, false, true); - usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); - ep->packey[dir].ep = ep; - ep->packey[dir].dir = dir; - - usb_handle_packet(dev, &ep->packey[dir].p); - - if (ep->packey[dir].p.status == USB_RET_ASYNC) { - usb_device_flush_ep_queue(dev, uep); - ep->status[dir] = len; - return; - } - - if (ep->packey[dir].p.status == USB_RET_SUCCESS) { - ep->status[dir] = ep->packey[dir].p.actual_length; - } else { - ep->status[dir] = ep->packey[dir].p.status; - } - musb_schedule_cb(&s->port, &ep->packey[dir].p); -} - -static void musb_tx_packet_complete(USBPacket *packey, void *opaque) -{ - /* Unfortunately we can't use packey->devep because that's the remote - * endpoint number and may be different than our local. */ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - int epnum = ep->epnum; - MUSBState *s = ep->musb; - - ep->fifostart[0] = 0; - ep->fifolen[0] = 0; -#ifdef CLEAR_NAK - if (ep->status[0] != USB_RET_NAK) { -#endif - if (epnum) - ep->csr[0] &= ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY); - else - ep->csr[0] &= ~MGC_M_CSR0_TXPKTRDY; -#ifdef CLEAR_NAK - } -#endif - - /* Clear all of the error bits first */ - if (epnum) - ep->csr[0] &= ~(MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_H_RXSTALL | - MGC_M_TXCSR_H_NAKTIMEOUT); - else - ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | - MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); - - if (ep->status[0] == USB_RET_STALL) { - /* Command not supported by target! */ - ep->status[0] = 0; - - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_H_RXSTALL; - else - ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; - } - - if (ep->status[0] == USB_RET_NAK) { - ep->status[0] = 0; - - /* NAK timeouts are only generated in Bulk transfers and - * Data-errors in Isochronous. */ - if (ep->interrupt[0]) { - return; - } - - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_H_NAKTIMEOUT; - else - ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; - } - - if (ep->status[0] < 0) { - if (ep->status[0] == USB_RET_BABBLE) - musb_intr_set(s, musb_irq_rst_babble, 1); - - /* Pretend we've tried three times already and failed (in - * case of USB_TOKEN_SETUP). */ - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_H_ERROR; - else - ep->csr[0] |= MGC_M_CSR0_H_ERROR; - - musb_tx_intr_set(s, epnum, 1); - return; - } - /* TODO: check len for over/underruns of an OUT packet? */ - -#ifdef SETUPLEN_HACK - if (!epnum && ep->packey[0].pid == USB_TOKEN_SETUP) - s->setup_len = ep->packey[0].data[6]; -#endif - - /* In DMA mode: if no error, assert DMA request for this EP, - * and skip the interrupt. */ - musb_tx_intr_set(s, epnum, 1); -} - -static void musb_rx_packet_complete(USBPacket *packey, void *opaque) -{ - /* Unfortunately we can't use packey->devep because that's the remote - * endpoint number and may be different than our local. */ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - int epnum = ep->epnum; - MUSBState *s = ep->musb; - - ep->fifostart[1] = 0; - ep->fifolen[1] = 0; - -#ifdef CLEAR_NAK - if (ep->status[1] != USB_RET_NAK) { -#endif - ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; - if (!epnum) - ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT; -#ifdef CLEAR_NAK - } -#endif - - /* Clear all of the imaginable error bits first */ - ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | - MGC_M_RXCSR_DATAERROR); - if (!epnum) - ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | - MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); - - if (ep->status[1] == USB_RET_STALL) { - ep->status[1] = 0; - - ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; - } - - if (ep->status[1] == USB_RET_NAK) { - ep->status[1] = 0; - - /* NAK timeouts are only generated in Bulk transfers and - * Data-errors in Isochronous. */ - if (ep->interrupt[1]) { - musb_packet(s, ep, epnum, USB_TOKEN_IN, - packey->iov.size, musb_rx_packet_complete, 1); - return; - } - - ep->csr[1] |= MGC_M_RXCSR_DATAERROR; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; - } - - if (ep->status[1] < 0) { - if (ep->status[1] == USB_RET_BABBLE) { - musb_intr_set(s, musb_irq_rst_babble, 1); - return; - } - - /* Pretend we've tried three times already and failed (in - * case of a control transfer). */ - ep->csr[1] |= MGC_M_RXCSR_H_ERROR; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_H_ERROR; - - musb_rx_intr_set(s, epnum, 1); - return; - } - /* TODO: check len for over/underruns of an OUT packet? */ - /* TODO: perhaps make use of e->ext_size[1] here. */ - - if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { - ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; - - ep->rxcount = ep->status[1]; /* XXX: MIN(packey->len, ep->maxp[1]); */ - /* In DMA mode: assert DMA request for this EP */ - } - - /* Only if DMA has not been asserted */ - musb_rx_intr_set(s, epnum, 1); -} - -static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) -{ - int ep, dir; - - for (ep = 0; ep < 16; ep++) { - for (dir = 0; dir < 2; dir++) { - if (!usb_packet_is_inflight(&s->ep[ep].packey[dir].p) || - s->ep[ep].packey[dir].p.ep->dev != dev) { - continue; - } - usb_cancel_packet(&s->ep[ep].packey[dir].p); - /* status updates needed here? */ - } - } -} - -static void musb_tx_rdy(MUSBState *s, int epnum) -{ - MUSBEndPoint *ep = s->ep + epnum; - int pid; - int total, valid = 0; - TRACE("start %d, len %d", ep->fifostart[0], ep->fifolen[0] ); - ep->fifostart[0] += ep->fifolen[0]; - ep->fifolen[0] = 0; - - /* XXX: how's the total size of the packet retrieved exactly in - * the generic case? */ - total = ep->maxp[0] & 0x3ff; - - if (ep->ext_size[0]) { - total = ep->ext_size[0]; - ep->ext_size[0] = 0; - valid = 1; - } - - /* If the packet is not fully ready yet, wait for a next segment. */ - if (epnum && (ep->fifostart[0]) < total) - return; - - if (!valid) - total = ep->fifostart[0]; - - pid = USB_TOKEN_OUT; - if (!epnum && (ep->csr[0] & MGC_M_CSR0_H_SETUPPKT)) { - pid = USB_TOKEN_SETUP; - if (total != 8) { - TRACE("illegal SETUPPKT length of %i bytes", total); - } - /* Controller should retry SETUP packets three times on errors - * but it doesn't make sense for us to do that. */ - } - - musb_packet(s, ep, epnum, pid, total, musb_tx_packet_complete, 0); -} - -static void musb_rx_req(MUSBState *s, int epnum) -{ - MUSBEndPoint *ep = s->ep + epnum; - int total; - - /* If we already have a packet, which didn't fit into the - * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ - if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && - (ep->fifostart[1]) + ep->rxcount < - ep->packey[1].p.iov.size) { - TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); - ep->fifostart[1] += ep->rxcount; - ep->fifolen[1] = 0; - - ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]), - ep->maxp[1]); - - ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; - if (!epnum) - ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT; - - /* Clear all of the error bits first */ - ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | - MGC_M_RXCSR_DATAERROR); - if (!epnum) - ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | - MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); - - ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; - musb_rx_intr_set(s, epnum, 1); - return; - } - - /* The driver sets maxp[1] to 64 or less because it knows the hardware - * FIFO is this deep. Bigger packets get split in - * usb_generic_handle_packet but we can also do the splitting locally - * for performance. It turns out we can also have a bigger FIFO and - * ignore the limit set in ep->maxp[1]. The Linux MUSB driver deals - * OK with single packets of even 32KB and we avoid splitting, however - * usb_msd.c sometimes sends a packet bigger than what Linux expects - * (e.g. 8192 bytes instead of 4096) and we get an OVERRUN. Splitting - * hides this overrun from Linux. Up to 4096 everything is fine - * though. Currently this is disabled. - * - * XXX: mind ep->fifosize. */ - total = MIN(ep->maxp[1] & 0x3ff, sizeof(s->buf)); - -#ifdef SETUPLEN_HACK - /* Why should *we* do that instead of Linux? */ - if (!epnum) { - if (ep->packey[0].p.devaddr == 2) { - total = MIN(s->setup_len, 8); - } else { - total = MIN(s->setup_len, 64); - } - s->setup_len -= total; - } -#endif - - musb_packet(s, ep, epnum, USB_TOKEN_IN, total, musb_rx_packet_complete, 1); -} - -static uint8_t musb_read_fifo(MUSBEndPoint *ep) -{ - uint8_t value; - if (ep->fifolen[1] >= 64) { - /* We have a FIFO underrun */ - TRACE("EP%d FIFO is now empty, stop reading", ep->epnum); - return 0x00000000; - } - /* In DMA mode clear RXPKTRDY and set REQPKT automatically - * (if AUTOREQ is set) */ - - ep->csr[1] &= ~MGC_M_RXCSR_FIFOFULL; - value=ep->buf[1][ep->fifostart[1] + ep->fifolen[1] ++]; - TRACE("EP%d 0x%02x, %d", ep->epnum, value, ep->fifolen[1] ); - return value; -} - -static void musb_write_fifo(MUSBEndPoint *ep, uint8_t value) -{ - TRACE("EP%d = %02x", ep->epnum, value); - if (ep->fifolen[0] >= 64) { - /* We have a FIFO overrun */ - TRACE("EP%d FIFO exceeded 64 bytes, stop feeding data", ep->epnum); - return; - } - - ep->buf[0][ep->fifostart[0] + ep->fifolen[0] ++] = value; - ep->csr[0] |= MGC_M_TXCSR_FIFONOTEMPTY; -} - -static void musb_ep_frame_cancel(MUSBEndPoint *ep, int dir) -{ - if (ep->intv_timer[dir]) - timer_del(ep->intv_timer[dir]); -} - -/* Bus control */ -static uint8_t musb_busctl_readb(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - /* For USB2.0 HS hubs only */ - case MUSB_HDRC_TXHUBADDR: - return s->ep[ep].haddr[0]; - case MUSB_HDRC_TXHUBPORT: - return s->ep[ep].hport[0]; - case MUSB_HDRC_RXHUBADDR: - return s->ep[ep].haddr[1]; - case MUSB_HDRC_RXHUBPORT: - return s->ep[ep].hport[1]; - - default: - TRACE("unknown register 0x%02x", addr); - return 0x00; - }; -} - -static void musb_busctl_writeb(void *opaque, int ep, int addr, uint8_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXFUNCADDR: - s->ep[ep].faddr[0] = value; - break; - case MUSB_HDRC_RXFUNCADDR: - s->ep[ep].faddr[1] = value; - break; - case MUSB_HDRC_TXHUBADDR: - s->ep[ep].haddr[0] = value; - break; - case MUSB_HDRC_TXHUBPORT: - s->ep[ep].hport[0] = value; - break; - case MUSB_HDRC_RXHUBADDR: - s->ep[ep].haddr[1] = value; - break; - case MUSB_HDRC_RXHUBPORT: - s->ep[ep].hport[1] = value; - break; - - default: - TRACE("unknown register 0x%02x", addr); - break; - }; -} - -static uint16_t musb_busctl_readh(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXFUNCADDR: - return s->ep[ep].faddr[0]; - case MUSB_HDRC_RXFUNCADDR: - return s->ep[ep].faddr[1]; - - default: - return musb_busctl_readb(s, ep, addr) | - (musb_busctl_readb(s, ep, addr | 1) << 8); - }; -} - -static void musb_busctl_writeh(void *opaque, int ep, int addr, uint16_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXFUNCADDR: - s->ep[ep].faddr[0] = value; - break; - case MUSB_HDRC_RXFUNCADDR: - s->ep[ep].faddr[1] = value; - break; - - default: - musb_busctl_writeb(s, ep, addr, value & 0xff); - musb_busctl_writeb(s, ep, addr | 1, value >> 8); - }; -} - -/* Endpoint control */ -static uint8_t musb_ep_readb(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXTYPE: - return s->ep[ep].type[0]; - case MUSB_HDRC_TXINTERVAL: - return s->ep[ep].interval[0]; - case MUSB_HDRC_RXTYPE: - return s->ep[ep].type[1]; - case MUSB_HDRC_RXINTERVAL: - return s->ep[ep].interval[1]; - case (MUSB_HDRC_FIFOSIZE & ~1): - return 0x00; - case MUSB_HDRC_FIFOSIZE: - return ep ? s->ep[ep].fifosize : s->ep[ep].config; - case MUSB_HDRC_RXCOUNT: - return s->ep[ep].rxcount; - - default: - TRACE("unknown register 0x%02x", addr); - return 0x00; - }; -} - -static void musb_ep_writeb(void *opaque, int ep, int addr, uint8_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXTYPE: - s->ep[ep].type[0] = value; - break; - case MUSB_HDRC_TXINTERVAL: - s->ep[ep].interval[0] = value; - musb_ep_frame_cancel(&s->ep[ep], 0); - break; - case MUSB_HDRC_RXTYPE: - s->ep[ep].type[1] = value; - break; - case MUSB_HDRC_RXINTERVAL: - s->ep[ep].interval[1] = value; - musb_ep_frame_cancel(&s->ep[ep], 1); - break; - case (MUSB_HDRC_FIFOSIZE & ~1): - break; - case MUSB_HDRC_FIFOSIZE: - TRACE("somebody messes with fifosize (now %i bytes)", value); - s->ep[ep].fifosize = value; - break; - default: - TRACE("unknown register 0x%02x", addr); - break; - }; -} - -static uint16_t musb_ep_readh(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - uint16_t ret; - - switch (addr) { - case MUSB_HDRC_TXMAXP: - return s->ep[ep].maxp[0]; - case MUSB_HDRC_TXCSR: - return s->ep[ep].csr[0]; - case MUSB_HDRC_RXMAXP: - return s->ep[ep].maxp[1]; - case MUSB_HDRC_RXCSR: - ret = s->ep[ep].csr[1]; - - /* TODO: This and other bits probably depend on - * ep->csr[1] & MGC_M_RXCSR_AUTOCLEAR. */ - if (s->ep[ep].csr[1] & MGC_M_RXCSR_AUTOCLEAR) - s->ep[ep].csr[1] &= ~MGC_M_RXCSR_RXPKTRDY; - - return ret; - case MUSB_HDRC_RXCOUNT: - return s->ep[ep].rxcount; - - default: - return musb_ep_readb(s, ep, addr) | - (musb_ep_readb(s, ep, addr | 1) << 8); - }; -} - -static void musb_ep_writeh(void *opaque, int ep, int addr, uint16_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXMAXP: - s->ep[ep].maxp[0] = value; - break; - case MUSB_HDRC_TXCSR: - if (ep) { - s->ep[ep].csr[0] &= value & 0xa6; - s->ep[ep].csr[0] |= value & 0xff59; - } else { - s->ep[ep].csr[0] &= value & 0x85; - s->ep[ep].csr[0] |= value & 0xf7a; - } - - musb_ep_frame_cancel(&s->ep[ep], 0); - - if ((ep && (value & MGC_M_TXCSR_FLUSHFIFO)) || - (!ep && (value & MGC_M_CSR0_FLUSHFIFO))) { - s->ep[ep].fifolen[0] = 0; - s->ep[ep].fifostart[0] = 0; - if (ep) - s->ep[ep].csr[0] &= - ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY); - else - s->ep[ep].csr[0] &= - ~(MGC_M_CSR0_TXPKTRDY | MGC_M_CSR0_RXPKTRDY); - } - if ( - (ep && -#ifdef CLEAR_NAK - (value & MGC_M_TXCSR_TXPKTRDY) && - !(value & MGC_M_TXCSR_H_NAKTIMEOUT)) || -#else - (value & MGC_M_TXCSR_TXPKTRDY)) || -#endif - (!ep && -#ifdef CLEAR_NAK - (value & MGC_M_CSR0_TXPKTRDY) && - !(value & MGC_M_CSR0_H_NAKTIMEOUT))) -#else - (value & MGC_M_CSR0_TXPKTRDY))) -#endif - musb_tx_rdy(s, ep); - if (!ep && - (value & MGC_M_CSR0_H_REQPKT) && -#ifdef CLEAR_NAK - !(value & (MGC_M_CSR0_H_NAKTIMEOUT | - MGC_M_CSR0_RXPKTRDY))) -#else - !(value & MGC_M_CSR0_RXPKTRDY)) -#endif - musb_rx_req(s, ep); - break; - - case MUSB_HDRC_RXMAXP: - s->ep[ep].maxp[1] = value; - break; - case MUSB_HDRC_RXCSR: - /* (DMA mode only) */ - if ( - (value & MGC_M_RXCSR_H_AUTOREQ) && - !(value & MGC_M_RXCSR_RXPKTRDY) && - (s->ep[ep].csr[1] & MGC_M_RXCSR_RXPKTRDY)) - value |= MGC_M_RXCSR_H_REQPKT; - - s->ep[ep].csr[1] &= 0x102 | (value & 0x4d); - s->ep[ep].csr[1] |= value & 0xfeb0; - - musb_ep_frame_cancel(&s->ep[ep], 1); - - if (value & MGC_M_RXCSR_FLUSHFIFO) { - s->ep[ep].fifolen[1] = 0; - s->ep[ep].fifostart[1] = 0; - s->ep[ep].csr[1] &= ~(MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY); - /* If double buffering and we have two packets ready, flush - * only the first one and set up the fifo at the second packet. */ - } -#ifdef CLEAR_NAK - if ((value & MGC_M_RXCSR_H_REQPKT) && !(value & MGC_M_RXCSR_DATAERROR)) -#else - if (value & MGC_M_RXCSR_H_REQPKT) -#endif - musb_rx_req(s, ep); - break; - case MUSB_HDRC_RXCOUNT: - s->ep[ep].rxcount = value; - break; - - default: - musb_ep_writeb(s, ep, addr, value & 0xff); - musb_ep_writeb(s, ep, addr | 1, value >> 8); - }; -} - -/* Generic control */ -static uint32_t musb_readb(void *opaque, hwaddr addr) -{ - MUSBState *s = (MUSBState *) opaque; - int ep, i; - uint8_t ret; - - switch (addr) { - case MUSB_HDRC_FADDR: - return s->faddr; - case MUSB_HDRC_POWER: - return s->power; - case MUSB_HDRC_INTRUSB: - ret = s->intr; - for (i = 0; i < sizeof(ret) * 8; i ++) - if (ret & (1 << i)) - musb_intr_set(s, i, 0); - return ret; - case MUSB_HDRC_INTRUSBE: - return s->mask; - case MUSB_HDRC_INDEX: - return s->idx; - case MUSB_HDRC_TESTMODE: - return 0x00; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - return musb_ep_readb(s, s->idx, addr & 0xf); - - case MUSB_HDRC_DEVCTL: - return s->devctl; - - case MUSB_HDRC_TXFIFOSZ: - case MUSB_HDRC_RXFIFOSZ: - case MUSB_HDRC_VCTRL: - /* TODO */ - return 0x00; - - case MUSB_HDRC_HWVERS: - return (1 << 10) | 400; - - case (MUSB_HDRC_VCTRL | 1): - case (MUSB_HDRC_HWVERS | 1): - case (MUSB_HDRC_DEVCTL | 1): - return 0x00; - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - return musb_busctl_readb(s, ep, addr & 0x7); - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - return musb_ep_readb(s, ep, addr & 0xf); - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - return musb_read_fifo(s->ep + ep); - - default: - TRACE("unknown register 0x%02x", (int) addr); - return 0x00; - }; -} - -static void musb_writeb(void *opaque, hwaddr addr, uint32_t value) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_FADDR: - s->faddr = value & 0x7f; - break; - case MUSB_HDRC_POWER: - s->power = (value & 0xef) | (s->power & 0x10); - /* MGC_M_POWER_RESET is also read-only in Peripheral Mode */ - if ((value & MGC_M_POWER_RESET) && s->port.dev) { - usb_device_reset(s->port.dev); - /* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set. */ - if ((value & MGC_M_POWER_HSENAB) && - s->port.dev->speed == USB_SPEED_HIGH) - s->power |= MGC_M_POWER_HSMODE; /* Success */ - /* Restart frame counting. */ - } - if (value & MGC_M_POWER_SUSPENDM) { - /* When all transfers finish, suspend and if MGC_M_POWER_ENSUSPEND - * is set, also go into low power mode. Frame counting stops. */ - /* XXX: Cleared when the interrupt register is read */ - } - if (value & MGC_M_POWER_RESUME) { - /* Wait 20ms and signal resuming on the bus. Frame counting - * restarts. */ - } - break; - case MUSB_HDRC_INTRUSB: - break; - case MUSB_HDRC_INTRUSBE: - s->mask = value & 0xff; - break; - case MUSB_HDRC_INDEX: - s->idx = value & 0xf; - break; - case MUSB_HDRC_TESTMODE: - break; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - musb_ep_writeb(s, s->idx, addr & 0xf, value); - break; - - case MUSB_HDRC_DEVCTL: - s->session = !!(value & MGC_M_DEVCTL_SESSION); - musb_session_update(s, - !!s->port.dev, - !!(s->devctl & MGC_M_DEVCTL_SESSION)); - - /* It seems this is the only R/W bit in this register? */ - s->devctl &= ~MGC_M_DEVCTL_SESSION; - s->devctl |= value & MGC_M_DEVCTL_SESSION; - break; - - case MUSB_HDRC_TXFIFOSZ: - case MUSB_HDRC_RXFIFOSZ: - case MUSB_HDRC_VCTRL: - /* TODO */ - break; - - case (MUSB_HDRC_VCTRL | 1): - case (MUSB_HDRC_DEVCTL | 1): - break; - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - musb_busctl_writeb(s, ep, addr & 0x7, value); - break; - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - musb_ep_writeb(s, ep, addr & 0xf, value); - break; - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - musb_write_fifo(s->ep + ep, value & 0xff); - break; - - default: - TRACE("unknown register 0x%02x", (int) addr); - break; - }; -} - -static uint32_t musb_readh(void *opaque, hwaddr addr) -{ - MUSBState *s = (MUSBState *) opaque; - int ep, i; - uint16_t ret; - - switch (addr) { - case MUSB_HDRC_INTRTX: - ret = s->tx_intr; - /* Auto clear */ - for (i = 0; i < sizeof(ret) * 8; i ++) - if (ret & (1 << i)) - musb_tx_intr_set(s, i, 0); - return ret; - case MUSB_HDRC_INTRRX: - ret = s->rx_intr; - /* Auto clear */ - for (i = 0; i < sizeof(ret) * 8; i ++) - if (ret & (1 << i)) - musb_rx_intr_set(s, i, 0); - return ret; - case MUSB_HDRC_INTRTXE: - return s->tx_mask; - case MUSB_HDRC_INTRRXE: - return s->rx_mask; - - case MUSB_HDRC_FRAME: - /* TODO */ - return 0x0000; - case MUSB_HDRC_TXFIFOADDR: - return s->ep[s->idx].fifoaddr[0]; - case MUSB_HDRC_RXFIFOADDR: - return s->ep[s->idx].fifoaddr[1]; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - return musb_ep_readh(s, s->idx, addr & 0xf); - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - return musb_busctl_readh(s, ep, addr & 0x7); - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - return musb_ep_readh(s, ep, addr & 0xf); - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - return (musb_read_fifo(s->ep + ep) | musb_read_fifo(s->ep + ep) << 8); - - default: - return musb_readb(s, addr) | (musb_readb(s, addr | 1) << 8); - }; -} - -static void musb_writeh(void *opaque, hwaddr addr, uint32_t value) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_INTRTXE: - s->tx_mask = value; - /* XXX: the masks seem to apply on the raising edge like with - * edge-triggered interrupts, thus no need to update. I may be - * wrong though. */ - break; - case MUSB_HDRC_INTRRXE: - s->rx_mask = value; - break; - - case MUSB_HDRC_FRAME: - /* TODO */ - break; - case MUSB_HDRC_TXFIFOADDR: - s->ep[s->idx].fifoaddr[0] = value; - s->ep[s->idx].buf[0] = - s->buf + ((value << 3) & 0x7ff ); - break; - case MUSB_HDRC_RXFIFOADDR: - s->ep[s->idx].fifoaddr[1] = value; - s->ep[s->idx].buf[1] = - s->buf + ((value << 3) & 0x7ff); - break; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - musb_ep_writeh(s, s->idx, addr & 0xf, value); - break; - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - musb_busctl_writeh(s, ep, addr & 0x7, value); - break; - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - musb_ep_writeh(s, ep, addr & 0xf, value); - break; - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - musb_write_fifo(s->ep + ep, value & 0xff); - musb_write_fifo(s->ep + ep, (value >> 8) & 0xff); - break; - - default: - musb_writeb(s, addr, value & 0xff); - musb_writeb(s, addr | 1, value >> 8); - }; -} - -static uint32_t musb_readw(void *opaque, hwaddr addr) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - return ( musb_read_fifo(s->ep + ep) | - musb_read_fifo(s->ep + ep) << 8 | - musb_read_fifo(s->ep + ep) << 16 | - musb_read_fifo(s->ep + ep) << 24 ); - default: - TRACE("unknown register 0x%02x", (int) addr); - return 0x00000000; - }; -} - -static void musb_writew(void *opaque, hwaddr addr, uint32_t value) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - musb_write_fifo(s->ep + ep, value & 0xff); - musb_write_fifo(s->ep + ep, (value >> 8 ) & 0xff); - musb_write_fifo(s->ep + ep, (value >> 16) & 0xff); - musb_write_fifo(s->ep + ep, (value >> 24) & 0xff); - break; - default: - TRACE("unknown register 0x%02x", (int) addr); - break; - }; -} - -CPUReadMemoryFunc * const musb_read[] = { - musb_readb, - musb_readh, - musb_readw, -}; - -CPUWriteMemoryFunc * const musb_write[] = { - musb_writeb, - musb_writeh, - musb_writew, -}; diff --git a/qemu/hw/usb/hcd-ohci.c b/qemu/hw/usb/hcd-ohci.c deleted file mode 100644 index ffab561cf..000000000 --- a/qemu/hw/usb/hcd-ohci.c +++ /dev/null @@ -1,2164 +0,0 @@ -/* - * QEMU USB OHCI Emulation - * Copyright (c) 2004 Gianni Tedesco - * Copyright (c) 2006 CodeSourcery - * Copyright (c) 2006 Openedhand Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - * - * TODO: - * o Isochronous transfers - * o Allocate bandwidth in frames properly - * o Disable timers when nothing needs to be done, or remove timer usage - * all together. - * o BIOS work to boot from USB storage -*/ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/pci/pci.h" -#include "hw/sysbus.h" -#include "hw/qdev-dma.h" -#include "trace.h" - -/* This causes frames to occur 1000x slower */ -//#define OHCI_TIME_WARP 1 - -/* Number of Downstream Ports on the root hub. */ - -#define OHCI_MAX_PORTS 15 - -static int64_t usb_frame_time; -static int64_t usb_bit_time; - -typedef struct OHCIPort { - USBPort port; - uint32_t ctrl; -} OHCIPort; - -typedef struct { - USBBus bus; - qemu_irq irq; - MemoryRegion mem; - AddressSpace *as; - int num_ports; - const char *name; - - QEMUTimer *eof_timer; - int64_t sof_time; - - /* OHCI state */ - /* Control partition */ - uint32_t ctl, status; - uint32_t intr_status; - uint32_t intr; - - /* memory pointer partition */ - uint32_t hcca; - uint32_t ctrl_head, ctrl_cur; - uint32_t bulk_head, bulk_cur; - uint32_t per_cur; - uint32_t done; - int32_t done_count; - - /* Frame counter partition */ - uint16_t fsmps; - uint8_t fit; - uint16_t fi; - uint8_t frt; - uint16_t frame_number; - uint16_t padding; - uint32_t pstart; - uint32_t lst; - - /* Root Hub partition */ - uint32_t rhdesc_a, rhdesc_b; - uint32_t rhstatus; - OHCIPort rhport[OHCI_MAX_PORTS]; - - /* PXA27x Non-OHCI events */ - uint32_t hstatus; - uint32_t hmask; - uint32_t hreset; - uint32_t htest; - - /* SM501 local memory offset */ - dma_addr_t localmem_base; - - /* Active packets. */ - uint32_t old_ctl; - USBPacket usb_packet; - uint8_t usb_buf[8192]; - uint32_t async_td; - bool async_complete; - -} OHCIState; - -/* Host Controller Communications Area */ -struct ohci_hcca { - uint32_t intr[32]; - uint16_t frame, pad; - uint32_t done; -}; -#define HCCA_WRITEBACK_OFFSET offsetof(struct ohci_hcca, frame) -#define HCCA_WRITEBACK_SIZE 8 /* frame, pad, done */ - -#define ED_WBACK_OFFSET offsetof(struct ohci_ed, head) -#define ED_WBACK_SIZE 4 - -static void ohci_bus_stop(OHCIState *ohci); -static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev); - -/* Bitfields for the first word of an Endpoint Desciptor. */ -#define OHCI_ED_FA_SHIFT 0 -#define OHCI_ED_FA_MASK (0x7f<<OHCI_ED_FA_SHIFT) -#define OHCI_ED_EN_SHIFT 7 -#define OHCI_ED_EN_MASK (0xf<<OHCI_ED_EN_SHIFT) -#define OHCI_ED_D_SHIFT 11 -#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT) -#define OHCI_ED_S (1<<13) -#define OHCI_ED_K (1<<14) -#define OHCI_ED_F (1<<15) -#define OHCI_ED_MPS_SHIFT 16 -#define OHCI_ED_MPS_MASK (0x7ff<<OHCI_ED_MPS_SHIFT) - -/* Flags in the head field of an Endpoint Desciptor. */ -#define OHCI_ED_H 1 -#define OHCI_ED_C 2 - -/* Bitfields for the first word of a Transfer Desciptor. */ -#define OHCI_TD_R (1<<18) -#define OHCI_TD_DP_SHIFT 19 -#define OHCI_TD_DP_MASK (3<<OHCI_TD_DP_SHIFT) -#define OHCI_TD_DI_SHIFT 21 -#define OHCI_TD_DI_MASK (7<<OHCI_TD_DI_SHIFT) -#define OHCI_TD_T0 (1<<24) -#define OHCI_TD_T1 (1<<25) -#define OHCI_TD_EC_SHIFT 26 -#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT) -#define OHCI_TD_CC_SHIFT 28 -#define OHCI_TD_CC_MASK (0xf<<OHCI_TD_CC_SHIFT) - -/* Bitfields for the first word of an Isochronous Transfer Desciptor. */ -/* CC & DI - same as in the General Transfer Desciptor */ -#define OHCI_TD_SF_SHIFT 0 -#define OHCI_TD_SF_MASK (0xffff<<OHCI_TD_SF_SHIFT) -#define OHCI_TD_FC_SHIFT 24 -#define OHCI_TD_FC_MASK (7<<OHCI_TD_FC_SHIFT) - -/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */ -#define OHCI_TD_PSW_CC_SHIFT 12 -#define OHCI_TD_PSW_CC_MASK (0xf<<OHCI_TD_PSW_CC_SHIFT) -#define OHCI_TD_PSW_SIZE_SHIFT 0 -#define OHCI_TD_PSW_SIZE_MASK (0xfff<<OHCI_TD_PSW_SIZE_SHIFT) - -#define OHCI_PAGE_MASK 0xfffff000 -#define OHCI_OFFSET_MASK 0xfff - -#define OHCI_DPTR_MASK 0xfffffff0 - -#define OHCI_BM(val, field) \ - (((val) & OHCI_##field##_MASK) >> OHCI_##field##_SHIFT) - -#define OHCI_SET_BM(val, field, newval) do { \ - val &= ~OHCI_##field##_MASK; \ - val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \ - } while(0) - -/* endpoint descriptor */ -struct ohci_ed { - uint32_t flags; - uint32_t tail; - uint32_t head; - uint32_t next; -}; - -/* General transfer descriptor */ -struct ohci_td { - uint32_t flags; - uint32_t cbp; - uint32_t next; - uint32_t be; -}; - -/* Isochronous transfer descriptor */ -struct ohci_iso_td { - uint32_t flags; - uint32_t bp; - uint32_t next; - uint32_t be; - uint16_t offset[8]; -}; - -#define USB_HZ 12000000 - -/* OHCI Local stuff */ -#define OHCI_CTL_CBSR ((1<<0)|(1<<1)) -#define OHCI_CTL_PLE (1<<2) -#define OHCI_CTL_IE (1<<3) -#define OHCI_CTL_CLE (1<<4) -#define OHCI_CTL_BLE (1<<5) -#define OHCI_CTL_HCFS ((1<<6)|(1<<7)) -#define OHCI_USB_RESET 0x00 -#define OHCI_USB_RESUME 0x40 -#define OHCI_USB_OPERATIONAL 0x80 -#define OHCI_USB_SUSPEND 0xc0 -#define OHCI_CTL_IR (1<<8) -#define OHCI_CTL_RWC (1<<9) -#define OHCI_CTL_RWE (1<<10) - -#define OHCI_STATUS_HCR (1<<0) -#define OHCI_STATUS_CLF (1<<1) -#define OHCI_STATUS_BLF (1<<2) -#define OHCI_STATUS_OCR (1<<3) -#define OHCI_STATUS_SOC ((1<<6)|(1<<7)) - -#define OHCI_INTR_SO (1U<<0) /* Scheduling overrun */ -#define OHCI_INTR_WD (1U<<1) /* HcDoneHead writeback */ -#define OHCI_INTR_SF (1U<<2) /* Start of frame */ -#define OHCI_INTR_RD (1U<<3) /* Resume detect */ -#define OHCI_INTR_UE (1U<<4) /* Unrecoverable error */ -#define OHCI_INTR_FNO (1U<<5) /* Frame number overflow */ -#define OHCI_INTR_RHSC (1U<<6) /* Root hub status change */ -#define OHCI_INTR_OC (1U<<30) /* Ownership change */ -#define OHCI_INTR_MIE (1U<<31) /* Master Interrupt Enable */ - -#define OHCI_HCCA_SIZE 0x100 -#define OHCI_HCCA_MASK 0xffffff00 - -#define OHCI_EDPTR_MASK 0xfffffff0 - -#define OHCI_FMI_FI 0x00003fff -#define OHCI_FMI_FSMPS 0xffff0000 -#define OHCI_FMI_FIT 0x80000000 - -#define OHCI_FR_RT (1U<<31) - -#define OHCI_LS_THRESH 0x628 - -#define OHCI_RHA_RW_MASK 0x00000000 /* Mask of supported features. */ -#define OHCI_RHA_PSM (1<<8) -#define OHCI_RHA_NPS (1<<9) -#define OHCI_RHA_DT (1<<10) -#define OHCI_RHA_OCPM (1<<11) -#define OHCI_RHA_NOCP (1<<12) -#define OHCI_RHA_POTPGT_MASK 0xff000000 - -#define OHCI_RHS_LPS (1U<<0) -#define OHCI_RHS_OCI (1U<<1) -#define OHCI_RHS_DRWE (1U<<15) -#define OHCI_RHS_LPSC (1U<<16) -#define OHCI_RHS_OCIC (1U<<17) -#define OHCI_RHS_CRWE (1U<<31) - -#define OHCI_PORT_CCS (1<<0) -#define OHCI_PORT_PES (1<<1) -#define OHCI_PORT_PSS (1<<2) -#define OHCI_PORT_POCI (1<<3) -#define OHCI_PORT_PRS (1<<4) -#define OHCI_PORT_PPS (1<<8) -#define OHCI_PORT_LSDA (1<<9) -#define OHCI_PORT_CSC (1<<16) -#define OHCI_PORT_PESC (1<<17) -#define OHCI_PORT_PSSC (1<<18) -#define OHCI_PORT_OCIC (1<<19) -#define OHCI_PORT_PRSC (1<<20) -#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \ - |OHCI_PORT_OCIC|OHCI_PORT_PRSC) - -#define OHCI_TD_DIR_SETUP 0x0 -#define OHCI_TD_DIR_OUT 0x1 -#define OHCI_TD_DIR_IN 0x2 -#define OHCI_TD_DIR_RESERVED 0x3 - -#define OHCI_CC_NOERROR 0x0 -#define OHCI_CC_CRC 0x1 -#define OHCI_CC_BITSTUFFING 0x2 -#define OHCI_CC_DATATOGGLEMISMATCH 0x3 -#define OHCI_CC_STALL 0x4 -#define OHCI_CC_DEVICENOTRESPONDING 0x5 -#define OHCI_CC_PIDCHECKFAILURE 0x6 -#define OHCI_CC_UNDEXPETEDPID 0x7 -#define OHCI_CC_DATAOVERRUN 0x8 -#define OHCI_CC_DATAUNDERRUN 0x9 -#define OHCI_CC_BUFFEROVERRUN 0xc -#define OHCI_CC_BUFFERUNDERRUN 0xd - -#define OHCI_HRESET_FSBIR (1 << 0) - -static void ohci_die(OHCIState *ohci); - -/* Update IRQ levels */ -static inline void ohci_intr_update(OHCIState *ohci) -{ - int level = 0; - - if ((ohci->intr & OHCI_INTR_MIE) && - (ohci->intr_status & ohci->intr)) - level = 1; - - qemu_set_irq(ohci->irq, level); -} - -/* Set an interrupt */ -static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr) -{ - ohci->intr_status |= intr; - ohci_intr_update(ohci); -} - -/* Attach or detach a device on a root hub port. */ -static void ohci_attach(USBPort *port1) -{ - OHCIState *s = port1->opaque; - OHCIPort *port = &s->rhport[port1->index]; - uint32_t old_state = port->ctrl; - - /* set connect status */ - port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; - - /* update speed */ - if (port->port.dev->speed == USB_SPEED_LOW) { - port->ctrl |= OHCI_PORT_LSDA; - } else { - port->ctrl &= ~OHCI_PORT_LSDA; - } - - /* notify of remote-wakeup */ - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { - ohci_set_interrupt(s, OHCI_INTR_RD); - } - - trace_usb_ohci_port_attach(port1->index); - - if (old_state != port->ctrl) { - ohci_set_interrupt(s, OHCI_INTR_RHSC); - } -} - -static void ohci_detach(USBPort *port1) -{ - OHCIState *s = port1->opaque; - OHCIPort *port = &s->rhport[port1->index]; - uint32_t old_state = port->ctrl; - - ohci_async_cancel_device(s, port1->dev); - - /* set connect status */ - if (port->ctrl & OHCI_PORT_CCS) { - port->ctrl &= ~OHCI_PORT_CCS; - port->ctrl |= OHCI_PORT_CSC; - } - /* disable port */ - if (port->ctrl & OHCI_PORT_PES) { - port->ctrl &= ~OHCI_PORT_PES; - port->ctrl |= OHCI_PORT_PESC; - } - trace_usb_ohci_port_detach(port1->index); - - if (old_state != port->ctrl) { - ohci_set_interrupt(s, OHCI_INTR_RHSC); - } -} - -static void ohci_wakeup(USBPort *port1) -{ - OHCIState *s = port1->opaque; - OHCIPort *port = &s->rhport[port1->index]; - uint32_t intr = 0; - if (port->ctrl & OHCI_PORT_PSS) { - trace_usb_ohci_port_wakeup(port1->index); - port->ctrl |= OHCI_PORT_PSSC; - port->ctrl &= ~OHCI_PORT_PSS; - intr = OHCI_INTR_RHSC; - } - /* Note that the controller can be suspended even if this port is not */ - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { - trace_usb_ohci_remote_wakeup(s->name); - /* This is the one state transition the controller can do by itself */ - s->ctl &= ~OHCI_CTL_HCFS; - s->ctl |= OHCI_USB_RESUME; - /* In suspend mode only ResumeDetected is possible, not RHSC: - * see the OHCI spec 5.1.2.3. - */ - intr = OHCI_INTR_RD; - } - ohci_set_interrupt(s, intr); -} - -static void ohci_child_detach(USBPort *port1, USBDevice *child) -{ - OHCIState *s = port1->opaque; - - ohci_async_cancel_device(s, child); -} - -static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr) -{ - USBDevice *dev; - int i; - - for (i = 0; i < ohci->num_ports; i++) { - if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) { - continue; - } - dev = usb_find_device(&ohci->rhport[i].port, addr); - if (dev != NULL) { - return dev; - } - } - return NULL; -} - -static void ohci_stop_endpoints(OHCIState *ohci) -{ - USBDevice *dev; - int i, j; - - for (i = 0; i < ohci->num_ports; i++) { - dev = ohci->rhport[i].port.dev; - if (dev && dev->attached) { - usb_device_ep_stopped(dev, &dev->ep_ctl); - for (j = 0; j < USB_MAX_ENDPOINTS; j++) { - usb_device_ep_stopped(dev, &dev->ep_in[j]); - usb_device_ep_stopped(dev, &dev->ep_out[j]); - } - } - } -} - -static void ohci_roothub_reset(OHCIState *ohci) -{ - OHCIPort *port; - int i; - - ohci_bus_stop(ohci); - ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports; - ohci->rhdesc_b = 0x0; /* Impl. specific */ - ohci->rhstatus = 0; - - for (i = 0; i < ohci->num_ports; i++) { - port = &ohci->rhport[i]; - port->ctrl = 0; - if (port->port.dev && port->port.dev->attached) { - usb_port_reset(&port->port); - } - } - if (ohci->async_td) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - } - ohci_stop_endpoints(ohci); -} - -/* Reset the controller */ -static void ohci_soft_reset(OHCIState *ohci) -{ - trace_usb_ohci_reset(ohci->name); - - ohci_bus_stop(ohci); - ohci->ctl = (ohci->ctl & OHCI_CTL_IR) | OHCI_USB_SUSPEND; - ohci->old_ctl = 0; - ohci->status = 0; - ohci->intr_status = 0; - ohci->intr = OHCI_INTR_MIE; - - ohci->hcca = 0; - ohci->ctrl_head = ohci->ctrl_cur = 0; - ohci->bulk_head = ohci->bulk_cur = 0; - ohci->per_cur = 0; - ohci->done = 0; - ohci->done_count = 7; - - /* FSMPS is marked TBD in OCHI 1.0, what gives ffs? - * I took the value linux sets ... - */ - ohci->fsmps = 0x2778; - ohci->fi = 0x2edf; - ohci->fit = 0; - ohci->frt = 0; - ohci->frame_number = 0; - ohci->pstart = 0; - ohci->lst = OHCI_LS_THRESH; -} - -static void ohci_hard_reset(OHCIState *ohci) -{ - ohci_soft_reset(ohci); - ohci->ctl = 0; - ohci_roothub_reset(ohci); -} - -/* Get an array of dwords from main memory */ -static inline int get_dwords(OHCIState *ohci, - dma_addr_t addr, uint32_t *buf, int num) -{ - int i; - - addr += ohci->localmem_base; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - if (dma_memory_read(ohci->as, addr, buf, sizeof(*buf))) { - return -1; - } - *buf = le32_to_cpu(*buf); - } - - return 0; -} - -/* Put an array of dwords in to main memory */ -static inline int put_dwords(OHCIState *ohci, - dma_addr_t addr, uint32_t *buf, int num) -{ - int i; - - addr += ohci->localmem_base; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - uint32_t tmp = cpu_to_le32(*buf); - if (dma_memory_write(ohci->as, addr, &tmp, sizeof(tmp))) { - return -1; - } - } - - return 0; -} - -/* Get an array of words from main memory */ -static inline int get_words(OHCIState *ohci, - dma_addr_t addr, uint16_t *buf, int num) -{ - int i; - - addr += ohci->localmem_base; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - if (dma_memory_read(ohci->as, addr, buf, sizeof(*buf))) { - return -1; - } - *buf = le16_to_cpu(*buf); - } - - return 0; -} - -/* Put an array of words in to main memory */ -static inline int put_words(OHCIState *ohci, - dma_addr_t addr, uint16_t *buf, int num) -{ - int i; - - addr += ohci->localmem_base; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - uint16_t tmp = cpu_to_le16(*buf); - if (dma_memory_write(ohci->as, addr, &tmp, sizeof(tmp))) { - return -1; - } - } - - return 0; -} - -static inline int ohci_read_ed(OHCIState *ohci, - dma_addr_t addr, struct ohci_ed *ed) -{ - return get_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2); -} - -static inline int ohci_read_td(OHCIState *ohci, - dma_addr_t addr, struct ohci_td *td) -{ - return get_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2); -} - -static inline int ohci_read_iso_td(OHCIState *ohci, - dma_addr_t addr, struct ohci_iso_td *td) -{ - return get_dwords(ohci, addr, (uint32_t *)td, 4) || - get_words(ohci, addr + 16, td->offset, 8); -} - -static inline int ohci_read_hcca(OHCIState *ohci, - dma_addr_t addr, struct ohci_hcca *hcca) -{ - return dma_memory_read(ohci->as, addr + ohci->localmem_base, - hcca, sizeof(*hcca)); -} - -static inline int ohci_put_ed(OHCIState *ohci, - dma_addr_t addr, struct ohci_ed *ed) -{ - /* ed->tail is under control of the HCD. - * Since just ed->head is changed by HC, just write back this - */ - - return put_dwords(ohci, addr + ED_WBACK_OFFSET, - (uint32_t *)((char *)ed + ED_WBACK_OFFSET), - ED_WBACK_SIZE >> 2); -} - -static inline int ohci_put_td(OHCIState *ohci, - dma_addr_t addr, struct ohci_td *td) -{ - return put_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2); -} - -static inline int ohci_put_iso_td(OHCIState *ohci, - dma_addr_t addr, struct ohci_iso_td *td) -{ - return put_dwords(ohci, addr, (uint32_t *)td, 4) || - put_words(ohci, addr + 16, td->offset, 8); -} - -static inline int ohci_put_hcca(OHCIState *ohci, - dma_addr_t addr, struct ohci_hcca *hcca) -{ - return dma_memory_write(ohci->as, - addr + ohci->localmem_base + HCCA_WRITEBACK_OFFSET, - (char *)hcca + HCCA_WRITEBACK_OFFSET, - HCCA_WRITEBACK_SIZE); -} - -/* Read/Write the contents of a TD from/to main memory. */ -static int ohci_copy_td(OHCIState *ohci, struct ohci_td *td, - uint8_t *buf, int len, DMADirection dir) -{ - dma_addr_t ptr, n; - - ptr = td->cbp; - n = 0x1000 - (ptr & 0xfff); - if (n > len) - n = len; - - if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, n, dir)) { - return -1; - } - if (n == len) { - return 0; - } - ptr = td->be & ~0xfffu; - buf += n; - if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, - len - n, dir)) { - return -1; - } - return 0; -} - -/* Read/Write the contents of an ISO TD from/to main memory. */ -static int ohci_copy_iso_td(OHCIState *ohci, - uint32_t start_addr, uint32_t end_addr, - uint8_t *buf, int len, DMADirection dir) -{ - dma_addr_t ptr, n; - - ptr = start_addr; - n = 0x1000 - (ptr & 0xfff); - if (n > len) - n = len; - - if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, n, dir)) { - return -1; - } - if (n == len) { - return 0; - } - ptr = end_addr & ~0xfffu; - buf += n; - if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, - len - n, dir)) { - return -1; - } - return 0; -} - -static void ohci_process_lists(OHCIState *ohci, int completion); - -static void ohci_async_complete_packet(USBPort *port, USBPacket *packet) -{ - OHCIState *ohci = container_of(packet, OHCIState, usb_packet); - - trace_usb_ohci_async_complete(); - ohci->async_complete = true; - ohci_process_lists(ohci, 1); -} - -#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b))) - -static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, - int completion) -{ - int dir; - size_t len = 0; - const char *str = NULL; - int pid; - int ret; - int i; - USBDevice *dev; - USBEndpoint *ep; - struct ohci_iso_td iso_td; - uint32_t addr; - uint16_t starting_frame; - int16_t relative_frame_number; - int frame_count; - uint32_t start_offset, next_offset, end_offset = 0; - uint32_t start_addr, end_addr; - - addr = ed->head & OHCI_DPTR_MASK; - - if (ohci_read_iso_td(ohci, addr, &iso_td)) { - trace_usb_ohci_iso_td_read_failed(addr); - ohci_die(ohci); - return 0; - } - - starting_frame = OHCI_BM(iso_td.flags, TD_SF); - frame_count = OHCI_BM(iso_td.flags, TD_FC); - relative_frame_number = USUB(ohci->frame_number, starting_frame); - - trace_usb_ohci_iso_td_head( - ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK, - iso_td.flags, iso_td.bp, iso_td.next, iso_td.be, - ohci->frame_number, starting_frame, - frame_count, relative_frame_number); - trace_usb_ohci_iso_td_head_offset( - iso_td.offset[0], iso_td.offset[1], - iso_td.offset[2], iso_td.offset[3], - iso_td.offset[4], iso_td.offset[5], - iso_td.offset[6], iso_td.offset[7]); - - if (relative_frame_number < 0) { - trace_usb_ohci_iso_td_relative_frame_number_neg(relative_frame_number); - return 1; - } else if (relative_frame_number > frame_count) { - /* ISO TD expired - retire the TD to the Done Queue and continue with - the next ISO TD of the same ED */ - trace_usb_ohci_iso_td_relative_frame_number_big(relative_frame_number, - frame_count); - OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN); - ed->head &= ~OHCI_DPTR_MASK; - ed->head |= (iso_td.next & OHCI_DPTR_MASK); - iso_td.next = ohci->done; - ohci->done = addr; - i = OHCI_BM(iso_td.flags, TD_DI); - if (i < ohci->done_count) - ohci->done_count = i; - if (ohci_put_iso_td(ohci, addr, &iso_td)) { - ohci_die(ohci); - return 1; - } - return 0; - } - - dir = OHCI_BM(ed->flags, ED_D); - switch (dir) { - case OHCI_TD_DIR_IN: - str = "in"; - pid = USB_TOKEN_IN; - break; - case OHCI_TD_DIR_OUT: - str = "out"; - pid = USB_TOKEN_OUT; - break; - case OHCI_TD_DIR_SETUP: - str = "setup"; - pid = USB_TOKEN_SETUP; - break; - default: - trace_usb_ohci_iso_td_bad_direction(dir); - return 1; - } - - if (!iso_td.bp || !iso_td.be) { - trace_usb_ohci_iso_td_bad_bp_be(iso_td.bp, iso_td.be); - return 1; - } - - start_offset = iso_td.offset[relative_frame_number]; - next_offset = iso_td.offset[relative_frame_number + 1]; - - if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || - ((relative_frame_number < frame_count) && - !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) { - trace_usb_ohci_iso_td_bad_cc_not_accessed(start_offset, next_offset); - return 1; - } - - if ((relative_frame_number < frame_count) && (start_offset > next_offset)) { - trace_usb_ohci_iso_td_bad_cc_overrun(start_offset, next_offset); - return 1; - } - - if ((start_offset & 0x1000) == 0) { - start_addr = (iso_td.bp & OHCI_PAGE_MASK) | - (start_offset & OHCI_OFFSET_MASK); - } else { - start_addr = (iso_td.be & OHCI_PAGE_MASK) | - (start_offset & OHCI_OFFSET_MASK); - } - - if (relative_frame_number < frame_count) { - end_offset = next_offset - 1; - if ((end_offset & 0x1000) == 0) { - end_addr = (iso_td.bp & OHCI_PAGE_MASK) | - (end_offset & OHCI_OFFSET_MASK); - } else { - end_addr = (iso_td.be & OHCI_PAGE_MASK) | - (end_offset & OHCI_OFFSET_MASK); - } - } else { - /* Last packet in the ISO TD */ - end_addr = iso_td.be; - } - - if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) { - len = (end_addr & OHCI_OFFSET_MASK) + 0x1001 - - (start_addr & OHCI_OFFSET_MASK); - } else { - len = end_addr - start_addr + 1; - } - - if (len && dir != OHCI_TD_DIR_IN) { - if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len, - DMA_DIRECTION_TO_DEVICE)) { - ohci_die(ohci); - return 1; - } - } - - if (!completion) { - bool int_req = relative_frame_number == frame_count && - OHCI_BM(iso_td.flags, TD_DI) == 0; - dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); - ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req); - usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); - usb_handle_packet(dev, &ohci->usb_packet); - if (ohci->usb_packet.status == USB_RET_ASYNC) { - usb_device_flush_ep_queue(dev, ep); - return 1; - } - } - if (ohci->usb_packet.status == USB_RET_SUCCESS) { - ret = ohci->usb_packet.actual_length; - } else { - ret = ohci->usb_packet.status; - } - - trace_usb_ohci_iso_td_so(start_offset, end_offset, start_addr, end_addr, - str, len, ret); - - /* Writeback */ - if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) { - /* IN transfer succeeded */ - if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret, - DMA_DIRECTION_FROM_DEVICE)) { - ohci_die(ohci); - return 1; - } - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_NOERROR); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret); - } else if (dir == OHCI_TD_DIR_OUT && ret == len) { - /* OUT transfer succeeded */ - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_NOERROR); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0); - } else { - if (ret > (ssize_t) len) { - trace_usb_ohci_iso_td_data_overrun(ret, len); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_DATAOVERRUN); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, - len); - } else if (ret >= 0) { - trace_usb_ohci_iso_td_data_underrun(ret); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_DATAUNDERRUN); - } else { - switch (ret) { - case USB_RET_IOERROR: - case USB_RET_NODEV: - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_DEVICENOTRESPONDING); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, - 0); - break; - case USB_RET_NAK: - case USB_RET_STALL: - trace_usb_ohci_iso_td_nak(ret); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_STALL); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, - 0); - break; - default: - trace_usb_ohci_iso_td_bad_response(ret); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_UNDEXPETEDPID); - break; - } - } - } - - if (relative_frame_number == frame_count) { - /* Last data packet of ISO TD - retire the TD to the Done Queue */ - OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR); - ed->head &= ~OHCI_DPTR_MASK; - ed->head |= (iso_td.next & OHCI_DPTR_MASK); - iso_td.next = ohci->done; - ohci->done = addr; - i = OHCI_BM(iso_td.flags, TD_DI); - if (i < ohci->done_count) - ohci->done_count = i; - } - if (ohci_put_iso_td(ohci, addr, &iso_td)) { - ohci_die(ohci); - } - return 1; -} - -#ifdef trace_event_get_state -static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) -{ - bool print16 = !!trace_event_get_state(TRACE_USB_OHCI_TD_PKT_SHORT); - bool printall = !!trace_event_get_state(TRACE_USB_OHCI_TD_PKT_FULL); - const int width = 16; - int i; - char tmp[3 * width + 1]; - char *p = tmp; - - if (!printall && !print16) { - return; - } - - for (i = 0; ; i++) { - if (i && (!(i % width) || (i == len))) { - if (!printall) { - trace_usb_ohci_td_pkt_short(msg, tmp); - break; - } - trace_usb_ohci_td_pkt_full(msg, tmp); - p = tmp; - *p = 0; - } - if (i == len) { - break; - } - - p += sprintf(p, " %.2x", buf[i]); - } -} -#else -static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) -{ -} -#endif - -/* Service a transport descriptor. - Returns nonzero to terminate processing of this endpoint. */ - -static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) -{ - int dir; - size_t len = 0, pktlen = 0; - const char *str = NULL; - int pid; - int ret; - int i; - USBDevice *dev; - USBEndpoint *ep; - struct ohci_td td; - uint32_t addr; - int flag_r; - int completion; - - addr = ed->head & OHCI_DPTR_MASK; - /* See if this TD has already been submitted to the device. */ - completion = (addr == ohci->async_td); - if (completion && !ohci->async_complete) { - trace_usb_ohci_td_skip_async(); - return 1; - } - if (ohci_read_td(ohci, addr, &td)) { - trace_usb_ohci_td_read_error(addr); - ohci_die(ohci); - return 0; - } - - dir = OHCI_BM(ed->flags, ED_D); - switch (dir) { - case OHCI_TD_DIR_OUT: - case OHCI_TD_DIR_IN: - /* Same value. */ - break; - default: - dir = OHCI_BM(td.flags, TD_DP); - break; - } - - switch (dir) { - case OHCI_TD_DIR_IN: - str = "in"; - pid = USB_TOKEN_IN; - break; - case OHCI_TD_DIR_OUT: - str = "out"; - pid = USB_TOKEN_OUT; - break; - case OHCI_TD_DIR_SETUP: - str = "setup"; - pid = USB_TOKEN_SETUP; - break; - default: - trace_usb_ohci_td_bad_direction(dir); - return 1; - } - if (td.cbp && td.be) { - if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) { - len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff); - } else { - len = (td.be - td.cbp) + 1; - } - - pktlen = len; - if (len && dir != OHCI_TD_DIR_IN) { - /* The endpoint may not allow us to transfer it all now */ - pktlen = (ed->flags & OHCI_ED_MPS_MASK) >> OHCI_ED_MPS_SHIFT; - if (pktlen > len) { - pktlen = len; - } - if (!completion) { - if (ohci_copy_td(ohci, &td, ohci->usb_buf, pktlen, - DMA_DIRECTION_TO_DEVICE)) { - ohci_die(ohci); - } - } - } - } - - flag_r = (td.flags & OHCI_TD_R) != 0; - trace_usb_ohci_td_pkt_hdr(addr, (int64_t)pktlen, (int64_t)len, str, - flag_r, td.cbp, td.be); - ohci_td_pkt("OUT", ohci->usb_buf, pktlen); - - if (completion) { - ohci->async_td = 0; - ohci->async_complete = false; - } else { - if (ohci->async_td) { - /* ??? The hardware should allow one active packet per - endpoint. We only allow one active packet per controller. - This should be sufficient as long as devices respond in a - timely manner. - */ - trace_usb_ohci_td_too_many_pending(); - return 1; - } - dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); - ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r, - OHCI_BM(td.flags, TD_DI) == 0); - usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); - usb_handle_packet(dev, &ohci->usb_packet); - trace_usb_ohci_td_packet_status(ohci->usb_packet.status); - - if (ohci->usb_packet.status == USB_RET_ASYNC) { - usb_device_flush_ep_queue(dev, ep); - ohci->async_td = addr; - return 1; - } - } - if (ohci->usb_packet.status == USB_RET_SUCCESS) { - ret = ohci->usb_packet.actual_length; - } else { - ret = ohci->usb_packet.status; - } - - if (ret >= 0) { - if (dir == OHCI_TD_DIR_IN) { - if (ohci_copy_td(ohci, &td, ohci->usb_buf, ret, - DMA_DIRECTION_FROM_DEVICE)) { - ohci_die(ohci); - } - ohci_td_pkt("IN", ohci->usb_buf, pktlen); - } else { - ret = pktlen; - } - } - - /* Writeback */ - if (ret == pktlen || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) { - /* Transmission succeeded. */ - if (ret == len) { - td.cbp = 0; - } else { - if ((td.cbp & 0xfff) + ret > 0xfff) { - td.cbp = (td.be & ~0xfff) + ((td.cbp + ret) & 0xfff); - } else { - td.cbp += ret; - } - } - td.flags |= OHCI_TD_T1; - td.flags ^= OHCI_TD_T0; - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR); - OHCI_SET_BM(td.flags, TD_EC, 0); - - if ((dir != OHCI_TD_DIR_IN) && (ret != len)) { - /* Partial packet transfer: TD not ready to retire yet */ - goto exit_no_retire; - } - - /* Setting ED_C is part of the TD retirement process */ - ed->head &= ~OHCI_ED_C; - if (td.flags & OHCI_TD_T0) - ed->head |= OHCI_ED_C; - } else { - if (ret >= 0) { - trace_usb_ohci_td_underrun(); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN); - } else { - switch (ret) { - case USB_RET_IOERROR: - case USB_RET_NODEV: - trace_usb_ohci_td_dev_error(); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING); - break; - case USB_RET_NAK: - trace_usb_ohci_td_nak(); - return 1; - case USB_RET_STALL: - trace_usb_ohci_td_stall(); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL); - break; - case USB_RET_BABBLE: - trace_usb_ohci_td_babble(); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN); - break; - default: - trace_usb_ohci_td_bad_device_response(ret); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID); - OHCI_SET_BM(td.flags, TD_EC, 3); - break; - } - } - ed->head |= OHCI_ED_H; - } - - /* Retire this TD */ - ed->head &= ~OHCI_DPTR_MASK; - ed->head |= td.next & OHCI_DPTR_MASK; - td.next = ohci->done; - ohci->done = addr; - i = OHCI_BM(td.flags, TD_DI); - if (i < ohci->done_count) - ohci->done_count = i; -exit_no_retire: - if (ohci_put_td(ohci, addr, &td)) { - ohci_die(ohci); - return 1; - } - return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR; -} - -/* Service an endpoint list. Returns nonzero if active TD were found. */ -static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) -{ - struct ohci_ed ed; - uint32_t next_ed; - uint32_t cur; - int active; - - active = 0; - - if (head == 0) - return 0; - - for (cur = head; cur; cur = next_ed) { - if (ohci_read_ed(ohci, cur, &ed)) { - trace_usb_ohci_ed_read_error(cur); - ohci_die(ohci); - return 0; - } - - next_ed = ed.next & OHCI_DPTR_MASK; - - if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { - uint32_t addr; - /* Cancel pending packets for ED that have been paused. */ - addr = ed.head & OHCI_DPTR_MASK; - if (ohci->async_td && addr == ohci->async_td) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - usb_device_ep_stopped(ohci->usb_packet.ep->dev, - ohci->usb_packet.ep); - } - continue; - } - - while ((ed.head & OHCI_DPTR_MASK) != ed.tail) { - trace_usb_ohci_ed_pkt(cur, (ed.head & OHCI_ED_H) != 0, - (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK, - ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK); - trace_usb_ohci_ed_pkt_flags( - OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN), - OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0, - (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0, - OHCI_BM(ed.flags, ED_MPS)); - - active = 1; - - if ((ed.flags & OHCI_ED_F) == 0) { - if (ohci_service_td(ohci, &ed)) - break; - } else { - /* Handle isochronous endpoints */ - if (ohci_service_iso_td(ohci, &ed, completion)) - break; - } - } - - if (ohci_put_ed(ohci, cur, &ed)) { - ohci_die(ohci); - return 0; - } - } - - return active; -} - -/* set a timer for EOF */ -static void ohci_eof_timer(OHCIState *ohci) -{ - ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - timer_mod(ohci->eof_timer, ohci->sof_time + usb_frame_time); -} -/* Set a timer for EOF and generate a SOF event */ -static void ohci_sof(OHCIState *ohci) -{ - ohci_eof_timer(ohci); - ohci_set_interrupt(ohci, OHCI_INTR_SF); -} - -/* Process Control and Bulk lists. */ -static void ohci_process_lists(OHCIState *ohci, int completion) -{ - if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { - if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) { - trace_usb_ohci_process_lists(ohci->ctrl_head, ohci->ctrl_cur); - } - if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) { - ohci->ctrl_cur = 0; - ohci->status &= ~OHCI_STATUS_CLF; - } - } - - if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) { - if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) { - ohci->bulk_cur = 0; - ohci->status &= ~OHCI_STATUS_BLF; - } - } -} - -/* Do frame processing on frame boundary */ -static void ohci_frame_boundary(void *opaque) -{ - OHCIState *ohci = opaque; - struct ohci_hcca hcca; - - if (ohci_read_hcca(ohci, ohci->hcca, &hcca)) { - trace_usb_ohci_hcca_read_error(ohci->hcca); - ohci_die(ohci); - return; - } - - /* Process all the lists at the end of the frame */ - if (ohci->ctl & OHCI_CTL_PLE) { - int n; - - n = ohci->frame_number & 0x1f; - ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0); - } - - /* Cancel all pending packets if either of the lists has been disabled. */ - if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { - if (ohci->async_td) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - } - ohci_stop_endpoints(ohci); - } - ohci->old_ctl = ohci->ctl; - ohci_process_lists(ohci, 0); - - /* Stop if UnrecoverableError happened or ohci_sof will crash */ - if (ohci->intr_status & OHCI_INTR_UE) { - return; - } - - /* Frame boundary, so do EOF stuf here */ - ohci->frt = ohci->fit; - - /* Increment frame number and take care of endianness. */ - ohci->frame_number = (ohci->frame_number + 1) & 0xffff; - hcca.frame = cpu_to_le16(ohci->frame_number); - - if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) { - if (!ohci->done) - abort(); - if (ohci->intr & ohci->intr_status) - ohci->done |= 1; - hcca.done = cpu_to_le32(ohci->done); - ohci->done = 0; - ohci->done_count = 7; - ohci_set_interrupt(ohci, OHCI_INTR_WD); - } - - if (ohci->done_count != 7 && ohci->done_count != 0) - ohci->done_count--; - - /* Do SOF stuff here */ - ohci_sof(ohci); - - /* Writeback HCCA */ - if (ohci_put_hcca(ohci, ohci->hcca, &hcca)) { - ohci_die(ohci); - } -} - -/* Start sending SOF tokens across the USB bus, lists are processed in - * next frame - */ -static int ohci_bus_start(OHCIState *ohci) -{ - trace_usb_ohci_start(ohci->name); - - /* Delay the first SOF event by one frame time as - * linux driver is not ready to receive it and - * can meet some race conditions - */ - - ohci_eof_timer(ohci); - - return 1; -} - -/* Stop sending SOF tokens on the bus */ -static void ohci_bus_stop(OHCIState *ohci) -{ - trace_usb_ohci_stop(ohci->name); - timer_del(ohci->eof_timer); -} - -/* Sets a flag in a port status register but only set it if the port is - * connected, if not set ConnectStatusChange flag. If flag is enabled - * return 1. - */ -static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) -{ - int ret = 1; - - /* writing a 0 has no effect */ - if (val == 0) - return 0; - - /* If CurrentConnectStatus is cleared we set - * ConnectStatusChange - */ - if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { - ohci->rhport[i].ctrl |= OHCI_PORT_CSC; - if (ohci->rhstatus & OHCI_RHS_DRWE) { - /* TODO: CSC is a wakeup event */ - } - return 0; - } - - if (ohci->rhport[i].ctrl & val) - ret = 0; - - /* set the bit */ - ohci->rhport[i].ctrl |= val; - - return ret; -} - -/* Set the frame interval - frame interval toggle is manipulated by the hcd only */ -static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val) -{ - val &= OHCI_FMI_FI; - - if (val != ohci->fi) { - trace_usb_ohci_set_frame_interval(ohci->name, ohci->fi, ohci->fi); - } - - ohci->fi = val; -} - -static void ohci_port_power(OHCIState *ohci, int i, int p) -{ - if (p) { - ohci->rhport[i].ctrl |= OHCI_PORT_PPS; - } else { - ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS| - OHCI_PORT_CCS| - OHCI_PORT_PSS| - OHCI_PORT_PRS); - } -} - -/* Set HcControlRegister */ -static void ohci_set_ctl(OHCIState *ohci, uint32_t val) -{ - uint32_t old_state; - uint32_t new_state; - - old_state = ohci->ctl & OHCI_CTL_HCFS; - ohci->ctl = val; - new_state = ohci->ctl & OHCI_CTL_HCFS; - - /* no state change */ - if (old_state == new_state) - return; - - trace_usb_ohci_set_ctl(ohci->name, new_state); - switch (new_state) { - case OHCI_USB_OPERATIONAL: - ohci_bus_start(ohci); - break; - case OHCI_USB_SUSPEND: - ohci_bus_stop(ohci); - /* clear pending SF otherwise linux driver loops in ohci_irq() */ - ohci->intr_status &= ~OHCI_INTR_SF; - ohci_intr_update(ohci); - break; - case OHCI_USB_RESUME: - trace_usb_ohci_resume(ohci->name); - break; - case OHCI_USB_RESET: - ohci_roothub_reset(ohci); - break; - } -} - -static uint32_t ohci_get_frame_remaining(OHCIState *ohci) -{ - uint16_t fr; - int64_t tks; - - if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL) - return (ohci->frt << 31); - - /* Being in USB operational state guarnatees sof_time was - * set already. - */ - tks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ohci->sof_time; - - /* avoid muldiv if possible */ - if (tks >= usb_frame_time) - return (ohci->frt << 31); - - tks = muldiv64(1, tks, usb_bit_time); - fr = (uint16_t)(ohci->fi - tks); - - return (ohci->frt << 31) | fr; -} - - -/* Set root hub status */ -static void ohci_set_hub_status(OHCIState *ohci, uint32_t val) -{ - uint32_t old_state; - - old_state = ohci->rhstatus; - - /* write 1 to clear OCIC */ - if (val & OHCI_RHS_OCIC) - ohci->rhstatus &= ~OHCI_RHS_OCIC; - - if (val & OHCI_RHS_LPS) { - int i; - - for (i = 0; i < ohci->num_ports; i++) - ohci_port_power(ohci, i, 0); - trace_usb_ohci_hub_power_down(); - } - - if (val & OHCI_RHS_LPSC) { - int i; - - for (i = 0; i < ohci->num_ports; i++) - ohci_port_power(ohci, i, 1); - trace_usb_ohci_hub_power_up(); - } - - if (val & OHCI_RHS_DRWE) - ohci->rhstatus |= OHCI_RHS_DRWE; - - if (val & OHCI_RHS_CRWE) - ohci->rhstatus &= ~OHCI_RHS_DRWE; - - if (old_state != ohci->rhstatus) - ohci_set_interrupt(ohci, OHCI_INTR_RHSC); -} - -/* Set root hub port status */ -static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) -{ - uint32_t old_state; - OHCIPort *port; - - port = &ohci->rhport[portnum]; - old_state = port->ctrl; - - /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */ - if (val & OHCI_PORT_WTC) - port->ctrl &= ~(val & OHCI_PORT_WTC); - - if (val & OHCI_PORT_CCS) - port->ctrl &= ~OHCI_PORT_PES; - - ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES); - - if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) { - trace_usb_ohci_port_suspend(portnum); - } - - if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) { - trace_usb_ohci_port_reset(portnum); - usb_device_reset(port->port.dev); - port->ctrl &= ~OHCI_PORT_PRS; - /* ??? Should this also set OHCI_PORT_PESC. */ - port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC; - } - - /* Invert order here to ensure in ambiguous case, device is - * powered up... - */ - if (val & OHCI_PORT_LSDA) - ohci_port_power(ohci, portnum, 0); - if (val & OHCI_PORT_PPS) - ohci_port_power(ohci, portnum, 1); - - if (old_state != port->ctrl) - ohci_set_interrupt(ohci, OHCI_INTR_RHSC); -} - -static uint64_t ohci_mem_read(void *opaque, - hwaddr addr, - unsigned size) -{ - OHCIState *ohci = opaque; - uint32_t retval; - - /* Only aligned reads are allowed on OHCI */ - if (addr & 3) { - trace_usb_ohci_mem_read_unaligned(addr); - return 0xffffffff; - } else if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { - /* HcRhPortStatus */ - retval = ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS; - } else { - switch (addr >> 2) { - case 0: /* HcRevision */ - retval = 0x10; - break; - - case 1: /* HcControl */ - retval = ohci->ctl; - break; - - case 2: /* HcCommandStatus */ - retval = ohci->status; - break; - - case 3: /* HcInterruptStatus */ - retval = ohci->intr_status; - break; - - case 4: /* HcInterruptEnable */ - case 5: /* HcInterruptDisable */ - retval = ohci->intr; - break; - - case 6: /* HcHCCA */ - retval = ohci->hcca; - break; - - case 7: /* HcPeriodCurrentED */ - retval = ohci->per_cur; - break; - - case 8: /* HcControlHeadED */ - retval = ohci->ctrl_head; - break; - - case 9: /* HcControlCurrentED */ - retval = ohci->ctrl_cur; - break; - - case 10: /* HcBulkHeadED */ - retval = ohci->bulk_head; - break; - - case 11: /* HcBulkCurrentED */ - retval = ohci->bulk_cur; - break; - - case 12: /* HcDoneHead */ - retval = ohci->done; - break; - - case 13: /* HcFmInterretval */ - retval = (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi); - break; - - case 14: /* HcFmRemaining */ - retval = ohci_get_frame_remaining(ohci); - break; - - case 15: /* HcFmNumber */ - retval = ohci->frame_number; - break; - - case 16: /* HcPeriodicStart */ - retval = ohci->pstart; - break; - - case 17: /* HcLSThreshold */ - retval = ohci->lst; - break; - - case 18: /* HcRhDescriptorA */ - retval = ohci->rhdesc_a; - break; - - case 19: /* HcRhDescriptorB */ - retval = ohci->rhdesc_b; - break; - - case 20: /* HcRhStatus */ - retval = ohci->rhstatus; - break; - - /* PXA27x specific registers */ - case 24: /* HcStatus */ - retval = ohci->hstatus & ohci->hmask; - break; - - case 25: /* HcHReset */ - retval = ohci->hreset; - break; - - case 26: /* HcHInterruptEnable */ - retval = ohci->hmask; - break; - - case 27: /* HcHInterruptTest */ - retval = ohci->htest; - break; - - default: - trace_usb_ohci_mem_read_bad_offset(addr); - retval = 0xffffffff; - } - } - - return retval; -} - -static void ohci_mem_write(void *opaque, - hwaddr addr, - uint64_t val, - unsigned size) -{ - OHCIState *ohci = opaque; - - /* Only aligned reads are allowed on OHCI */ - if (addr & 3) { - trace_usb_ohci_mem_write_unaligned(addr); - return; - } - - if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { - /* HcRhPortStatus */ - ohci_port_set_status(ohci, (addr - 0x54) >> 2, val); - return; - } - - switch (addr >> 2) { - case 1: /* HcControl */ - ohci_set_ctl(ohci, val); - break; - - case 2: /* HcCommandStatus */ - /* SOC is read-only */ - val = (val & ~OHCI_STATUS_SOC); - - /* Bits written as '0' remain unchanged in the register */ - ohci->status |= val; - - if (ohci->status & OHCI_STATUS_HCR) - ohci_soft_reset(ohci); - break; - - case 3: /* HcInterruptStatus */ - ohci->intr_status &= ~val; - ohci_intr_update(ohci); - break; - - case 4: /* HcInterruptEnable */ - ohci->intr |= val; - ohci_intr_update(ohci); - break; - - case 5: /* HcInterruptDisable */ - ohci->intr &= ~val; - ohci_intr_update(ohci); - break; - - case 6: /* HcHCCA */ - ohci->hcca = val & OHCI_HCCA_MASK; - break; - - case 7: /* HcPeriodCurrentED */ - /* Ignore writes to this read-only register, Linux does them */ - break; - - case 8: /* HcControlHeadED */ - ohci->ctrl_head = val & OHCI_EDPTR_MASK; - break; - - case 9: /* HcControlCurrentED */ - ohci->ctrl_cur = val & OHCI_EDPTR_MASK; - break; - - case 10: /* HcBulkHeadED */ - ohci->bulk_head = val & OHCI_EDPTR_MASK; - break; - - case 11: /* HcBulkCurrentED */ - ohci->bulk_cur = val & OHCI_EDPTR_MASK; - break; - - case 13: /* HcFmInterval */ - ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16; - ohci->fit = (val & OHCI_FMI_FIT) >> 31; - ohci_set_frame_interval(ohci, val); - break; - - case 15: /* HcFmNumber */ - break; - - case 16: /* HcPeriodicStart */ - ohci->pstart = val & 0xffff; - break; - - case 17: /* HcLSThreshold */ - ohci->lst = val & 0xffff; - break; - - case 18: /* HcRhDescriptorA */ - ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK; - ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK; - break; - - case 19: /* HcRhDescriptorB */ - break; - - case 20: /* HcRhStatus */ - ohci_set_hub_status(ohci, val); - break; - - /* PXA27x specific registers */ - case 24: /* HcStatus */ - ohci->hstatus &= ~(val & ohci->hmask); - break; - - case 25: /* HcHReset */ - ohci->hreset = val & ~OHCI_HRESET_FSBIR; - if (val & OHCI_HRESET_FSBIR) - ohci_hard_reset(ohci); - break; - - case 26: /* HcHInterruptEnable */ - ohci->hmask = val; - break; - - case 27: /* HcHInterruptTest */ - ohci->htest = val; - break; - - default: - trace_usb_ohci_mem_write_bad_offset(addr); - break; - } -} - -static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) -{ - if (ohci->async_td && - usb_packet_is_inflight(&ohci->usb_packet) && - ohci->usb_packet.ep->dev == dev) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - } -} - -static const MemoryRegionOps ohci_mem_ops = { - .read = ohci_mem_read, - .write = ohci_mem_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static USBPortOps ohci_port_ops = { - .attach = ohci_attach, - .detach = ohci_detach, - .child_detach = ohci_child_detach, - .wakeup = ohci_wakeup, - .complete = ohci_async_complete_packet, -}; - -static USBBusOps ohci_bus_ops = { -}; - -static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, - int num_ports, dma_addr_t localmem_base, - char *masterbus, uint32_t firstport, - AddressSpace *as, Error **errp) -{ - Error *err = NULL; - int i; - - ohci->as = as; - - if (usb_frame_time == 0) { -#ifdef OHCI_TIME_WARP - usb_frame_time = NANOSECONDS_PER_SECOND; - usb_bit_time = NANOSECONDS_PER_SECOND / (USB_HZ / 1000); -#else - usb_frame_time = NANOSECONDS_PER_SECOND / 1000; - if (NANOSECONDS_PER_SECOND >= USB_HZ) { - usb_bit_time = NANOSECONDS_PER_SECOND / USB_HZ; - } else { - usb_bit_time = 1; - } -#endif - trace_usb_ohci_init_time(usb_frame_time, usb_bit_time); - } - - ohci->num_ports = num_ports; - if (masterbus) { - USBPort *ports[OHCI_MAX_PORTS]; - for(i = 0; i < num_ports; i++) { - ports[i] = &ohci->rhport[i].port; - } - usb_register_companion(masterbus, ports, num_ports, - firstport, ohci, &ohci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL, - &err); - if (err) { - error_propagate(errp, err); - return; - } - } else { - usb_bus_new(&ohci->bus, sizeof(ohci->bus), &ohci_bus_ops, dev); - for (i = 0; i < num_ports; i++) { - usb_register_port(&ohci->bus, &ohci->rhport[i].port, - ohci, i, &ohci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - } - } - - memory_region_init_io(&ohci->mem, OBJECT(dev), &ohci_mem_ops, - ohci, "ohci", 256); - ohci->localmem_base = localmem_base; - - ohci->name = object_get_typename(OBJECT(dev)); - usb_packet_init(&ohci->usb_packet); - - ohci->async_td = 0; - - ohci->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - ohci_frame_boundary, ohci); -} - -#define TYPE_PCI_OHCI "pci-ohci" -#define PCI_OHCI(obj) OBJECT_CHECK(OHCIPCIState, (obj), TYPE_PCI_OHCI) - -typedef struct { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - OHCIState state; - char *masterbus; - uint32_t num_ports; - uint32_t firstport; -} OHCIPCIState; - -/** A typical O/EHCI will stop operating, set itself into error state - * (which can be queried by MMIO) and will set PERR in its config - * space to signal that it got an error - */ -static void ohci_die(OHCIState *ohci) -{ - OHCIPCIState *dev = container_of(ohci, OHCIPCIState, state); - - trace_usb_ohci_die(); - - ohci_set_interrupt(ohci, OHCI_INTR_UE); - ohci_bus_stop(ohci); - pci_set_word(dev->parent_obj.config + PCI_STATUS, - PCI_STATUS_DETECTED_PARITY); -} - -static void usb_ohci_realize_pci(PCIDevice *dev, Error **errp) -{ - Error *err = NULL; - OHCIPCIState *ohci = PCI_OHCI(dev); - - dev->config[PCI_CLASS_PROG] = 0x10; /* OHCI */ - dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */ - - usb_ohci_init(&ohci->state, DEVICE(dev), ohci->num_ports, 0, - ohci->masterbus, ohci->firstport, - pci_get_address_space(dev), &err); - if (err) { - error_propagate(errp, err); - return; - } - - ohci->state.irq = pci_allocate_irq(dev); - pci_register_bar(dev, 0, 0, &ohci->state.mem); -} - -static void usb_ohci_exit(PCIDevice *dev) -{ - OHCIPCIState *ohci = PCI_OHCI(dev); - OHCIState *s = &ohci->state; - - trace_usb_ohci_exit(s->name); - ohci_bus_stop(s); - - if (s->async_td) { - usb_cancel_packet(&s->usb_packet); - s->async_td = 0; - } - ohci_stop_endpoints(s); - - if (!ohci->masterbus) { - usb_bus_release(&s->bus); - } - - timer_del(s->eof_timer); - timer_free(s->eof_timer); -} - -static void usb_ohci_reset_pci(DeviceState *d) -{ - PCIDevice *dev = PCI_DEVICE(d); - OHCIPCIState *ohci = PCI_OHCI(dev); - OHCIState *s = &ohci->state; - - ohci_hard_reset(s); -} - -#define TYPE_SYSBUS_OHCI "sysbus-ohci" -#define SYSBUS_OHCI(obj) OBJECT_CHECK(OHCISysBusState, (obj), TYPE_SYSBUS_OHCI) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - OHCIState ohci; - uint32_t num_ports; - dma_addr_t dma_offset; -} OHCISysBusState; - -static void ohci_realize_pxa(DeviceState *dev, Error **errp) -{ - OHCISysBusState *s = SYSBUS_OHCI(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - /* Cannot fail as we pass NULL for masterbus */ - usb_ohci_init(&s->ohci, dev, s->num_ports, s->dma_offset, NULL, 0, - &address_space_memory, &error_abort); - sysbus_init_irq(sbd, &s->ohci.irq); - sysbus_init_mmio(sbd, &s->ohci.mem); -} - -static void usb_ohci_reset_sysbus(DeviceState *dev) -{ - OHCISysBusState *s = SYSBUS_OHCI(dev); - OHCIState *ohci = &s->ohci; - - ohci_hard_reset(ohci); -} - -static Property ohci_pci_properties[] = { - DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), - DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), - DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_ohci_state_port = { - .name = "ohci-core/port", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ctrl, OHCIPort), - VMSTATE_END_OF_LIST() - }, -}; - -static bool ohci_eof_timer_needed(void *opaque) -{ - OHCIState *ohci = opaque; - - return timer_pending(ohci->eof_timer); -} - -static const VMStateDescription vmstate_ohci_eof_timer = { - .name = "ohci-core/eof-timer", - .version_id = 1, - .minimum_version_id = 1, - .needed = ohci_eof_timer_needed, - .fields = (VMStateField[]) { - VMSTATE_TIMER_PTR(eof_timer, OHCIState), - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_ohci_state = { - .name = "ohci-core", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT64(sof_time, OHCIState), - VMSTATE_UINT32(ctl, OHCIState), - VMSTATE_UINT32(status, OHCIState), - VMSTATE_UINT32(intr_status, OHCIState), - VMSTATE_UINT32(intr, OHCIState), - VMSTATE_UINT32(hcca, OHCIState), - VMSTATE_UINT32(ctrl_head, OHCIState), - VMSTATE_UINT32(ctrl_cur, OHCIState), - VMSTATE_UINT32(bulk_head, OHCIState), - VMSTATE_UINT32(bulk_cur, OHCIState), - VMSTATE_UINT32(per_cur, OHCIState), - VMSTATE_UINT32(done, OHCIState), - VMSTATE_INT32(done_count, OHCIState), - VMSTATE_UINT16(fsmps, OHCIState), - VMSTATE_UINT8(fit, OHCIState), - VMSTATE_UINT16(fi, OHCIState), - VMSTATE_UINT8(frt, OHCIState), - VMSTATE_UINT16(frame_number, OHCIState), - VMSTATE_UINT16(padding, OHCIState), - VMSTATE_UINT32(pstart, OHCIState), - VMSTATE_UINT32(lst, OHCIState), - VMSTATE_UINT32(rhdesc_a, OHCIState), - VMSTATE_UINT32(rhdesc_b, OHCIState), - VMSTATE_UINT32(rhstatus, OHCIState), - VMSTATE_STRUCT_ARRAY(rhport, OHCIState, OHCI_MAX_PORTS, 0, - vmstate_ohci_state_port, OHCIPort), - VMSTATE_UINT32(hstatus, OHCIState), - VMSTATE_UINT32(hmask, OHCIState), - VMSTATE_UINT32(hreset, OHCIState), - VMSTATE_UINT32(htest, OHCIState), - VMSTATE_UINT32(old_ctl, OHCIState), - VMSTATE_UINT8_ARRAY(usb_buf, OHCIState, 8192), - VMSTATE_UINT32(async_td, OHCIState), - VMSTATE_BOOL(async_complete, OHCIState), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_ohci_eof_timer, - NULL - } -}; - -static const VMStateDescription vmstate_ohci = { - .name = "ohci", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, OHCIPCIState), - VMSTATE_STRUCT(state, OHCIPCIState, 1, vmstate_ohci_state, OHCIState), - VMSTATE_END_OF_LIST() - } -}; - -static void ohci_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = usb_ohci_realize_pci; - k->exit = usb_ohci_exit; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_IPID_USB; - k->class_id = PCI_CLASS_SERIAL_USB; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - dc->desc = "Apple USB Controller"; - dc->props = ohci_pci_properties; - dc->hotpluggable = false; - dc->vmsd = &vmstate_ohci; - dc->reset = usb_ohci_reset_pci; -} - -static const TypeInfo ohci_pci_info = { - .name = TYPE_PCI_OHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(OHCIPCIState), - .class_init = ohci_pci_class_init, -}; - -static Property ohci_sysbus_properties[] = { - DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), - DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 3), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ohci_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = ohci_realize_pxa; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - dc->desc = "OHCI USB Controller"; - dc->props = ohci_sysbus_properties; - dc->reset = usb_ohci_reset_sysbus; -} - -static const TypeInfo ohci_sysbus_info = { - .name = TYPE_SYSBUS_OHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OHCISysBusState), - .class_init = ohci_sysbus_class_init, -}; - -static void ohci_register_types(void) -{ - type_register_static(&ohci_pci_info); - type_register_static(&ohci_sysbus_info); -} - -type_init(ohci_register_types) diff --git a/qemu/hw/usb/hcd-uhci.c b/qemu/hw/usb/hcd-uhci.c deleted file mode 100644 index ca72a80f2..000000000 --- a/qemu/hw/usb/hcd-uhci.c +++ /dev/null @@ -1,1435 +0,0 @@ -/* - * USB UHCI controller emulation - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Magor rewrite of the UHCI data structures parser and frame processor - * Support for fully async operation and multiple outstanding transactions - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/usb.h" -#include "hw/usb/uhci-regs.h" -#include "hw/pci/pci.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "qemu/iov.h" -#include "sysemu/dma.h" -#include "trace.h" -#include "qemu/main-loop.h" - -#define FRAME_TIMER_FREQ 1000 - -#define FRAME_MAX_LOOPS 256 - -/* Must be large enough to handle 10 frame delay for initial isoc requests */ -#define QH_VALID 32 - -#define MAX_FRAMES_PER_TICK (QH_VALID / 2) - -#define NB_PORTS 2 - -enum { - TD_RESULT_STOP_FRAME = 10, - TD_RESULT_COMPLETE, - TD_RESULT_NEXT_QH, - TD_RESULT_ASYNC_START, - TD_RESULT_ASYNC_CONT, -}; - -typedef struct UHCIState UHCIState; -typedef struct UHCIAsync UHCIAsync; -typedef struct UHCIQueue UHCIQueue; -typedef struct UHCIInfo UHCIInfo; -typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass; - -struct UHCIInfo { - const char *name; - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision; - uint8_t irq_pin; - void (*realize)(PCIDevice *dev, Error **errp); - bool unplug; -}; - -struct UHCIPCIDeviceClass { - PCIDeviceClass parent_class; - UHCIInfo info; -}; - -/* - * Pending async transaction. - * 'packet' must be the first field because completion - * handler does "(UHCIAsync *) pkt" cast. - */ - -struct UHCIAsync { - USBPacket packet; - uint8_t static_buf[64]; /* 64 bytes is enough, except for isoc packets */ - uint8_t *buf; - UHCIQueue *queue; - QTAILQ_ENTRY(UHCIAsync) next; - uint32_t td_addr; - uint8_t done; -}; - -struct UHCIQueue { - uint32_t qh_addr; - uint32_t token; - UHCIState *uhci; - USBEndpoint *ep; - QTAILQ_ENTRY(UHCIQueue) next; - QTAILQ_HEAD(asyncs_head, UHCIAsync) asyncs; - int8_t valid; -}; - -typedef struct UHCIPort { - USBPort port; - uint16_t ctrl; -} UHCIPort; - -struct UHCIState { - PCIDevice dev; - MemoryRegion io_bar; - USBBus bus; /* Note unused when we're a companion controller */ - uint16_t cmd; /* cmd register */ - uint16_t status; - uint16_t intr; /* interrupt enable register */ - uint16_t frnum; /* frame number */ - uint32_t fl_base_addr; /* frame list base address */ - uint8_t sof_timing; - uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ - int64_t expire_time; - QEMUTimer *frame_timer; - QEMUBH *bh; - uint32_t frame_bytes; - uint32_t frame_bandwidth; - bool completions_only; - UHCIPort ports[NB_PORTS]; - - /* Interrupts that should be raised at the end of the current frame. */ - uint32_t pending_int_mask; - - /* Active packets */ - QTAILQ_HEAD(, UHCIQueue) queues; - uint8_t num_ports_vmstate; - - /* Properties */ - char *masterbus; - uint32_t firstport; - uint32_t maxframes; -}; - -typedef struct UHCI_TD { - uint32_t link; - uint32_t ctrl; /* see TD_CTRL_xxx */ - uint32_t token; - uint32_t buffer; -} UHCI_TD; - -typedef struct UHCI_QH { - uint32_t link; - uint32_t el_link; -} UHCI_QH; - -static void uhci_async_cancel(UHCIAsync *async); -static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td); -static void uhci_resume(void *opaque); - -#define TYPE_UHCI "pci-uhci-usb" -#define UHCI(obj) OBJECT_CHECK(UHCIState, (obj), TYPE_UHCI) - -static inline int32_t uhci_queue_token(UHCI_TD *td) -{ - if ((td->token & (0xf << 15)) == 0) { - /* ctrl ep, cover ep and dev, not pid! */ - return td->token & 0x7ff00; - } else { - /* covers ep, dev, pid -> identifies the endpoint */ - return td->token & 0x7ffff; - } -} - -static UHCIQueue *uhci_queue_new(UHCIState *s, uint32_t qh_addr, UHCI_TD *td, - USBEndpoint *ep) -{ - UHCIQueue *queue; - - queue = g_new0(UHCIQueue, 1); - queue->uhci = s; - queue->qh_addr = qh_addr; - queue->token = uhci_queue_token(td); - queue->ep = ep; - QTAILQ_INIT(&queue->asyncs); - QTAILQ_INSERT_HEAD(&s->queues, queue, next); - queue->valid = QH_VALID; - trace_usb_uhci_queue_add(queue->token); - return queue; -} - -static void uhci_queue_free(UHCIQueue *queue, const char *reason) -{ - UHCIState *s = queue->uhci; - UHCIAsync *async; - - while (!QTAILQ_EMPTY(&queue->asyncs)) { - async = QTAILQ_FIRST(&queue->asyncs); - uhci_async_cancel(async); - } - usb_device_ep_stopped(queue->ep->dev, queue->ep); - - trace_usb_uhci_queue_del(queue->token, reason); - QTAILQ_REMOVE(&s->queues, queue, next); - g_free(queue); -} - -static UHCIQueue *uhci_queue_find(UHCIState *s, UHCI_TD *td) -{ - uint32_t token = uhci_queue_token(td); - UHCIQueue *queue; - - QTAILQ_FOREACH(queue, &s->queues, next) { - if (queue->token == token) { - return queue; - } - } - return NULL; -} - -static bool uhci_queue_verify(UHCIQueue *queue, uint32_t qh_addr, UHCI_TD *td, - uint32_t td_addr, bool queuing) -{ - UHCIAsync *first = QTAILQ_FIRST(&queue->asyncs); - uint32_t queue_token_addr = (queue->token >> 8) & 0x7f; - - return queue->qh_addr == qh_addr && - queue->token == uhci_queue_token(td) && - queue_token_addr == queue->ep->dev->addr && - (queuing || !(td->ctrl & TD_CTRL_ACTIVE) || first == NULL || - first->td_addr == td_addr); -} - -static UHCIAsync *uhci_async_alloc(UHCIQueue *queue, uint32_t td_addr) -{ - UHCIAsync *async = g_new0(UHCIAsync, 1); - - async->queue = queue; - async->td_addr = td_addr; - usb_packet_init(&async->packet); - trace_usb_uhci_packet_add(async->queue->token, async->td_addr); - - return async; -} - -static void uhci_async_free(UHCIAsync *async) -{ - trace_usb_uhci_packet_del(async->queue->token, async->td_addr); - usb_packet_cleanup(&async->packet); - if (async->buf != async->static_buf) { - g_free(async->buf); - } - g_free(async); -} - -static void uhci_async_link(UHCIAsync *async) -{ - UHCIQueue *queue = async->queue; - QTAILQ_INSERT_TAIL(&queue->asyncs, async, next); - trace_usb_uhci_packet_link_async(async->queue->token, async->td_addr); -} - -static void uhci_async_unlink(UHCIAsync *async) -{ - UHCIQueue *queue = async->queue; - QTAILQ_REMOVE(&queue->asyncs, async, next); - trace_usb_uhci_packet_unlink_async(async->queue->token, async->td_addr); -} - -static void uhci_async_cancel(UHCIAsync *async) -{ - uhci_async_unlink(async); - trace_usb_uhci_packet_cancel(async->queue->token, async->td_addr, - async->done); - if (!async->done) - usb_cancel_packet(&async->packet); - uhci_async_free(async); -} - -/* - * Mark all outstanding async packets as invalid. - * This is used for canceling them when TDs are removed by the HCD. - */ -static void uhci_async_validate_begin(UHCIState *s) -{ - UHCIQueue *queue; - - QTAILQ_FOREACH(queue, &s->queues, next) { - queue->valid--; - } -} - -/* - * Cancel async packets that are no longer valid - */ -static void uhci_async_validate_end(UHCIState *s) -{ - UHCIQueue *queue, *n; - - QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) { - if (!queue->valid) { - uhci_queue_free(queue, "validate-end"); - } - } -} - -static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev) -{ - UHCIQueue *queue, *n; - - QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) { - if (queue->ep->dev == dev) { - uhci_queue_free(queue, "cancel-device"); - } - } -} - -static void uhci_async_cancel_all(UHCIState *s) -{ - UHCIQueue *queue, *nq; - - QTAILQ_FOREACH_SAFE(queue, &s->queues, next, nq) { - uhci_queue_free(queue, "cancel-all"); - } -} - -static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t td_addr) -{ - UHCIQueue *queue; - UHCIAsync *async; - - QTAILQ_FOREACH(queue, &s->queues, next) { - QTAILQ_FOREACH(async, &queue->asyncs, next) { - if (async->td_addr == td_addr) { - return async; - } - } - } - return NULL; -} - -static void uhci_update_irq(UHCIState *s) -{ - int level; - if (((s->status2 & 1) && (s->intr & (1 << 2))) || - ((s->status2 & 2) && (s->intr & (1 << 3))) || - ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) || - ((s->status & UHCI_STS_RD) && (s->intr & (1 << 1))) || - (s->status & UHCI_STS_HSERR) || - (s->status & UHCI_STS_HCPERR)) { - level = 1; - } else { - level = 0; - } - pci_set_irq(&s->dev, level); -} - -static void uhci_reset(DeviceState *dev) -{ - PCIDevice *d = PCI_DEVICE(dev); - UHCIState *s = UHCI(d); - uint8_t *pci_conf; - int i; - UHCIPort *port; - - trace_usb_uhci_reset(); - - pci_conf = s->dev.config; - - pci_conf[0x6a] = 0x01; /* usb clock */ - pci_conf[0x6b] = 0x00; - s->cmd = 0; - s->status = UHCI_STS_HCHALTED; - s->status2 = 0; - s->intr = 0; - s->fl_base_addr = 0; - s->sof_timing = 64; - - for(i = 0; i < NB_PORTS; i++) { - port = &s->ports[i]; - port->ctrl = 0x0080; - if (port->port.dev && port->port.dev->attached) { - usb_port_reset(&port->port); - } - } - - uhci_async_cancel_all(s); - qemu_bh_cancel(s->bh); - uhci_update_irq(s); -} - -static const VMStateDescription vmstate_uhci_port = { - .name = "uhci port", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(ctrl, UHCIPort), - VMSTATE_END_OF_LIST() - } -}; - -static int uhci_post_load(void *opaque, int version_id) -{ - UHCIState *s = opaque; - - if (version_id < 2) { - s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ); - } - return 0; -} - -static const VMStateDescription vmstate_uhci = { - .name = "uhci", - .version_id = 3, - .minimum_version_id = 1, - .post_load = uhci_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, UHCIState), - VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState), - VMSTATE_STRUCT_ARRAY(ports, UHCIState, NB_PORTS, 1, - vmstate_uhci_port, UHCIPort), - VMSTATE_UINT16(cmd, UHCIState), - VMSTATE_UINT16(status, UHCIState), - VMSTATE_UINT16(intr, UHCIState), - VMSTATE_UINT16(frnum, UHCIState), - VMSTATE_UINT32(fl_base_addr, UHCIState), - VMSTATE_UINT8(sof_timing, UHCIState), - VMSTATE_UINT8(status2, UHCIState), - VMSTATE_TIMER_PTR(frame_timer, UHCIState), - VMSTATE_INT64_V(expire_time, UHCIState, 2), - VMSTATE_UINT32_V(pending_int_mask, UHCIState, 3), - VMSTATE_END_OF_LIST() - } -}; - -static void uhci_port_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - UHCIState *s = opaque; - - trace_usb_uhci_mmio_writew(addr, val); - - switch(addr) { - case 0x00: - if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { - /* start frame processing */ - trace_usb_uhci_schedule_start(); - s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ); - timer_mod(s->frame_timer, s->expire_time); - s->status &= ~UHCI_STS_HCHALTED; - } else if (!(val & UHCI_CMD_RS)) { - s->status |= UHCI_STS_HCHALTED; - } - if (val & UHCI_CMD_GRESET) { - UHCIPort *port; - int i; - - /* send reset on the USB bus */ - for(i = 0; i < NB_PORTS; i++) { - port = &s->ports[i]; - usb_device_reset(port->port.dev); - } - uhci_reset(DEVICE(s)); - return; - } - if (val & UHCI_CMD_HCRESET) { - uhci_reset(DEVICE(s)); - return; - } - s->cmd = val; - if (val & UHCI_CMD_EGSM) { - if ((s->ports[0].ctrl & UHCI_PORT_RD) || - (s->ports[1].ctrl & UHCI_PORT_RD)) { - uhci_resume(s); - } - } - break; - case 0x02: - s->status &= ~val; - /* XXX: the chip spec is not coherent, so we add a hidden - register to distinguish between IOC and SPD */ - if (val & UHCI_STS_USBINT) - s->status2 = 0; - uhci_update_irq(s); - break; - case 0x04: - s->intr = val; - uhci_update_irq(s); - break; - case 0x06: - if (s->status & UHCI_STS_HCHALTED) - s->frnum = val & 0x7ff; - break; - case 0x08: - s->fl_base_addr &= 0xffff0000; - s->fl_base_addr |= val & ~0xfff; - break; - case 0x0a: - s->fl_base_addr &= 0x0000ffff; - s->fl_base_addr |= (val << 16); - break; - case 0x0c: - s->sof_timing = val & 0xff; - break; - case 0x10 ... 0x1f: - { - UHCIPort *port; - USBDevice *dev; - int n; - - n = (addr >> 1) & 7; - if (n >= NB_PORTS) - return; - port = &s->ports[n]; - dev = port->port.dev; - if (dev && dev->attached) { - /* port reset */ - if ( (val & UHCI_PORT_RESET) && - !(port->ctrl & UHCI_PORT_RESET) ) { - usb_device_reset(dev); - } - } - port->ctrl &= UHCI_PORT_READ_ONLY; - /* enabled may only be set if a device is connected */ - if (!(port->ctrl & UHCI_PORT_CCS)) { - val &= ~UHCI_PORT_EN; - } - port->ctrl |= (val & ~UHCI_PORT_READ_ONLY); - /* some bits are reset when a '1' is written to them */ - port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR); - } - break; - } -} - -static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) -{ - UHCIState *s = opaque; - uint32_t val; - - switch(addr) { - case 0x00: - val = s->cmd; - break; - case 0x02: - val = s->status; - break; - case 0x04: - val = s->intr; - break; - case 0x06: - val = s->frnum; - break; - case 0x08: - val = s->fl_base_addr & 0xffff; - break; - case 0x0a: - val = (s->fl_base_addr >> 16) & 0xffff; - break; - case 0x0c: - val = s->sof_timing; - break; - case 0x10 ... 0x1f: - { - UHCIPort *port; - int n; - n = (addr >> 1) & 7; - if (n >= NB_PORTS) - goto read_default; - port = &s->ports[n]; - val = port->ctrl; - } - break; - default: - read_default: - val = 0xff7f; /* disabled port */ - break; - } - - trace_usb_uhci_mmio_readw(addr, val); - - return val; -} - -/* signal resume if controller suspended */ -static void uhci_resume (void *opaque) -{ - UHCIState *s = (UHCIState *)opaque; - - if (!s) - return; - - if (s->cmd & UHCI_CMD_EGSM) { - s->cmd |= UHCI_CMD_FGR; - s->status |= UHCI_STS_RD; - uhci_update_irq(s); - } -} - -static void uhci_attach(USBPort *port1) -{ - UHCIState *s = port1->opaque; - UHCIPort *port = &s->ports[port1->index]; - - /* set connect status */ - port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; - - /* update speed */ - if (port->port.dev->speed == USB_SPEED_LOW) { - port->ctrl |= UHCI_PORT_LSDA; - } else { - port->ctrl &= ~UHCI_PORT_LSDA; - } - - uhci_resume(s); -} - -static void uhci_detach(USBPort *port1) -{ - UHCIState *s = port1->opaque; - UHCIPort *port = &s->ports[port1->index]; - - uhci_async_cancel_device(s, port1->dev); - - /* set connect status */ - if (port->ctrl & UHCI_PORT_CCS) { - port->ctrl &= ~UHCI_PORT_CCS; - port->ctrl |= UHCI_PORT_CSC; - } - /* disable port */ - if (port->ctrl & UHCI_PORT_EN) { - port->ctrl &= ~UHCI_PORT_EN; - port->ctrl |= UHCI_PORT_ENC; - } - - uhci_resume(s); -} - -static void uhci_child_detach(USBPort *port1, USBDevice *child) -{ - UHCIState *s = port1->opaque; - - uhci_async_cancel_device(s, child); -} - -static void uhci_wakeup(USBPort *port1) -{ - UHCIState *s = port1->opaque; - UHCIPort *port = &s->ports[port1->index]; - - if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) { - port->ctrl |= UHCI_PORT_RD; - uhci_resume(s); - } -} - -static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr) -{ - USBDevice *dev; - int i; - - for (i = 0; i < NB_PORTS; i++) { - UHCIPort *port = &s->ports[i]; - if (!(port->ctrl & UHCI_PORT_EN)) { - continue; - } - dev = usb_find_device(&port->port, addr); - if (dev != NULL) { - return dev; - } - } - return NULL; -} - -static void uhci_read_td(UHCIState *s, UHCI_TD *td, uint32_t link) -{ - pci_dma_read(&s->dev, link & ~0xf, td, sizeof(*td)); - le32_to_cpus(&td->link); - le32_to_cpus(&td->ctrl); - le32_to_cpus(&td->token); - le32_to_cpus(&td->buffer); -} - -static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr, - int status, uint32_t *int_mask) -{ - uint32_t queue_token = uhci_queue_token(td); - int ret; - - switch (status) { - case USB_RET_NAK: - td->ctrl |= TD_CTRL_NAK; - return TD_RESULT_NEXT_QH; - - case USB_RET_STALL: - td->ctrl |= TD_CTRL_STALL; - trace_usb_uhci_packet_complete_stall(queue_token, td_addr); - ret = TD_RESULT_NEXT_QH; - break; - - case USB_RET_BABBLE: - td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; - /* frame interrupted */ - trace_usb_uhci_packet_complete_babble(queue_token, td_addr); - ret = TD_RESULT_STOP_FRAME; - break; - - case USB_RET_IOERROR: - case USB_RET_NODEV: - default: - td->ctrl |= TD_CTRL_TIMEOUT; - td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT); - trace_usb_uhci_packet_complete_error(queue_token, td_addr); - ret = TD_RESULT_NEXT_QH; - break; - } - - td->ctrl &= ~TD_CTRL_ACTIVE; - s->status |= UHCI_STS_USBERR; - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; - } - uhci_update_irq(s); - return ret; -} - -static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask) -{ - int len = 0, max_len; - uint8_t pid; - - max_len = ((td->token >> 21) + 1) & 0x7ff; - pid = td->token & 0xff; - - if (td->ctrl & TD_CTRL_IOS) - td->ctrl &= ~TD_CTRL_ACTIVE; - - if (async->packet.status != USB_RET_SUCCESS) { - return uhci_handle_td_error(s, td, async->td_addr, - async->packet.status, int_mask); - } - - len = async->packet.actual_length; - td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); - - /* The NAK bit may have been set by a previous frame, so clear it - here. The docs are somewhat unclear, but win2k relies on this - behavior. */ - td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK); - if (td->ctrl & TD_CTRL_IOC) - *int_mask |= 0x01; - - if (pid == USB_TOKEN_IN) { - pci_dma_write(&s->dev, td->buffer, async->buf, len); - if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { - *int_mask |= 0x02; - /* short packet: do not update QH */ - trace_usb_uhci_packet_complete_shortxfer(async->queue->token, - async->td_addr); - return TD_RESULT_NEXT_QH; - } - } - - /* success */ - trace_usb_uhci_packet_complete_success(async->queue->token, - async->td_addr); - return TD_RESULT_COMPLETE; -} - -static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, - UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask) -{ - int ret, max_len; - bool spd; - bool queuing = (q != NULL); - uint8_t pid = td->token & 0xff; - UHCIAsync *async; - - async = uhci_async_find_td(s, td_addr); - if (async) { - if (uhci_queue_verify(async->queue, qh_addr, td, td_addr, queuing)) { - assert(q == NULL || q == async->queue); - q = async->queue; - } else { - uhci_queue_free(async->queue, "guest re-used pending td"); - async = NULL; - } - } - - if (q == NULL) { - q = uhci_queue_find(s, td); - if (q && !uhci_queue_verify(q, qh_addr, td, td_addr, queuing)) { - uhci_queue_free(q, "guest re-used qh"); - q = NULL; - } - } - - if (q) { - q->valid = QH_VALID; - } - - /* Is active ? */ - if (!(td->ctrl & TD_CTRL_ACTIVE)) { - if (async) { - /* Guest marked a pending td non-active, cancel the queue */ - uhci_queue_free(async->queue, "pending td non-active"); - } - /* - * ehci11d spec page 22: "Even if the Active bit in the TD is already - * cleared when the TD is fetched ... an IOC interrupt is generated" - */ - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; - } - return TD_RESULT_NEXT_QH; - } - - switch (pid) { - case USB_TOKEN_OUT: - case USB_TOKEN_SETUP: - case USB_TOKEN_IN: - break; - default: - /* invalid pid : frame interrupted */ - s->status |= UHCI_STS_HCPERR; - s->cmd &= ~UHCI_CMD_RS; - uhci_update_irq(s); - return TD_RESULT_STOP_FRAME; - } - - if (async) { - if (queuing) { - /* we are busy filling the queue, we are not prepared - to consume completed packages then, just leave them - in async state */ - return TD_RESULT_ASYNC_CONT; - } - if (!async->done) { - UHCI_TD last_td; - UHCIAsync *last = QTAILQ_LAST(&async->queue->asyncs, asyncs_head); - /* - * While we are waiting for the current td to complete, the guest - * may have added more tds to the queue. Note we re-read the td - * rather then caching it, as we want to see guest made changes! - */ - uhci_read_td(s, &last_td, last->td_addr); - uhci_queue_fill(async->queue, &last_td); - - return TD_RESULT_ASYNC_CONT; - } - uhci_async_unlink(async); - goto done; - } - - if (s->completions_only) { - return TD_RESULT_ASYNC_CONT; - } - - /* Allocate new packet */ - if (q == NULL) { - USBDevice *dev = uhci_find_device(s, (td->token >> 8) & 0x7f); - USBEndpoint *ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); - - if (ep == NULL) { - return uhci_handle_td_error(s, td, td_addr, USB_RET_NODEV, - int_mask); - } - q = uhci_queue_new(s, qh_addr, td, ep); - } - async = uhci_async_alloc(q, td_addr); - - max_len = ((td->token >> 21) + 1) & 0x7ff; - spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0); - usb_packet_setup(&async->packet, pid, q->ep, 0, td_addr, spd, - (td->ctrl & TD_CTRL_IOC) != 0); - if (max_len <= sizeof(async->static_buf)) { - async->buf = async->static_buf; - } else { - async->buf = g_malloc(max_len); - } - usb_packet_addbuf(&async->packet, async->buf, max_len); - - switch(pid) { - case USB_TOKEN_OUT: - case USB_TOKEN_SETUP: - pci_dma_read(&s->dev, td->buffer, async->buf, max_len); - usb_handle_packet(q->ep->dev, &async->packet); - if (async->packet.status == USB_RET_SUCCESS) { - async->packet.actual_length = max_len; - } - break; - - case USB_TOKEN_IN: - usb_handle_packet(q->ep->dev, &async->packet); - break; - - default: - abort(); /* Never to execute */ - } - - if (async->packet.status == USB_RET_ASYNC) { - uhci_async_link(async); - if (!queuing) { - uhci_queue_fill(q, td); - } - return TD_RESULT_ASYNC_START; - } - -done: - ret = uhci_complete_td(s, td, async, int_mask); - uhci_async_free(async); - return ret; -} - -static void uhci_async_complete(USBPort *port, USBPacket *packet) -{ - UHCIAsync *async = container_of(packet, UHCIAsync, packet); - UHCIState *s = async->queue->uhci; - - if (packet->status == USB_RET_REMOVE_FROM_QUEUE) { - uhci_async_cancel(async); - return; - } - - async->done = 1; - /* Force processing of this packet *now*, needed for migration */ - s->completions_only = true; - qemu_bh_schedule(s->bh); -} - -static int is_valid(uint32_t link) -{ - return (link & 1) == 0; -} - -static int is_qh(uint32_t link) -{ - return (link & 2) != 0; -} - -static int depth_first(uint32_t link) -{ - return (link & 4) != 0; -} - -/* QH DB used for detecting QH loops */ -#define UHCI_MAX_QUEUES 128 -typedef struct { - uint32_t addr[UHCI_MAX_QUEUES]; - int count; -} QhDb; - -static void qhdb_reset(QhDb *db) -{ - db->count = 0; -} - -/* Add QH to DB. Returns 1 if already present or DB is full. */ -static int qhdb_insert(QhDb *db, uint32_t addr) -{ - int i; - for (i = 0; i < db->count; i++) - if (db->addr[i] == addr) - return 1; - - if (db->count >= UHCI_MAX_QUEUES) - return 1; - - db->addr[db->count++] = addr; - return 0; -} - -static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td) -{ - uint32_t int_mask = 0; - uint32_t plink = td->link; - UHCI_TD ptd; - int ret; - - while (is_valid(plink)) { - uhci_read_td(q->uhci, &ptd, plink); - if (!(ptd.ctrl & TD_CTRL_ACTIVE)) { - break; - } - if (uhci_queue_token(&ptd) != q->token) { - break; - } - trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token); - ret = uhci_handle_td(q->uhci, q, q->qh_addr, &ptd, plink, &int_mask); - if (ret == TD_RESULT_ASYNC_CONT) { - break; - } - assert(ret == TD_RESULT_ASYNC_START); - assert(int_mask == 0); - plink = ptd.link; - } - usb_device_flush_ep_queue(q->ep->dev, q->ep); -} - -static void uhci_process_frame(UHCIState *s) -{ - uint32_t frame_addr, link, old_td_ctrl, val, int_mask; - uint32_t curr_qh, td_count = 0; - int cnt, ret; - UHCI_TD td; - UHCI_QH qh; - QhDb qhdb; - - frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); - - pci_dma_read(&s->dev, frame_addr, &link, 4); - le32_to_cpus(&link); - - int_mask = 0; - curr_qh = 0; - - qhdb_reset(&qhdb); - - for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) { - if (!s->completions_only && s->frame_bytes >= s->frame_bandwidth) { - /* We've reached the usb 1.1 bandwidth, which is - 1280 bytes/frame, stop processing */ - trace_usb_uhci_frame_stop_bandwidth(); - break; - } - if (is_qh(link)) { - /* QH */ - trace_usb_uhci_qh_load(link & ~0xf); - - if (qhdb_insert(&qhdb, link)) { - /* - * We're going in circles. Which is not a bug because - * HCD is allowed to do that as part of the BW management. - * - * Stop processing here if no transaction has been done - * since we've been here last time. - */ - if (td_count == 0) { - trace_usb_uhci_frame_loop_stop_idle(); - break; - } else { - trace_usb_uhci_frame_loop_continue(); - td_count = 0; - qhdb_reset(&qhdb); - qhdb_insert(&qhdb, link); - } - } - - pci_dma_read(&s->dev, link & ~0xf, &qh, sizeof(qh)); - le32_to_cpus(&qh.link); - le32_to_cpus(&qh.el_link); - - if (!is_valid(qh.el_link)) { - /* QH w/o elements */ - curr_qh = 0; - link = qh.link; - } else { - /* QH with elements */ - curr_qh = link; - link = qh.el_link; - } - continue; - } - - /* TD */ - uhci_read_td(s, &td, link); - trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token); - - old_td_ctrl = td.ctrl; - ret = uhci_handle_td(s, NULL, curr_qh, &td, link, &int_mask); - if (old_td_ctrl != td.ctrl) { - /* update the status bits of the TD */ - val = cpu_to_le32(td.ctrl); - pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); - } - - switch (ret) { - case TD_RESULT_STOP_FRAME: /* interrupted frame */ - goto out; - - case TD_RESULT_NEXT_QH: - case TD_RESULT_ASYNC_CONT: - trace_usb_uhci_td_nextqh(curr_qh & ~0xf, link & ~0xf); - link = curr_qh ? qh.link : td.link; - continue; - - case TD_RESULT_ASYNC_START: - trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf); - link = curr_qh ? qh.link : td.link; - continue; - - case TD_RESULT_COMPLETE: - trace_usb_uhci_td_complete(curr_qh & ~0xf, link & ~0xf); - link = td.link; - td_count++; - s->frame_bytes += (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 */ - curr_qh = 0; - link = qh.link; - } - } - break; - - default: - assert(!"unknown return code"); - } - - /* go to the next entry */ - } - -out: - s->pending_int_mask |= int_mask; -} - -static void uhci_bh(void *opaque) -{ - UHCIState *s = opaque; - uhci_process_frame(s); -} - -static void uhci_frame_timer(void *opaque) -{ - UHCIState *s = opaque; - uint64_t t_now, t_last_run; - int i, frames; - const uint64_t frame_t = NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ; - - s->completions_only = false; - qemu_bh_cancel(s->bh); - - if (!(s->cmd & UHCI_CMD_RS)) { - /* Full stop */ - trace_usb_uhci_schedule_stop(); - timer_del(s->frame_timer); - uhci_async_cancel_all(s); - /* set hchalted bit in status - UHCI11D 2.1.2 */ - s->status |= UHCI_STS_HCHALTED; - return; - } - - /* We still store expire_time in our state, for migration */ - t_last_run = s->expire_time - frame_t; - t_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - /* Process up to MAX_FRAMES_PER_TICK frames */ - frames = (t_now - t_last_run) / frame_t; - if (frames > s->maxframes) { - int skipped = frames - s->maxframes; - s->expire_time += skipped * frame_t; - s->frnum = (s->frnum + skipped) & 0x7ff; - frames -= skipped; - } - if (frames > MAX_FRAMES_PER_TICK) { - frames = MAX_FRAMES_PER_TICK; - } - - for (i = 0; i < frames; i++) { - s->frame_bytes = 0; - trace_usb_uhci_frame_start(s->frnum); - uhci_async_validate_begin(s); - uhci_process_frame(s); - uhci_async_validate_end(s); - /* The spec says frnum is the frame currently being processed, and - * the guest must look at frnum - 1 on interrupt, so inc frnum now */ - s->frnum = (s->frnum + 1) & 0x7ff; - s->expire_time += frame_t; - } - - /* Complete the previous frame(s) */ - if (s->pending_int_mask) { - s->status2 |= s->pending_int_mask; - s->status |= UHCI_STS_USBINT; - uhci_update_irq(s); - } - s->pending_int_mask = 0; - - timer_mod(s->frame_timer, t_now + frame_t); -} - -static const MemoryRegionOps uhci_ioport_ops = { - .read = uhci_port_read, - .write = uhci_port_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 2, - .impl.max_access_size = 2, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static USBPortOps uhci_port_ops = { - .attach = uhci_attach, - .detach = uhci_detach, - .child_detach = uhci_child_detach, - .wakeup = uhci_wakeup, - .complete = uhci_async_complete, -}; - -static USBBusOps uhci_bus_ops = { -}; - -static void usb_uhci_common_realize(PCIDevice *dev, Error **errp) -{ - Error *err = NULL; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - UHCIPCIDeviceClass *u = container_of(pc, UHCIPCIDeviceClass, parent_class); - UHCIState *s = UHCI(dev); - uint8_t *pci_conf = s->dev.config; - int i; - - pci_conf[PCI_CLASS_PROG] = 0x00; - /* TODO: reset value should be 0. */ - pci_conf[USB_SBRN] = USB_RELEASE_1; // release number - - pci_config_set_interrupt_pin(pci_conf, u->info.irq_pin + 1); - - if (s->masterbus) { - USBPort *ports[NB_PORTS]; - for(i = 0; i < NB_PORTS; i++) { - ports[i] = &s->ports[i].port; - } - usb_register_companion(s->masterbus, ports, NB_PORTS, - s->firstport, s, &uhci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL, - &err); - if (err) { - error_propagate(errp, err); - return; - } - } else { - usb_bus_new(&s->bus, sizeof(s->bus), &uhci_bus_ops, DEVICE(dev)); - for (i = 0; i < NB_PORTS; i++) { - usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - } - } - s->bh = qemu_bh_new(uhci_bh, s); - s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, uhci_frame_timer, s); - s->num_ports_vmstate = NB_PORTS; - QTAILQ_INIT(&s->queues); - - memory_region_init_io(&s->io_bar, OBJECT(s), &uhci_ioport_ops, s, - "uhci", 0x20); - - /* Use region 4 for consistency with real hardware. BSD guests seem - to rely on this. */ - pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); -} - -static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) -{ - UHCIState *s = UHCI(dev); - uint8_t *pci_conf = s->dev.config; - - /* USB misc control 1/2 */ - pci_set_long(pci_conf + 0x40,0x00001000); - /* PM capability */ - pci_set_long(pci_conf + 0x80,0x00020001); - /* USB legacy support */ - pci_set_long(pci_conf + 0xc0,0x00002000); - - usb_uhci_common_realize(dev, errp); -} - -static void usb_uhci_exit(PCIDevice *dev) -{ - UHCIState *s = UHCI(dev); - - trace_usb_uhci_exit(); - - if (s->frame_timer) { - timer_del(s->frame_timer); - timer_free(s->frame_timer); - s->frame_timer = NULL; - } - - if (s->bh) { - qemu_bh_delete(s->bh); - } - - uhci_async_cancel_all(s); - - if (!s->masterbus) { - usb_bus_release(&s->bus); - } -} - -static Property uhci_properties_companion[] = { - DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), - DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), - DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), - DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; -static Property uhci_properties_standalone[] = { - DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), - DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static void uhci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->reset = uhci_reset; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo uhci_pci_type_info = { - .name = TYPE_UHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_size = sizeof(UHCIPCIDeviceClass), - .abstract = true, - .class_init = uhci_class_init, -}; - -static void uhci_data_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class); - UHCIInfo *info = data; - - k->realize = info->realize ? info->realize : usb_uhci_common_realize; - k->exit = info->unplug ? usb_uhci_exit : NULL; - k->vendor_id = info->vendor_id; - k->device_id = info->device_id; - k->revision = info->revision; - if (!info->unplug) { - /* uhci controllers in companion setups can't be hotplugged */ - dc->hotpluggable = false; - dc->props = uhci_properties_companion; - } else { - dc->props = uhci_properties_standalone; - } - u->info = *info; -} - -static UHCIInfo uhci_info[] = { - { - .name = "piix3-usb-uhci", - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, - .revision = 0x01, - .irq_pin = 3, - .unplug = true, - },{ - .name = "piix4-usb-uhci", - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, - .revision = 0x01, - .irq_pin = 3, - .unplug = true, - },{ - .name = "vt82c686b-usb-uhci", - .vendor_id = PCI_VENDOR_ID_VIA, - .device_id = PCI_DEVICE_ID_VIA_UHCI, - .revision = 0x01, - .irq_pin = 3, - .realize = usb_uhci_vt82c686b_realize, - .unplug = true, - },{ - .name = "ich9-usb-uhci1", /* 00:1d.0 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, - .revision = 0x03, - .irq_pin = 0, - .unplug = false, - },{ - .name = "ich9-usb-uhci2", /* 00:1d.1 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, - .revision = 0x03, - .irq_pin = 1, - .unplug = false, - },{ - .name = "ich9-usb-uhci3", /* 00:1d.2 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, - .revision = 0x03, - .irq_pin = 2, - .unplug = false, - },{ - .name = "ich9-usb-uhci4", /* 00:1a.0 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI4, - .revision = 0x03, - .irq_pin = 0, - .unplug = false, - },{ - .name = "ich9-usb-uhci5", /* 00:1a.1 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI5, - .revision = 0x03, - .irq_pin = 1, - .unplug = false, - },{ - .name = "ich9-usb-uhci6", /* 00:1a.2 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI6, - .revision = 0x03, - .irq_pin = 2, - .unplug = false, - } -}; - -static void uhci_register_types(void) -{ - TypeInfo uhci_type_info = { - .parent = TYPE_UHCI, - .class_init = uhci_data_class_init, - }; - int i; - - type_register_static(&uhci_pci_type_info); - - for (i = 0; i < ARRAY_SIZE(uhci_info); i++) { - uhci_type_info.name = uhci_info[i].name; - uhci_type_info.class_data = uhci_info + i; - type_register(&uhci_type_info); - } -} - -type_init(uhci_register_types) diff --git a/qemu/hw/usb/hcd-xhci.c b/qemu/hw/usb/hcd-xhci.c deleted file mode 100644 index bcde8a2f4..000000000 --- a/qemu/hw/usb/hcd-xhci.c +++ /dev/null @@ -1,3917 +0,0 @@ -/* - * USB xHCI controller emulation - * - * Copyright (c) 2011 Securiforest - * Date: 2011-05-11 ; Author: Hector Martin <hector@marcansoft.com> - * Based on usb-ohci.c, emulates Renesas NEC USB 3.0 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/pci/pci.h" -#include "hw/pci/msi.h" -#include "hw/pci/msix.h" -#include "trace.h" - -//#define DEBUG_XHCI -//#define DEBUG_DATA - -#ifdef DEBUG_XHCI -#define DPRINTF(...) fprintf(stderr, __VA_ARGS__) -#else -#define DPRINTF(...) do {} while (0) -#endif -#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \ - __func__, __LINE__, _msg); abort(); } while (0) - -#define MAXPORTS_2 15 -#define MAXPORTS_3 15 - -#define MAXPORTS (MAXPORTS_2+MAXPORTS_3) -#define MAXSLOTS 64 -#define MAXINTRS 16 - -#define TD_QUEUE 24 - -/* Very pessimistic, let's hope it's enough for all cases */ -#define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS) -/* Do not deliver ER Full events. NEC's driver does some things not bound - * to the specs when it gets them */ -#define ER_FULL_HACK - -#define LEN_CAP 0x40 -#define LEN_OPER (0x400 + 0x10 * MAXPORTS) -#define LEN_RUNTIME ((MAXINTRS + 1) * 0x20) -#define LEN_DOORBELL ((MAXSLOTS + 1) * 0x20) - -#define OFF_OPER LEN_CAP -#define OFF_RUNTIME 0x1000 -#define OFF_DOORBELL 0x2000 -#define OFF_MSIX_TABLE 0x3000 -#define OFF_MSIX_PBA 0x3800 -/* must be power of 2 */ -#define LEN_REGS 0x4000 - -#if (OFF_OPER + LEN_OPER) > OFF_RUNTIME -#error Increase OFF_RUNTIME -#endif -#if (OFF_RUNTIME + LEN_RUNTIME) > OFF_DOORBELL -#error Increase OFF_DOORBELL -#endif -#if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS -# error Increase LEN_REGS -#endif - -/* bit definitions */ -#define USBCMD_RS (1<<0) -#define USBCMD_HCRST (1<<1) -#define USBCMD_INTE (1<<2) -#define USBCMD_HSEE (1<<3) -#define USBCMD_LHCRST (1<<7) -#define USBCMD_CSS (1<<8) -#define USBCMD_CRS (1<<9) -#define USBCMD_EWE (1<<10) -#define USBCMD_EU3S (1<<11) - -#define USBSTS_HCH (1<<0) -#define USBSTS_HSE (1<<2) -#define USBSTS_EINT (1<<3) -#define USBSTS_PCD (1<<4) -#define USBSTS_SSS (1<<8) -#define USBSTS_RSS (1<<9) -#define USBSTS_SRE (1<<10) -#define USBSTS_CNR (1<<11) -#define USBSTS_HCE (1<<12) - - -#define PORTSC_CCS (1<<0) -#define PORTSC_PED (1<<1) -#define PORTSC_OCA (1<<3) -#define PORTSC_PR (1<<4) -#define PORTSC_PLS_SHIFT 5 -#define PORTSC_PLS_MASK 0xf -#define PORTSC_PP (1<<9) -#define PORTSC_SPEED_SHIFT 10 -#define PORTSC_SPEED_MASK 0xf -#define PORTSC_SPEED_FULL (1<<10) -#define PORTSC_SPEED_LOW (2<<10) -#define PORTSC_SPEED_HIGH (3<<10) -#define PORTSC_SPEED_SUPER (4<<10) -#define PORTSC_PIC_SHIFT 14 -#define PORTSC_PIC_MASK 0x3 -#define PORTSC_LWS (1<<16) -#define PORTSC_CSC (1<<17) -#define PORTSC_PEC (1<<18) -#define PORTSC_WRC (1<<19) -#define PORTSC_OCC (1<<20) -#define PORTSC_PRC (1<<21) -#define PORTSC_PLC (1<<22) -#define PORTSC_CEC (1<<23) -#define PORTSC_CAS (1<<24) -#define PORTSC_WCE (1<<25) -#define PORTSC_WDE (1<<26) -#define PORTSC_WOE (1<<27) -#define PORTSC_DR (1<<30) -#define PORTSC_WPR (1<<31) - -#define CRCR_RCS (1<<0) -#define CRCR_CS (1<<1) -#define CRCR_CA (1<<2) -#define CRCR_CRR (1<<3) - -#define IMAN_IP (1<<0) -#define IMAN_IE (1<<1) - -#define ERDP_EHB (1<<3) - -#define TRB_SIZE 16 -typedef struct XHCITRB { - uint64_t parameter; - uint32_t status; - uint32_t control; - dma_addr_t addr; - bool ccs; -} XHCITRB; - -enum { - PLS_U0 = 0, - PLS_U1 = 1, - PLS_U2 = 2, - PLS_U3 = 3, - PLS_DISABLED = 4, - PLS_RX_DETECT = 5, - PLS_INACTIVE = 6, - PLS_POLLING = 7, - PLS_RECOVERY = 8, - PLS_HOT_RESET = 9, - PLS_COMPILANCE_MODE = 10, - PLS_TEST_MODE = 11, - PLS_RESUME = 15, -}; - -typedef enum TRBType { - TRB_RESERVED = 0, - TR_NORMAL, - TR_SETUP, - TR_DATA, - TR_STATUS, - TR_ISOCH, - TR_LINK, - TR_EVDATA, - TR_NOOP, - CR_ENABLE_SLOT, - CR_DISABLE_SLOT, - CR_ADDRESS_DEVICE, - CR_CONFIGURE_ENDPOINT, - CR_EVALUATE_CONTEXT, - CR_RESET_ENDPOINT, - CR_STOP_ENDPOINT, - CR_SET_TR_DEQUEUE, - CR_RESET_DEVICE, - CR_FORCE_EVENT, - CR_NEGOTIATE_BW, - CR_SET_LATENCY_TOLERANCE, - CR_GET_PORT_BANDWIDTH, - CR_FORCE_HEADER, - CR_NOOP, - ER_TRANSFER = 32, - ER_COMMAND_COMPLETE, - ER_PORT_STATUS_CHANGE, - ER_BANDWIDTH_REQUEST, - ER_DOORBELL, - ER_HOST_CONTROLLER, - ER_DEVICE_NOTIFICATION, - ER_MFINDEX_WRAP, - /* vendor specific bits */ - CR_VENDOR_VIA_CHALLENGE_RESPONSE = 48, - CR_VENDOR_NEC_FIRMWARE_REVISION = 49, - CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50, -} TRBType; - -#define CR_LINK TR_LINK - -typedef enum TRBCCode { - CC_INVALID = 0, - CC_SUCCESS, - CC_DATA_BUFFER_ERROR, - CC_BABBLE_DETECTED, - CC_USB_TRANSACTION_ERROR, - CC_TRB_ERROR, - CC_STALL_ERROR, - CC_RESOURCE_ERROR, - CC_BANDWIDTH_ERROR, - CC_NO_SLOTS_ERROR, - CC_INVALID_STREAM_TYPE_ERROR, - CC_SLOT_NOT_ENABLED_ERROR, - CC_EP_NOT_ENABLED_ERROR, - CC_SHORT_PACKET, - CC_RING_UNDERRUN, - CC_RING_OVERRUN, - CC_VF_ER_FULL, - CC_PARAMETER_ERROR, - CC_BANDWIDTH_OVERRUN, - CC_CONTEXT_STATE_ERROR, - CC_NO_PING_RESPONSE_ERROR, - CC_EVENT_RING_FULL_ERROR, - CC_INCOMPATIBLE_DEVICE_ERROR, - CC_MISSED_SERVICE_ERROR, - CC_COMMAND_RING_STOPPED, - CC_COMMAND_ABORTED, - CC_STOPPED, - CC_STOPPED_LENGTH_INVALID, - CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29, - CC_ISOCH_BUFFER_OVERRUN = 31, - CC_EVENT_LOST_ERROR, - CC_UNDEFINED_ERROR, - CC_INVALID_STREAM_ID_ERROR, - CC_SECONDARY_BANDWIDTH_ERROR, - CC_SPLIT_TRANSACTION_ERROR -} TRBCCode; - -#define TRB_C (1<<0) -#define TRB_TYPE_SHIFT 10 -#define TRB_TYPE_MASK 0x3f -#define TRB_TYPE(t) (((t).control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK) - -#define TRB_EV_ED (1<<2) - -#define TRB_TR_ENT (1<<1) -#define TRB_TR_ISP (1<<2) -#define TRB_TR_NS (1<<3) -#define TRB_TR_CH (1<<4) -#define TRB_TR_IOC (1<<5) -#define TRB_TR_IDT (1<<6) -#define TRB_TR_TBC_SHIFT 7 -#define TRB_TR_TBC_MASK 0x3 -#define TRB_TR_BEI (1<<9) -#define TRB_TR_TLBPC_SHIFT 16 -#define TRB_TR_TLBPC_MASK 0xf -#define TRB_TR_FRAMEID_SHIFT 20 -#define TRB_TR_FRAMEID_MASK 0x7ff -#define TRB_TR_SIA (1<<31) - -#define TRB_TR_DIR (1<<16) - -#define TRB_CR_SLOTID_SHIFT 24 -#define TRB_CR_SLOTID_MASK 0xff -#define TRB_CR_EPID_SHIFT 16 -#define TRB_CR_EPID_MASK 0x1f - -#define TRB_CR_BSR (1<<9) -#define TRB_CR_DC (1<<9) - -#define TRB_LK_TC (1<<1) - -#define TRB_INTR_SHIFT 22 -#define TRB_INTR_MASK 0x3ff -#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK) - -#define EP_TYPE_MASK 0x7 -#define EP_TYPE_SHIFT 3 - -#define EP_STATE_MASK 0x7 -#define EP_DISABLED (0<<0) -#define EP_RUNNING (1<<0) -#define EP_HALTED (2<<0) -#define EP_STOPPED (3<<0) -#define EP_ERROR (4<<0) - -#define SLOT_STATE_MASK 0x1f -#define SLOT_STATE_SHIFT 27 -#define SLOT_STATE(s) (((s)>>SLOT_STATE_SHIFT)&SLOT_STATE_MASK) -#define SLOT_ENABLED 0 -#define SLOT_DEFAULT 1 -#define SLOT_ADDRESSED 2 -#define SLOT_CONFIGURED 3 - -#define SLOT_CONTEXT_ENTRIES_MASK 0x1f -#define SLOT_CONTEXT_ENTRIES_SHIFT 27 - -typedef struct XHCIState XHCIState; -typedef struct XHCIStreamContext XHCIStreamContext; -typedef struct XHCIEPContext XHCIEPContext; - -#define get_field(data, field) \ - (((data) >> field##_SHIFT) & field##_MASK) - -#define set_field(data, newval, field) do { \ - uint32_t val = *data; \ - val &= ~(field##_MASK << field##_SHIFT); \ - val |= ((newval) & field##_MASK) << field##_SHIFT; \ - *data = val; \ - } while (0) - -typedef enum EPType { - ET_INVALID = 0, - ET_ISO_OUT, - ET_BULK_OUT, - ET_INTR_OUT, - ET_CONTROL, - ET_ISO_IN, - ET_BULK_IN, - ET_INTR_IN, -} EPType; - -typedef struct XHCIRing { - dma_addr_t dequeue; - bool ccs; -} XHCIRing; - -typedef struct XHCIPort { - XHCIState *xhci; - uint32_t portsc; - uint32_t portnr; - USBPort *uport; - uint32_t speedmask; - char name[16]; - MemoryRegion mem; -} XHCIPort; - -typedef struct XHCITransfer { - XHCIState *xhci; - USBPacket packet; - QEMUSGList sgl; - bool running_async; - bool running_retry; - bool complete; - bool int_req; - unsigned int iso_pkts; - unsigned int slotid; - unsigned int epid; - unsigned int streamid; - bool in_xfer; - bool iso_xfer; - bool timed_xfer; - - unsigned int trb_count; - unsigned int trb_alloced; - XHCITRB *trbs; - - TRBCCode status; - - unsigned int pkts; - unsigned int pktsize; - unsigned int cur_pkt; - - uint64_t mfindex_kick; -} XHCITransfer; - -struct XHCIStreamContext { - dma_addr_t pctx; - unsigned int sct; - XHCIRing ring; -}; - -struct XHCIEPContext { - XHCIState *xhci; - unsigned int slotid; - unsigned int epid; - - XHCIRing ring; - unsigned int next_xfer; - unsigned int comp_xfer; - XHCITransfer transfers[TD_QUEUE]; - XHCITransfer *retry; - EPType type; - dma_addr_t pctx; - unsigned int max_psize; - uint32_t state; - - /* streams */ - unsigned int max_pstreams; - bool lsa; - unsigned int nr_pstreams; - XHCIStreamContext *pstreams; - - /* iso xfer scheduling */ - unsigned int interval; - int64_t mfindex_last; - QEMUTimer *kick_timer; -}; - -typedef struct XHCISlot { - bool enabled; - bool addressed; - dma_addr_t ctx; - USBPort *uport; - XHCIEPContext * eps[31]; -} XHCISlot; - -typedef struct XHCIEvent { - TRBType type; - TRBCCode ccode; - uint64_t ptr; - uint32_t length; - uint32_t flags; - uint8_t slotid; - uint8_t epid; -} XHCIEvent; - -typedef struct XHCIInterrupter { - uint32_t iman; - uint32_t imod; - uint32_t erstsz; - uint32_t erstba_low; - uint32_t erstba_high; - uint32_t erdp_low; - uint32_t erdp_high; - - bool msix_used, er_pcs, er_full; - - dma_addr_t er_start; - uint32_t er_size; - unsigned int er_ep_idx; - - XHCIEvent ev_buffer[EV_QUEUE]; - unsigned int ev_buffer_put; - unsigned int ev_buffer_get; - -} XHCIInterrupter; - -struct XHCIState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - USBBus bus; - MemoryRegion mem; - MemoryRegion mem_cap; - MemoryRegion mem_oper; - MemoryRegion mem_runtime; - MemoryRegion mem_doorbell; - - /* properties */ - uint32_t numports_2; - uint32_t numports_3; - uint32_t numintrs; - uint32_t numslots; - uint32_t flags; - uint32_t max_pstreams_mask; - - /* Operational Registers */ - uint32_t usbcmd; - uint32_t usbsts; - uint32_t dnctrl; - uint32_t crcr_low; - uint32_t crcr_high; - uint32_t dcbaap_low; - uint32_t dcbaap_high; - uint32_t config; - - USBPort uports[MAX(MAXPORTS_2, MAXPORTS_3)]; - XHCIPort ports[MAXPORTS]; - XHCISlot slots[MAXSLOTS]; - uint32_t numports; - - /* Runtime Registers */ - int64_t mfindex_start; - QEMUTimer *mfwrap_timer; - XHCIInterrupter intr[MAXINTRS]; - - XHCIRing cmd_ring; -}; - -#define TYPE_XHCI "nec-usb-xhci" - -#define XHCI(obj) \ - OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI) - -typedef struct XHCIEvRingSeg { - uint32_t addr_low; - uint32_t addr_high; - uint32_t size; - uint32_t rsvd; -} XHCIEvRingSeg; - -enum xhci_flags { - XHCI_FLAG_USE_MSI = 1, - XHCI_FLAG_USE_MSI_X, - XHCI_FLAG_SS_FIRST, - XHCI_FLAG_FORCE_PCIE_ENDCAP, - XHCI_FLAG_ENABLE_STREAMS, -}; - -static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid, unsigned int streamid); -static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid); -static void xhci_xfer_report(XHCITransfer *xfer); -static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); -static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v); -static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci, - unsigned int slotid, unsigned int epid); - -static const char *TRBType_names[] = { - [TRB_RESERVED] = "TRB_RESERVED", - [TR_NORMAL] = "TR_NORMAL", - [TR_SETUP] = "TR_SETUP", - [TR_DATA] = "TR_DATA", - [TR_STATUS] = "TR_STATUS", - [TR_ISOCH] = "TR_ISOCH", - [TR_LINK] = "TR_LINK", - [TR_EVDATA] = "TR_EVDATA", - [TR_NOOP] = "TR_NOOP", - [CR_ENABLE_SLOT] = "CR_ENABLE_SLOT", - [CR_DISABLE_SLOT] = "CR_DISABLE_SLOT", - [CR_ADDRESS_DEVICE] = "CR_ADDRESS_DEVICE", - [CR_CONFIGURE_ENDPOINT] = "CR_CONFIGURE_ENDPOINT", - [CR_EVALUATE_CONTEXT] = "CR_EVALUATE_CONTEXT", - [CR_RESET_ENDPOINT] = "CR_RESET_ENDPOINT", - [CR_STOP_ENDPOINT] = "CR_STOP_ENDPOINT", - [CR_SET_TR_DEQUEUE] = "CR_SET_TR_DEQUEUE", - [CR_RESET_DEVICE] = "CR_RESET_DEVICE", - [CR_FORCE_EVENT] = "CR_FORCE_EVENT", - [CR_NEGOTIATE_BW] = "CR_NEGOTIATE_BW", - [CR_SET_LATENCY_TOLERANCE] = "CR_SET_LATENCY_TOLERANCE", - [CR_GET_PORT_BANDWIDTH] = "CR_GET_PORT_BANDWIDTH", - [CR_FORCE_HEADER] = "CR_FORCE_HEADER", - [CR_NOOP] = "CR_NOOP", - [ER_TRANSFER] = "ER_TRANSFER", - [ER_COMMAND_COMPLETE] = "ER_COMMAND_COMPLETE", - [ER_PORT_STATUS_CHANGE] = "ER_PORT_STATUS_CHANGE", - [ER_BANDWIDTH_REQUEST] = "ER_BANDWIDTH_REQUEST", - [ER_DOORBELL] = "ER_DOORBELL", - [ER_HOST_CONTROLLER] = "ER_HOST_CONTROLLER", - [ER_DEVICE_NOTIFICATION] = "ER_DEVICE_NOTIFICATION", - [ER_MFINDEX_WRAP] = "ER_MFINDEX_WRAP", - [CR_VENDOR_VIA_CHALLENGE_RESPONSE] = "CR_VENDOR_VIA_CHALLENGE_RESPONSE", - [CR_VENDOR_NEC_FIRMWARE_REVISION] = "CR_VENDOR_NEC_FIRMWARE_REVISION", - [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE", -}; - -static const char *TRBCCode_names[] = { - [CC_INVALID] = "CC_INVALID", - [CC_SUCCESS] = "CC_SUCCESS", - [CC_DATA_BUFFER_ERROR] = "CC_DATA_BUFFER_ERROR", - [CC_BABBLE_DETECTED] = "CC_BABBLE_DETECTED", - [CC_USB_TRANSACTION_ERROR] = "CC_USB_TRANSACTION_ERROR", - [CC_TRB_ERROR] = "CC_TRB_ERROR", - [CC_STALL_ERROR] = "CC_STALL_ERROR", - [CC_RESOURCE_ERROR] = "CC_RESOURCE_ERROR", - [CC_BANDWIDTH_ERROR] = "CC_BANDWIDTH_ERROR", - [CC_NO_SLOTS_ERROR] = "CC_NO_SLOTS_ERROR", - [CC_INVALID_STREAM_TYPE_ERROR] = "CC_INVALID_STREAM_TYPE_ERROR", - [CC_SLOT_NOT_ENABLED_ERROR] = "CC_SLOT_NOT_ENABLED_ERROR", - [CC_EP_NOT_ENABLED_ERROR] = "CC_EP_NOT_ENABLED_ERROR", - [CC_SHORT_PACKET] = "CC_SHORT_PACKET", - [CC_RING_UNDERRUN] = "CC_RING_UNDERRUN", - [CC_RING_OVERRUN] = "CC_RING_OVERRUN", - [CC_VF_ER_FULL] = "CC_VF_ER_FULL", - [CC_PARAMETER_ERROR] = "CC_PARAMETER_ERROR", - [CC_BANDWIDTH_OVERRUN] = "CC_BANDWIDTH_OVERRUN", - [CC_CONTEXT_STATE_ERROR] = "CC_CONTEXT_STATE_ERROR", - [CC_NO_PING_RESPONSE_ERROR] = "CC_NO_PING_RESPONSE_ERROR", - [CC_EVENT_RING_FULL_ERROR] = "CC_EVENT_RING_FULL_ERROR", - [CC_INCOMPATIBLE_DEVICE_ERROR] = "CC_INCOMPATIBLE_DEVICE_ERROR", - [CC_MISSED_SERVICE_ERROR] = "CC_MISSED_SERVICE_ERROR", - [CC_COMMAND_RING_STOPPED] = "CC_COMMAND_RING_STOPPED", - [CC_COMMAND_ABORTED] = "CC_COMMAND_ABORTED", - [CC_STOPPED] = "CC_STOPPED", - [CC_STOPPED_LENGTH_INVALID] = "CC_STOPPED_LENGTH_INVALID", - [CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR] - = "CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR", - [CC_ISOCH_BUFFER_OVERRUN] = "CC_ISOCH_BUFFER_OVERRUN", - [CC_EVENT_LOST_ERROR] = "CC_EVENT_LOST_ERROR", - [CC_UNDEFINED_ERROR] = "CC_UNDEFINED_ERROR", - [CC_INVALID_STREAM_ID_ERROR] = "CC_INVALID_STREAM_ID_ERROR", - [CC_SECONDARY_BANDWIDTH_ERROR] = "CC_SECONDARY_BANDWIDTH_ERROR", - [CC_SPLIT_TRANSACTION_ERROR] = "CC_SPLIT_TRANSACTION_ERROR", -}; - -static const char *ep_state_names[] = { - [EP_DISABLED] = "disabled", - [EP_RUNNING] = "running", - [EP_HALTED] = "halted", - [EP_STOPPED] = "stopped", - [EP_ERROR] = "error", -}; - -static const char *lookup_name(uint32_t index, const char **list, uint32_t llen) -{ - if (index >= llen || list[index] == NULL) { - return "???"; - } - return list[index]; -} - -static const char *trb_name(XHCITRB *trb) -{ - return lookup_name(TRB_TYPE(*trb), TRBType_names, - ARRAY_SIZE(TRBType_names)); -} - -static const char *event_name(XHCIEvent *event) -{ - return lookup_name(event->ccode, TRBCCode_names, - ARRAY_SIZE(TRBCCode_names)); -} - -static const char *ep_state_name(uint32_t state) -{ - return lookup_name(state, ep_state_names, - ARRAY_SIZE(ep_state_names)); -} - -static bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit) -{ - return xhci->flags & (1 << bit); -} - -static uint64_t xhci_mfindex_get(XHCIState *xhci) -{ - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - return (now - xhci->mfindex_start) / 125000; -} - -static void xhci_mfwrap_update(XHCIState *xhci) -{ - const uint32_t bits = USBCMD_RS | USBCMD_EWE; - uint32_t mfindex, left; - int64_t now; - - if ((xhci->usbcmd & bits) == bits) { - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - mfindex = ((now - xhci->mfindex_start) / 125000) & 0x3fff; - left = 0x4000 - mfindex; - timer_mod(xhci->mfwrap_timer, now + left * 125000); - } else { - timer_del(xhci->mfwrap_timer); - } -} - -static void xhci_mfwrap_timer(void *opaque) -{ - XHCIState *xhci = opaque; - XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS }; - - xhci_event(xhci, &wrap, 0); - xhci_mfwrap_update(xhci); -} - -static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high) -{ - if (sizeof(dma_addr_t) == 4) { - return low; - } else { - return low | (((dma_addr_t)high << 16) << 16); - } -} - -static inline dma_addr_t xhci_mask64(uint64_t addr) -{ - if (sizeof(dma_addr_t) == 4) { - return addr & 0xffffffff; - } else { - return addr; - } -} - -static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr, - uint32_t *buf, size_t len) -{ - int i; - - assert((len % sizeof(uint32_t)) == 0); - - pci_dma_read(PCI_DEVICE(xhci), addr, buf, len); - - for (i = 0; i < (len / sizeof(uint32_t)); i++) { - buf[i] = le32_to_cpu(buf[i]); - } -} - -static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr, - uint32_t *buf, size_t len) -{ - int i; - uint32_t tmp[5]; - uint32_t n = len / sizeof(uint32_t); - - assert((len % sizeof(uint32_t)) == 0); - assert(n <= ARRAY_SIZE(tmp)); - - for (i = 0; i < n; i++) { - tmp[i] = cpu_to_le32(buf[i]); - } - pci_dma_write(PCI_DEVICE(xhci), addr, tmp, len); -} - -static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) -{ - int index; - - if (!uport->dev) { - return NULL; - } - switch (uport->dev->speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - index = uport->index + xhci->numports_3; - } else { - index = uport->index; - } - break; - case USB_SPEED_SUPER: - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - index = uport->index; - } else { - index = uport->index + xhci->numports_2; - } - break; - default: - return NULL; - } - return &xhci->ports[index]; -} - -static void xhci_intx_update(XHCIState *xhci) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - int level = 0; - - if (msix_enabled(pci_dev) || - msi_enabled(pci_dev)) { - return; - } - - if (xhci->intr[0].iman & IMAN_IP && - xhci->intr[0].iman & IMAN_IE && - xhci->usbcmd & USBCMD_INTE) { - level = 1; - } - - trace_usb_xhci_irq_intx(level); - pci_set_irq(pci_dev, level); -} - -static void xhci_msix_update(XHCIState *xhci, int v) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - bool enabled; - - if (!msix_enabled(pci_dev)) { - return; - } - - enabled = xhci->intr[v].iman & IMAN_IE; - if (enabled == xhci->intr[v].msix_used) { - return; - } - - if (enabled) { - trace_usb_xhci_irq_msix_use(v); - msix_vector_use(pci_dev, v); - xhci->intr[v].msix_used = true; - } else { - trace_usb_xhci_irq_msix_unuse(v); - msix_vector_unuse(pci_dev, v); - xhci->intr[v].msix_used = false; - } -} - -static void xhci_intr_raise(XHCIState *xhci, int v) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - - xhci->intr[v].erdp_low |= ERDP_EHB; - xhci->intr[v].iman |= IMAN_IP; - xhci->usbsts |= USBSTS_EINT; - - if (!(xhci->intr[v].iman & IMAN_IE)) { - return; - } - - if (!(xhci->usbcmd & USBCMD_INTE)) { - return; - } - - if (msix_enabled(pci_dev)) { - trace_usb_xhci_irq_msix(v); - msix_notify(pci_dev, v); - return; - } - - if (msi_enabled(pci_dev)) { - trace_usb_xhci_irq_msi(v); - msi_notify(pci_dev, v); - return; - } - - if (v == 0) { - trace_usb_xhci_irq_intx(1); - pci_irq_assert(pci_dev); - } -} - -static inline int xhci_running(XHCIState *xhci) -{ - return !(xhci->usbsts & USBSTS_HCH) && !xhci->intr[0].er_full; -} - -static void xhci_die(XHCIState *xhci) -{ - xhci->usbsts |= USBSTS_HCE; - DPRINTF("xhci: asserted controller error\n"); -} - -static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - XHCIInterrupter *intr = &xhci->intr[v]; - XHCITRB ev_trb; - dma_addr_t addr; - - ev_trb.parameter = cpu_to_le64(event->ptr); - ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24)); - ev_trb.control = (event->slotid << 24) | (event->epid << 16) | - event->flags | (event->type << TRB_TYPE_SHIFT); - if (intr->er_pcs) { - ev_trb.control |= TRB_C; - } - ev_trb.control = cpu_to_le32(ev_trb.control); - - trace_usb_xhci_queue_event(v, intr->er_ep_idx, trb_name(&ev_trb), - event_name(event), ev_trb.parameter, - ev_trb.status, ev_trb.control); - - addr = intr->er_start + TRB_SIZE*intr->er_ep_idx; - pci_dma_write(pci_dev, addr, &ev_trb, TRB_SIZE); - - intr->er_ep_idx++; - if (intr->er_ep_idx >= intr->er_size) { - intr->er_ep_idx = 0; - intr->er_pcs = !intr->er_pcs; - } -} - -static void xhci_events_update(XHCIState *xhci, int v) -{ - XHCIInterrupter *intr = &xhci->intr[v]; - dma_addr_t erdp; - unsigned int dp_idx; - bool do_irq = 0; - - if (xhci->usbsts & USBSTS_HCH) { - return; - } - - erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); - if (erdp < intr->er_start || - erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { - DPRINTF("xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); - DPRINTF("xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", - v, intr->er_start, intr->er_size); - xhci_die(xhci); - return; - } - dp_idx = (erdp - intr->er_start) / TRB_SIZE; - assert(dp_idx < intr->er_size); - - /* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus - * deadlocks when the ER is full. Hack it by holding off events until - * the driver decides to free at least half of the ring */ - if (intr->er_full) { - int er_free = dp_idx - intr->er_ep_idx; - if (er_free <= 0) { - er_free += intr->er_size; - } - if (er_free < (intr->er_size/2)) { - DPRINTF("xhci_events_update(): event ring still " - "more than half full (hack)\n"); - return; - } - } - - while (intr->ev_buffer_put != intr->ev_buffer_get) { - assert(intr->er_full); - if (((intr->er_ep_idx+1) % intr->er_size) == dp_idx) { - DPRINTF("xhci_events_update(): event ring full again\n"); -#ifndef ER_FULL_HACK - XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR}; - xhci_write_event(xhci, &full, v); -#endif - do_irq = 1; - break; - } - XHCIEvent *event = &intr->ev_buffer[intr->ev_buffer_get]; - xhci_write_event(xhci, event, v); - intr->ev_buffer_get++; - do_irq = 1; - if (intr->ev_buffer_get == EV_QUEUE) { - intr->ev_buffer_get = 0; - } - } - - if (do_irq) { - xhci_intr_raise(xhci, v); - } - - if (intr->er_full && intr->ev_buffer_put == intr->ev_buffer_get) { - DPRINTF("xhci_events_update(): event ring no longer full\n"); - intr->er_full = 0; - } -} - -static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) -{ - XHCIInterrupter *intr; - dma_addr_t erdp; - unsigned int dp_idx; - - if (v >= xhci->numintrs) { - DPRINTF("intr nr out of range (%d >= %d)\n", v, xhci->numintrs); - return; - } - intr = &xhci->intr[v]; - - if (intr->er_full) { - DPRINTF("xhci_event(): ER full, queueing\n"); - if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { - DPRINTF("xhci: event queue full, dropping event!\n"); - return; - } - intr->ev_buffer[intr->ev_buffer_put++] = *event; - if (intr->ev_buffer_put == EV_QUEUE) { - intr->ev_buffer_put = 0; - } - return; - } - - erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); - if (erdp < intr->er_start || - erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { - DPRINTF("xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); - DPRINTF("xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", - v, intr->er_start, intr->er_size); - xhci_die(xhci); - return; - } - - dp_idx = (erdp - intr->er_start) / TRB_SIZE; - assert(dp_idx < intr->er_size); - - if ((intr->er_ep_idx+1) % intr->er_size == dp_idx) { - DPRINTF("xhci_event(): ER full, queueing\n"); -#ifndef ER_FULL_HACK - XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR}; - xhci_write_event(xhci, &full); -#endif - intr->er_full = 1; - if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { - DPRINTF("xhci: event queue full, dropping event!\n"); - return; - } - intr->ev_buffer[intr->ev_buffer_put++] = *event; - if (intr->ev_buffer_put == EV_QUEUE) { - intr->ev_buffer_put = 0; - } - } else { - xhci_write_event(xhci, event, v); - } - - xhci_intr_raise(xhci, v); -} - -static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring, - dma_addr_t base) -{ - ring->dequeue = base; - ring->ccs = 1; -} - -static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb, - dma_addr_t *addr) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - - while (1) { - TRBType type; - pci_dma_read(pci_dev, ring->dequeue, trb, TRB_SIZE); - trb->addr = ring->dequeue; - trb->ccs = ring->ccs; - le64_to_cpus(&trb->parameter); - le32_to_cpus(&trb->status); - le32_to_cpus(&trb->control); - - trace_usb_xhci_fetch_trb(ring->dequeue, trb_name(trb), - trb->parameter, trb->status, trb->control); - - if ((trb->control & TRB_C) != ring->ccs) { - return 0; - } - - type = TRB_TYPE(*trb); - - if (type != TR_LINK) { - if (addr) { - *addr = ring->dequeue; - } - ring->dequeue += TRB_SIZE; - return type; - } else { - ring->dequeue = xhci_mask64(trb->parameter); - if (trb->control & TRB_LK_TC) { - ring->ccs = !ring->ccs; - } - } - } -} - -static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - XHCITRB trb; - int length = 0; - dma_addr_t dequeue = ring->dequeue; - bool ccs = ring->ccs; - /* hack to bundle together the two/three TDs that make a setup transfer */ - bool control_td_set = 0; - - while (1) { - TRBType type; - pci_dma_read(pci_dev, dequeue, &trb, TRB_SIZE); - le64_to_cpus(&trb.parameter); - le32_to_cpus(&trb.status); - le32_to_cpus(&trb.control); - - if ((trb.control & TRB_C) != ccs) { - return -length; - } - - type = TRB_TYPE(trb); - - if (type == TR_LINK) { - dequeue = xhci_mask64(trb.parameter); - if (trb.control & TRB_LK_TC) { - ccs = !ccs; - } - continue; - } - - length += 1; - dequeue += TRB_SIZE; - - if (type == TR_SETUP) { - control_td_set = 1; - } else if (type == TR_STATUS) { - control_td_set = 0; - } - - if (!control_td_set && !(trb.control & TRB_TR_CH)) { - return length; - } - } -} - -static void xhci_er_reset(XHCIState *xhci, int v) -{ - XHCIInterrupter *intr = &xhci->intr[v]; - XHCIEvRingSeg seg; - - if (intr->erstsz == 0) { - /* disabled */ - intr->er_start = 0; - intr->er_size = 0; - return; - } - /* cache the (sole) event ring segment location */ - if (intr->erstsz != 1) { - DPRINTF("xhci: invalid value for ERSTSZ: %d\n", intr->erstsz); - xhci_die(xhci); - return; - } - dma_addr_t erstba = xhci_addr64(intr->erstba_low, intr->erstba_high); - pci_dma_read(PCI_DEVICE(xhci), erstba, &seg, sizeof(seg)); - le32_to_cpus(&seg.addr_low); - le32_to_cpus(&seg.addr_high); - le32_to_cpus(&seg.size); - if (seg.size < 16 || seg.size > 4096) { - DPRINTF("xhci: invalid value for segment size: %d\n", seg.size); - xhci_die(xhci); - return; - } - intr->er_start = xhci_addr64(seg.addr_low, seg.addr_high); - intr->er_size = seg.size; - - intr->er_ep_idx = 0; - intr->er_pcs = 1; - intr->er_full = 0; - - DPRINTF("xhci: event ring[%d]:" DMA_ADDR_FMT " [%d]\n", - v, intr->er_start, intr->er_size); -} - -static void xhci_run(XHCIState *xhci) -{ - trace_usb_xhci_run(); - xhci->usbsts &= ~USBSTS_HCH; - xhci->mfindex_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); -} - -static void xhci_stop(XHCIState *xhci) -{ - trace_usb_xhci_stop(); - xhci->usbsts |= USBSTS_HCH; - xhci->crcr_low &= ~CRCR_CRR; -} - -static XHCIStreamContext *xhci_alloc_stream_contexts(unsigned count, - dma_addr_t base) -{ - XHCIStreamContext *stctx; - unsigned int i; - - stctx = g_new0(XHCIStreamContext, count); - for (i = 0; i < count; i++) { - stctx[i].pctx = base + i * 16; - stctx[i].sct = -1; - } - return stctx; -} - -static void xhci_reset_streams(XHCIEPContext *epctx) -{ - unsigned int i; - - for (i = 0; i < epctx->nr_pstreams; i++) { - epctx->pstreams[i].sct = -1; - } -} - -static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base) -{ - assert(epctx->pstreams == NULL); - epctx->nr_pstreams = 2 << epctx->max_pstreams; - epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base); -} - -static void xhci_free_streams(XHCIEPContext *epctx) -{ - assert(epctx->pstreams != NULL); - - g_free(epctx->pstreams); - epctx->pstreams = NULL; - epctx->nr_pstreams = 0; -} - -static int xhci_epmask_to_eps_with_streams(XHCIState *xhci, - unsigned int slotid, - uint32_t epmask, - XHCIEPContext **epctxs, - USBEndpoint **eps) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - USBEndpoint *ep; - int i, j; - - assert(slotid >= 1 && slotid <= xhci->numslots); - - slot = &xhci->slots[slotid - 1]; - - for (i = 2, j = 0; i <= 31; i++) { - if (!(epmask & (1u << i))) { - continue; - } - - epctx = slot->eps[i - 1]; - ep = xhci_epid_to_usbep(xhci, slotid, i); - if (!epctx || !epctx->nr_pstreams || !ep) { - continue; - } - - if (epctxs) { - epctxs[j] = epctx; - } - eps[j++] = ep; - } - return j; -} - -static void xhci_free_device_streams(XHCIState *xhci, unsigned int slotid, - uint32_t epmask) -{ - USBEndpoint *eps[30]; - int nr_eps; - - nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, NULL, eps); - if (nr_eps) { - usb_device_free_streams(eps[0]->dev, eps, nr_eps); - } -} - -static TRBCCode xhci_alloc_device_streams(XHCIState *xhci, unsigned int slotid, - uint32_t epmask) -{ - XHCIEPContext *epctxs[30]; - USBEndpoint *eps[30]; - int i, r, nr_eps, req_nr_streams, dev_max_streams; - - nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, epctxs, - eps); - if (nr_eps == 0) { - return CC_SUCCESS; - } - - req_nr_streams = epctxs[0]->nr_pstreams; - dev_max_streams = eps[0]->max_streams; - - for (i = 1; i < nr_eps; i++) { - /* - * HdG: I don't expect these to ever trigger, but if they do we need - * to come up with another solution, ie group identical endpoints - * together and make an usb_device_alloc_streams call per group. - */ - if (epctxs[i]->nr_pstreams != req_nr_streams) { - FIXME("guest streams config not identical for all eps"); - return CC_RESOURCE_ERROR; - } - if (eps[i]->max_streams != dev_max_streams) { - FIXME("device streams config not identical for all eps"); - return CC_RESOURCE_ERROR; - } - } - - /* - * max-streams in both the device descriptor and in the controller is a - * power of 2. But stream id 0 is reserved, so if a device can do up to 4 - * streams the guest will ask for 5 rounded up to the next power of 2 which - * becomes 8. For emulated devices usb_device_alloc_streams is a nop. - * - * For redirected devices however this is an issue, as there we must ask - * the real xhci controller to alloc streams, and the host driver for the - * real xhci controller will likely disallow allocating more streams then - * the device can handle. - * - * So we limit the requested nr_streams to the maximum number the device - * can handle. - */ - if (req_nr_streams > dev_max_streams) { - req_nr_streams = dev_max_streams; - } - - r = usb_device_alloc_streams(eps[0]->dev, eps, nr_eps, req_nr_streams); - if (r != 0) { - DPRINTF("xhci: alloc streams failed\n"); - return CC_RESOURCE_ERROR; - } - - return CC_SUCCESS; -} - -static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, - unsigned int streamid, - uint32_t *cc_error) -{ - XHCIStreamContext *sctx; - dma_addr_t base; - uint32_t ctx[2], sct; - - assert(streamid != 0); - if (epctx->lsa) { - if (streamid >= epctx->nr_pstreams) { - *cc_error = CC_INVALID_STREAM_ID_ERROR; - return NULL; - } - sctx = epctx->pstreams + streamid; - } else { - FIXME("secondary streams not implemented yet"); - } - - if (sctx->sct == -1) { - xhci_dma_read_u32s(epctx->xhci, sctx->pctx, ctx, sizeof(ctx)); - sct = (ctx[0] >> 1) & 0x07; - if (epctx->lsa && sct != 1) { - *cc_error = CC_INVALID_STREAM_TYPE_ERROR; - return NULL; - } - sctx->sct = sct; - base = xhci_addr64(ctx[0] & ~0xf, ctx[1]); - xhci_ring_init(epctx->xhci, &sctx->ring, base); - } - return sctx; -} - -static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, - XHCIStreamContext *sctx, uint32_t state) -{ - XHCIRing *ring = NULL; - uint32_t ctx[5]; - uint32_t ctx2[2]; - - xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); - ctx[0] &= ~EP_STATE_MASK; - ctx[0] |= state; - - /* update ring dequeue ptr */ - if (epctx->nr_pstreams) { - if (sctx != NULL) { - ring = &sctx->ring; - xhci_dma_read_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); - ctx2[0] &= 0xe; - ctx2[0] |= sctx->ring.dequeue | sctx->ring.ccs; - ctx2[1] = (sctx->ring.dequeue >> 16) >> 16; - xhci_dma_write_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); - } - } else { - ring = &epctx->ring; - } - if (ring) { - ctx[2] = ring->dequeue | ring->ccs; - ctx[3] = (ring->dequeue >> 16) >> 16; - - DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", - epctx->pctx, state, ctx[3], ctx[2]); - } - - xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); - if (epctx->state != state) { - trace_usb_xhci_ep_state(epctx->slotid, epctx->epid, - ep_state_name(epctx->state), - ep_state_name(state)); - } - epctx->state = state; -} - -static void xhci_ep_kick_timer(void *opaque) -{ - XHCIEPContext *epctx = opaque; - xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0); -} - -static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci, - unsigned int slotid, - unsigned int epid) -{ - XHCIEPContext *epctx; - int i; - - epctx = g_new0(XHCIEPContext, 1); - epctx->xhci = xhci; - epctx->slotid = slotid; - epctx->epid = epid; - - for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { - epctx->transfers[i].xhci = xhci; - epctx->transfers[i].slotid = slotid; - epctx->transfers[i].epid = epid; - usb_packet_init(&epctx->transfers[i].packet); - } - epctx->kick_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_ep_kick_timer, epctx); - - return epctx; -} - -static void xhci_init_epctx(XHCIEPContext *epctx, - dma_addr_t pctx, uint32_t *ctx) -{ - dma_addr_t dequeue; - - dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]); - - epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK; - epctx->pctx = pctx; - epctx->max_psize = ctx[1]>>16; - epctx->max_psize *= 1+((ctx[1]>>8)&0xff); - epctx->max_pstreams = (ctx[0] >> 10) & epctx->xhci->max_pstreams_mask; - epctx->lsa = (ctx[0] >> 15) & 1; - if (epctx->max_pstreams) { - xhci_alloc_streams(epctx, dequeue); - } else { - xhci_ring_init(epctx->xhci, &epctx->ring, dequeue); - epctx->ring.ccs = ctx[2] & 1; - } - - epctx->interval = 1 << ((ctx[0] >> 16) & 0xff); -} - -static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid, dma_addr_t pctx, - uint32_t *ctx) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - - trace_usb_xhci_ep_enable(slotid, epid); - assert(slotid >= 1 && slotid <= xhci->numslots); - assert(epid >= 1 && epid <= 31); - - slot = &xhci->slots[slotid-1]; - if (slot->eps[epid-1]) { - xhci_disable_ep(xhci, slotid, epid); - } - - epctx = xhci_alloc_epctx(xhci, slotid, epid); - slot->eps[epid-1] = epctx; - xhci_init_epctx(epctx, pctx, ctx); - - DPRINTF("xhci: endpoint %d.%d type is %d, max transaction (burst) " - "size is %d\n", epid/2, epid%2, epctx->type, epctx->max_psize); - - epctx->mfindex_last = 0; - - epctx->state = EP_RUNNING; - ctx[0] &= ~EP_STATE_MASK; - ctx[0] |= EP_RUNNING; - - return CC_SUCCESS; -} - -static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) -{ - int killed = 0; - - if (report && (t->running_async || t->running_retry)) { - t->status = report; - xhci_xfer_report(t); - } - - if (t->running_async) { - usb_cancel_packet(&t->packet); - t->running_async = 0; - killed = 1; - } - if (t->running_retry) { - XHCIEPContext *epctx = t->xhci->slots[t->slotid-1].eps[t->epid-1]; - if (epctx) { - epctx->retry = NULL; - timer_del(epctx->kick_timer); - } - t->running_retry = 0; - killed = 1; - } - g_free(t->trbs); - - t->trbs = NULL; - t->trb_count = t->trb_alloced = 0; - - return killed; -} - -static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, - unsigned int epid, TRBCCode report) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - int i, xferi, killed = 0; - USBEndpoint *ep = NULL; - assert(slotid >= 1 && slotid <= xhci->numslots); - assert(epid >= 1 && epid <= 31); - - DPRINTF("xhci_ep_nuke_xfers(%d, %d)\n", slotid, epid); - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - return 0; - } - - epctx = slot->eps[epid-1]; - - xferi = epctx->next_xfer; - for (i = 0; i < TD_QUEUE; i++) { - killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi], report); - if (killed) { - report = 0; /* Only report once */ - } - epctx->transfers[xferi].packet.ep = NULL; - xferi = (xferi + 1) % TD_QUEUE; - } - - ep = xhci_epid_to_usbep(xhci, slotid, epid); - if (ep) { - usb_device_ep_stopped(ep->dev, ep); - } - return killed; -} - -static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - int i; - - trace_usb_xhci_ep_disable(slotid, epid); - assert(slotid >= 1 && slotid <= xhci->numslots); - assert(epid >= 1 && epid <= 31); - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - DPRINTF("xhci: slot %d ep %d already disabled\n", slotid, epid); - return CC_SUCCESS; - } - - xhci_ep_nuke_xfers(xhci, slotid, epid, 0); - - epctx = slot->eps[epid-1]; - - if (epctx->nr_pstreams) { - xhci_free_streams(epctx); - } - - for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { - usb_packet_cleanup(&epctx->transfers[i].packet); - } - - xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); - - timer_free(epctx->kick_timer); - g_free(epctx); - slot->eps[epid-1] = NULL; - - return CC_SUCCESS; -} - -static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - - trace_usb_xhci_ep_stop(slotid, epid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - if (epid < 1 || epid > 31) { - DPRINTF("xhci: bad ep %d\n", epid); - return CC_TRB_ERROR; - } - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid); - return CC_EP_NOT_ENABLED_ERROR; - } - - if (xhci_ep_nuke_xfers(xhci, slotid, epid, CC_STOPPED) > 0) { - DPRINTF("xhci: FIXME: endpoint stopped w/ xfers running, " - "data might be lost\n"); - } - - epctx = slot->eps[epid-1]; - - xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED); - - if (epctx->nr_pstreams) { - xhci_reset_streams(epctx); - } - - return CC_SUCCESS; -} - -static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - - trace_usb_xhci_ep_reset(slotid, epid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - if (epid < 1 || epid > 31) { - DPRINTF("xhci: bad ep %d\n", epid); - return CC_TRB_ERROR; - } - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid); - return CC_EP_NOT_ENABLED_ERROR; - } - - epctx = slot->eps[epid-1]; - - if (epctx->state != EP_HALTED) { - DPRINTF("xhci: reset EP while EP %d not halted (%d)\n", - epid, epctx->state); - return CC_CONTEXT_STATE_ERROR; - } - - if (xhci_ep_nuke_xfers(xhci, slotid, epid, 0) > 0) { - DPRINTF("xhci: FIXME: endpoint reset w/ xfers running, " - "data might be lost\n"); - } - - if (!xhci->slots[slotid-1].uport || - !xhci->slots[slotid-1].uport->dev || - !xhci->slots[slotid-1].uport->dev->attached) { - return CC_USB_TRANSACTION_ERROR; - } - - xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED); - - if (epctx->nr_pstreams) { - xhci_reset_streams(epctx); - } - - return CC_SUCCESS; -} - -static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, - unsigned int epid, unsigned int streamid, - uint64_t pdequeue) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - XHCIStreamContext *sctx; - dma_addr_t dequeue; - - assert(slotid >= 1 && slotid <= xhci->numslots); - - if (epid < 1 || epid > 31) { - DPRINTF("xhci: bad ep %d\n", epid); - return CC_TRB_ERROR; - } - - trace_usb_xhci_ep_set_dequeue(slotid, epid, streamid, pdequeue); - dequeue = xhci_mask64(pdequeue); - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid); - return CC_EP_NOT_ENABLED_ERROR; - } - - epctx = slot->eps[epid-1]; - - if (epctx->state != EP_STOPPED) { - DPRINTF("xhci: set EP dequeue pointer while EP %d not stopped\n", epid); - return CC_CONTEXT_STATE_ERROR; - } - - if (epctx->nr_pstreams) { - uint32_t err; - sctx = xhci_find_stream(epctx, streamid, &err); - if (sctx == NULL) { - return err; - } - xhci_ring_init(xhci, &sctx->ring, dequeue & ~0xf); - sctx->ring.ccs = dequeue & 1; - } else { - sctx = NULL; - xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF); - epctx->ring.ccs = dequeue & 1; - } - - xhci_set_ep_state(xhci, epctx, sctx, EP_STOPPED); - - return CC_SUCCESS; -} - -static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) -{ - XHCIState *xhci = xfer->xhci; - int i; - - xfer->int_req = false; - pci_dma_sglist_init(&xfer->sgl, PCI_DEVICE(xhci), xfer->trb_count); - for (i = 0; i < xfer->trb_count; i++) { - XHCITRB *trb = &xfer->trbs[i]; - dma_addr_t addr; - unsigned int chunk = 0; - - if (trb->control & TRB_TR_IOC) { - xfer->int_req = true; - } - - switch (TRB_TYPE(*trb)) { - case TR_DATA: - if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) { - DPRINTF("xhci: data direction mismatch for TR_DATA\n"); - goto err; - } - /* fallthrough */ - case TR_NORMAL: - case TR_ISOCH: - addr = xhci_mask64(trb->parameter); - chunk = trb->status & 0x1ffff; - if (trb->control & TRB_TR_IDT) { - if (chunk > 8 || in_xfer) { - DPRINTF("xhci: invalid immediate data TRB\n"); - goto err; - } - qemu_sglist_add(&xfer->sgl, trb->addr, chunk); - } else { - qemu_sglist_add(&xfer->sgl, addr, chunk); - } - break; - } - } - - return 0; - -err: - qemu_sglist_destroy(&xfer->sgl); - xhci_die(xhci); - return -1; -} - -static void xhci_xfer_unmap(XHCITransfer *xfer) -{ - usb_packet_unmap(&xfer->packet, &xfer->sgl); - qemu_sglist_destroy(&xfer->sgl); -} - -static void xhci_xfer_report(XHCITransfer *xfer) -{ - uint32_t edtla = 0; - unsigned int left; - bool reported = 0; - bool shortpkt = 0; - XHCIEvent event = {ER_TRANSFER, CC_SUCCESS}; - XHCIState *xhci = xfer->xhci; - int i; - - left = xfer->packet.actual_length; - - for (i = 0; i < xfer->trb_count; i++) { - XHCITRB *trb = &xfer->trbs[i]; - unsigned int chunk = 0; - - switch (TRB_TYPE(*trb)) { - case TR_DATA: - case TR_NORMAL: - case TR_ISOCH: - chunk = trb->status & 0x1ffff; - if (chunk > left) { - chunk = left; - if (xfer->status == CC_SUCCESS) { - shortpkt = 1; - } - } - left -= chunk; - edtla += chunk; - break; - case TR_STATUS: - reported = 0; - shortpkt = 0; - break; - } - - if (!reported && ((trb->control & TRB_TR_IOC) || - (shortpkt && (trb->control & TRB_TR_ISP)) || - (xfer->status != CC_SUCCESS && left == 0))) { - event.slotid = xfer->slotid; - event.epid = xfer->epid; - event.length = (trb->status & 0x1ffff) - chunk; - event.flags = 0; - event.ptr = trb->addr; - if (xfer->status == CC_SUCCESS) { - event.ccode = shortpkt ? CC_SHORT_PACKET : CC_SUCCESS; - } else { - event.ccode = xfer->status; - } - if (TRB_TYPE(*trb) == TR_EVDATA) { - event.ptr = trb->parameter; - event.flags |= TRB_EV_ED; - event.length = edtla & 0xffffff; - DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length); - edtla = 0; - } - xhci_event(xhci, &event, TRB_INTR(*trb)); - reported = 1; - if (xfer->status != CC_SUCCESS) { - return; - } - } - - switch (TRB_TYPE(*trb)) { - case TR_SETUP: - reported = 0; - shortpkt = 0; - break; - } - - } -} - -static void xhci_stall_ep(XHCITransfer *xfer) -{ - XHCIState *xhci = xfer->xhci; - XHCISlot *slot = &xhci->slots[xfer->slotid-1]; - XHCIEPContext *epctx = slot->eps[xfer->epid-1]; - uint32_t err; - XHCIStreamContext *sctx; - - if (epctx->nr_pstreams) { - sctx = xhci_find_stream(epctx, xfer->streamid, &err); - if (sctx == NULL) { - return; - } - sctx->ring.dequeue = xfer->trbs[0].addr; - sctx->ring.ccs = xfer->trbs[0].ccs; - xhci_set_ep_state(xhci, epctx, sctx, EP_HALTED); - } else { - epctx->ring.dequeue = xfer->trbs[0].addr; - epctx->ring.ccs = xfer->trbs[0].ccs; - xhci_set_ep_state(xhci, epctx, NULL, EP_HALTED); - } -} - -static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, - XHCIEPContext *epctx); - -static int xhci_setup_packet(XHCITransfer *xfer) -{ - XHCIState *xhci = xfer->xhci; - USBEndpoint *ep; - int dir; - - dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT; - - if (xfer->packet.ep) { - ep = xfer->packet.ep; - } else { - ep = xhci_epid_to_usbep(xhci, xfer->slotid, xfer->epid); - if (!ep) { - DPRINTF("xhci: slot %d has no device\n", - xfer->slotid); - return -1; - } - } - - xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */ - usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid, - xfer->trbs[0].addr, false, xfer->int_req); - usb_packet_map(&xfer->packet, &xfer->sgl); - DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", - xfer->packet.pid, ep->dev->addr, ep->nr); - return 0; -} - -static int xhci_complete_packet(XHCITransfer *xfer) -{ - if (xfer->packet.status == USB_RET_ASYNC) { - trace_usb_xhci_xfer_async(xfer); - xfer->running_async = 1; - xfer->running_retry = 0; - xfer->complete = 0; - return 0; - } else if (xfer->packet.status == USB_RET_NAK) { - trace_usb_xhci_xfer_nak(xfer); - xfer->running_async = 0; - xfer->running_retry = 1; - xfer->complete = 0; - return 0; - } else { - xfer->running_async = 0; - xfer->running_retry = 0; - xfer->complete = 1; - xhci_xfer_unmap(xfer); - } - - if (xfer->packet.status == USB_RET_SUCCESS) { - trace_usb_xhci_xfer_success(xfer, xfer->packet.actual_length); - xfer->status = CC_SUCCESS; - xhci_xfer_report(xfer); - return 0; - } - - /* error */ - trace_usb_xhci_xfer_error(xfer, xfer->packet.status); - switch (xfer->packet.status) { - case USB_RET_NODEV: - case USB_RET_IOERROR: - xfer->status = CC_USB_TRANSACTION_ERROR; - xhci_xfer_report(xfer); - xhci_stall_ep(xfer); - break; - case USB_RET_STALL: - xfer->status = CC_STALL_ERROR; - xhci_xfer_report(xfer); - xhci_stall_ep(xfer); - break; - case USB_RET_BABBLE: - xfer->status = CC_BABBLE_DETECTED; - xhci_xfer_report(xfer); - xhci_stall_ep(xfer); - break; - default: - DPRINTF("%s: FIXME: status = %d\n", __func__, - xfer->packet.status); - FIXME("unhandled USB_RET_*"); - } - return 0; -} - -static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) -{ - XHCITRB *trb_setup, *trb_status; - uint8_t bmRequestType; - - trb_setup = &xfer->trbs[0]; - trb_status = &xfer->trbs[xfer->trb_count-1]; - - trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid); - - /* at most one Event Data TRB allowed after STATUS */ - if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) { - trb_status--; - } - - /* do some sanity checks */ - if (TRB_TYPE(*trb_setup) != TR_SETUP) { - DPRINTF("xhci: ep0 first TD not SETUP: %d\n", - TRB_TYPE(*trb_setup)); - return -1; - } - if (TRB_TYPE(*trb_status) != TR_STATUS) { - DPRINTF("xhci: ep0 last TD not STATUS: %d\n", - TRB_TYPE(*trb_status)); - return -1; - } - if (!(trb_setup->control & TRB_TR_IDT)) { - DPRINTF("xhci: Setup TRB doesn't have IDT set\n"); - return -1; - } - if ((trb_setup->status & 0x1ffff) != 8) { - DPRINTF("xhci: Setup TRB has bad length (%d)\n", - (trb_setup->status & 0x1ffff)); - return -1; - } - - bmRequestType = trb_setup->parameter; - - xfer->in_xfer = bmRequestType & USB_DIR_IN; - xfer->iso_xfer = false; - xfer->timed_xfer = false; - - if (xhci_setup_packet(xfer) < 0) { - return -1; - } - xfer->packet.parameter = trb_setup->parameter; - - usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); - - xhci_complete_packet(xfer); - if (!xfer->running_async && !xfer->running_retry) { - xhci_kick_ep(xhci, xfer->slotid, xfer->epid, 0); - } - return 0; -} - -static void xhci_calc_intr_kick(XHCIState *xhci, XHCITransfer *xfer, - XHCIEPContext *epctx, uint64_t mfindex) -{ - uint64_t asap = ((mfindex + epctx->interval - 1) & - ~(epctx->interval-1)); - uint64_t kick = epctx->mfindex_last + epctx->interval; - - assert(epctx->interval != 0); - xfer->mfindex_kick = MAX(asap, kick); -} - -static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer, - XHCIEPContext *epctx, uint64_t mfindex) -{ - if (xfer->trbs[0].control & TRB_TR_SIA) { - uint64_t asap = ((mfindex + epctx->interval - 1) & - ~(epctx->interval-1)); - if (asap >= epctx->mfindex_last && - asap <= epctx->mfindex_last + epctx->interval * 4) { - xfer->mfindex_kick = epctx->mfindex_last + epctx->interval; - } else { - xfer->mfindex_kick = asap; - } - } else { - xfer->mfindex_kick = ((xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT) - & TRB_TR_FRAMEID_MASK) << 3; - xfer->mfindex_kick |= mfindex & ~0x3fff; - if (xfer->mfindex_kick + 0x100 < mfindex) { - xfer->mfindex_kick += 0x4000; - } - } -} - -static void xhci_check_intr_iso_kick(XHCIState *xhci, XHCITransfer *xfer, - XHCIEPContext *epctx, uint64_t mfindex) -{ - if (xfer->mfindex_kick > mfindex) { - timer_mod(epctx->kick_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (xfer->mfindex_kick - mfindex) * 125000); - xfer->running_retry = 1; - } else { - epctx->mfindex_last = xfer->mfindex_kick; - timer_del(epctx->kick_timer); - xfer->running_retry = 0; - } -} - - -static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) -{ - uint64_t mfindex; - - DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); - - xfer->in_xfer = epctx->type>>2; - - switch(epctx->type) { - case ET_INTR_OUT: - case ET_INTR_IN: - xfer->pkts = 0; - xfer->iso_xfer = false; - xfer->timed_xfer = true; - mfindex = xhci_mfindex_get(xhci); - xhci_calc_intr_kick(xhci, xfer, epctx, mfindex); - xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex); - if (xfer->running_retry) { - return -1; - } - break; - case ET_BULK_OUT: - case ET_BULK_IN: - xfer->pkts = 0; - xfer->iso_xfer = false; - xfer->timed_xfer = false; - break; - case ET_ISO_OUT: - case ET_ISO_IN: - xfer->pkts = 1; - xfer->iso_xfer = true; - xfer->timed_xfer = true; - mfindex = xhci_mfindex_get(xhci); - xhci_calc_iso_kick(xhci, xfer, epctx, mfindex); - xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex); - if (xfer->running_retry) { - return -1; - } - break; - default: - trace_usb_xhci_unimplemented("endpoint type", epctx->type); - return -1; - } - - if (xhci_setup_packet(xfer) < 0) { - return -1; - } - usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); - - xhci_complete_packet(xfer); - if (!xfer->running_async && !xfer->running_retry) { - xhci_kick_ep(xhci, xfer->slotid, xfer->epid, xfer->streamid); - } - return 0; -} - -static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) -{ - trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid); - return xhci_submit(xhci, xfer, epctx); -} - -static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid, unsigned int streamid) -{ - XHCIStreamContext *stctx; - XHCIEPContext *epctx; - XHCIRing *ring; - USBEndpoint *ep = NULL; - uint64_t mfindex; - int length; - int i; - - trace_usb_xhci_ep_kick(slotid, epid, streamid); - assert(slotid >= 1 && slotid <= xhci->numslots); - assert(epid >= 1 && epid <= 31); - - if (!xhci->slots[slotid-1].enabled) { - DPRINTF("xhci: xhci_kick_ep for disabled slot %d\n", slotid); - return; - } - epctx = xhci->slots[slotid-1].eps[epid-1]; - if (!epctx) { - DPRINTF("xhci: xhci_kick_ep for disabled endpoint %d,%d\n", - epid, slotid); - return; - } - - /* If the device has been detached, but the guest has not noticed this - yet the 2 above checks will succeed, but we must NOT continue */ - if (!xhci->slots[slotid - 1].uport || - !xhci->slots[slotid - 1].uport->dev || - !xhci->slots[slotid - 1].uport->dev->attached) { - return; - } - - if (epctx->retry) { - XHCITransfer *xfer = epctx->retry; - - trace_usb_xhci_xfer_retry(xfer); - assert(xfer->running_retry); - if (xfer->timed_xfer) { - /* time to kick the transfer? */ - mfindex = xhci_mfindex_get(xhci); - xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex); - if (xfer->running_retry) { - return; - } - xfer->timed_xfer = 0; - xfer->running_retry = 1; - } - if (xfer->iso_xfer) { - /* retry iso transfer */ - if (xhci_setup_packet(xfer) < 0) { - return; - } - usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); - assert(xfer->packet.status != USB_RET_NAK); - xhci_complete_packet(xfer); - } else { - /* retry nak'ed transfer */ - if (xhci_setup_packet(xfer) < 0) { - return; - } - usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); - if (xfer->packet.status == USB_RET_NAK) { - return; - } - xhci_complete_packet(xfer); - } - assert(!xfer->running_retry); - epctx->retry = NULL; - } - - if (epctx->state == EP_HALTED) { - DPRINTF("xhci: ep halted, not running schedule\n"); - return; - } - - - if (epctx->nr_pstreams) { - uint32_t err; - stctx = xhci_find_stream(epctx, streamid, &err); - if (stctx == NULL) { - return; - } - ring = &stctx->ring; - xhci_set_ep_state(xhci, epctx, stctx, EP_RUNNING); - } else { - ring = &epctx->ring; - streamid = 0; - xhci_set_ep_state(xhci, epctx, NULL, EP_RUNNING); - } - assert(ring->dequeue != 0); - - while (1) { - XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer]; - if (xfer->running_async || xfer->running_retry) { - break; - } - length = xhci_ring_chain_length(xhci, ring); - if (length < 0) { - break; - } else if (length == 0) { - break; - } - if (xfer->trbs && xfer->trb_alloced < length) { - xfer->trb_count = 0; - xfer->trb_alloced = 0; - g_free(xfer->trbs); - xfer->trbs = NULL; - } - if (!xfer->trbs) { - xfer->trbs = g_new(XHCITRB, length); - xfer->trb_alloced = length; - } - xfer->trb_count = length; - - for (i = 0; i < length; i++) { - assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL)); - } - xfer->streamid = streamid; - - if (epid == 1) { - if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) { - epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; - } else { - DPRINTF("xhci: error firing CTL transfer\n"); - } - } else { - if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) { - epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; - } else { - if (!xfer->timed_xfer) { - DPRINTF("xhci: error firing data transfer\n"); - } - } - } - - if (epctx->state == EP_HALTED) { - break; - } - if (xfer->running_retry) { - DPRINTF("xhci: xfer nacked, stopping schedule\n"); - epctx->retry = xfer; - break; - } - } - - ep = xhci_epid_to_usbep(xhci, slotid, epid); - if (ep) { - usb_device_flush_ep_queue(ep->dev, ep); - } -} - -static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid) -{ - trace_usb_xhci_slot_enable(slotid); - assert(slotid >= 1 && slotid <= xhci->numslots); - xhci->slots[slotid-1].enabled = 1; - xhci->slots[slotid-1].uport = NULL; - memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31); - - return CC_SUCCESS; -} - -static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid) -{ - int i; - - trace_usb_xhci_slot_disable(slotid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - for (i = 1; i <= 31; i++) { - if (xhci->slots[slotid-1].eps[i-1]) { - xhci_disable_ep(xhci, slotid, i); - } - } - - xhci->slots[slotid-1].enabled = 0; - xhci->slots[slotid-1].addressed = 0; - xhci->slots[slotid-1].uport = NULL; - return CC_SUCCESS; -} - -static USBPort *xhci_lookup_uport(XHCIState *xhci, uint32_t *slot_ctx) -{ - USBPort *uport; - char path[32]; - int i, pos, port; - - port = (slot_ctx[1]>>16) & 0xFF; - if (port < 1 || port > xhci->numports) { - return NULL; - } - port = xhci->ports[port-1].uport->index+1; - pos = snprintf(path, sizeof(path), "%d", port); - for (i = 0; i < 5; i++) { - port = (slot_ctx[0] >> 4*i) & 0x0f; - if (!port) { - break; - } - pos += snprintf(path + pos, sizeof(path) - pos, ".%d", port); - } - - QTAILQ_FOREACH(uport, &xhci->bus.used, next) { - if (strcmp(uport->path, path) == 0) { - return uport; - } - } - return NULL; -} - -static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, - uint64_t pictx, bool bsr) -{ - XHCISlot *slot; - USBPort *uport; - USBDevice *dev; - dma_addr_t ictx, octx, dcbaap; - uint64_t poctx; - uint32_t ictl_ctx[2]; - uint32_t slot_ctx[4]; - uint32_t ep0_ctx[5]; - int i; - TRBCCode res; - - assert(slotid >= 1 && slotid <= xhci->numslots); - - dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high); - poctx = ldq_le_pci_dma(PCI_DEVICE(xhci), dcbaap + 8 * slotid); - ictx = xhci_mask64(pictx); - octx = xhci_mask64(poctx); - - DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); - DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); - - xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx)); - - if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) { - DPRINTF("xhci: invalid input context control %08x %08x\n", - ictl_ctx[0], ictl_ctx[1]); - return CC_TRB_ERROR; - } - - xhci_dma_read_u32s(xhci, ictx+32, slot_ctx, sizeof(slot_ctx)); - xhci_dma_read_u32s(xhci, ictx+64, ep0_ctx, sizeof(ep0_ctx)); - - DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - - DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n", - ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); - - uport = xhci_lookup_uport(xhci, slot_ctx); - if (uport == NULL) { - DPRINTF("xhci: port not found\n"); - return CC_TRB_ERROR; - } - trace_usb_xhci_slot_address(slotid, uport->path); - - dev = uport->dev; - if (!dev || !dev->attached) { - DPRINTF("xhci: port %s not connected\n", uport->path); - return CC_USB_TRANSACTION_ERROR; - } - - for (i = 0; i < xhci->numslots; i++) { - if (i == slotid-1) { - continue; - } - if (xhci->slots[i].uport == uport) { - DPRINTF("xhci: port %s already assigned to slot %d\n", - uport->path, i+1); - return CC_TRB_ERROR; - } - } - - slot = &xhci->slots[slotid-1]; - slot->uport = uport; - slot->ctx = octx; - - if (bsr) { - slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT; - } else { - USBPacket p; - uint8_t buf[1]; - - slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid; - usb_device_reset(dev); - memset(&p, 0, sizeof(p)); - usb_packet_addbuf(&p, buf, sizeof(buf)); - usb_packet_setup(&p, USB_TOKEN_OUT, - usb_ep_get(dev, USB_TOKEN_OUT, 0), 0, - 0, false, false); - usb_device_handle_control(dev, &p, - DeviceOutRequest | USB_REQ_SET_ADDRESS, - slotid, 0, 0, NULL); - assert(p.status != USB_RET_ASYNC); - } - - res = xhci_enable_ep(xhci, slotid, 1, octx+32, ep0_ctx); - - DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n", - ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); - - xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx)); - - xhci->slots[slotid-1].addressed = 1; - return res; -} - - -static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, - uint64_t pictx, bool dc) -{ - dma_addr_t ictx, octx; - uint32_t ictl_ctx[2]; - uint32_t slot_ctx[4]; - uint32_t islot_ctx[4]; - uint32_t ep_ctx[5]; - int i; - TRBCCode res; - - trace_usb_xhci_slot_configure(slotid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - ictx = xhci_mask64(pictx); - octx = xhci->slots[slotid-1].ctx; - - DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); - DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); - - if (dc) { - for (i = 2; i <= 31; i++) { - if (xhci->slots[slotid-1].eps[i-1]) { - xhci_disable_ep(xhci, slotid, i); - } - } - - xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); - slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT; - DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - - return CC_SUCCESS; - } - - xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx)); - - if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) { - DPRINTF("xhci: invalid input context control %08x %08x\n", - ictl_ctx[0], ictl_ctx[1]); - return CC_TRB_ERROR; - } - - xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx)); - xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - - if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) { - DPRINTF("xhci: invalid slot state %08x\n", slot_ctx[3]); - return CC_CONTEXT_STATE_ERROR; - } - - xhci_free_device_streams(xhci, slotid, ictl_ctx[0] | ictl_ctx[1]); - - for (i = 2; i <= 31; i++) { - if (ictl_ctx[0] & (1<<i)) { - xhci_disable_ep(xhci, slotid, i); - } - if (ictl_ctx[1] & (1<<i)) { - xhci_dma_read_u32s(xhci, ictx+32+(32*i), ep_ctx, sizeof(ep_ctx)); - DPRINTF("xhci: input ep%d.%d context: %08x %08x %08x %08x %08x\n", - i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2], - ep_ctx[3], ep_ctx[4]); - xhci_disable_ep(xhci, slotid, i); - res = xhci_enable_ep(xhci, slotid, i, octx+(32*i), ep_ctx); - if (res != CC_SUCCESS) { - return res; - } - DPRINTF("xhci: output ep%d.%d context: %08x %08x %08x %08x %08x\n", - i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2], - ep_ctx[3], ep_ctx[4]); - xhci_dma_write_u32s(xhci, octx+(32*i), ep_ctx, sizeof(ep_ctx)); - } - } - - res = xhci_alloc_device_streams(xhci, slotid, ictl_ctx[1]); - if (res != CC_SUCCESS) { - for (i = 2; i <= 31; i++) { - if (ictl_ctx[1] & (1u << i)) { - xhci_disable_ep(xhci, slotid, i); - } - } - return res; - } - - slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); - slot_ctx[3] |= SLOT_CONFIGURED << SLOT_STATE_SHIFT; - slot_ctx[0] &= ~(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); - slot_ctx[0] |= islot_ctx[0] & (SLOT_CONTEXT_ENTRIES_MASK << - SLOT_CONTEXT_ENTRIES_SHIFT); - DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - - xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - - return CC_SUCCESS; -} - - -static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid, - uint64_t pictx) -{ - dma_addr_t ictx, octx; - uint32_t ictl_ctx[2]; - uint32_t iep0_ctx[5]; - uint32_t ep0_ctx[5]; - uint32_t islot_ctx[4]; - uint32_t slot_ctx[4]; - - trace_usb_xhci_slot_evaluate(slotid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - ictx = xhci_mask64(pictx); - octx = xhci->slots[slotid-1].ctx; - - DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); - DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); - - xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx)); - - if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) { - DPRINTF("xhci: invalid input context control %08x %08x\n", - ictl_ctx[0], ictl_ctx[1]); - return CC_TRB_ERROR; - } - - if (ictl_ctx[1] & 0x1) { - xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx)); - - DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n", - islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]); - - xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - - slot_ctx[1] &= ~0xFFFF; /* max exit latency */ - slot_ctx[1] |= islot_ctx[1] & 0xFFFF; - slot_ctx[2] &= ~0xFF00000; /* interrupter target */ - slot_ctx[2] |= islot_ctx[2] & 0xFF000000; - - DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - - xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - } - - if (ictl_ctx[1] & 0x2) { - xhci_dma_read_u32s(xhci, ictx+64, iep0_ctx, sizeof(iep0_ctx)); - - DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n", - iep0_ctx[0], iep0_ctx[1], iep0_ctx[2], - iep0_ctx[3], iep0_ctx[4]); - - xhci_dma_read_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx)); - - ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/ - ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000; - - DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n", - ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); - - xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx)); - } - - return CC_SUCCESS; -} - -static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid) -{ - uint32_t slot_ctx[4]; - dma_addr_t octx; - int i; - - trace_usb_xhci_slot_reset(slotid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - octx = xhci->slots[slotid-1].ctx; - - DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); - - for (i = 2; i <= 31; i++) { - if (xhci->slots[slotid-1].eps[i-1]) { - xhci_disable_ep(xhci, slotid, i); - } - } - - xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); - slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT; - DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - - return CC_SUCCESS; -} - -static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *trb) -{ - unsigned int slotid; - slotid = (trb->control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK; - if (slotid < 1 || slotid > xhci->numslots) { - DPRINTF("xhci: bad slot id %d\n", slotid); - event->ccode = CC_TRB_ERROR; - return 0; - } else if (!xhci->slots[slotid-1].enabled) { - DPRINTF("xhci: slot id %d not enabled\n", slotid); - event->ccode = CC_SLOT_NOT_ENABLED_ERROR; - return 0; - } - return slotid; -} - -/* cleanup slot state on usb device detach */ -static void xhci_detach_slot(XHCIState *xhci, USBPort *uport) -{ - int slot, ep; - - for (slot = 0; slot < xhci->numslots; slot++) { - if (xhci->slots[slot].uport == uport) { - break; - } - } - if (slot == xhci->numslots) { - return; - } - - for (ep = 0; ep < 31; ep++) { - if (xhci->slots[slot].eps[ep]) { - xhci_ep_nuke_xfers(xhci, slot + 1, ep + 1, 0); - } - } - xhci->slots[slot].uport = NULL; -} - -static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) -{ - dma_addr_t ctx; - uint8_t bw_ctx[xhci->numports+1]; - - DPRINTF("xhci_get_port_bandwidth()\n"); - - ctx = xhci_mask64(pctx); - - DPRINTF("xhci: bandwidth context at "DMA_ADDR_FMT"\n", ctx); - - /* TODO: actually implement real values here */ - bw_ctx[0] = 0; - memset(&bw_ctx[1], 80, xhci->numports); /* 80% */ - pci_dma_write(PCI_DEVICE(xhci), ctx, bw_ctx, sizeof(bw_ctx)); - - return CC_SUCCESS; -} - -static uint32_t rotl(uint32_t v, unsigned count) -{ - count &= 31; - return (v << count) | (v >> (32 - count)); -} - - -static uint32_t xhci_nec_challenge(uint32_t hi, uint32_t lo) -{ - uint32_t val; - val = rotl(lo - 0x49434878, 32 - ((hi>>8) & 0x1F)); - val += rotl(lo + 0x49434878, hi & 0x1F); - val -= rotl(hi ^ 0x49434878, (lo >> 16) & 0x1F); - return ~val; -} - -static void xhci_via_challenge(XHCIState *xhci, uint64_t addr) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - uint32_t buf[8]; - uint32_t obuf[8]; - dma_addr_t paddr = xhci_mask64(addr); - - pci_dma_read(pci_dev, paddr, &buf, 32); - - memcpy(obuf, buf, sizeof(obuf)); - - if ((buf[0] & 0xff) == 2) { - obuf[0] = 0x49932000 + 0x54dc200 * buf[2] + 0x7429b578 * buf[3]; - obuf[0] |= (buf[2] * buf[3]) & 0xff; - obuf[1] = 0x0132bb37 + 0xe89 * buf[2] + 0xf09 * buf[3]; - obuf[2] = 0x0066c2e9 + 0x2091 * buf[2] + 0x19bd * buf[3]; - obuf[3] = 0xd5281342 + 0x2cc9691 * buf[2] + 0x2367662 * buf[3]; - obuf[4] = 0x0123c75c + 0x1595 * buf[2] + 0x19ec * buf[3]; - obuf[5] = 0x00f695de + 0x26fd * buf[2] + 0x3e9 * buf[3]; - obuf[6] = obuf[2] ^ obuf[3] ^ 0x29472956; - obuf[7] = obuf[2] ^ obuf[3] ^ 0x65866593; - } - - pci_dma_write(pci_dev, paddr, &obuf, 32); -} - -static void xhci_process_commands(XHCIState *xhci) -{ - XHCITRB trb; - TRBType type; - XHCIEvent event = {ER_COMMAND_COMPLETE, CC_SUCCESS}; - dma_addr_t addr; - unsigned int i, slotid = 0; - - DPRINTF("xhci_process_commands()\n"); - if (!xhci_running(xhci)) { - DPRINTF("xhci_process_commands() called while xHC stopped or paused\n"); - return; - } - - xhci->crcr_low |= CRCR_CRR; - - while ((type = xhci_ring_fetch(xhci, &xhci->cmd_ring, &trb, &addr))) { - event.ptr = addr; - switch (type) { - case CR_ENABLE_SLOT: - for (i = 0; i < xhci->numslots; i++) { - if (!xhci->slots[i].enabled) { - break; - } - } - if (i >= xhci->numslots) { - DPRINTF("xhci: no device slots available\n"); - event.ccode = CC_NO_SLOTS_ERROR; - } else { - slotid = i+1; - event.ccode = xhci_enable_slot(xhci, slotid); - } - break; - case CR_DISABLE_SLOT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_disable_slot(xhci, slotid); - } - break; - case CR_ADDRESS_DEVICE: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_address_slot(xhci, slotid, trb.parameter, - trb.control & TRB_CR_BSR); - } - break; - case CR_CONFIGURE_ENDPOINT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_configure_slot(xhci, slotid, trb.parameter, - trb.control & TRB_CR_DC); - } - break; - case CR_EVALUATE_CONTEXT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_evaluate_slot(xhci, slotid, trb.parameter); - } - break; - case CR_STOP_ENDPOINT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT) - & TRB_CR_EPID_MASK; - event.ccode = xhci_stop_ep(xhci, slotid, epid); - } - break; - case CR_RESET_ENDPOINT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT) - & TRB_CR_EPID_MASK; - event.ccode = xhci_reset_ep(xhci, slotid, epid); - } - break; - case CR_SET_TR_DEQUEUE: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT) - & TRB_CR_EPID_MASK; - unsigned int streamid = (trb.status >> 16) & 0xffff; - event.ccode = xhci_set_ep_dequeue(xhci, slotid, - epid, streamid, - trb.parameter); - } - break; - case CR_RESET_DEVICE: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_reset_slot(xhci, slotid); - } - break; - case CR_GET_PORT_BANDWIDTH: - event.ccode = xhci_get_port_bandwidth(xhci, trb.parameter); - break; - case CR_VENDOR_VIA_CHALLENGE_RESPONSE: - xhci_via_challenge(xhci, trb.parameter); - break; - case CR_VENDOR_NEC_FIRMWARE_REVISION: - event.type = 48; /* NEC reply */ - event.length = 0x3025; - break; - case CR_VENDOR_NEC_CHALLENGE_RESPONSE: - { - uint32_t chi = trb.parameter >> 32; - uint32_t clo = trb.parameter; - uint32_t val = xhci_nec_challenge(chi, clo); - event.length = val & 0xFFFF; - event.epid = val >> 16; - slotid = val >> 24; - event.type = 48; /* NEC reply */ - } - break; - default: - trace_usb_xhci_unimplemented("command", type); - event.ccode = CC_TRB_ERROR; - break; - } - event.slotid = slotid; - xhci_event(xhci, &event, 0); - } -} - -static bool xhci_port_have_device(XHCIPort *port) -{ - if (!port->uport->dev || !port->uport->dev->attached) { - return false; /* no device present */ - } - if (!((1 << port->uport->dev->speed) & port->speedmask)) { - return false; /* speed mismatch */ - } - return true; -} - -static void xhci_port_notify(XHCIPort *port, uint32_t bits) -{ - XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, - port->portnr << 24 }; - - if ((port->portsc & bits) == bits) { - return; - } - trace_usb_xhci_port_notify(port->portnr, bits); - port->portsc |= bits; - if (!xhci_running(port->xhci)) { - return; - } - xhci_event(port->xhci, &ev, 0); -} - -static void xhci_port_update(XHCIPort *port, int is_detach) -{ - uint32_t pls = PLS_RX_DETECT; - - port->portsc = PORTSC_PP; - if (!is_detach && xhci_port_have_device(port)) { - port->portsc |= PORTSC_CCS; - switch (port->uport->dev->speed) { - case USB_SPEED_LOW: - port->portsc |= PORTSC_SPEED_LOW; - pls = PLS_POLLING; - break; - case USB_SPEED_FULL: - port->portsc |= PORTSC_SPEED_FULL; - pls = PLS_POLLING; - break; - case USB_SPEED_HIGH: - port->portsc |= PORTSC_SPEED_HIGH; - pls = PLS_POLLING; - break; - case USB_SPEED_SUPER: - port->portsc |= PORTSC_SPEED_SUPER; - port->portsc |= PORTSC_PED; - pls = PLS_U0; - break; - } - } - set_field(&port->portsc, pls, PORTSC_PLS); - trace_usb_xhci_port_link(port->portnr, pls); - xhci_port_notify(port, PORTSC_CSC); -} - -static void xhci_port_reset(XHCIPort *port, bool warm_reset) -{ - trace_usb_xhci_port_reset(port->portnr, warm_reset); - - if (!xhci_port_have_device(port)) { - return; - } - - usb_device_reset(port->uport->dev); - - switch (port->uport->dev->speed) { - case USB_SPEED_SUPER: - if (warm_reset) { - port->portsc |= PORTSC_WRC; - } - /* fall through */ - case USB_SPEED_LOW: - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - set_field(&port->portsc, PLS_U0, PORTSC_PLS); - trace_usb_xhci_port_link(port->portnr, PLS_U0); - port->portsc |= PORTSC_PED; - break; - } - - port->portsc &= ~PORTSC_PR; - xhci_port_notify(port, PORTSC_PRC); -} - -static void xhci_reset(DeviceState *dev) -{ - XHCIState *xhci = XHCI(dev); - int i; - - trace_usb_xhci_reset(); - if (!(xhci->usbsts & USBSTS_HCH)) { - DPRINTF("xhci: reset while running!\n"); - } - - xhci->usbcmd = 0; - xhci->usbsts = USBSTS_HCH; - xhci->dnctrl = 0; - xhci->crcr_low = 0; - xhci->crcr_high = 0; - xhci->dcbaap_low = 0; - xhci->dcbaap_high = 0; - xhci->config = 0; - - for (i = 0; i < xhci->numslots; i++) { - xhci_disable_slot(xhci, i+1); - } - - for (i = 0; i < xhci->numports; i++) { - xhci_port_update(xhci->ports + i, 0); - } - - for (i = 0; i < xhci->numintrs; i++) { - xhci->intr[i].iman = 0; - xhci->intr[i].imod = 0; - xhci->intr[i].erstsz = 0; - xhci->intr[i].erstba_low = 0; - xhci->intr[i].erstba_high = 0; - xhci->intr[i].erdp_low = 0; - xhci->intr[i].erdp_high = 0; - xhci->intr[i].msix_used = 0; - - xhci->intr[i].er_ep_idx = 0; - xhci->intr[i].er_pcs = 1; - xhci->intr[i].er_full = 0; - xhci->intr[i].ev_buffer_put = 0; - xhci->intr[i].ev_buffer_get = 0; - } - - xhci->mfindex_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - xhci_mfwrap_update(xhci); -} - -static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) -{ - XHCIState *xhci = ptr; - uint32_t ret; - - switch (reg) { - case 0x00: /* HCIVERSION, CAPLENGTH */ - ret = 0x01000000 | LEN_CAP; - break; - case 0x04: /* HCSPARAMS 1 */ - ret = ((xhci->numports_2+xhci->numports_3)<<24) - | (xhci->numintrs<<8) | xhci->numslots; - break; - case 0x08: /* HCSPARAMS 2 */ - ret = 0x0000000f; - break; - case 0x0c: /* HCSPARAMS 3 */ - ret = 0x00000000; - break; - case 0x10: /* HCCPARAMS */ - if (sizeof(dma_addr_t) == 4) { - ret = 0x00080000 | (xhci->max_pstreams_mask << 12); - } else { - ret = 0x00080001 | (xhci->max_pstreams_mask << 12); - } - break; - case 0x14: /* DBOFF */ - ret = OFF_DOORBELL; - break; - case 0x18: /* RTSOFF */ - ret = OFF_RUNTIME; - break; - - /* extended capabilities */ - case 0x20: /* Supported Protocol:00 */ - ret = 0x02000402; /* USB 2.0 */ - break; - case 0x24: /* Supported Protocol:04 */ - ret = 0x20425355; /* "USB " */ - break; - case 0x28: /* Supported Protocol:08 */ - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - ret = (xhci->numports_2<<8) | (xhci->numports_3+1); - } else { - ret = (xhci->numports_2<<8) | 1; - } - break; - case 0x2c: /* Supported Protocol:0c */ - ret = 0x00000000; /* reserved */ - break; - case 0x30: /* Supported Protocol:00 */ - ret = 0x03000002; /* USB 3.0 */ - break; - case 0x34: /* Supported Protocol:04 */ - ret = 0x20425355; /* "USB " */ - break; - case 0x38: /* Supported Protocol:08 */ - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - ret = (xhci->numports_3<<8) | 1; - } else { - ret = (xhci->numports_3<<8) | (xhci->numports_2+1); - } - break; - case 0x3c: /* Supported Protocol:0c */ - ret = 0x00000000; /* reserved */ - break; - default: - trace_usb_xhci_unimplemented("cap read", reg); - ret = 0; - } - - trace_usb_xhci_cap_read(reg, ret); - return ret; -} - -static uint64_t xhci_port_read(void *ptr, hwaddr reg, unsigned size) -{ - XHCIPort *port = ptr; - uint32_t ret; - - switch (reg) { - case 0x00: /* PORTSC */ - ret = port->portsc; - break; - case 0x04: /* PORTPMSC */ - case 0x08: /* PORTLI */ - ret = 0; - break; - case 0x0c: /* reserved */ - default: - trace_usb_xhci_unimplemented("port read", reg); - ret = 0; - } - - trace_usb_xhci_port_read(port->portnr, reg, ret); - return ret; -} - -static void xhci_port_write(void *ptr, hwaddr reg, - uint64_t val, unsigned size) -{ - XHCIPort *port = ptr; - uint32_t portsc, notify; - - trace_usb_xhci_port_write(port->portnr, reg, val); - - switch (reg) { - case 0x00: /* PORTSC */ - /* write-1-to-start bits */ - if (val & PORTSC_WPR) { - xhci_port_reset(port, true); - break; - } - if (val & PORTSC_PR) { - xhci_port_reset(port, false); - break; - } - - portsc = port->portsc; - notify = 0; - /* write-1-to-clear bits*/ - portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC| - PORTSC_PRC|PORTSC_PLC|PORTSC_CEC)); - if (val & PORTSC_LWS) { - /* overwrite PLS only when LWS=1 */ - uint32_t old_pls = get_field(port->portsc, PORTSC_PLS); - uint32_t new_pls = get_field(val, PORTSC_PLS); - switch (new_pls) { - case PLS_U0: - if (old_pls != PLS_U0) { - set_field(&portsc, new_pls, PORTSC_PLS); - trace_usb_xhci_port_link(port->portnr, new_pls); - notify = PORTSC_PLC; - } - break; - case PLS_U3: - if (old_pls < PLS_U3) { - set_field(&portsc, new_pls, PORTSC_PLS); - trace_usb_xhci_port_link(port->portnr, new_pls); - } - break; - case PLS_RESUME: - /* windows does this for some reason, don't spam stderr */ - break; - default: - DPRINTF("%s: ignore pls write (old %d, new %d)\n", - __func__, old_pls, new_pls); - break; - } - } - /* read/write bits */ - portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE); - portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE)); - port->portsc = portsc; - if (notify) { - xhci_port_notify(port, notify); - } - break; - case 0x04: /* PORTPMSC */ - case 0x08: /* PORTLI */ - default: - trace_usb_xhci_unimplemented("port write", reg); - } -} - -static uint64_t xhci_oper_read(void *ptr, hwaddr reg, unsigned size) -{ - XHCIState *xhci = ptr; - uint32_t ret; - - switch (reg) { - case 0x00: /* USBCMD */ - ret = xhci->usbcmd; - break; - case 0x04: /* USBSTS */ - ret = xhci->usbsts; - break; - case 0x08: /* PAGESIZE */ - ret = 1; /* 4KiB */ - break; - case 0x14: /* DNCTRL */ - ret = xhci->dnctrl; - break; - case 0x18: /* CRCR low */ - ret = xhci->crcr_low & ~0xe; - break; - case 0x1c: /* CRCR high */ - ret = xhci->crcr_high; - break; - case 0x30: /* DCBAAP low */ - ret = xhci->dcbaap_low; - break; - case 0x34: /* DCBAAP high */ - ret = xhci->dcbaap_high; - break; - case 0x38: /* CONFIG */ - ret = xhci->config; - break; - default: - trace_usb_xhci_unimplemented("oper read", reg); - ret = 0; - } - - trace_usb_xhci_oper_read(reg, ret); - return ret; -} - -static void xhci_oper_write(void *ptr, hwaddr reg, - uint64_t val, unsigned size) -{ - XHCIState *xhci = ptr; - DeviceState *d = DEVICE(ptr); - - trace_usb_xhci_oper_write(reg, val); - - switch (reg) { - case 0x00: /* USBCMD */ - if ((val & USBCMD_RS) && !(xhci->usbcmd & USBCMD_RS)) { - xhci_run(xhci); - } else if (!(val & USBCMD_RS) && (xhci->usbcmd & USBCMD_RS)) { - xhci_stop(xhci); - } - if (val & USBCMD_CSS) { - /* save state */ - xhci->usbsts &= ~USBSTS_SRE; - } - if (val & USBCMD_CRS) { - /* restore state */ - xhci->usbsts |= USBSTS_SRE; - } - xhci->usbcmd = val & 0xc0f; - xhci_mfwrap_update(xhci); - if (val & USBCMD_HCRST) { - xhci_reset(d); - } - xhci_intx_update(xhci); - break; - - case 0x04: /* USBSTS */ - /* these bits are write-1-to-clear */ - xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE)); - xhci_intx_update(xhci); - break; - - case 0x14: /* DNCTRL */ - xhci->dnctrl = val & 0xffff; - break; - case 0x18: /* CRCR low */ - xhci->crcr_low = (val & 0xffffffcf) | (xhci->crcr_low & CRCR_CRR); - break; - case 0x1c: /* CRCR high */ - xhci->crcr_high = val; - if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) { - XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED}; - xhci->crcr_low &= ~CRCR_CRR; - xhci_event(xhci, &event, 0); - DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low); - } else { - dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val); - xhci_ring_init(xhci, &xhci->cmd_ring, base); - } - xhci->crcr_low &= ~(CRCR_CA | CRCR_CS); - break; - case 0x30: /* DCBAAP low */ - xhci->dcbaap_low = val & 0xffffffc0; - break; - case 0x34: /* DCBAAP high */ - xhci->dcbaap_high = val; - break; - case 0x38: /* CONFIG */ - xhci->config = val & 0xff; - break; - default: - trace_usb_xhci_unimplemented("oper write", reg); - } -} - -static uint64_t xhci_runtime_read(void *ptr, hwaddr reg, - unsigned size) -{ - XHCIState *xhci = ptr; - uint32_t ret = 0; - - if (reg < 0x20) { - switch (reg) { - case 0x00: /* MFINDEX */ - ret = xhci_mfindex_get(xhci) & 0x3fff; - break; - default: - trace_usb_xhci_unimplemented("runtime read", reg); - break; - } - } else { - int v = (reg - 0x20) / 0x20; - XHCIInterrupter *intr = &xhci->intr[v]; - switch (reg & 0x1f) { - case 0x00: /* IMAN */ - ret = intr->iman; - break; - case 0x04: /* IMOD */ - ret = intr->imod; - break; - case 0x08: /* ERSTSZ */ - ret = intr->erstsz; - break; - case 0x10: /* ERSTBA low */ - ret = intr->erstba_low; - break; - case 0x14: /* ERSTBA high */ - ret = intr->erstba_high; - break; - case 0x18: /* ERDP low */ - ret = intr->erdp_low; - break; - case 0x1c: /* ERDP high */ - ret = intr->erdp_high; - break; - } - } - - trace_usb_xhci_runtime_read(reg, ret); - return ret; -} - -static void xhci_runtime_write(void *ptr, hwaddr reg, - uint64_t val, unsigned size) -{ - XHCIState *xhci = ptr; - int v = (reg - 0x20) / 0x20; - XHCIInterrupter *intr = &xhci->intr[v]; - trace_usb_xhci_runtime_write(reg, val); - - if (reg < 0x20) { - trace_usb_xhci_unimplemented("runtime write", reg); - return; - } - - switch (reg & 0x1f) { - case 0x00: /* IMAN */ - if (val & IMAN_IP) { - intr->iman &= ~IMAN_IP; - } - intr->iman &= ~IMAN_IE; - intr->iman |= val & IMAN_IE; - if (v == 0) { - xhci_intx_update(xhci); - } - xhci_msix_update(xhci, v); - break; - case 0x04: /* IMOD */ - intr->imod = val; - break; - case 0x08: /* ERSTSZ */ - intr->erstsz = val & 0xffff; - break; - case 0x10: /* ERSTBA low */ - /* XXX NEC driver bug: it doesn't align this to 64 bytes - intr->erstba_low = val & 0xffffffc0; */ - intr->erstba_low = val & 0xfffffff0; - break; - case 0x14: /* ERSTBA high */ - intr->erstba_high = val; - xhci_er_reset(xhci, v); - break; - case 0x18: /* ERDP low */ - if (val & ERDP_EHB) { - intr->erdp_low &= ~ERDP_EHB; - } - intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB); - break; - case 0x1c: /* ERDP high */ - intr->erdp_high = val; - xhci_events_update(xhci, v); - break; - default: - trace_usb_xhci_unimplemented("oper write", reg); - } -} - -static uint64_t xhci_doorbell_read(void *ptr, hwaddr reg, - unsigned size) -{ - /* doorbells always read as 0 */ - trace_usb_xhci_doorbell_read(reg, 0); - return 0; -} - -static void xhci_doorbell_write(void *ptr, hwaddr reg, - uint64_t val, unsigned size) -{ - XHCIState *xhci = ptr; - unsigned int epid, streamid; - - trace_usb_xhci_doorbell_write(reg, val); - - if (!xhci_running(xhci)) { - DPRINTF("xhci: wrote doorbell while xHC stopped or paused\n"); - return; - } - - reg >>= 2; - - if (reg == 0) { - if (val == 0) { - xhci_process_commands(xhci); - } else { - DPRINTF("xhci: bad doorbell 0 write: 0x%x\n", - (uint32_t)val); - } - } else { - epid = val & 0xff; - streamid = (val >> 16) & 0xffff; - if (reg > xhci->numslots) { - DPRINTF("xhci: bad doorbell %d\n", (int)reg); - } else if (epid > 31) { - DPRINTF("xhci: bad doorbell %d write: 0x%x\n", - (int)reg, (uint32_t)val); - } else { - xhci_kick_ep(xhci, reg, epid, streamid); - } - } -} - -static void xhci_cap_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - /* nothing */ -} - -static const MemoryRegionOps xhci_cap_ops = { - .read = xhci_cap_read, - .write = xhci_cap_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 4, - .impl.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps xhci_oper_ops = { - .read = xhci_oper_read, - .write = xhci_oper_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps xhci_port_ops = { - .read = xhci_port_read, - .write = xhci_port_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps xhci_runtime_ops = { - .read = xhci_runtime_read, - .write = xhci_runtime_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps xhci_doorbell_ops = { - .read = xhci_doorbell_read, - .write = xhci_doorbell_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void xhci_attach(USBPort *usbport) -{ - XHCIState *xhci = usbport->opaque; - XHCIPort *port = xhci_lookup_port(xhci, usbport); - - xhci_port_update(port, 0); -} - -static void xhci_detach(USBPort *usbport) -{ - XHCIState *xhci = usbport->opaque; - XHCIPort *port = xhci_lookup_port(xhci, usbport); - - xhci_detach_slot(xhci, usbport); - xhci_port_update(port, 1); -} - -static void xhci_wakeup(USBPort *usbport) -{ - XHCIState *xhci = usbport->opaque; - XHCIPort *port = xhci_lookup_port(xhci, usbport); - - if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) { - return; - } - set_field(&port->portsc, PLS_RESUME, PORTSC_PLS); - xhci_port_notify(port, PORTSC_PLC); -} - -static void xhci_complete(USBPort *port, USBPacket *packet) -{ - XHCITransfer *xfer = container_of(packet, XHCITransfer, packet); - - if (packet->status == USB_RET_REMOVE_FROM_QUEUE) { - xhci_ep_nuke_one_xfer(xfer, 0); - return; - } - xhci_complete_packet(xfer); - xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid, xfer->streamid); -} - -static void xhci_child_detach(USBPort *uport, USBDevice *child) -{ - USBBus *bus = usb_bus_from_device(child); - XHCIState *xhci = container_of(bus, XHCIState, bus); - - xhci_detach_slot(xhci, child->port); -} - -static USBPortOps xhci_uport_ops = { - .attach = xhci_attach, - .detach = xhci_detach, - .wakeup = xhci_wakeup, - .complete = xhci_complete, - .child_detach = xhci_child_detach, -}; - -static int xhci_find_epid(USBEndpoint *ep) -{ - if (ep->nr == 0) { - return 1; - } - if (ep->pid == USB_TOKEN_IN) { - return ep->nr * 2 + 1; - } else { - return ep->nr * 2; - } -} - -static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci, - unsigned int slotid, unsigned int epid) -{ - assert(slotid >= 1 && slotid <= xhci->numslots); - - if (!xhci->slots[slotid - 1].uport) { - return NULL; - } - - return usb_ep_get(xhci->slots[slotid - 1].uport->dev, - (epid & 1) ? USB_TOKEN_IN : USB_TOKEN_OUT, epid >> 1); -} - -static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, - unsigned int stream) -{ - XHCIState *xhci = container_of(bus, XHCIState, bus); - int slotid; - - DPRINTF("%s\n", __func__); - slotid = ep->dev->addr; - if (slotid == 0 || !xhci->slots[slotid-1].enabled) { - DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr); - return; - } - xhci_kick_ep(xhci, slotid, xhci_find_epid(ep), stream); -} - -static USBBusOps xhci_bus_ops = { - .wakeup_endpoint = xhci_wakeup_endpoint, -}; - -static void usb_xhci_init(XHCIState *xhci) -{ - DeviceState *dev = DEVICE(xhci); - XHCIPort *port; - int i, usbports, speedmask; - - xhci->usbsts = USBSTS_HCH; - - if (xhci->numports_2 > MAXPORTS_2) { - xhci->numports_2 = MAXPORTS_2; - } - if (xhci->numports_3 > MAXPORTS_3) { - xhci->numports_3 = MAXPORTS_3; - } - usbports = MAX(xhci->numports_2, xhci->numports_3); - xhci->numports = xhci->numports_2 + xhci->numports_3; - - usb_bus_new(&xhci->bus, sizeof(xhci->bus), &xhci_bus_ops, dev); - - for (i = 0; i < usbports; i++) { - speedmask = 0; - if (i < xhci->numports_2) { - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - port = &xhci->ports[i + xhci->numports_3]; - port->portnr = i + 1 + xhci->numports_3; - } else { - port = &xhci->ports[i]; - port->portnr = i + 1; - } - port->uport = &xhci->uports[i]; - port->speedmask = - USB_SPEED_MASK_LOW | - USB_SPEED_MASK_FULL | - USB_SPEED_MASK_HIGH; - snprintf(port->name, sizeof(port->name), "usb2 port #%d", i+1); - speedmask |= port->speedmask; - } - if (i < xhci->numports_3) { - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - port = &xhci->ports[i]; - port->portnr = i + 1; - } else { - port = &xhci->ports[i + xhci->numports_2]; - port->portnr = i + 1 + xhci->numports_2; - } - port->uport = &xhci->uports[i]; - port->speedmask = USB_SPEED_MASK_SUPER; - snprintf(port->name, sizeof(port->name), "usb3 port #%d", i+1); - speedmask |= port->speedmask; - } - usb_register_port(&xhci->bus, &xhci->uports[i], xhci, i, - &xhci_uport_ops, speedmask); - } -} - -static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) -{ - int i, ret; - - XHCIState *xhci = XHCI(dev); - - dev->config[PCI_CLASS_PROG] = 0x30; /* xHCI */ - dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */ - dev->config[PCI_CACHE_LINE_SIZE] = 0x10; - dev->config[0x60] = 0x30; /* release number */ - - usb_xhci_init(xhci); - - if (xhci->numintrs > MAXINTRS) { - xhci->numintrs = MAXINTRS; - } - while (xhci->numintrs & (xhci->numintrs - 1)) { /* ! power of 2 */ - xhci->numintrs++; - } - if (xhci->numintrs < 1) { - xhci->numintrs = 1; - } - if (xhci->numslots > MAXSLOTS) { - xhci->numslots = MAXSLOTS; - } - if (xhci->numslots < 1) { - xhci->numslots = 1; - } - if (xhci_get_flag(xhci, XHCI_FLAG_ENABLE_STREAMS)) { - xhci->max_pstreams_mask = 7; /* == 256 primary streams */ - } else { - xhci->max_pstreams_mask = 0; - } - - xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci); - - memory_region_init(&xhci->mem, OBJECT(xhci), "xhci", LEN_REGS); - memory_region_init_io(&xhci->mem_cap, OBJECT(xhci), &xhci_cap_ops, xhci, - "capabilities", LEN_CAP); - memory_region_init_io(&xhci->mem_oper, OBJECT(xhci), &xhci_oper_ops, xhci, - "operational", 0x400); - memory_region_init_io(&xhci->mem_runtime, OBJECT(xhci), &xhci_runtime_ops, xhci, - "runtime", LEN_RUNTIME); - memory_region_init_io(&xhci->mem_doorbell, OBJECT(xhci), &xhci_doorbell_ops, xhci, - "doorbell", LEN_DOORBELL); - - memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap); - memory_region_add_subregion(&xhci->mem, OFF_OPER, &xhci->mem_oper); - memory_region_add_subregion(&xhci->mem, OFF_RUNTIME, &xhci->mem_runtime); - memory_region_add_subregion(&xhci->mem, OFF_DOORBELL, &xhci->mem_doorbell); - - for (i = 0; i < xhci->numports; i++) { - XHCIPort *port = &xhci->ports[i]; - uint32_t offset = OFF_OPER + 0x400 + 0x10 * i; - port->xhci = xhci; - memory_region_init_io(&port->mem, OBJECT(xhci), &xhci_port_ops, port, - port->name, 0x10); - memory_region_add_subregion(&xhci->mem, offset, &port->mem); - } - - pci_register_bar(dev, 0, - PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64, - &xhci->mem); - - if (pci_bus_is_express(dev->bus) || - xhci_get_flag(xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) { - ret = pcie_endpoint_cap_init(dev, 0xa0); - assert(ret >= 0); - } - - if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI)) { - msi_init(dev, 0x70, xhci->numintrs, true, false); - } - if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI_X)) { - msix_init(dev, xhci->numintrs, - &xhci->mem, 0, OFF_MSIX_TABLE, - &xhci->mem, 0, OFF_MSIX_PBA, - 0x90); - } -} - -static void usb_xhci_exit(PCIDevice *dev) -{ - int i; - XHCIState *xhci = XHCI(dev); - - trace_usb_xhci_exit(); - - for (i = 0; i < xhci->numslots; i++) { - xhci_disable_slot(xhci, i + 1); - } - - if (xhci->mfwrap_timer) { - timer_del(xhci->mfwrap_timer); - timer_free(xhci->mfwrap_timer); - xhci->mfwrap_timer = NULL; - } - - memory_region_del_subregion(&xhci->mem, &xhci->mem_cap); - memory_region_del_subregion(&xhci->mem, &xhci->mem_oper); - memory_region_del_subregion(&xhci->mem, &xhci->mem_runtime); - memory_region_del_subregion(&xhci->mem, &xhci->mem_doorbell); - - for (i = 0; i < xhci->numports; i++) { - XHCIPort *port = &xhci->ports[i]; - memory_region_del_subregion(&xhci->mem, &port->mem); - } - - /* destroy msix memory region */ - if (dev->msix_table && dev->msix_pba - && dev->msix_entry_used) { - memory_region_del_subregion(&xhci->mem, &dev->msix_table_mmio); - memory_region_del_subregion(&xhci->mem, &dev->msix_pba_mmio); - } - - usb_bus_release(&xhci->bus); -} - -static int usb_xhci_post_load(void *opaque, int version_id) -{ - XHCIState *xhci = opaque; - PCIDevice *pci_dev = PCI_DEVICE(xhci); - XHCISlot *slot; - XHCIEPContext *epctx; - dma_addr_t dcbaap, pctx; - uint32_t slot_ctx[4]; - uint32_t ep_ctx[5]; - int slotid, epid, state, intr; - - dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high); - - for (slotid = 1; slotid <= xhci->numslots; slotid++) { - slot = &xhci->slots[slotid-1]; - if (!slot->addressed) { - continue; - } - slot->ctx = - xhci_mask64(ldq_le_pci_dma(pci_dev, dcbaap + 8 * slotid)); - xhci_dma_read_u32s(xhci, slot->ctx, slot_ctx, sizeof(slot_ctx)); - slot->uport = xhci_lookup_uport(xhci, slot_ctx); - if (!slot->uport) { - /* should not happen, but may trigger on guest bugs */ - slot->enabled = 0; - slot->addressed = 0; - continue; - } - assert(slot->uport && slot->uport->dev); - - for (epid = 1; epid <= 31; epid++) { - pctx = slot->ctx + 32 * epid; - xhci_dma_read_u32s(xhci, pctx, ep_ctx, sizeof(ep_ctx)); - state = ep_ctx[0] & EP_STATE_MASK; - if (state == EP_DISABLED) { - continue; - } - epctx = xhci_alloc_epctx(xhci, slotid, epid); - slot->eps[epid-1] = epctx; - xhci_init_epctx(epctx, pctx, ep_ctx); - epctx->state = state; - if (state == EP_RUNNING) { - /* kick endpoint after vmload is finished */ - timer_mod(epctx->kick_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - } - } - } - - for (intr = 0; intr < xhci->numintrs; intr++) { - if (xhci->intr[intr].msix_used) { - msix_vector_use(pci_dev, intr); - } else { - msix_vector_unuse(pci_dev, intr); - } - } - - return 0; -} - -static const VMStateDescription vmstate_xhci_ring = { - .name = "xhci-ring", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(dequeue, XHCIRing), - VMSTATE_BOOL(ccs, XHCIRing), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xhci_port = { - .name = "xhci-port", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(portsc, XHCIPort), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xhci_slot = { - .name = "xhci-slot", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(enabled, XHCISlot), - VMSTATE_BOOL(addressed, XHCISlot), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xhci_event = { - .name = "xhci-event", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(type, XHCIEvent), - VMSTATE_UINT32(ccode, XHCIEvent), - VMSTATE_UINT64(ptr, XHCIEvent), - VMSTATE_UINT32(length, XHCIEvent), - VMSTATE_UINT32(flags, XHCIEvent), - VMSTATE_UINT8(slotid, XHCIEvent), - VMSTATE_UINT8(epid, XHCIEvent), - VMSTATE_END_OF_LIST() - } -}; - -static bool xhci_er_full(void *opaque, int version_id) -{ - struct XHCIInterrupter *intr = opaque; - return intr->er_full; -} - -static const VMStateDescription vmstate_xhci_intr = { - .name = "xhci-intr", - .version_id = 1, - .fields = (VMStateField[]) { - /* registers */ - VMSTATE_UINT32(iman, XHCIInterrupter), - VMSTATE_UINT32(imod, XHCIInterrupter), - VMSTATE_UINT32(erstsz, XHCIInterrupter), - VMSTATE_UINT32(erstba_low, XHCIInterrupter), - VMSTATE_UINT32(erstba_high, XHCIInterrupter), - VMSTATE_UINT32(erdp_low, XHCIInterrupter), - VMSTATE_UINT32(erdp_high, XHCIInterrupter), - - /* state */ - VMSTATE_BOOL(msix_used, XHCIInterrupter), - VMSTATE_BOOL(er_pcs, XHCIInterrupter), - VMSTATE_UINT64(er_start, XHCIInterrupter), - VMSTATE_UINT32(er_size, XHCIInterrupter), - VMSTATE_UINT32(er_ep_idx, XHCIInterrupter), - - /* event queue (used if ring is full) */ - VMSTATE_BOOL(er_full, XHCIInterrupter), - VMSTATE_UINT32_TEST(ev_buffer_put, XHCIInterrupter, xhci_er_full), - VMSTATE_UINT32_TEST(ev_buffer_get, XHCIInterrupter, xhci_er_full), - VMSTATE_STRUCT_ARRAY_TEST(ev_buffer, XHCIInterrupter, EV_QUEUE, - xhci_er_full, 1, - vmstate_xhci_event, XHCIEvent), - - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xhci = { - .name = "xhci", - .version_id = 1, - .post_load = usb_xhci_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(parent_obj, XHCIState), - VMSTATE_MSIX(parent_obj, XHCIState), - - VMSTATE_STRUCT_VARRAY_UINT32(ports, XHCIState, numports, 1, - vmstate_xhci_port, XHCIPort), - VMSTATE_STRUCT_VARRAY_UINT32(slots, XHCIState, numslots, 1, - vmstate_xhci_slot, XHCISlot), - VMSTATE_STRUCT_VARRAY_UINT32(intr, XHCIState, numintrs, 1, - vmstate_xhci_intr, XHCIInterrupter), - - /* Operational Registers */ - VMSTATE_UINT32(usbcmd, XHCIState), - VMSTATE_UINT32(usbsts, XHCIState), - VMSTATE_UINT32(dnctrl, XHCIState), - VMSTATE_UINT32(crcr_low, XHCIState), - VMSTATE_UINT32(crcr_high, XHCIState), - VMSTATE_UINT32(dcbaap_low, XHCIState), - VMSTATE_UINT32(dcbaap_high, XHCIState), - VMSTATE_UINT32(config, XHCIState), - - /* Runtime Registers & state */ - VMSTATE_INT64(mfindex_start, XHCIState), - VMSTATE_TIMER_PTR(mfwrap_timer, XHCIState), - VMSTATE_STRUCT(cmd_ring, XHCIState, 1, vmstate_xhci_ring, XHCIRing), - - VMSTATE_END_OF_LIST() - } -}; - -static Property xhci_properties[] = { - DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true), - DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true), - DEFINE_PROP_BIT("superspeed-ports-first", - XHCIState, flags, XHCI_FLAG_SS_FIRST, true), - DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags, - XHCI_FLAG_FORCE_PCIE_ENDCAP, false), - DEFINE_PROP_BIT("streams", XHCIState, flags, - XHCI_FLAG_ENABLE_STREAMS, true), - DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS), - DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS), - DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), - DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xhci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_xhci; - dc->props = xhci_properties; - dc->reset = xhci_reset; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - k->realize = usb_xhci_realize; - k->exit = usb_xhci_exit; - k->vendor_id = PCI_VENDOR_ID_NEC; - k->device_id = PCI_DEVICE_ID_NEC_UPD720200; - k->class_id = PCI_CLASS_SERIAL_USB; - k->revision = 0x03; - k->is_express = 1; -} - -static const TypeInfo xhci_info = { - .name = TYPE_XHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(XHCIState), - .class_init = xhci_class_init, -}; - -static void xhci_register_types(void) -{ - type_register_static(&xhci_info); -} - -type_init(xhci_register_types) diff --git a/qemu/hw/usb/host-legacy.c b/qemu/hw/usb/host-legacy.c deleted file mode 100644 index 3b57e21b5..000000000 --- a/qemu/hw/usb/host-legacy.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Linux host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ <linux@tjworld.net> - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/usb.h" -#include "hw/usb/host.h" - -/* - * Autoconnect filter - * Format: - * auto:bus:dev[:vid:pid] - * auto:bus.dev[:vid:pid] - * - * bus - bus number (dec, * means any) - * dev - device number (dec, * means any) - * vid - vendor id (hex, * means any) - * pid - product id (hex, * means any) - * - * See 'lsusb' output. - */ -static int parse_filter(const char *spec, struct USBAutoFilter *f) -{ - enum { BUS, DEV, VID, PID, DONE }; - const char *p = spec; - int i; - - f->bus_num = 0; - f->addr = 0; - f->vendor_id = 0; - f->product_id = 0; - - for (i = BUS; i < DONE; i++) { - p = strpbrk(p, ":."); - if (!p) { - break; - } - p++; - - if (*p == '*') { - continue; - } - switch (i) { - case BUS: - f->bus_num = strtol(p, NULL, 10); - break; - case DEV: - f->addr = strtol(p, NULL, 10); - break; - case VID: - f->vendor_id = strtol(p, NULL, 16); - break; - case PID: - f->product_id = strtol(p, NULL, 16); - break; - } - } - - if (i < DEV) { - fprintf(stderr, "husb: invalid auto filter spec %s\n", spec); - return -1; - } - - return 0; -} - -USBDevice *usb_host_device_open(USBBus *bus, const char *devname) -{ - struct USBAutoFilter filter; - USBDevice *dev; - char *p; - - dev = usb_create(bus, "usb-host"); - - if (strstr(devname, "auto:")) { - if (parse_filter(devname, &filter) < 0) { - goto fail; - } - } else { - p = strchr(devname, '.'); - if (p) { - filter.bus_num = strtoul(devname, NULL, 0); - filter.addr = strtoul(p + 1, NULL, 0); - filter.vendor_id = 0; - filter.product_id = 0; - } else { - p = strchr(devname, ':'); - if (p) { - filter.bus_num = 0; - filter.addr = 0; - filter.vendor_id = strtoul(devname, NULL, 16); - filter.product_id = strtoul(p + 1, NULL, 16); - } else { - goto fail; - } - } - } - - qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num); - qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr); - qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id); - qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id); - return dev; - -fail: - object_unparent(OBJECT(dev)); - return NULL; -} - -static void usb_host_register_types(void) -{ - usb_legacy_register("usb-host", "host", usb_host_device_open); -} - -type_init(usb_host_register_types) diff --git a/qemu/hw/usb/host-libusb.c b/qemu/hw/usb/host-libusb.c deleted file mode 100644 index 6458a9448..000000000 --- a/qemu/hw/usb/host-libusb.c +++ /dev/null @@ -1,1688 +0,0 @@ -/* - * Linux host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ <linux@tjworld.net> - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * (c) 2012 Gerd Hoffmann <kraxel@redhat.com> - * Completely rewritten to use libusb instead of usbfs ioctls. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include <poll.h> -#include <libusb.h> - -#include "qapi/error.h" -#include "qemu-common.h" -#include "monitor/monitor.h" -#include "qemu/error-report.h" -#include "sysemu/sysemu.h" -#include "trace.h" - -#include "hw/usb.h" - -/* ------------------------------------------------------------------------ */ - -#define TYPE_USB_HOST_DEVICE "usb-host" -#define USB_HOST_DEVICE(obj) \ - OBJECT_CHECK(USBHostDevice, (obj), TYPE_USB_HOST_DEVICE) - -typedef struct USBHostDevice USBHostDevice; -typedef struct USBHostRequest USBHostRequest; -typedef struct USBHostIsoXfer USBHostIsoXfer; -typedef struct USBHostIsoRing USBHostIsoRing; - -struct USBAutoFilter { - uint32_t bus_num; - uint32_t addr; - char *port; - uint32_t vendor_id; - uint32_t product_id; -}; - -enum USBHostDeviceOptions { - USB_HOST_OPT_PIPELINE, -}; - -struct USBHostDevice { - USBDevice parent_obj; - - /* properties */ - struct USBAutoFilter match; - int32_t bootindex; - uint32_t iso_urb_count; - uint32_t iso_urb_frames; - uint32_t options; - uint32_t loglevel; - - /* state */ - QTAILQ_ENTRY(USBHostDevice) next; - int seen, errcount; - int bus_num; - int addr; - char port[16]; - - libusb_device *dev; - libusb_device_handle *dh; - struct libusb_device_descriptor ddesc; - - struct { - bool detached; - bool claimed; - } ifs[USB_MAX_INTERFACES]; - - /* callbacks & friends */ - QEMUBH *bh_nodev; - QEMUBH *bh_postld; - Notifier exit; - - /* request queues */ - QTAILQ_HEAD(, USBHostRequest) requests; - QTAILQ_HEAD(, USBHostIsoRing) isorings; -}; - -struct USBHostRequest { - USBHostDevice *host; - USBPacket *p; - bool in; - struct libusb_transfer *xfer; - unsigned char *buffer; - unsigned char *cbuf; - unsigned int clen; - bool usb3ep0quirk; - QTAILQ_ENTRY(USBHostRequest) next; -}; - -struct USBHostIsoXfer { - USBHostIsoRing *ring; - struct libusb_transfer *xfer; - bool copy_complete; - unsigned int packet; - QTAILQ_ENTRY(USBHostIsoXfer) next; -}; - -struct USBHostIsoRing { - USBHostDevice *host; - USBEndpoint *ep; - QTAILQ_HEAD(, USBHostIsoXfer) unused; - QTAILQ_HEAD(, USBHostIsoXfer) inflight; - QTAILQ_HEAD(, USBHostIsoXfer) copy; - QTAILQ_ENTRY(USBHostIsoRing) next; -}; - -static QTAILQ_HEAD(, USBHostDevice) hostdevs = - QTAILQ_HEAD_INITIALIZER(hostdevs); - -static void usb_host_auto_check(void *unused); -static void usb_host_release_interfaces(USBHostDevice *s); -static void usb_host_nodev(USBHostDevice *s); -static void usb_host_detach_kernel(USBHostDevice *s); -static void usb_host_attach_kernel(USBHostDevice *s); - -/* ------------------------------------------------------------------------ */ - -#ifndef LIBUSB_LOG_LEVEL_WARNING /* older libusb didn't define these */ -#define LIBUSB_LOG_LEVEL_WARNING 2 -#endif - -/* ------------------------------------------------------------------------ */ - -#define CONTROL_TIMEOUT 10000 /* 10 sec */ -#define BULK_TIMEOUT 0 /* unlimited */ -#define INTR_TIMEOUT 0 /* unlimited */ - -#if LIBUSBX_API_VERSION >= 0x01000103 -# define HAVE_STREAMS 1 -#endif - -static const char *speed_name[] = { - [LIBUSB_SPEED_UNKNOWN] = "?", - [LIBUSB_SPEED_LOW] = "1.5", - [LIBUSB_SPEED_FULL] = "12", - [LIBUSB_SPEED_HIGH] = "480", - [LIBUSB_SPEED_SUPER] = "5000", -}; - -static const unsigned int speed_map[] = { - [LIBUSB_SPEED_LOW] = USB_SPEED_LOW, - [LIBUSB_SPEED_FULL] = USB_SPEED_FULL, - [LIBUSB_SPEED_HIGH] = USB_SPEED_HIGH, - [LIBUSB_SPEED_SUPER] = USB_SPEED_SUPER, -}; - -static const unsigned int status_map[] = { - [LIBUSB_TRANSFER_COMPLETED] = USB_RET_SUCCESS, - [LIBUSB_TRANSFER_ERROR] = USB_RET_IOERROR, - [LIBUSB_TRANSFER_TIMED_OUT] = USB_RET_IOERROR, - [LIBUSB_TRANSFER_CANCELLED] = USB_RET_IOERROR, - [LIBUSB_TRANSFER_STALL] = USB_RET_STALL, - [LIBUSB_TRANSFER_NO_DEVICE] = USB_RET_NODEV, - [LIBUSB_TRANSFER_OVERFLOW] = USB_RET_BABBLE, -}; - -static const char *err_names[] = { - [-LIBUSB_ERROR_IO] = "IO", - [-LIBUSB_ERROR_INVALID_PARAM] = "INVALID_PARAM", - [-LIBUSB_ERROR_ACCESS] = "ACCESS", - [-LIBUSB_ERROR_NO_DEVICE] = "NO_DEVICE", - [-LIBUSB_ERROR_NOT_FOUND] = "NOT_FOUND", - [-LIBUSB_ERROR_BUSY] = "BUSY", - [-LIBUSB_ERROR_TIMEOUT] = "TIMEOUT", - [-LIBUSB_ERROR_OVERFLOW] = "OVERFLOW", - [-LIBUSB_ERROR_PIPE] = "PIPE", - [-LIBUSB_ERROR_INTERRUPTED] = "INTERRUPTED", - [-LIBUSB_ERROR_NO_MEM] = "NO_MEM", - [-LIBUSB_ERROR_NOT_SUPPORTED] = "NOT_SUPPORTED", - [-LIBUSB_ERROR_OTHER] = "OTHER", -}; - -static libusb_context *ctx; -static uint32_t loglevel; - -static void usb_host_handle_fd(void *opaque) -{ - struct timeval tv = { 0, 0 }; - libusb_handle_events_timeout(ctx, &tv); -} - -static void usb_host_add_fd(int fd, short events, void *user_data) -{ - qemu_set_fd_handler(fd, - (events & POLLIN) ? usb_host_handle_fd : NULL, - (events & POLLOUT) ? usb_host_handle_fd : NULL, - ctx); -} - -static void usb_host_del_fd(int fd, void *user_data) -{ - qemu_set_fd_handler(fd, NULL, NULL, NULL); -} - -static int usb_host_init(void) -{ - const struct libusb_pollfd **poll; - int i, rc; - - if (ctx) { - return 0; - } - rc = libusb_init(&ctx); - if (rc != 0) { - return -1; - } - libusb_set_debug(ctx, loglevel); - - libusb_set_pollfd_notifiers(ctx, usb_host_add_fd, - usb_host_del_fd, - ctx); - poll = libusb_get_pollfds(ctx); - if (poll) { - for (i = 0; poll[i] != NULL; i++) { - usb_host_add_fd(poll[i]->fd, poll[i]->events, ctx); - } - } - free(poll); - return 0; -} - -static int usb_host_get_port(libusb_device *dev, char *port, size_t len) -{ - uint8_t path[7]; - size_t off; - int rc, i; - -#if LIBUSBX_API_VERSION >= 0x01000102 - rc = libusb_get_port_numbers(dev, path, 7); -#else - rc = libusb_get_port_path(ctx, dev, path, 7); -#endif - if (rc < 0) { - return 0; - } - off = snprintf(port, len, "%d", path[0]); - for (i = 1; i < rc; i++) { - off += snprintf(port+off, len-off, ".%d", path[i]); - } - return off; -} - -static void usb_host_libusb_error(const char *func, int rc) -{ - const char *errname; - - if (rc >= 0) { - return; - } - - if (-rc < ARRAY_SIZE(err_names) && err_names[-rc]) { - errname = err_names[-rc]; - } else { - errname = "?"; - } - error_report("%s: %d [%s]", func, rc, errname); -} - -/* ------------------------------------------------------------------------ */ - -static bool usb_host_use_combining(USBEndpoint *ep) -{ - int type; - - if (!ep->pipeline) { - return false; - } - if (ep->pid != USB_TOKEN_IN) { - return false; - } - type = usb_ep_get_type(ep->dev, ep->pid, ep->nr); - if (type != USB_ENDPOINT_XFER_BULK) { - return false; - } - return true; -} - -/* ------------------------------------------------------------------------ */ - -static USBHostRequest *usb_host_req_alloc(USBHostDevice *s, USBPacket *p, - bool in, size_t bufsize) -{ - USBHostRequest *r = g_new0(USBHostRequest, 1); - - r->host = s; - r->p = p; - r->in = in; - r->xfer = libusb_alloc_transfer(0); - if (bufsize) { - r->buffer = g_malloc(bufsize); - } - QTAILQ_INSERT_TAIL(&s->requests, r, next); - return r; -} - -static void usb_host_req_free(USBHostRequest *r) -{ - if (r->host) { - QTAILQ_REMOVE(&r->host->requests, r, next); - } - libusb_free_transfer(r->xfer); - g_free(r->buffer); - g_free(r); -} - -static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p) -{ - USBHostRequest *r; - - QTAILQ_FOREACH(r, &s->requests, next) { - if (r->p == p) { - return r; - } - } - return NULL; -} - -static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer) -{ - USBHostRequest *r = xfer->user_data; - USBHostDevice *s = r->host; - bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE); - - if (r->p == NULL) { - goto out; /* request was canceled */ - } - - r->p->status = status_map[xfer->status]; - r->p->actual_length = xfer->actual_length; - if (r->in && xfer->actual_length) { - memcpy(r->cbuf, r->buffer + 8, xfer->actual_length); - - /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices - * to work redirected to a not superspeed capable hcd */ - if (r->usb3ep0quirk && xfer->actual_length >= 18 && - r->cbuf[7] == 9) { - r->cbuf[7] = 64; - } - } - trace_usb_host_req_complete(s->bus_num, s->addr, r->p, - r->p->status, r->p->actual_length); - usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p); - -out: - usb_host_req_free(r); - if (disconnect) { - usb_host_nodev(s); - } -} - -static void usb_host_req_complete_data(struct libusb_transfer *xfer) -{ - USBHostRequest *r = xfer->user_data; - USBHostDevice *s = r->host; - bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE); - - if (r->p == NULL) { - goto out; /* request was canceled */ - } - - r->p->status = status_map[xfer->status]; - if (r->in && xfer->actual_length) { - usb_packet_copy(r->p, r->buffer, xfer->actual_length); - } - trace_usb_host_req_complete(s->bus_num, s->addr, r->p, - r->p->status, r->p->actual_length); - if (usb_host_use_combining(r->p->ep)) { - usb_combined_input_packet_complete(USB_DEVICE(s), r->p); - } else { - usb_packet_complete(USB_DEVICE(s), r->p); - } - -out: - usb_host_req_free(r); - if (disconnect) { - usb_host_nodev(s); - } -} - -static void usb_host_req_abort(USBHostRequest *r) -{ - USBHostDevice *s = r->host; - bool inflight = (r->p && r->p->state == USB_PACKET_ASYNC); - - if (inflight) { - r->p->status = USB_RET_NODEV; - trace_usb_host_req_complete(s->bus_num, s->addr, r->p, - r->p->status, r->p->actual_length); - if (r->p->ep->nr == 0) { - usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p); - } else { - usb_packet_complete(USB_DEVICE(s), r->p); - } - r->p = NULL; - } - - QTAILQ_REMOVE(&r->host->requests, r, next); - r->host = NULL; - - if (inflight) { - libusb_cancel_transfer(r->xfer); - } -} - -/* ------------------------------------------------------------------------ */ - -static void usb_host_req_complete_iso(struct libusb_transfer *transfer) -{ - USBHostIsoXfer *xfer = transfer->user_data; - - if (!xfer) { - /* USBHostIsoXfer released while inflight */ - g_free(transfer->buffer); - libusb_free_transfer(transfer); - return; - } - - QTAILQ_REMOVE(&xfer->ring->inflight, xfer, next); - if (QTAILQ_EMPTY(&xfer->ring->inflight)) { - USBHostDevice *s = xfer->ring->host; - trace_usb_host_iso_stop(s->bus_num, s->addr, xfer->ring->ep->nr); - } - if (xfer->ring->ep->pid == USB_TOKEN_IN) { - QTAILQ_INSERT_TAIL(&xfer->ring->copy, xfer, next); - usb_wakeup(xfer->ring->ep, 0); - } else { - QTAILQ_INSERT_TAIL(&xfer->ring->unused, xfer, next); - } -} - -static USBHostIsoRing *usb_host_iso_alloc(USBHostDevice *s, USBEndpoint *ep) -{ - USBHostIsoRing *ring = g_new0(USBHostIsoRing, 1); - USBHostIsoXfer *xfer; - /* FIXME: check interval (for now assume one xfer per frame) */ - int packets = s->iso_urb_frames; - int i; - - ring->host = s; - ring->ep = ep; - QTAILQ_INIT(&ring->unused); - QTAILQ_INIT(&ring->inflight); - QTAILQ_INIT(&ring->copy); - QTAILQ_INSERT_TAIL(&s->isorings, ring, next); - - for (i = 0; i < s->iso_urb_count; i++) { - xfer = g_new0(USBHostIsoXfer, 1); - xfer->ring = ring; - xfer->xfer = libusb_alloc_transfer(packets); - xfer->xfer->dev_handle = s->dh; - xfer->xfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; - - xfer->xfer->endpoint = ring->ep->nr; - if (ring->ep->pid == USB_TOKEN_IN) { - xfer->xfer->endpoint |= USB_DIR_IN; - } - xfer->xfer->callback = usb_host_req_complete_iso; - xfer->xfer->user_data = xfer; - - xfer->xfer->num_iso_packets = packets; - xfer->xfer->length = ring->ep->max_packet_size * packets; - xfer->xfer->buffer = g_malloc0(xfer->xfer->length); - - QTAILQ_INSERT_TAIL(&ring->unused, xfer, next); - } - - return ring; -} - -static USBHostIsoRing *usb_host_iso_find(USBHostDevice *s, USBEndpoint *ep) -{ - USBHostIsoRing *ring; - - QTAILQ_FOREACH(ring, &s->isorings, next) { - if (ring->ep == ep) { - return ring; - } - } - return NULL; -} - -static void usb_host_iso_reset_xfer(USBHostIsoXfer *xfer) -{ - libusb_set_iso_packet_lengths(xfer->xfer, - xfer->ring->ep->max_packet_size); - xfer->packet = 0; - xfer->copy_complete = false; -} - -static void usb_host_iso_free_xfer(USBHostIsoXfer *xfer, bool inflight) -{ - if (inflight) { - xfer->xfer->user_data = NULL; - } else { - g_free(xfer->xfer->buffer); - libusb_free_transfer(xfer->xfer); - } - g_free(xfer); -} - -static void usb_host_iso_free(USBHostIsoRing *ring) -{ - USBHostIsoXfer *xfer; - - while ((xfer = QTAILQ_FIRST(&ring->inflight)) != NULL) { - QTAILQ_REMOVE(&ring->inflight, xfer, next); - usb_host_iso_free_xfer(xfer, true); - } - while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) { - QTAILQ_REMOVE(&ring->unused, xfer, next); - usb_host_iso_free_xfer(xfer, false); - } - while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL) { - QTAILQ_REMOVE(&ring->copy, xfer, next); - usb_host_iso_free_xfer(xfer, false); - } - - QTAILQ_REMOVE(&ring->host->isorings, ring, next); - g_free(ring); -} - -static void usb_host_iso_free_all(USBHostDevice *s) -{ - USBHostIsoRing *ring; - - while ((ring = QTAILQ_FIRST(&s->isorings)) != NULL) { - usb_host_iso_free(ring); - } -} - -static bool usb_host_iso_data_copy(USBHostIsoXfer *xfer, USBPacket *p) -{ - unsigned int psize; - unsigned char *buf; - - buf = libusb_get_iso_packet_buffer_simple(xfer->xfer, xfer->packet); - if (p->pid == USB_TOKEN_OUT) { - psize = p->iov.size; - if (psize > xfer->ring->ep->max_packet_size) { - /* should not happen (guest bug) */ - psize = xfer->ring->ep->max_packet_size; - } - xfer->xfer->iso_packet_desc[xfer->packet].length = psize; - } else { - psize = xfer->xfer->iso_packet_desc[xfer->packet].actual_length; - if (psize > p->iov.size) { - /* should not happen (guest bug) */ - psize = p->iov.size; - } - } - usb_packet_copy(p, buf, psize); - xfer->packet++; - xfer->copy_complete = (xfer->packet == xfer->xfer->num_iso_packets); - return xfer->copy_complete; -} - -static void usb_host_iso_data_in(USBHostDevice *s, USBPacket *p) -{ - USBHostIsoRing *ring; - USBHostIsoXfer *xfer; - bool disconnect = false; - int rc; - - ring = usb_host_iso_find(s, p->ep); - if (ring == NULL) { - ring = usb_host_iso_alloc(s, p->ep); - } - - /* copy data to guest */ - xfer = QTAILQ_FIRST(&ring->copy); - if (xfer != NULL) { - if (usb_host_iso_data_copy(xfer, p)) { - QTAILQ_REMOVE(&ring->copy, xfer, next); - QTAILQ_INSERT_TAIL(&ring->unused, xfer, next); - } - } - - /* submit empty bufs to host */ - while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) { - QTAILQ_REMOVE(&ring->unused, xfer, next); - usb_host_iso_reset_xfer(xfer); - rc = libusb_submit_transfer(xfer->xfer); - if (rc != 0) { - usb_host_libusb_error("libusb_submit_transfer [iso]", rc); - QTAILQ_INSERT_TAIL(&ring->unused, xfer, next); - if (rc == LIBUSB_ERROR_NO_DEVICE) { - disconnect = true; - } - break; - } - if (QTAILQ_EMPTY(&ring->inflight)) { - trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr); - } - QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next); - } - - if (disconnect) { - usb_host_nodev(s); - } -} - -static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p) -{ - USBHostIsoRing *ring; - USBHostIsoXfer *xfer; - bool disconnect = false; - int rc, filled = 0; - - ring = usb_host_iso_find(s, p->ep); - if (ring == NULL) { - ring = usb_host_iso_alloc(s, p->ep); - } - - /* copy data from guest */ - xfer = QTAILQ_FIRST(&ring->copy); - while (xfer != NULL && xfer->copy_complete) { - filled++; - xfer = QTAILQ_NEXT(xfer, next); - } - if (xfer == NULL) { - xfer = QTAILQ_FIRST(&ring->unused); - if (xfer == NULL) { - trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, p->ep->nr); - return; - } - QTAILQ_REMOVE(&ring->unused, xfer, next); - usb_host_iso_reset_xfer(xfer); - QTAILQ_INSERT_TAIL(&ring->copy, xfer, next); - } - usb_host_iso_data_copy(xfer, p); - - if (QTAILQ_EMPTY(&ring->inflight)) { - /* wait until half of our buffers are filled - before kicking the iso out stream */ - if (filled*2 < s->iso_urb_count) { - return; - } - } - - /* submit filled bufs to host */ - while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL && - xfer->copy_complete) { - QTAILQ_REMOVE(&ring->copy, xfer, next); - rc = libusb_submit_transfer(xfer->xfer); - if (rc != 0) { - usb_host_libusb_error("libusb_submit_transfer [iso]", rc); - QTAILQ_INSERT_TAIL(&ring->unused, xfer, next); - if (rc == LIBUSB_ERROR_NO_DEVICE) { - disconnect = true; - } - break; - } - if (QTAILQ_EMPTY(&ring->inflight)) { - trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr); - } - QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next); - } - - if (disconnect) { - usb_host_nodev(s); - } -} - -/* ------------------------------------------------------------------------ */ - -static void usb_host_speed_compat(USBHostDevice *s) -{ - USBDevice *udev = USB_DEVICE(s); - struct libusb_config_descriptor *conf; - const struct libusb_interface_descriptor *intf; - const struct libusb_endpoint_descriptor *endp; -#ifdef HAVE_STREAMS - struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp; -#endif - bool compat_high = true; - bool compat_full = true; - uint8_t type; - int rc, c, i, a, e; - - for (c = 0;; c++) { - rc = libusb_get_config_descriptor(s->dev, c, &conf); - if (rc != 0) { - break; - } - for (i = 0; i < conf->bNumInterfaces; i++) { - for (a = 0; a < conf->interface[i].num_altsetting; a++) { - intf = &conf->interface[i].altsetting[a]; - for (e = 0; e < intf->bNumEndpoints; e++) { - endp = &intf->endpoint[e]; - type = endp->bmAttributes & 0x3; - switch (type) { - case 0x01: /* ISO */ - compat_full = false; - compat_high = false; - break; - case 0x02: /* BULK */ -#ifdef HAVE_STREAMS - rc = libusb_get_ss_endpoint_companion_descriptor - (ctx, endp, &endp_ss_comp); - if (rc == LIBUSB_SUCCESS) { - libusb_free_ss_endpoint_companion_descriptor - (endp_ss_comp); - compat_full = false; - compat_high = false; - } -#endif - break; - case 0x03: /* INTERRUPT */ - if (endp->wMaxPacketSize > 64) { - compat_full = false; - } - if (endp->wMaxPacketSize > 1024) { - compat_high = false; - } - break; - } - } - } - } - libusb_free_config_descriptor(conf); - } - - udev->speedmask = (1 << udev->speed); - if (udev->speed == USB_SPEED_SUPER && compat_high) { - udev->speedmask |= USB_SPEED_MASK_HIGH; - } - if (udev->speed == USB_SPEED_SUPER && compat_full) { - udev->speedmask |= USB_SPEED_MASK_FULL; - } - if (udev->speed == USB_SPEED_HIGH && compat_full) { - udev->speedmask |= USB_SPEED_MASK_FULL; - } -} - -static void usb_host_ep_update(USBHostDevice *s) -{ - static const char *tname[] = { - [USB_ENDPOINT_XFER_CONTROL] = "control", - [USB_ENDPOINT_XFER_ISOC] = "isoc", - [USB_ENDPOINT_XFER_BULK] = "bulk", - [USB_ENDPOINT_XFER_INT] = "int", - }; - USBDevice *udev = USB_DEVICE(s); - struct libusb_config_descriptor *conf; - const struct libusb_interface_descriptor *intf; - const struct libusb_endpoint_descriptor *endp; -#ifdef HAVE_STREAMS - struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp; -#endif - uint8_t devep, type; - int pid, ep; - int rc, i, e; - - usb_ep_reset(udev); - rc = libusb_get_active_config_descriptor(s->dev, &conf); - if (rc != 0) { - return; - } - trace_usb_host_parse_config(s->bus_num, s->addr, - conf->bConfigurationValue, true); - - for (i = 0; i < conf->bNumInterfaces; i++) { - assert(udev->altsetting[i] < conf->interface[i].num_altsetting); - intf = &conf->interface[i].altsetting[udev->altsetting[i]]; - trace_usb_host_parse_interface(s->bus_num, s->addr, - intf->bInterfaceNumber, - intf->bAlternateSetting, true); - for (e = 0; e < intf->bNumEndpoints; e++) { - endp = &intf->endpoint[e]; - - devep = endp->bEndpointAddress; - pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; - ep = devep & 0xf; - type = endp->bmAttributes & 0x3; - - if (ep == 0) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "invalid endpoint address"); - return; - } - if (usb_ep_get_type(udev, pid, ep) != USB_ENDPOINT_XFER_INVALID) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "duplicate endpoint address"); - return; - } - - trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep, - (devep & USB_DIR_IN) ? "in" : "out", - tname[type], true); - usb_ep_set_max_packet_size(udev, pid, ep, - endp->wMaxPacketSize); - usb_ep_set_type(udev, pid, ep, type); - usb_ep_set_ifnum(udev, pid, ep, i); - usb_ep_set_halted(udev, pid, ep, 0); -#ifdef HAVE_STREAMS - if (type == LIBUSB_TRANSFER_TYPE_BULK && - libusb_get_ss_endpoint_companion_descriptor(ctx, endp, - &endp_ss_comp) == LIBUSB_SUCCESS) { - usb_ep_set_max_streams(udev, pid, ep, - endp_ss_comp->bmAttributes); - libusb_free_ss_endpoint_companion_descriptor(endp_ss_comp); - } -#endif - } - } - - libusb_free_config_descriptor(conf); -} - -static int usb_host_open(USBHostDevice *s, libusb_device *dev) -{ - USBDevice *udev = USB_DEVICE(s); - int bus_num = libusb_get_bus_number(dev); - int addr = libusb_get_device_address(dev); - int rc; - Error *local_err = NULL; - - trace_usb_host_open_started(bus_num, addr); - - if (s->dh != NULL) { - goto fail; - } - rc = libusb_open(dev, &s->dh); - if (rc != 0) { - goto fail; - } - - s->dev = dev; - s->bus_num = bus_num; - s->addr = addr; - - usb_host_detach_kernel(s); - - libusb_get_device_descriptor(dev, &s->ddesc); - usb_host_get_port(s->dev, s->port, sizeof(s->port)); - - usb_ep_init(udev); - usb_host_ep_update(s); - - udev->speed = speed_map[libusb_get_device_speed(dev)]; - usb_host_speed_compat(s); - - if (s->ddesc.iProduct) { - libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct, - (unsigned char *)udev->product_desc, - sizeof(udev->product_desc)); - } else { - snprintf(udev->product_desc, sizeof(udev->product_desc), - "host:%d.%d", bus_num, addr); - } - - usb_device_attach(udev, &local_err); - if (local_err) { - error_report_err(local_err); - goto fail; - } - - trace_usb_host_open_success(bus_num, addr); - return 0; - -fail: - trace_usb_host_open_failure(bus_num, addr); - if (s->dh != NULL) { - usb_host_release_interfaces(s); - libusb_reset_device(s->dh); - usb_host_attach_kernel(s); - libusb_close(s->dh); - s->dh = NULL; - s->dev = NULL; - } - return -1; -} - -static void usb_host_abort_xfers(USBHostDevice *s) -{ - USBHostRequest *r, *rtmp; - - QTAILQ_FOREACH_SAFE(r, &s->requests, next, rtmp) { - usb_host_req_abort(r); - } -} - -static int usb_host_close(USBHostDevice *s) -{ - USBDevice *udev = USB_DEVICE(s); - - if (s->dh == NULL) { - return -1; - } - - trace_usb_host_close(s->bus_num, s->addr); - - usb_host_abort_xfers(s); - usb_host_iso_free_all(s); - - if (udev->attached) { - usb_device_detach(udev); - } - - usb_host_release_interfaces(s); - libusb_reset_device(s->dh); - usb_host_attach_kernel(s); - libusb_close(s->dh); - s->dh = NULL; - s->dev = NULL; - - usb_host_auto_check(NULL); - return 0; -} - -static void usb_host_nodev_bh(void *opaque) -{ - USBHostDevice *s = opaque; - usb_host_close(s); -} - -static void usb_host_nodev(USBHostDevice *s) -{ - if (!s->bh_nodev) { - s->bh_nodev = qemu_bh_new(usb_host_nodev_bh, s); - } - qemu_bh_schedule(s->bh_nodev); -} - -static void usb_host_exit_notifier(struct Notifier *n, void *data) -{ - USBHostDevice *s = container_of(n, USBHostDevice, exit); - - if (s->dh) { - usb_host_release_interfaces(s); - usb_host_attach_kernel(s); - } -} - -static void usb_host_realize(USBDevice *udev, Error **errp) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - - if (s->match.vendor_id > 0xffff) { - error_setg(errp, "vendorid out of range"); - return; - } - if (s->match.product_id > 0xffff) { - error_setg(errp, "productid out of range"); - return; - } - if (s->match.addr > 127) { - error_setg(errp, "hostaddr out of range"); - return; - } - - loglevel = s->loglevel; - udev->flags |= (1 << USB_DEV_FLAG_IS_HOST); - udev->auto_attach = 0; - QTAILQ_INIT(&s->requests); - QTAILQ_INIT(&s->isorings); - - s->exit.notify = usb_host_exit_notifier; - qemu_add_exit_notifier(&s->exit); - - QTAILQ_INSERT_TAIL(&hostdevs, s, next); - usb_host_auto_check(NULL); -} - -static void usb_host_instance_init(Object *obj) -{ - USBDevice *udev = USB_DEVICE(obj); - USBHostDevice *s = USB_HOST_DEVICE(udev); - - device_add_bootindex_property(obj, &s->bootindex, - "bootindex", NULL, - &udev->qdev, NULL); -} - -static void usb_host_handle_destroy(USBDevice *udev) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - - qemu_remove_exit_notifier(&s->exit); - QTAILQ_REMOVE(&hostdevs, s, next); - usb_host_close(s); -} - -static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - USBHostRequest *r; - - if (p->combined) { - usb_combined_packet_cancel(udev, p); - return; - } - - trace_usb_host_req_canceled(s->bus_num, s->addr, p); - - r = usb_host_req_find(s, p); - if (r && r->p) { - r->p = NULL; /* mark as dead */ - libusb_cancel_transfer(r->xfer); - } -} - -static void usb_host_detach_kernel(USBHostDevice *s) -{ - struct libusb_config_descriptor *conf; - int rc, i; - - rc = libusb_get_active_config_descriptor(s->dev, &conf); - if (rc != 0) { - return; - } - for (i = 0; i < conf->bNumInterfaces; i++) { - rc = libusb_kernel_driver_active(s->dh, i); - usb_host_libusb_error("libusb_kernel_driver_active", rc); - if (rc != 1) { - continue; - } - trace_usb_host_detach_kernel(s->bus_num, s->addr, i); - rc = libusb_detach_kernel_driver(s->dh, i); - usb_host_libusb_error("libusb_detach_kernel_driver", rc); - s->ifs[i].detached = true; - } - libusb_free_config_descriptor(conf); -} - -static void usb_host_attach_kernel(USBHostDevice *s) -{ - struct libusb_config_descriptor *conf; - int rc, i; - - rc = libusb_get_active_config_descriptor(s->dev, &conf); - if (rc != 0) { - return; - } - for (i = 0; i < conf->bNumInterfaces; i++) { - if (!s->ifs[i].detached) { - continue; - } - trace_usb_host_attach_kernel(s->bus_num, s->addr, i); - libusb_attach_kernel_driver(s->dh, i); - s->ifs[i].detached = false; - } - libusb_free_config_descriptor(conf); -} - -static int usb_host_claim_interfaces(USBHostDevice *s, int configuration) -{ - USBDevice *udev = USB_DEVICE(s); - struct libusb_config_descriptor *conf; - int rc, i; - - for (i = 0; i < USB_MAX_INTERFACES; i++) { - udev->altsetting[i] = 0; - } - udev->ninterfaces = 0; - udev->configuration = 0; - - usb_host_detach_kernel(s); - - rc = libusb_get_active_config_descriptor(s->dev, &conf); - if (rc != 0) { - if (rc == LIBUSB_ERROR_NOT_FOUND) { - /* address state - ignore */ - return USB_RET_SUCCESS; - } - return USB_RET_STALL; - } - - for (i = 0; i < conf->bNumInterfaces; i++) { - trace_usb_host_claim_interface(s->bus_num, s->addr, configuration, i); - rc = libusb_claim_interface(s->dh, i); - usb_host_libusb_error("libusb_claim_interface", rc); - if (rc != 0) { - return USB_RET_STALL; - } - s->ifs[i].claimed = true; - } - - udev->ninterfaces = conf->bNumInterfaces; - udev->configuration = configuration; - - libusb_free_config_descriptor(conf); - return USB_RET_SUCCESS; -} - -static void usb_host_release_interfaces(USBHostDevice *s) -{ - USBDevice *udev = USB_DEVICE(s); - int i, rc; - - for (i = 0; i < udev->ninterfaces; i++) { - if (!s->ifs[i].claimed) { - continue; - } - trace_usb_host_release_interface(s->bus_num, s->addr, i); - rc = libusb_release_interface(s->dh, i); - usb_host_libusb_error("libusb_release_interface", rc); - s->ifs[i].claimed = false; - } -} - -static void usb_host_set_address(USBHostDevice *s, int addr) -{ - USBDevice *udev = USB_DEVICE(s); - - trace_usb_host_set_address(s->bus_num, s->addr, addr); - udev->addr = addr; -} - -static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p) -{ - int rc; - - trace_usb_host_set_config(s->bus_num, s->addr, config); - - usb_host_release_interfaces(s); - rc = libusb_set_configuration(s->dh, config); - if (rc != 0) { - usb_host_libusb_error("libusb_set_configuration", rc); - p->status = USB_RET_STALL; - if (rc == LIBUSB_ERROR_NO_DEVICE) { - usb_host_nodev(s); - } - return; - } - p->status = usb_host_claim_interfaces(s, config); - if (p->status != USB_RET_SUCCESS) { - return; - } - usb_host_ep_update(s); -} - -static void usb_host_set_interface(USBHostDevice *s, int iface, int alt, - USBPacket *p) -{ - USBDevice *udev = USB_DEVICE(s); - int rc; - - trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt); - - usb_host_iso_free_all(s); - - if (iface >= USB_MAX_INTERFACES) { - p->status = USB_RET_STALL; - return; - } - - rc = libusb_set_interface_alt_setting(s->dh, iface, alt); - if (rc != 0) { - usb_host_libusb_error("libusb_set_interface_alt_setting", rc); - p->status = USB_RET_STALL; - if (rc == LIBUSB_ERROR_NO_DEVICE) { - usb_host_nodev(s); - } - return; - } - - udev->altsetting[iface] = alt; - usb_host_ep_update(s); -} - -static void usb_host_handle_control(USBDevice *udev, USBPacket *p, - int request, int value, int index, - int length, uint8_t *data) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - USBHostRequest *r; - int rc; - - trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index); - - if (s->dh == NULL) { - p->status = USB_RET_NODEV; - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - } - - switch (request) { - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - usb_host_set_address(s, value); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - usb_host_set_config(s, value & 0xff, p); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - usb_host_set_interface(s, index, value, p); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == 0) { /* clear halt */ - int pid = (index & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; - libusb_clear_halt(s->dh, index); - usb_ep_set_halted(udev, pid, index & 0x0f, 0); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - } - } - - r = usb_host_req_alloc(s, p, (request >> 8) & USB_DIR_IN, length + 8); - r->cbuf = data; - r->clen = length; - memcpy(r->buffer, udev->setup_buf, 8); - if (!r->in) { - memcpy(r->buffer + 8, r->cbuf, r->clen); - } - - /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices - * to work redirected to a not superspeed capable hcd */ - if ((udev->speedmask & USB_SPEED_MASK_SUPER) && - !(udev->port->speedmask & USB_SPEED_MASK_SUPER) && - request == 0x8006 && value == 0x100 && index == 0) { - r->usb3ep0quirk = true; - } - - libusb_fill_control_transfer(r->xfer, s->dh, r->buffer, - usb_host_req_complete_ctrl, r, - CONTROL_TIMEOUT); - rc = libusb_submit_transfer(r->xfer); - if (rc != 0) { - p->status = USB_RET_NODEV; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - if (rc == LIBUSB_ERROR_NO_DEVICE) { - usb_host_nodev(s); - } - return; - } - - p->status = USB_RET_ASYNC; -} - -static void usb_host_handle_data(USBDevice *udev, USBPacket *p) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - USBHostRequest *r; - size_t size; - int ep, rc; - - if (usb_host_use_combining(p->ep) && p->state == USB_PACKET_SETUP) { - p->status = USB_RET_ADD_TO_QUEUE; - return; - } - - trace_usb_host_req_data(s->bus_num, s->addr, p, - p->pid == USB_TOKEN_IN, - p->ep->nr, p->iov.size); - - if (s->dh == NULL) { - p->status = USB_RET_NODEV; - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - } - if (p->ep->halted) { - p->status = USB_RET_STALL; - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - } - - switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) { - case USB_ENDPOINT_XFER_BULK: - size = usb_packet_size(p); - r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, size); - if (!r->in) { - usb_packet_copy(p, r->buffer, size); - } - ep = p->ep->nr | (r->in ? USB_DIR_IN : 0); - if (p->stream) { -#ifdef HAVE_STREAMS - libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream, - r->buffer, size, - usb_host_req_complete_data, r, - BULK_TIMEOUT); -#else - usb_host_req_free(r); - p->status = USB_RET_STALL; - return; -#endif - } else { - libusb_fill_bulk_transfer(r->xfer, s->dh, ep, - r->buffer, size, - usb_host_req_complete_data, r, - BULK_TIMEOUT); - } - break; - case USB_ENDPOINT_XFER_INT: - r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, p->iov.size); - if (!r->in) { - usb_packet_copy(p, r->buffer, p->iov.size); - } - ep = p->ep->nr | (r->in ? USB_DIR_IN : 0); - libusb_fill_interrupt_transfer(r->xfer, s->dh, ep, - r->buffer, p->iov.size, - usb_host_req_complete_data, r, - INTR_TIMEOUT); - break; - case USB_ENDPOINT_XFER_ISOC: - if (p->pid == USB_TOKEN_IN) { - usb_host_iso_data_in(s, p); - } else { - usb_host_iso_data_out(s, p); - } - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - return; - default: - p->status = USB_RET_STALL; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - return; - } - - rc = libusb_submit_transfer(r->xfer); - if (rc != 0) { - p->status = USB_RET_NODEV; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - if (rc == LIBUSB_ERROR_NO_DEVICE) { - usb_host_nodev(s); - } - return; - } - - p->status = USB_RET_ASYNC; -} - -static void usb_host_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) -{ - if (usb_host_use_combining(ep)) { - usb_ep_combine_input_packets(ep); - } -} - -static void usb_host_handle_reset(USBDevice *udev) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - int rc; - - trace_usb_host_reset(s->bus_num, s->addr); - - rc = libusb_reset_device(s->dh); - if (rc != 0) { - usb_host_nodev(s); - } -} - -static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps, - int nr_eps, int streams) -{ -#ifdef HAVE_STREAMS - USBHostDevice *s = USB_HOST_DEVICE(udev); - unsigned char endpoints[30]; - int i, rc; - - for (i = 0; i < nr_eps; i++) { - endpoints[i] = eps[i]->nr; - if (eps[i]->pid == USB_TOKEN_IN) { - endpoints[i] |= 0x80; - } - } - rc = libusb_alloc_streams(s->dh, streams, endpoints, nr_eps); - if (rc < 0) { - usb_host_libusb_error("libusb_alloc_streams", rc); - } else if (rc != streams) { - error_report("libusb_alloc_streams: got less streams " - "then requested %d < %d", rc, streams); - } - - return (rc == streams) ? 0 : -1; -#else - error_report("libusb_alloc_streams: error not implemented"); - return -1; -#endif -} - -static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps, - int nr_eps) -{ -#ifdef HAVE_STREAMS - USBHostDevice *s = USB_HOST_DEVICE(udev); - unsigned char endpoints[30]; - int i; - - for (i = 0; i < nr_eps; i++) { - endpoints[i] = eps[i]->nr; - if (eps[i]->pid == USB_TOKEN_IN) { - endpoints[i] |= 0x80; - } - } - libusb_free_streams(s->dh, endpoints, nr_eps); -#endif -} - -/* - * This is *NOT* about restoring state. We have absolutely no idea - * what state the host device is in at the moment and whenever it is - * still present in the first place. Attemping to contine where we - * left off is impossible. - * - * What we are going to do here is emulate a surprise removal of - * the usb device passed through, then kick host scan so the device - * will get re-attached (and re-initialized by the guest) in case it - * is still present. - * - * As the device removal will change the state of other devices (usb - * host controller, most likely interrupt controller too) we have to - * wait with it until *all* vmstate is loaded. Thus post_load just - * kicks a bottom half which then does the actual work. - */ -static void usb_host_post_load_bh(void *opaque) -{ - USBHostDevice *dev = opaque; - USBDevice *udev = USB_DEVICE(dev); - - if (dev->dh != NULL) { - usb_host_close(dev); - } - if (udev->attached) { - usb_device_detach(udev); - } - usb_host_auto_check(NULL); -} - -static int usb_host_post_load(void *opaque, int version_id) -{ - USBHostDevice *dev = opaque; - - if (!dev->bh_postld) { - dev->bh_postld = qemu_bh_new(usb_host_post_load_bh, dev); - } - qemu_bh_schedule(dev->bh_postld); - return 0; -} - -static const VMStateDescription vmstate_usb_host = { - .name = "usb-host", - .version_id = 1, - .minimum_version_id = 1, - .post_load = usb_host_post_load, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(parent_obj, USBHostDevice), - VMSTATE_END_OF_LIST() - } -}; - -static Property usb_host_dev_properties[] = { - 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_UINT32("vendorid", USBHostDevice, match.vendor_id, 0), - DEFINE_PROP_UINT32("productid", USBHostDevice, match.product_id, 0), - DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), - DEFINE_PROP_UINT32("isobsize", USBHostDevice, iso_urb_frames, 32), - DEFINE_PROP_UINT32("loglevel", USBHostDevice, loglevel, - LIBUSB_LOG_LEVEL_WARNING), - DEFINE_PROP_BIT("pipeline", USBHostDevice, options, - USB_HOST_OPT_PIPELINE, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_host_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_host_realize; - uc->product_desc = "USB Host Device"; - uc->cancel_packet = usb_host_cancel_packet; - uc->handle_data = usb_host_handle_data; - uc->handle_control = usb_host_handle_control; - uc->handle_reset = usb_host_handle_reset; - uc->handle_destroy = usb_host_handle_destroy; - uc->flush_ep_queue = usb_host_flush_ep_queue; - uc->alloc_streams = usb_host_alloc_streams; - uc->free_streams = usb_host_free_streams; - dc->vmsd = &vmstate_usb_host; - dc->props = usb_host_dev_properties; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static TypeInfo usb_host_dev_info = { - .name = TYPE_USB_HOST_DEVICE, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBHostDevice), - .class_init = usb_host_class_initfn, - .instance_init = usb_host_instance_init, -}; - -static void usb_host_register_types(void) -{ - type_register_static(&usb_host_dev_info); -} - -type_init(usb_host_register_types) - -/* ------------------------------------------------------------------------ */ - -static QEMUTimer *usb_auto_timer; -static VMChangeStateEntry *usb_vmstate; - -static void usb_host_vm_state(void *unused, int running, RunState state) -{ - if (running) { - usb_host_auto_check(unused); - } -} - -static void usb_host_auto_check(void *unused) -{ - struct USBHostDevice *s; - struct USBAutoFilter *f; - libusb_device **devs = NULL; - struct libusb_device_descriptor ddesc; - int unconnected = 0; - int i, n; - - if (usb_host_init() != 0) { - return; - } - - if (runstate_is_running()) { - n = libusb_get_device_list(ctx, &devs); - for (i = 0; i < n; i++) { - if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) { - continue; - } - if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) { - continue; - } - QTAILQ_FOREACH(s, &hostdevs, next) { - f = &s->match; - if (f->bus_num > 0 && - f->bus_num != libusb_get_bus_number(devs[i])) { - continue; - } - if (f->addr > 0 && - f->addr != libusb_get_device_address(devs[i])) { - continue; - } - if (f->port != NULL) { - char port[16] = "-"; - usb_host_get_port(devs[i], port, sizeof(port)); - if (strcmp(f->port, port) != 0) { - continue; - } - } - if (f->vendor_id > 0 && - f->vendor_id != ddesc.idVendor) { - continue; - } - if (f->product_id > 0 && - f->product_id != ddesc.idProduct) { - continue; - } - - /* We got a match */ - s->seen++; - if (s->errcount >= 3) { - continue; - } - if (s->dh != NULL) { - continue; - } - if (usb_host_open(s, devs[i]) < 0) { - s->errcount++; - continue; - } - break; - } - } - libusb_free_device_list(devs, 1); - - QTAILQ_FOREACH(s, &hostdevs, next) { - if (s->dh == NULL) { - unconnected++; - } - if (s->seen == 0) { - if (s->dh) { - usb_host_close(s); - } - s->errcount = 0; - } - s->seen = 0; - } - -#if 0 - if (unconnected == 0) { - /* nothing to watch */ - if (usb_auto_timer) { - timer_del(usb_auto_timer); - trace_usb_host_auto_scan_disabled(); - } - return; - } -#endif - } - - if (!usb_vmstate) { - usb_vmstate = qemu_add_vm_change_state_handler(usb_host_vm_state, NULL); - } - if (!usb_auto_timer) { - usb_auto_timer = timer_new_ms(QEMU_CLOCK_REALTIME, usb_host_auto_check, NULL); - if (!usb_auto_timer) { - return; - } - trace_usb_host_auto_scan_enabled(); - } - timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000); -} - -void hmp_info_usbhost(Monitor *mon, const QDict *qdict) -{ - libusb_device **devs = NULL; - struct libusb_device_descriptor ddesc; - char port[16]; - int i, n; - - if (usb_host_init() != 0) { - return; - } - - n = libusb_get_device_list(ctx, &devs); - for (i = 0; i < n; i++) { - if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) { - continue; - } - if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) { - continue; - } - usb_host_get_port(devs[i], port, sizeof(port)); - monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n", - libusb_get_bus_number(devs[i]), - libusb_get_device_address(devs[i]), - port, - speed_name[libusb_get_device_speed(devs[i])]); - monitor_printf(mon, " Class %02x:", ddesc.bDeviceClass); - monitor_printf(mon, " USB device %04x:%04x", - ddesc.idVendor, ddesc.idProduct); - if (ddesc.iProduct) { - libusb_device_handle *handle; - if (libusb_open(devs[i], &handle) == 0) { - unsigned char name[64] = ""; - libusb_get_string_descriptor_ascii(handle, - ddesc.iProduct, - name, sizeof(name)); - libusb_close(handle); - monitor_printf(mon, ", %s", name); - } - } - monitor_printf(mon, "\n"); - } - libusb_free_device_list(devs, 1); -} diff --git a/qemu/hw/usb/host-stub.c b/qemu/hw/usb/host-stub.c deleted file mode 100644 index 6ba65a1f6..000000000 --- a/qemu/hw/usb/host-stub.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Stub host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ <linux@tjworld.net> - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "ui/console.h" -#include "hw/usb.h" -#include "monitor/monitor.h" - -void hmp_info_usbhost(Monitor *mon, const QDict *qdict) -{ - monitor_printf(mon, "USB host devices not supported\n"); -} - -/* XXX: modify configure to compile the right host driver */ -USBDevice *usb_host_device_open(USBBus *bus, const char *devname) -{ - return NULL; -} diff --git a/qemu/hw/usb/host.h b/qemu/hw/usb/host.h deleted file mode 100644 index 048ff3b48..000000000 --- a/qemu/hw/usb/host.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Linux host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ <linux@tjworld.net> - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QEMU_USB_HOST_H -#define QEMU_USB_HOST_H - -struct USBAutoFilter { - uint32_t bus_num; - uint32_t addr; - char *port; - uint32_t vendor_id; - uint32_t product_id; -}; - -#endif /* QEMU_USB_HOST_H */ diff --git a/qemu/hw/usb/libhw.c b/qemu/hw/usb/libhw.c deleted file mode 100644 index 73cdf0c97..000000000 --- a/qemu/hw/usb/libhw.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * QEMU USB emulation, libhw bits. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/hw.h" -#include "hw/usb.h" -#include "sysemu/dma.h" - -int usb_packet_map(USBPacket *p, QEMUSGList *sgl) -{ - DMADirection dir = (p->pid == USB_TOKEN_IN) ? - DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE; - void *mem; - int i; - - for (i = 0; i < sgl->nsg; i++) { - dma_addr_t base = sgl->sg[i].base; - dma_addr_t len = sgl->sg[i].len; - - while (len) { - dma_addr_t xlen = len; - mem = dma_memory_map(sgl->as, base, &xlen, dir); - if (!mem) { - goto err; - } - if (xlen > len) { - xlen = len; - } - qemu_iovec_add(&p->iov, mem, xlen); - len -= xlen; - base += xlen; - } - } - return 0; - -err: - usb_packet_unmap(p, sgl); - return -1; -} - -void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl) -{ - DMADirection dir = (p->pid == USB_TOKEN_IN) ? - DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE; - int i; - - for (i = 0; i < p->iov.niov; i++) { - dma_memory_unmap(sgl->as, p->iov.iov[i].iov_base, - p->iov.iov[i].iov_len, dir, - p->iov.iov[i].iov_len); - } -} diff --git a/qemu/hw/usb/quirks-ftdi-ids.h b/qemu/hw/usb/quirks-ftdi-ids.h deleted file mode 100644 index 57c12ef66..000000000 --- a/qemu/hw/usb/quirks-ftdi-ids.h +++ /dev/null @@ -1,1255 +0,0 @@ -/* - * vendor/product IDs (VID/PID) of devices using FTDI USB serial converters. - * Please keep numerically sorted within individual areas, thanks! - * - * Philipp Gühring - pg@futureware.at - added the Device ID of the USB relais - * from Rudolf Gugler - * - */ - - -/**********************************/ -/***** devices using FTDI VID *****/ -/**********************************/ - - -#define FTDI_VID 0x0403 /* Vendor Id */ - - -/*** "original" FTDI device PIDs ***/ - -#define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */ -#define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */ -#define FTDI_8U2232C_PID 0x6010 /* Dual channel device */ -#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */ -#define FTDI_232H_PID 0x6014 /* Single channel hi-speed device */ -#define FTDI_FTX_PID 0x6015 /* FT-X series (FT201X, FT230X, FT231X, etc) */ -#define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */ -#define FTDI_232RL_PID 0xFBFA /* Product ID for FT232RL */ - - -/*** third-party PIDs (using FTDI_VID) ***/ - -#define FTDI_LUMEL_PD12_PID 0x6002 - -/* - * Marvell OpenRD Base, Client - * http://www.open-rd.org - * OpenRD Base, Client use VID 0x0403 - */ -#define MARVELL_OPENRD_PID 0x9e90 - -/* www.candapter.com Ewert Energy Systems CANdapter device */ -#define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */ - -/* - * Texas Instruments XDS100v2 JTAG / BeagleBone A3 - * http://processors.wiki.ti.com/index.php/XDS100 - * http://beagleboard.org/bone - */ -#define TI_XDS100V2_PID 0xa6d0 - -#define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */ - -/* US Interface Navigator (http://www.usinterface.com/) */ -#define FTDI_USINT_CAT_PID 0xb810 /* Navigator CAT and 2nd PTT lines */ -#define FTDI_USINT_WKEY_PID 0xb811 /* Navigator WKEY and FSK lines */ -#define FTDI_USINT_RS232_PID 0xb812 /* Navigator RS232 and CONFIG lines */ - -/* OOCDlink by Joern Kaipf <joernk@web.de> - * (http://www.joernonline.de/) */ -#define FTDI_OOCDLINK_PID 0xbaf8 /* Amontec JTAGkey */ - -/* Luminary Micro Stellaris Boards, VID = FTDI_VID */ -/* FTDI 2332C Dual channel device, side A=245 FIFO (JTAG), Side B=RS232 UART */ -#define LMI_LM3S_DEVEL_BOARD_PID 0xbcd8 -#define LMI_LM3S_EVAL_BOARD_PID 0xbcd9 -#define LMI_LM3S_ICDI_BOARD_PID 0xbcda - -#define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */ - -/* OpenDCC (www.opendcc.de) product id */ -#define FTDI_OPENDCC_PID 0xBFD8 -#define FTDI_OPENDCC_SNIFFER_PID 0xBFD9 -#define FTDI_OPENDCC_THROTTLE_PID 0xBFDA -#define FTDI_OPENDCC_GATEWAY_PID 0xBFDB -#define FTDI_OPENDCC_GBM_PID 0xBFDC - -/* NZR SEM 16+ USB (http://www.nzr.de) */ -#define FTDI_NZR_SEM_USB_PID 0xC1E0 /* NZR SEM-LOG16+ */ - -/* - * RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com) - */ -#define FTDI_RRCIRKITS_LOCOBUFFER_PID 0xc7d0 /* LocoBuffer USB */ - -/* DMX4ALL DMX Interfaces */ -#define FTDI_DMX4ALL 0xC850 - -/* - * ASK.fr devices - */ -#define FTDI_ASK_RDR400_PID 0xC991 /* ASK RDR 400 series card reader */ - -/* www.starting-point-systems.com µChameleon device */ -#define FTDI_MICRO_CHAMELEON_PID 0xCAA0 /* Product Id */ - -/* - * Tactrix OpenPort (ECU) devices. - * OpenPort 1.3M submitted by Donour Sizemore. - * OpenPort 1.3S and 1.3U submitted by Ian Abbott. - */ -#define FTDI_TACTRIX_OPENPORT_13M_PID 0xCC48 /* OpenPort 1.3 Mitsubishi */ -#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */ -#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */ - -#define FTDI_DISTORTEC_JTAG_LOCK_PICK_PID 0xCFF8 - -/* SCS HF Radio Modems PID's (http://www.scs-ptc.com) */ -/* the VID is the standard ftdi vid (FTDI_VID) */ -#define FTDI_SCS_DEVICE_0_PID 0xD010 /* SCS PTC-IIusb */ -#define FTDI_SCS_DEVICE_1_PID 0xD011 /* SCS Tracker / DSP TNC */ -#define FTDI_SCS_DEVICE_2_PID 0xD012 -#define FTDI_SCS_DEVICE_3_PID 0xD013 -#define FTDI_SCS_DEVICE_4_PID 0xD014 -#define FTDI_SCS_DEVICE_5_PID 0xD015 -#define FTDI_SCS_DEVICE_6_PID 0xD016 -#define FTDI_SCS_DEVICE_7_PID 0xD017 - -/* iPlus device */ -#define FTDI_IPLUS_PID 0xD070 /* Product Id */ -#define FTDI_IPLUS2_PID 0xD071 /* Product Id */ - -/* - * Gamma Scout (http://gamma-scout.com/). Submitted by rsc@runtux.com. - */ -#define FTDI_GAMMA_SCOUT_PID 0xD678 /* Gamma Scout online */ - -/* Propox devices */ -#define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 -#define FTDI_PROPOX_ISPCABLEIII_PID 0xD739 - -/* Lenz LI-USB Computer Interface. */ -#define FTDI_LENZ_LIUSB_PID 0xD780 - -/* Vardaan Enterprises Serial Interface VEUSB422R3 */ -#define FTDI_VARDAAN_PID 0xF070 - -/* - * Xsens Technologies BV products (http://www.xsens.com). - */ -#define XSENS_CONVERTER_0_PID 0xD388 -#define XSENS_CONVERTER_1_PID 0xD389 -#define XSENS_CONVERTER_2_PID 0xD38A -#define XSENS_CONVERTER_3_PID 0xD38B -#define XSENS_CONVERTER_4_PID 0xD38C -#define XSENS_CONVERTER_5_PID 0xD38D -#define XSENS_CONVERTER_6_PID 0xD38E -#define XSENS_CONVERTER_7_PID 0xD38F - -/* - * NDI (www.ndigital.com) product ids - */ -#define FTDI_NDI_HUC_PID 0xDA70 /* NDI Host USB Converter */ -#define FTDI_NDI_SPECTRA_SCU_PID 0xDA71 /* NDI Spectra SCU */ -#define FTDI_NDI_FUTURE_2_PID 0xDA72 /* NDI future device #2 */ -#define FTDI_NDI_FUTURE_3_PID 0xDA73 /* NDI future device #3 */ -#define FTDI_NDI_AURORA_SCU_PID 0xDA74 /* NDI Aurora SCU */ - -/* - * ChamSys Limited (www.chamsys.co.uk) USB wing/interface product IDs - */ -#define FTDI_CHAMSYS_24_MASTER_WING_PID 0xDAF8 -#define FTDI_CHAMSYS_PC_WING_PID 0xDAF9 -#define FTDI_CHAMSYS_USB_DMX_PID 0xDAFA -#define FTDI_CHAMSYS_MIDI_TIMECODE_PID 0xDAFB -#define FTDI_CHAMSYS_MINI_WING_PID 0xDAFC -#define FTDI_CHAMSYS_MAXI_WING_PID 0xDAFD -#define FTDI_CHAMSYS_MEDIA_WING_PID 0xDAFE -#define FTDI_CHAMSYS_WING_PID 0xDAFF - -/* - * Westrex International devices submitted by Cory Lee - */ -#define FTDI_WESTREX_MODEL_777_PID 0xDC00 /* Model 777 */ -#define FTDI_WESTREX_MODEL_8900F_PID 0xDC01 /* Model 8900F */ - -/* - * ACG Identification Technologies GmbH products (http://www.acg.de/). - * Submitted by anton -at- goto10 -dot- org. - */ -#define FTDI_ACG_HFDUAL_PID 0xDD20 /* HF Dual ISO Reader (RFID) */ - -/* - * Definitions for Artemis astronomical USB based cameras - * Check it at http://www.artemisccd.co.uk/ - */ -#define FTDI_ARTEMIS_PID 0xDF28 /* All Artemis Cameras */ - -/* - * Definitions for ATIK Instruments astronomical USB based cameras - * Check it at http://www.atik-instruments.com/ - */ -#define FTDI_ATIK_ATK16_PID 0xDF30 /* ATIK ATK-16 Grayscale Camera */ -#define FTDI_ATIK_ATK16C_PID 0xDF32 /* ATIK ATK-16C Colour Camera */ -#define FTDI_ATIK_ATK16HR_PID 0xDF31 /* ATIK ATK-16HR Grayscale Camera */ -#define FTDI_ATIK_ATK16HRC_PID 0xDF33 /* ATIK ATK-16HRC Colour Camera */ -#define FTDI_ATIK_ATK16IC_PID 0xDF35 /* ATIK ATK-16IC Grayscale Camera */ - -/* - * Yost Engineering, Inc. products (www.yostengineering.com). - * PID 0xE050 submitted by Aaron Prose. - */ -#define FTDI_YEI_SERVOCENTER31_PID 0xE050 /* YEI ServoCenter3.1 USB */ - -/* - * ELV USB devices submitted by Christian Abt of ELV (www.elv.de). - * All of these devices use FTDI's vendor ID (0x0403). - * Further IDs taken from ELV Windows .inf file. - * - * The previously included PID for the UO 100 module was incorrect. - * In fact, that PID was for ELV's UR 100 USB-RS232 converter (0xFB58). - * - * Armin Laeuger originally sent the PID for the UM 100 module. - */ -#define FTDI_ELV_USR_PID 0xE000 /* ELV Universal-Sound-Recorder */ -#define FTDI_ELV_MSM1_PID 0xE001 /* ELV Mini-Sound-Modul */ -#define FTDI_ELV_KL100_PID 0xE002 /* ELV Kfz-Leistungsmesser KL 100 */ -#define FTDI_ELV_WS550_PID 0xE004 /* WS 550 */ -#define FTDI_ELV_EC3000_PID 0xE006 /* ENERGY CONTROL 3000 USB */ -#define FTDI_ELV_WS888_PID 0xE008 /* WS 888 */ -#define FTDI_ELV_TWS550_PID 0xE009 /* Technoline WS 550 */ -#define FTDI_ELV_FEM_PID 0xE00A /* Funk Energie Monitor */ -#define FTDI_ELV_FHZ1300PC_PID 0xE0E8 /* FHZ 1300 PC */ -#define FTDI_ELV_WS500_PID 0xE0E9 /* PC-Wetterstation (WS 500) */ -#define FTDI_ELV_HS485_PID 0xE0EA /* USB to RS-485 adapter */ -#define FTDI_ELV_UMS100_PID 0xE0EB /* ELV USB Master-Slave Schaltsteckdose UMS 100 */ -#define FTDI_ELV_TFD128_PID 0xE0EC /* ELV Temperatur-Feuchte-Datenlogger TFD 128 */ -#define FTDI_ELV_FM3RX_PID 0xE0ED /* ELV Messwertuebertragung FM3 RX */ -#define FTDI_ELV_WS777_PID 0xE0EE /* Conrad WS 777 */ -#define FTDI_ELV_EM1010PC_PID 0xE0EF /* Energy monitor EM 1010 PC */ -#define FTDI_ELV_CSI8_PID 0xE0F0 /* Computer-Schalt-Interface (CSI 8) */ -#define FTDI_ELV_EM1000DL_PID 0xE0F1 /* PC-Datenlogger fuer Energiemonitor (EM 1000 DL) */ -#define FTDI_ELV_PCK100_PID 0xE0F2 /* PC-Kabeltester (PCK 100) */ -#define FTDI_ELV_RFP500_PID 0xE0F3 /* HF-Leistungsmesser (RFP 500) */ -#define FTDI_ELV_FS20SIG_PID 0xE0F4 /* Signalgeber (FS 20 SIG) */ -#define FTDI_ELV_UTP8_PID 0xE0F5 /* ELV UTP 8 */ -#define FTDI_ELV_WS300PC_PID 0xE0F6 /* PC-Wetterstation (WS 300 PC) */ -#define FTDI_ELV_WS444PC_PID 0xE0F7 /* Conrad WS 444 PC */ -#define FTDI_PHI_FISCO_PID 0xE40B /* PHI Fisco USB to Serial cable */ -#define FTDI_ELV_UAD8_PID 0xF068 /* USB-AD-Wandler (UAD 8) */ -#define FTDI_ELV_UDA7_PID 0xF069 /* USB-DA-Wandler (UDA 7) */ -#define FTDI_ELV_USI2_PID 0xF06A /* USB-Schrittmotoren-Interface (USI 2) */ -#define FTDI_ELV_T1100_PID 0xF06B /* Thermometer (T 1100) */ -#define FTDI_ELV_PCD200_PID 0xF06C /* PC-Datenlogger (PCD 200) */ -#define FTDI_ELV_ULA200_PID 0xF06D /* USB-LCD-Ansteuerung (ULA 200) */ -#define FTDI_ELV_ALC8500_PID 0xF06E /* ALC 8500 Expert */ -#define FTDI_ELV_FHZ1000PC_PID 0xF06F /* FHZ 1000 PC */ -#define FTDI_ELV_UR100_PID 0xFB58 /* USB-RS232-Umsetzer (UR 100) */ -#define FTDI_ELV_UM100_PID 0xFB5A /* USB-Modul UM 100 */ -#define FTDI_ELV_UO100_PID 0xFB5B /* USB-Modul UO 100 */ -/* Additional ELV PIDs that default to using the FTDI D2XX drivers on - * MS Windows, rather than the FTDI Virtual Com Port drivers. - * Maybe these will be easier to use with the libftdi/libusb user-space - * drivers, or possibly the Comedi drivers in some cases. */ -#define FTDI_ELV_CLI7000_PID 0xFB59 /* Computer-Light-Interface (CLI 7000) */ -#define FTDI_ELV_PPS7330_PID 0xFB5C /* Processor-Power-Supply (PPS 7330) */ -#define FTDI_ELV_TFM100_PID 0xFB5D /* Temperatur-Feuchte-Messgeraet (TFM 100) */ -#define FTDI_ELV_UDF77_PID 0xFB5E /* USB DCF Funkuhr (UDF 77) */ -#define FTDI_ELV_UIO88_PID 0xFB5F /* USB-I/O Interface (UIO 88) */ - -/* - * EVER Eco Pro UPS (http://www.ever.com.pl/) - */ - -#define EVER_ECO_PRO_CDS 0xe520 /* RS-232 converter */ - -/* - * Active Robots product ids. - */ -#define FTDI_ACTIVE_ROBOTS_PID 0xE548 /* USB comms board */ - -/* Pyramid Computer GmbH */ -#define FTDI_PYRAMID_PID 0xE6C8 /* Pyramid Appliance Display */ - -/* www.elsterelectricity.com Elster Unicom III Optical Probe */ -#define FTDI_ELSTER_UNICOM_PID 0xE700 /* Product Id */ - -/* - * Gude Analog- und Digitalsysteme GmbH - */ -#define FTDI_GUDEADS_E808_PID 0xE808 -#define FTDI_GUDEADS_E809_PID 0xE809 -#define FTDI_GUDEADS_E80A_PID 0xE80A -#define FTDI_GUDEADS_E80B_PID 0xE80B -#define FTDI_GUDEADS_E80C_PID 0xE80C -#define FTDI_GUDEADS_E80D_PID 0xE80D -#define FTDI_GUDEADS_E80E_PID 0xE80E -#define FTDI_GUDEADS_E80F_PID 0xE80F -#define FTDI_GUDEADS_E888_PID 0xE888 /* Expert ISDN Control USB */ -#define FTDI_GUDEADS_E889_PID 0xE889 /* USB RS-232 OptoBridge */ -#define FTDI_GUDEADS_E88A_PID 0xE88A -#define FTDI_GUDEADS_E88B_PID 0xE88B -#define FTDI_GUDEADS_E88C_PID 0xE88C -#define FTDI_GUDEADS_E88D_PID 0xE88D -#define FTDI_GUDEADS_E88E_PID 0xE88E -#define FTDI_GUDEADS_E88F_PID 0xE88F - -/* - * Eclo (http://www.eclo.pt/) product IDs. - * PID 0xEA90 submitted by Martin Grill. - */ -#define FTDI_ECLO_COM_1WIRE_PID 0xEA90 /* COM to 1-Wire USB adaptor */ - -/* TNC-X USB-to-packet-radio adapter, versions prior to 3.0 (DLP module) */ -#define FTDI_TNC_X_PID 0xEBE0 - -/* - * Teratronik product ids. - * Submitted by O. Wölfelschneider. - */ -#define FTDI_TERATRONIK_VCP_PID 0xEC88 /* Teratronik device (preferring VCP driver on windows) */ -#define FTDI_TERATRONIK_D2XX_PID 0xEC89 /* Teratronik device (preferring D2XX driver on windows) */ - -/* Rig Expert Ukraine devices */ -#define FTDI_REU_TINY_PID 0xED22 /* RigExpert Tiny */ - -/* - * Hameg HO820 and HO870 interface (using VID 0x0403) - */ -#define HAMEG_HO820_PID 0xed74 -#define HAMEG_HO730_PID 0xed73 -#define HAMEG_HO720_PID 0xed72 -#define HAMEG_HO870_PID 0xed71 - -/* - * MaxStream devices www.maxstream.net - */ -#define FTDI_MAXSTREAM_PID 0xEE18 /* Xbee PKG-U Module */ - -/* - * microHAM product IDs (http://www.microham.com). - * Submitted by Justin Burket (KL1RL) <zorton@jtan.com> - * and Mike Studer (K6EEP) <k6eep@hamsoftware.org>. - * Ian Abbott <abbotti@mev.co.uk> added a few more from the driver INF file. - */ -#define FTDI_MHAM_KW_PID 0xEEE8 /* USB-KW interface */ -#define FTDI_MHAM_YS_PID 0xEEE9 /* USB-YS interface */ -#define FTDI_MHAM_Y6_PID 0xEEEA /* USB-Y6 interface */ -#define FTDI_MHAM_Y8_PID 0xEEEB /* USB-Y8 interface */ -#define FTDI_MHAM_IC_PID 0xEEEC /* USB-IC interface */ -#define FTDI_MHAM_DB9_PID 0xEEED /* USB-DB9 interface */ -#define FTDI_MHAM_RS232_PID 0xEEEE /* USB-RS232 interface */ -#define FTDI_MHAM_Y9_PID 0xEEEF /* USB-Y9 interface */ - -/* Domintell products http://www.domintell.com */ -#define FTDI_DOMINTELL_DGQG_PID 0xEF50 /* Master */ -#define FTDI_DOMINTELL_DUSB_PID 0xEF51 /* DUSB01 module */ - -/* - * The following are the values for the Perle Systems - * UltraPort USB serial converters - */ -#define FTDI_PERLE_ULTRAPORT_PID 0xF0C0 /* Perle UltraPort Product Id */ - -/* Sprog II (Andrew Crosland's SprogII DCC interface) */ -#define FTDI_SPROG_II 0xF0C8 - -/* an infrared receiver for user access control with IR tags */ -#define FTDI_PIEGROUP_PID 0xF208 /* Product Id */ - -/* ACT Solutions HomePro ZWave interface - (http://www.act-solutions.com/HomePro-Product-Matrix.html) */ -#define FTDI_ACTZWAVE_PID 0xF2D0 - -/* - * 4N-GALAXY.DE PIDs for CAN-USB, USB-RS232, USB-RS422, USB-RS485, - * USB-TTY aktiv, USB-TTY passiv. Some PIDs are used by several devices - * and I'm not entirely sure which are used by which. - */ -#define FTDI_4N_GALAXY_DE_1_PID 0xF3C0 -#define FTDI_4N_GALAXY_DE_2_PID 0xF3C1 -#define FTDI_4N_GALAXY_DE_3_PID 0xF3C2 - -/* - * Linx Technologies product ids - */ -#define LINX_SDMUSBQSS_PID 0xF448 /* Linx SDM-USB-QS-S */ -#define LINX_MASTERDEVEL2_PID 0xF449 /* Linx Master Development 2.0 */ -#define LINX_FUTURE_0_PID 0xF44A /* Linx future device */ -#define LINX_FUTURE_1_PID 0xF44B /* Linx future device */ -#define LINX_FUTURE_2_PID 0xF44C /* Linx future device */ - -/* - * Oceanic product ids - */ -#define FTDI_OCEANIC_PID 0xF460 /* Oceanic dive instrument */ - -/* - * SUUNTO product ids - */ -#define FTDI_SUUNTO_SPORTS_PID 0xF680 /* Suunto Sports instrument */ - -/* USB-UIRT - An infrared receiver and transmitter using the 8U232AM chip */ -/* http://www.usbuirt.com/ */ -#define FTDI_USB_UIRT_PID 0xF850 /* Product Id */ - -/* CCS Inc. ICDU/ICDU40 product ID - - * the FT232BM is used in an in-circuit-debugger unit for PIC16's/PIC18's */ -#define FTDI_CCSICDU20_0_PID 0xF9D0 -#define FTDI_CCSICDU40_1_PID 0xF9D1 -#define FTDI_CCSMACHX_2_PID 0xF9D2 -#define FTDI_CCSLOAD_N_GO_3_PID 0xF9D3 -#define FTDI_CCSICDU64_4_PID 0xF9D4 -#define FTDI_CCSPRIME8_5_PID 0xF9D5 - -/* - * The following are the values for the Matrix Orbital LCD displays, - * which are the FT232BM ( similar to the 8U232AM ) - */ -#define FTDI_MTXORB_0_PID 0xFA00 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_1_PID 0xFA01 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_2_PID 0xFA02 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_3_PID 0xFA03 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_4_PID 0xFA04 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_5_PID 0xFA05 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_6_PID 0xFA06 /* Matrix Orbital Product Id */ - -/* - * Home Electronics (www.home-electro.com) USB gadgets - */ -#define FTDI_HE_TIRA1_PID 0xFA78 /* Tira-1 IR transceiver */ - -/* Inside Accesso contactless reader (http://www.insidecontactless.com/) */ -#define INSIDE_ACCESSO 0xFAD0 - -/* - * ThorLabs USB motor drivers - */ -#define FTDI_THORLABS_PID 0xfaf0 /* ThorLabs USB motor drivers */ - -/* - * Protego product ids - */ -#define PROTEGO_SPECIAL_1 0xFC70 /* special/unknown device */ -#define PROTEGO_R2X0 0xFC71 /* R200-USB TRNG unit (R210, R220, and R230) */ -#define PROTEGO_SPECIAL_3 0xFC72 /* special/unknown device */ -#define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */ - -/* - * Sony Ericsson product ids - */ -#define FTDI_DSS20_PID 0xFC82 /* DSS-20 Sync Station for Sony Ericsson P800 */ -#define FTDI_URBAN_0_PID 0xFC8A /* Sony Ericsson Urban, uart #0 */ -#define FTDI_URBAN_1_PID 0xFC8B /* Sony Ericsson Urban, uart #1 */ - -/* www.irtrans.de device */ -#define FTDI_IRTRANS_PID 0xFC60 /* Product Id */ - -/* - * RM Michaelides CANview USB (http://www.rmcan.com) (FTDI_VID) - * CAN fieldbus interface adapter, added by port GmbH www.port.de) - * Ian Abbott changed the macro names for consistency. - */ -#define FTDI_RM_CANVIEW_PID 0xfd60 /* Product Id */ -/* www.thoughttechnology.com/ TT-USB provide with procomp use ftdi_sio */ -#define FTDI_TTUSB_PID 0xFF20 /* Product Id */ - -#define FTDI_USBX_707_PID 0xF857 /* ADSTech IR Blaster USBX-707 (FTDI_VID) */ - -#define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */ - -/* - * PCDJ use ftdi based dj-controllers. The following PID is - * for their DAC-2 device http://www.pcdjhardware.com/DAC2.asp - * (the VID is the standard ftdi vid (FTDI_VID), PID sent by Wouter Paesen) - */ -#define FTDI_PCDJ_DAC2_PID 0xFA88 - -#define FTDI_R2000KU_TRUE_RNG 0xFB80 /* R2000KU TRUE RNG (FTDI_VID) */ - -/* - * DIEBOLD BCS SE923 (FTDI_VID) - */ -#define DIEBOLD_BCS_SE923_PID 0xfb99 - -/* www.crystalfontz.com devices - * - thanx for providing free devices for evaluation ! - * they use the ftdi chipset for the USB interface - * and the vendor id is the same - */ -#define FTDI_XF_632_PID 0xFC08 /* 632: 16x2 Character Display */ -#define FTDI_XF_634_PID 0xFC09 /* 634: 20x4 Character Display */ -#define FTDI_XF_547_PID 0xFC0A /* 547: Two line Display */ -#define FTDI_XF_633_PID 0xFC0B /* 633: 16x2 Character Display with Keys */ -#define FTDI_XF_631_PID 0xFC0C /* 631: 20x2 Character Display */ -#define FTDI_XF_635_PID 0xFC0D /* 635: 20x4 Character Display */ -#define FTDI_XF_640_PID 0xFC0E /* 640: Two line Display */ -#define FTDI_XF_642_PID 0xFC0F /* 642: Two line Display */ - -/* - * Video Networks Limited / Homechoice in the UK use an ftdi-based device - * for their 1Mb broadband internet service. The following PID is exhibited - * by the usb device supplied (the VID is the standard ftdi vid (FTDI_VID) - */ -#define FTDI_VNHCPCUSB_D_PID 0xfe38 /* Product Id */ - -/* AlphaMicro Components AMC-232USB01 device (FTDI_VID) */ -#define FTDI_AMC232_PID 0xFF00 /* Product Id */ - -/* - * IBS elektronik product ids (FTDI_VID) - * Submitted by Thomas Schleusener - */ -#define FTDI_IBS_US485_PID 0xff38 /* IBS US485 (USB<-->RS422/485 interface) */ -#define FTDI_IBS_PICPRO_PID 0xff39 /* IBS PIC-Programmer */ -#define FTDI_IBS_PCMCIA_PID 0xff3a /* IBS Card reader for PCMCIA SRAM-cards */ -#define FTDI_IBS_PK1_PID 0xff3b /* IBS PK1 - Particel counter */ -#define FTDI_IBS_RS232MON_PID 0xff3c /* IBS RS232 - Monitor */ -#define FTDI_IBS_APP70_PID 0xff3d /* APP 70 (dust monitoring system) */ -#define FTDI_IBS_PEDO_PID 0xff3e /* IBS PEDO-Modem (RF modem 868.35 MHz) */ -#define FTDI_IBS_PROD_PID 0xff3f /* future device */ -/* www.canusb.com Lawicel CANUSB device (FTDI_VID) */ -#define FTDI_CANUSB_PID 0xFFA8 /* Product Id */ - -/* - * TavIR AVR product ids (FTDI_VID) - */ -#define FTDI_TAVIR_STK500_PID 0xFA33 /* STK500 AVR programmer */ - -/* - * TIAO product ids (FTDI_VID) - * http://www.tiaowiki.com/w/Main_Page - */ -#define FTDI_TIAO_UMPA_PID 0x8a98 /* TIAO/DIYGADGET USB Multi-Protocol Adapter */ - - -/********************************/ -/** third-party VID/PID combos **/ -/********************************/ - - - -/* - * Atmel STK541 - */ -#define ATMEL_VID 0x03eb /* Vendor ID */ -#define STK541_PID 0x2109 /* Zigbee Controller */ - -/* - * Blackfin gnICE JTAG - * http://docs.blackfin.uclinux.org/doku.php?id=hw:jtag:gnice - */ -#define ADI_VID 0x0456 -#define ADI_GNICE_PID 0xF000 -#define ADI_GNICEPLUS_PID 0xF001 - -/* - * Microchip Technology, Inc. - * - * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are - * used by single function CDC ACM class based firmware demo - * applications. The VID/PID has also been used in firmware - * emulating FTDI serial chips by: - * Hornby Elite - Digital Command Control Console - * http://www.hornby.com/hornby-dcc/controllers/ - */ -#define MICROCHIP_VID 0x04D8 -#define MICROCHIP_USB_BOARD_PID 0x000A /* CDC RS-232 Emulation Demo */ - -/* - * RATOC REX-USB60F - */ -#define RATOC_VENDOR_ID 0x0584 -#define RATOC_PRODUCT_ID_USB60F 0xb020 - -/* - * Acton Research Corp. - */ -#define ACTON_VID 0x0647 /* Vendor ID */ -#define ACTON_SPECTRAPRO_PID 0x0100 - -/* - * Contec products (http://www.contec.com) - * Submitted by Daniel Sangorrin - */ -#define CONTEC_VID 0x06CE /* Vendor ID */ -#define CONTEC_COM1USBH_PID 0x8311 /* COM-1(USB)H */ - -/* - * Definitions for B&B Electronics products. - */ -#define BANDB_VID 0x0856 /* B&B Electronics Vendor ID */ -#define BANDB_USOTL4_PID 0xAC01 /* USOTL4 Isolated RS-485 Converter */ -#define BANDB_USTL4_PID 0xAC02 /* USTL4 RS-485 Converter */ -#define BANDB_USO9ML2_PID 0xAC03 /* USO9ML2 Isolated RS-232 Converter */ -#define BANDB_USOPTL4_PID 0xAC11 -#define BANDB_USPTL4_PID 0xAC12 -#define BANDB_USO9ML2DR_2_PID 0xAC16 -#define BANDB_USO9ML2DR_PID 0xAC17 -#define BANDB_USOPTL4DR2_PID 0xAC18 /* USOPTL4R-2 2-port Isolated RS-232 Converter */ -#define BANDB_USOPTL4DR_PID 0xAC19 -#define BANDB_485USB9F_2W_PID 0xAC25 -#define BANDB_485USB9F_4W_PID 0xAC26 -#define BANDB_232USB9M_PID 0xAC27 -#define BANDB_485USBTB_2W_PID 0xAC33 -#define BANDB_485USBTB_4W_PID 0xAC34 -#define BANDB_TTL5USB9M_PID 0xAC49 -#define BANDB_TTL3USB9M_PID 0xAC50 -#define BANDB_ZZ_PROG1_USB_PID 0xBA02 - -/* - * Intrepid Control Systems (http://www.intrepidcs.com/) ValueCAN and NeoVI - */ -#define INTREPID_VID 0x093C -#define INTREPID_VALUECAN_PID 0x0601 -#define INTREPID_NEOVI_PID 0x0701 - -/* - * Definitions for ID TECH (www.idt-net.com) devices - */ -#define IDTECH_VID 0x0ACD /* ID TECH Vendor ID */ -#define IDTECH_IDT1221U_PID 0x0300 /* IDT1221U USB to RS-232 adapter */ - -/* - * Definitions for Omnidirectional Control Technology, Inc. devices - */ -#define OCT_VID 0x0B39 /* OCT vendor ID */ -/* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */ -/* Also rebadged as Dick Smith Electronics (Aus) XH6451 */ -/* Also rebadged as SIIG Inc. model US2308 hardware version 1 */ -#define OCT_DK201_PID 0x0103 /* OCT DK201 USB docking station */ -#define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */ - -/* - * Definitions for Icom Inc. devices - */ -#define ICOM_VID 0x0C26 /* Icom vendor ID */ -/* Note: ID-1 is a communications tranceiver for HAM-radio operators */ -#define ICOM_ID_1_PID 0x0004 /* ID-1 USB to RS-232 */ -/* Note: OPC is an Optional cable to connect an Icom Tranceiver */ -#define ICOM_OPC_U_UC_PID 0x0018 /* OPC-478UC, OPC-1122U cloning cable */ -/* Note: ID-RP* devices are Icom Repeater Devices for HAM-radio */ -#define ICOM_ID_RP2C1_PID 0x0009 /* ID-RP2C Asset 1 to RS-232 */ -#define ICOM_ID_RP2C2_PID 0x000A /* ID-RP2C Asset 2 to RS-232 */ -#define ICOM_ID_RP2D_PID 0x000B /* ID-RP2D configuration port*/ -#define ICOM_ID_RP2VT_PID 0x000C /* ID-RP2V Transmit config port */ -#define ICOM_ID_RP2VR_PID 0x000D /* ID-RP2V Receive config port */ -#define ICOM_ID_RP4KVT_PID 0x0010 /* ID-RP4000V Transmit config port */ -#define ICOM_ID_RP4KVR_PID 0x0011 /* ID-RP4000V Receive config port */ -#define ICOM_ID_RP2KVT_PID 0x0012 /* ID-RP2000V Transmit config port */ -#define ICOM_ID_RP2KVR_PID 0x0013 /* ID-RP2000V Receive config port */ - -/* - * GN Otometrics (http://www.otometrics.com) - * Submitted by Ville Sundberg. - */ -#define GN_OTOMETRICS_VID 0x0c33 /* Vendor ID */ -#define AURICAL_USB_PID 0x0010 /* Aurical USB Audiometer */ - -/* - * The following are the values for the Sealevel SeaLINK+ adapters. - * (Original list sent by Tuan Hoang. Ian Abbott renamed the macros and - * removed some PIDs that don't seem to match any existing products.) - */ -#define SEALEVEL_VID 0x0c52 /* Sealevel Vendor ID */ -#define SEALEVEL_2101_PID 0x2101 /* SeaLINK+232 (2101/2105) */ -#define SEALEVEL_2102_PID 0x2102 /* SeaLINK+485 (2102) */ -#define SEALEVEL_2103_PID 0x2103 /* SeaLINK+232I (2103) */ -#define SEALEVEL_2104_PID 0x2104 /* SeaLINK+485I (2104) */ -#define SEALEVEL_2106_PID 0x9020 /* SeaLINK+422 (2106) */ -#define SEALEVEL_2201_1_PID 0x2211 /* SeaPORT+2/232 (2201) Port 1 */ -#define SEALEVEL_2201_2_PID 0x2221 /* SeaPORT+2/232 (2201) Port 2 */ -#define SEALEVEL_2202_1_PID 0x2212 /* SeaPORT+2/485 (2202) Port 1 */ -#define SEALEVEL_2202_2_PID 0x2222 /* SeaPORT+2/485 (2202) Port 2 */ -#define SEALEVEL_2203_1_PID 0x2213 /* SeaPORT+2 (2203) Port 1 */ -#define SEALEVEL_2203_2_PID 0x2223 /* SeaPORT+2 (2203) Port 2 */ -#define SEALEVEL_2401_1_PID 0x2411 /* SeaPORT+4/232 (2401) Port 1 */ -#define SEALEVEL_2401_2_PID 0x2421 /* SeaPORT+4/232 (2401) Port 2 */ -#define SEALEVEL_2401_3_PID 0x2431 /* SeaPORT+4/232 (2401) Port 3 */ -#define SEALEVEL_2401_4_PID 0x2441 /* SeaPORT+4/232 (2401) Port 4 */ -#define SEALEVEL_2402_1_PID 0x2412 /* SeaPORT+4/485 (2402) Port 1 */ -#define SEALEVEL_2402_2_PID 0x2422 /* SeaPORT+4/485 (2402) Port 2 */ -#define SEALEVEL_2402_3_PID 0x2432 /* SeaPORT+4/485 (2402) Port 3 */ -#define SEALEVEL_2402_4_PID 0x2442 /* SeaPORT+4/485 (2402) Port 4 */ -#define SEALEVEL_2403_1_PID 0x2413 /* SeaPORT+4 (2403) Port 1 */ -#define SEALEVEL_2403_2_PID 0x2423 /* SeaPORT+4 (2403) Port 2 */ -#define SEALEVEL_2403_3_PID 0x2433 /* SeaPORT+4 (2403) Port 3 */ -#define SEALEVEL_2403_4_PID 0x2443 /* SeaPORT+4 (2403) Port 4 */ -#define SEALEVEL_2801_1_PID 0X2811 /* SeaLINK+8/232 (2801) Port 1 */ -#define SEALEVEL_2801_2_PID 0X2821 /* SeaLINK+8/232 (2801) Port 2 */ -#define SEALEVEL_2801_3_PID 0X2831 /* SeaLINK+8/232 (2801) Port 3 */ -#define SEALEVEL_2801_4_PID 0X2841 /* SeaLINK+8/232 (2801) Port 4 */ -#define SEALEVEL_2801_5_PID 0X2851 /* SeaLINK+8/232 (2801) Port 5 */ -#define SEALEVEL_2801_6_PID 0X2861 /* SeaLINK+8/232 (2801) Port 6 */ -#define SEALEVEL_2801_7_PID 0X2871 /* SeaLINK+8/232 (2801) Port 7 */ -#define SEALEVEL_2801_8_PID 0X2881 /* SeaLINK+8/232 (2801) Port 8 */ -#define SEALEVEL_2802_1_PID 0X2812 /* SeaLINK+8/485 (2802) Port 1 */ -#define SEALEVEL_2802_2_PID 0X2822 /* SeaLINK+8/485 (2802) Port 2 */ -#define SEALEVEL_2802_3_PID 0X2832 /* SeaLINK+8/485 (2802) Port 3 */ -#define SEALEVEL_2802_4_PID 0X2842 /* SeaLINK+8/485 (2802) Port 4 */ -#define SEALEVEL_2802_5_PID 0X2852 /* SeaLINK+8/485 (2802) Port 5 */ -#define SEALEVEL_2802_6_PID 0X2862 /* SeaLINK+8/485 (2802) Port 6 */ -#define SEALEVEL_2802_7_PID 0X2872 /* SeaLINK+8/485 (2802) Port 7 */ -#define SEALEVEL_2802_8_PID 0X2882 /* SeaLINK+8/485 (2802) Port 8 */ -#define SEALEVEL_2803_1_PID 0X2813 /* SeaLINK+8 (2803) Port 1 */ -#define SEALEVEL_2803_2_PID 0X2823 /* SeaLINK+8 (2803) Port 2 */ -#define SEALEVEL_2803_3_PID 0X2833 /* SeaLINK+8 (2803) Port 3 */ -#define SEALEVEL_2803_4_PID 0X2843 /* SeaLINK+8 (2803) Port 4 */ -#define SEALEVEL_2803_5_PID 0X2853 /* SeaLINK+8 (2803) Port 5 */ -#define SEALEVEL_2803_6_PID 0X2863 /* SeaLINK+8 (2803) Port 6 */ -#define SEALEVEL_2803_7_PID 0X2873 /* SeaLINK+8 (2803) Port 7 */ -#define SEALEVEL_2803_8_PID 0X2883 /* SeaLINK+8 (2803) Port 8 */ -#define SEALEVEL_2803R_1_PID 0Xa02a /* SeaLINK+8 (2803-ROHS) Port 1+2 */ -#define SEALEVEL_2803R_2_PID 0Xa02b /* SeaLINK+8 (2803-ROHS) Port 3+4 */ -#define SEALEVEL_2803R_3_PID 0Xa02c /* SeaLINK+8 (2803-ROHS) Port 5+6 */ -#define SEALEVEL_2803R_4_PID 0Xa02d /* SeaLINK+8 (2803-ROHS) Port 7+8 */ - -/* - * JETI SPECTROMETER SPECBOS 1201 - * http://www.jeti.com/cms/index.php/instruments/other-instruments/specbos-2101 - */ -#define JETI_VID 0x0c6c -#define JETI_SPC1201_PID 0x04b2 - -/* - * FTDI USB UART chips used in construction projects from the - * Elektor Electronics magazine (http://www.elektor.com/) - */ -#define ELEKTOR_VID 0x0C7D -#define ELEKTOR_FT323R_PID 0x0005 /* RFID-Reader, issue 09-2006 */ - -/* - * Posiflex inc retail equipment (http://www.posiflex.com.tw) - */ -#define POSIFLEX_VID 0x0d3a /* Vendor ID */ -#define POSIFLEX_PP7000_PID 0x0300 /* PP-7000II thermal printer */ - -/* - * The following are the values for two KOBIL chipcard terminals. - */ -#define KOBIL_VID 0x0d46 /* KOBIL Vendor ID */ -#define KOBIL_CONV_B1_PID 0x2020 /* KOBIL Konverter for B1 */ -#define KOBIL_CONV_KAAN_PID 0x2021 /* KOBIL_Konverter for KAAN */ - -#define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ -#define FTDI_NF_RIC_PID 0x0001 /* Product Id */ - -/* - * Falcom Wireless Communications GmbH - */ -#define FALCOM_VID 0x0F94 /* Vendor Id */ -#define FALCOM_TWIST_PID 0x0001 /* Falcom Twist USB GPRS modem */ -#define FALCOM_SAMBA_PID 0x0005 /* Falcom Samba USB GPRS modem */ - -/* Larsen and Brusgaard AltiTrack/USBtrack */ -#define LARSENBRUSGAARD_VID 0x0FD8 -#define LB_ALTITRACK_PID 0x0001 - -/* - * TTi (Thurlby Thandar Instruments) - */ -#define TTI_VID 0x103E /* Vendor Id */ -#define TTI_QL355P_PID 0x03E8 /* TTi QL355P power supply */ - -/* Interbiometrics USB I/O Board */ -/* Developed for Interbiometrics by Rudolf Gugler */ -#define INTERBIOMETRICS_VID 0x1209 -#define INTERBIOMETRICS_IOBOARD_PID 0x1002 -#define INTERBIOMETRICS_MINI_IOBOARD_PID 0x1006 - -/* - * Testo products (http://www.testo.com/) - * Submitted by Colin Leroy - */ -#define TESTO_VID 0x128D -#define TESTO_USB_INTERFACE_PID 0x0001 - -/* - * Mobility Electronics products. - */ -#define MOBILITY_VID 0x1342 -#define MOBILITY_USB_SERIAL_PID 0x0202 /* EasiDock USB 200 serial */ - -/* - * FIC / OpenMoko, Inc. http://wiki.openmoko.org/wiki/Neo1973_Debug_Board_v3 - * Submitted by Harald Welte <laforge@openmoko.org> - */ -#define FIC_VID 0x1457 -#define FIC_NEO1973_DEBUG_PID 0x5118 - -/* Olimex */ -#define OLIMEX_VID 0x15BA -#define OLIMEX_ARM_USB_OCD_PID 0x0003 -#define OLIMEX_ARM_USB_OCD_H_PID 0x002b - -/* - * Telldus Technologies - */ -#define TELLDUS_VID 0x1781 /* Vendor ID */ -#define TELLDUS_TELLSTICK_PID 0x0C30 /* RF control dongle 433 MHz using FT232RL */ - -/* - * RT Systems programming cables for various ham radios - */ -#define RTSYSTEMS_VID 0x2100 /* Vendor ID */ -#define RTSYSTEMS_SERIAL_VX7_PID 0x9e52 /* Serial converter for VX-7 Radios using FT232RL */ -#define RTSYSTEMS_CT29B_PID 0x9e54 /* CT29B Radio Cable */ -#define RTSYSTEMS_RTS01_PID 0x9e57 /* USB-RTS01 Radio Cable */ - - -/* - * Physik Instrumente - * http://www.physikinstrumente.com/en/products/ - */ -/* These two devices use the VID of FTDI */ -#define PI_C865_PID 0xe0a0 /* PI C-865 Piezomotor Controller */ -#define PI_C857_PID 0xe0a1 /* PI Encoder Trigger Box */ - -#define PI_VID 0x1a72 /* Vendor ID */ -#define PI_C866_PID 0x1000 /* PI C-866 Piezomotor Controller */ -#define PI_C663_PID 0x1001 /* PI C-663 Mercury-Step */ -#define PI_C725_PID 0x1002 /* PI C-725 Piezomotor Controller */ -#define PI_E517_PID 0x1005 /* PI E-517 Digital Piezo Controller Operation Module */ -#define PI_C863_PID 0x1007 /* PI C-863 */ -#define PI_E861_PID 0x1008 /* PI E-861 Piezomotor Controller */ -#define PI_C867_PID 0x1009 /* PI C-867 Piezomotor Controller */ -#define PI_E609_PID 0x100D /* PI E-609 Digital Piezo Controller */ -#define PI_E709_PID 0x100E /* PI E-709 Digital Piezo Controller */ -#define PI_100F_PID 0x100F /* PI Digital Piezo Controller */ -#define PI_1011_PID 0x1011 /* PI Digital Piezo Controller */ -#define PI_1012_PID 0x1012 /* PI Motion Controller */ -#define PI_1013_PID 0x1013 /* PI Motion Controller */ -#define PI_1014_PID 0x1014 /* PI Device */ -#define PI_1015_PID 0x1015 /* PI Device */ -#define PI_1016_PID 0x1016 /* PI Digital Servo Module */ - -/* - * Kondo Kagaku Co.Ltd. - * http://www.kondo-robot.com/EN - */ -#define KONDO_VID 0x165c -#define KONDO_USB_SERIAL_PID 0x0002 - -/* - * Bayer Ascensia Contour blood glucose meter USB-converter cable. - * http://winglucofacts.com/cables/ - */ -#define BAYER_VID 0x1A79 -#define BAYER_CONTOUR_CABLE_PID 0x6001 - -/* - * The following are the values for the Matrix Orbital FTDI Range - * Anything in this range will use an FT232RL. - */ -#define MTXORB_VID 0x1B3D -#define MTXORB_FTDI_RANGE_0100_PID 0x0100 -#define MTXORB_FTDI_RANGE_0101_PID 0x0101 -#define MTXORB_FTDI_RANGE_0102_PID 0x0102 -#define MTXORB_FTDI_RANGE_0103_PID 0x0103 -#define MTXORB_FTDI_RANGE_0104_PID 0x0104 -#define MTXORB_FTDI_RANGE_0105_PID 0x0105 -#define MTXORB_FTDI_RANGE_0106_PID 0x0106 -#define MTXORB_FTDI_RANGE_0107_PID 0x0107 -#define MTXORB_FTDI_RANGE_0108_PID 0x0108 -#define MTXORB_FTDI_RANGE_0109_PID 0x0109 -#define MTXORB_FTDI_RANGE_010A_PID 0x010A -#define MTXORB_FTDI_RANGE_010B_PID 0x010B -#define MTXORB_FTDI_RANGE_010C_PID 0x010C -#define MTXORB_FTDI_RANGE_010D_PID 0x010D -#define MTXORB_FTDI_RANGE_010E_PID 0x010E -#define MTXORB_FTDI_RANGE_010F_PID 0x010F -#define MTXORB_FTDI_RANGE_0110_PID 0x0110 -#define MTXORB_FTDI_RANGE_0111_PID 0x0111 -#define MTXORB_FTDI_RANGE_0112_PID 0x0112 -#define MTXORB_FTDI_RANGE_0113_PID 0x0113 -#define MTXORB_FTDI_RANGE_0114_PID 0x0114 -#define MTXORB_FTDI_RANGE_0115_PID 0x0115 -#define MTXORB_FTDI_RANGE_0116_PID 0x0116 -#define MTXORB_FTDI_RANGE_0117_PID 0x0117 -#define MTXORB_FTDI_RANGE_0118_PID 0x0118 -#define MTXORB_FTDI_RANGE_0119_PID 0x0119 -#define MTXORB_FTDI_RANGE_011A_PID 0x011A -#define MTXORB_FTDI_RANGE_011B_PID 0x011B -#define MTXORB_FTDI_RANGE_011C_PID 0x011C -#define MTXORB_FTDI_RANGE_011D_PID 0x011D -#define MTXORB_FTDI_RANGE_011E_PID 0x011E -#define MTXORB_FTDI_RANGE_011F_PID 0x011F -#define MTXORB_FTDI_RANGE_0120_PID 0x0120 -#define MTXORB_FTDI_RANGE_0121_PID 0x0121 -#define MTXORB_FTDI_RANGE_0122_PID 0x0122 -#define MTXORB_FTDI_RANGE_0123_PID 0x0123 -#define MTXORB_FTDI_RANGE_0124_PID 0x0124 -#define MTXORB_FTDI_RANGE_0125_PID 0x0125 -#define MTXORB_FTDI_RANGE_0126_PID 0x0126 -#define MTXORB_FTDI_RANGE_0127_PID 0x0127 -#define MTXORB_FTDI_RANGE_0128_PID 0x0128 -#define MTXORB_FTDI_RANGE_0129_PID 0x0129 -#define MTXORB_FTDI_RANGE_012A_PID 0x012A -#define MTXORB_FTDI_RANGE_012B_PID 0x012B -#define MTXORB_FTDI_RANGE_012C_PID 0x012C -#define MTXORB_FTDI_RANGE_012D_PID 0x012D -#define MTXORB_FTDI_RANGE_012E_PID 0x012E -#define MTXORB_FTDI_RANGE_012F_PID 0x012F -#define MTXORB_FTDI_RANGE_0130_PID 0x0130 -#define MTXORB_FTDI_RANGE_0131_PID 0x0131 -#define MTXORB_FTDI_RANGE_0132_PID 0x0132 -#define MTXORB_FTDI_RANGE_0133_PID 0x0133 -#define MTXORB_FTDI_RANGE_0134_PID 0x0134 -#define MTXORB_FTDI_RANGE_0135_PID 0x0135 -#define MTXORB_FTDI_RANGE_0136_PID 0x0136 -#define MTXORB_FTDI_RANGE_0137_PID 0x0137 -#define MTXORB_FTDI_RANGE_0138_PID 0x0138 -#define MTXORB_FTDI_RANGE_0139_PID 0x0139 -#define MTXORB_FTDI_RANGE_013A_PID 0x013A -#define MTXORB_FTDI_RANGE_013B_PID 0x013B -#define MTXORB_FTDI_RANGE_013C_PID 0x013C -#define MTXORB_FTDI_RANGE_013D_PID 0x013D -#define MTXORB_FTDI_RANGE_013E_PID 0x013E -#define MTXORB_FTDI_RANGE_013F_PID 0x013F -#define MTXORB_FTDI_RANGE_0140_PID 0x0140 -#define MTXORB_FTDI_RANGE_0141_PID 0x0141 -#define MTXORB_FTDI_RANGE_0142_PID 0x0142 -#define MTXORB_FTDI_RANGE_0143_PID 0x0143 -#define MTXORB_FTDI_RANGE_0144_PID 0x0144 -#define MTXORB_FTDI_RANGE_0145_PID 0x0145 -#define MTXORB_FTDI_RANGE_0146_PID 0x0146 -#define MTXORB_FTDI_RANGE_0147_PID 0x0147 -#define MTXORB_FTDI_RANGE_0148_PID 0x0148 -#define MTXORB_FTDI_RANGE_0149_PID 0x0149 -#define MTXORB_FTDI_RANGE_014A_PID 0x014A -#define MTXORB_FTDI_RANGE_014B_PID 0x014B -#define MTXORB_FTDI_RANGE_014C_PID 0x014C -#define MTXORB_FTDI_RANGE_014D_PID 0x014D -#define MTXORB_FTDI_RANGE_014E_PID 0x014E -#define MTXORB_FTDI_RANGE_014F_PID 0x014F -#define MTXORB_FTDI_RANGE_0150_PID 0x0150 -#define MTXORB_FTDI_RANGE_0151_PID 0x0151 -#define MTXORB_FTDI_RANGE_0152_PID 0x0152 -#define MTXORB_FTDI_RANGE_0153_PID 0x0153 -#define MTXORB_FTDI_RANGE_0154_PID 0x0154 -#define MTXORB_FTDI_RANGE_0155_PID 0x0155 -#define MTXORB_FTDI_RANGE_0156_PID 0x0156 -#define MTXORB_FTDI_RANGE_0157_PID 0x0157 -#define MTXORB_FTDI_RANGE_0158_PID 0x0158 -#define MTXORB_FTDI_RANGE_0159_PID 0x0159 -#define MTXORB_FTDI_RANGE_015A_PID 0x015A -#define MTXORB_FTDI_RANGE_015B_PID 0x015B -#define MTXORB_FTDI_RANGE_015C_PID 0x015C -#define MTXORB_FTDI_RANGE_015D_PID 0x015D -#define MTXORB_FTDI_RANGE_015E_PID 0x015E -#define MTXORB_FTDI_RANGE_015F_PID 0x015F -#define MTXORB_FTDI_RANGE_0160_PID 0x0160 -#define MTXORB_FTDI_RANGE_0161_PID 0x0161 -#define MTXORB_FTDI_RANGE_0162_PID 0x0162 -#define MTXORB_FTDI_RANGE_0163_PID 0x0163 -#define MTXORB_FTDI_RANGE_0164_PID 0x0164 -#define MTXORB_FTDI_RANGE_0165_PID 0x0165 -#define MTXORB_FTDI_RANGE_0166_PID 0x0166 -#define MTXORB_FTDI_RANGE_0167_PID 0x0167 -#define MTXORB_FTDI_RANGE_0168_PID 0x0168 -#define MTXORB_FTDI_RANGE_0169_PID 0x0169 -#define MTXORB_FTDI_RANGE_016A_PID 0x016A -#define MTXORB_FTDI_RANGE_016B_PID 0x016B -#define MTXORB_FTDI_RANGE_016C_PID 0x016C -#define MTXORB_FTDI_RANGE_016D_PID 0x016D -#define MTXORB_FTDI_RANGE_016E_PID 0x016E -#define MTXORB_FTDI_RANGE_016F_PID 0x016F -#define MTXORB_FTDI_RANGE_0170_PID 0x0170 -#define MTXORB_FTDI_RANGE_0171_PID 0x0171 -#define MTXORB_FTDI_RANGE_0172_PID 0x0172 -#define MTXORB_FTDI_RANGE_0173_PID 0x0173 -#define MTXORB_FTDI_RANGE_0174_PID 0x0174 -#define MTXORB_FTDI_RANGE_0175_PID 0x0175 -#define MTXORB_FTDI_RANGE_0176_PID 0x0176 -#define MTXORB_FTDI_RANGE_0177_PID 0x0177 -#define MTXORB_FTDI_RANGE_0178_PID 0x0178 -#define MTXORB_FTDI_RANGE_0179_PID 0x0179 -#define MTXORB_FTDI_RANGE_017A_PID 0x017A -#define MTXORB_FTDI_RANGE_017B_PID 0x017B -#define MTXORB_FTDI_RANGE_017C_PID 0x017C -#define MTXORB_FTDI_RANGE_017D_PID 0x017D -#define MTXORB_FTDI_RANGE_017E_PID 0x017E -#define MTXORB_FTDI_RANGE_017F_PID 0x017F -#define MTXORB_FTDI_RANGE_0180_PID 0x0180 -#define MTXORB_FTDI_RANGE_0181_PID 0x0181 -#define MTXORB_FTDI_RANGE_0182_PID 0x0182 -#define MTXORB_FTDI_RANGE_0183_PID 0x0183 -#define MTXORB_FTDI_RANGE_0184_PID 0x0184 -#define MTXORB_FTDI_RANGE_0185_PID 0x0185 -#define MTXORB_FTDI_RANGE_0186_PID 0x0186 -#define MTXORB_FTDI_RANGE_0187_PID 0x0187 -#define MTXORB_FTDI_RANGE_0188_PID 0x0188 -#define MTXORB_FTDI_RANGE_0189_PID 0x0189 -#define MTXORB_FTDI_RANGE_018A_PID 0x018A -#define MTXORB_FTDI_RANGE_018B_PID 0x018B -#define MTXORB_FTDI_RANGE_018C_PID 0x018C -#define MTXORB_FTDI_RANGE_018D_PID 0x018D -#define MTXORB_FTDI_RANGE_018E_PID 0x018E -#define MTXORB_FTDI_RANGE_018F_PID 0x018F -#define MTXORB_FTDI_RANGE_0190_PID 0x0190 -#define MTXORB_FTDI_RANGE_0191_PID 0x0191 -#define MTXORB_FTDI_RANGE_0192_PID 0x0192 -#define MTXORB_FTDI_RANGE_0193_PID 0x0193 -#define MTXORB_FTDI_RANGE_0194_PID 0x0194 -#define MTXORB_FTDI_RANGE_0195_PID 0x0195 -#define MTXORB_FTDI_RANGE_0196_PID 0x0196 -#define MTXORB_FTDI_RANGE_0197_PID 0x0197 -#define MTXORB_FTDI_RANGE_0198_PID 0x0198 -#define MTXORB_FTDI_RANGE_0199_PID 0x0199 -#define MTXORB_FTDI_RANGE_019A_PID 0x019A -#define MTXORB_FTDI_RANGE_019B_PID 0x019B -#define MTXORB_FTDI_RANGE_019C_PID 0x019C -#define MTXORB_FTDI_RANGE_019D_PID 0x019D -#define MTXORB_FTDI_RANGE_019E_PID 0x019E -#define MTXORB_FTDI_RANGE_019F_PID 0x019F -#define MTXORB_FTDI_RANGE_01A0_PID 0x01A0 -#define MTXORB_FTDI_RANGE_01A1_PID 0x01A1 -#define MTXORB_FTDI_RANGE_01A2_PID 0x01A2 -#define MTXORB_FTDI_RANGE_01A3_PID 0x01A3 -#define MTXORB_FTDI_RANGE_01A4_PID 0x01A4 -#define MTXORB_FTDI_RANGE_01A5_PID 0x01A5 -#define MTXORB_FTDI_RANGE_01A6_PID 0x01A6 -#define MTXORB_FTDI_RANGE_01A7_PID 0x01A7 -#define MTXORB_FTDI_RANGE_01A8_PID 0x01A8 -#define MTXORB_FTDI_RANGE_01A9_PID 0x01A9 -#define MTXORB_FTDI_RANGE_01AA_PID 0x01AA -#define MTXORB_FTDI_RANGE_01AB_PID 0x01AB -#define MTXORB_FTDI_RANGE_01AC_PID 0x01AC -#define MTXORB_FTDI_RANGE_01AD_PID 0x01AD -#define MTXORB_FTDI_RANGE_01AE_PID 0x01AE -#define MTXORB_FTDI_RANGE_01AF_PID 0x01AF -#define MTXORB_FTDI_RANGE_01B0_PID 0x01B0 -#define MTXORB_FTDI_RANGE_01B1_PID 0x01B1 -#define MTXORB_FTDI_RANGE_01B2_PID 0x01B2 -#define MTXORB_FTDI_RANGE_01B3_PID 0x01B3 -#define MTXORB_FTDI_RANGE_01B4_PID 0x01B4 -#define MTXORB_FTDI_RANGE_01B5_PID 0x01B5 -#define MTXORB_FTDI_RANGE_01B6_PID 0x01B6 -#define MTXORB_FTDI_RANGE_01B7_PID 0x01B7 -#define MTXORB_FTDI_RANGE_01B8_PID 0x01B8 -#define MTXORB_FTDI_RANGE_01B9_PID 0x01B9 -#define MTXORB_FTDI_RANGE_01BA_PID 0x01BA -#define MTXORB_FTDI_RANGE_01BB_PID 0x01BB -#define MTXORB_FTDI_RANGE_01BC_PID 0x01BC -#define MTXORB_FTDI_RANGE_01BD_PID 0x01BD -#define MTXORB_FTDI_RANGE_01BE_PID 0x01BE -#define MTXORB_FTDI_RANGE_01BF_PID 0x01BF -#define MTXORB_FTDI_RANGE_01C0_PID 0x01C0 -#define MTXORB_FTDI_RANGE_01C1_PID 0x01C1 -#define MTXORB_FTDI_RANGE_01C2_PID 0x01C2 -#define MTXORB_FTDI_RANGE_01C3_PID 0x01C3 -#define MTXORB_FTDI_RANGE_01C4_PID 0x01C4 -#define MTXORB_FTDI_RANGE_01C5_PID 0x01C5 -#define MTXORB_FTDI_RANGE_01C6_PID 0x01C6 -#define MTXORB_FTDI_RANGE_01C7_PID 0x01C7 -#define MTXORB_FTDI_RANGE_01C8_PID 0x01C8 -#define MTXORB_FTDI_RANGE_01C9_PID 0x01C9 -#define MTXORB_FTDI_RANGE_01CA_PID 0x01CA -#define MTXORB_FTDI_RANGE_01CB_PID 0x01CB -#define MTXORB_FTDI_RANGE_01CC_PID 0x01CC -#define MTXORB_FTDI_RANGE_01CD_PID 0x01CD -#define MTXORB_FTDI_RANGE_01CE_PID 0x01CE -#define MTXORB_FTDI_RANGE_01CF_PID 0x01CF -#define MTXORB_FTDI_RANGE_01D0_PID 0x01D0 -#define MTXORB_FTDI_RANGE_01D1_PID 0x01D1 -#define MTXORB_FTDI_RANGE_01D2_PID 0x01D2 -#define MTXORB_FTDI_RANGE_01D3_PID 0x01D3 -#define MTXORB_FTDI_RANGE_01D4_PID 0x01D4 -#define MTXORB_FTDI_RANGE_01D5_PID 0x01D5 -#define MTXORB_FTDI_RANGE_01D6_PID 0x01D6 -#define MTXORB_FTDI_RANGE_01D7_PID 0x01D7 -#define MTXORB_FTDI_RANGE_01D8_PID 0x01D8 -#define MTXORB_FTDI_RANGE_01D9_PID 0x01D9 -#define MTXORB_FTDI_RANGE_01DA_PID 0x01DA -#define MTXORB_FTDI_RANGE_01DB_PID 0x01DB -#define MTXORB_FTDI_RANGE_01DC_PID 0x01DC -#define MTXORB_FTDI_RANGE_01DD_PID 0x01DD -#define MTXORB_FTDI_RANGE_01DE_PID 0x01DE -#define MTXORB_FTDI_RANGE_01DF_PID 0x01DF -#define MTXORB_FTDI_RANGE_01E0_PID 0x01E0 -#define MTXORB_FTDI_RANGE_01E1_PID 0x01E1 -#define MTXORB_FTDI_RANGE_01E2_PID 0x01E2 -#define MTXORB_FTDI_RANGE_01E3_PID 0x01E3 -#define MTXORB_FTDI_RANGE_01E4_PID 0x01E4 -#define MTXORB_FTDI_RANGE_01E5_PID 0x01E5 -#define MTXORB_FTDI_RANGE_01E6_PID 0x01E6 -#define MTXORB_FTDI_RANGE_01E7_PID 0x01E7 -#define MTXORB_FTDI_RANGE_01E8_PID 0x01E8 -#define MTXORB_FTDI_RANGE_01E9_PID 0x01E9 -#define MTXORB_FTDI_RANGE_01EA_PID 0x01EA -#define MTXORB_FTDI_RANGE_01EB_PID 0x01EB -#define MTXORB_FTDI_RANGE_01EC_PID 0x01EC -#define MTXORB_FTDI_RANGE_01ED_PID 0x01ED -#define MTXORB_FTDI_RANGE_01EE_PID 0x01EE -#define MTXORB_FTDI_RANGE_01EF_PID 0x01EF -#define MTXORB_FTDI_RANGE_01F0_PID 0x01F0 -#define MTXORB_FTDI_RANGE_01F1_PID 0x01F1 -#define MTXORB_FTDI_RANGE_01F2_PID 0x01F2 -#define MTXORB_FTDI_RANGE_01F3_PID 0x01F3 -#define MTXORB_FTDI_RANGE_01F4_PID 0x01F4 -#define MTXORB_FTDI_RANGE_01F5_PID 0x01F5 -#define MTXORB_FTDI_RANGE_01F6_PID 0x01F6 -#define MTXORB_FTDI_RANGE_01F7_PID 0x01F7 -#define MTXORB_FTDI_RANGE_01F8_PID 0x01F8 -#define MTXORB_FTDI_RANGE_01F9_PID 0x01F9 -#define MTXORB_FTDI_RANGE_01FA_PID 0x01FA -#define MTXORB_FTDI_RANGE_01FB_PID 0x01FB -#define MTXORB_FTDI_RANGE_01FC_PID 0x01FC -#define MTXORB_FTDI_RANGE_01FD_PID 0x01FD -#define MTXORB_FTDI_RANGE_01FE_PID 0x01FE -#define MTXORB_FTDI_RANGE_01FF_PID 0x01FF - - - -/* - * The Mobility Lab (TML) - * Submitted by Pierre Castella - */ -#define TML_VID 0x1B91 /* Vendor ID */ -#define TML_USB_SERIAL_PID 0x0064 /* USB - Serial Converter */ - -/* Alti-2 products http://www.alti-2.com */ -#define ALTI2_VID 0x1BC9 -#define ALTI2_N3_PID 0x6001 /* Neptune 3 */ - -/* - * Ionics PlugComputer - */ -#define IONICS_VID 0x1c0c -#define IONICS_PLUGCOMPUTER_PID 0x0102 - -/* - * Dresden Elektronik Sensor Terminal Board - */ -#define DE_VID 0x1cf1 /* Vendor ID */ -#define STB_PID 0x0001 /* Sensor Terminal Board */ -#define WHT_PID 0x0004 /* Wireless Handheld Terminal */ - -/* - * STMicroelectonics - */ -#define ST_VID 0x0483 -#define ST_STMCLT1030_PID 0x3747 /* ST Micro Connect Lite STMCLT1030 */ - -/* - * Papouch products (http://www.papouch.com/) - * Submitted by Folkert van Heusden - */ - -#define PAPOUCH_VID 0x5050 /* Vendor ID */ -#define PAPOUCH_SB485_PID 0x0100 /* Papouch SB485 USB-485/422 Converter */ -#define PAPOUCH_AP485_PID 0x0101 /* AP485 USB-RS485 Converter */ -#define PAPOUCH_SB422_PID 0x0102 /* Papouch SB422 USB-RS422 Converter */ -#define PAPOUCH_SB485_2_PID 0x0103 /* Papouch SB485 USB-485/422 Converter */ -#define PAPOUCH_AP485_2_PID 0x0104 /* AP485 USB-RS485 Converter */ -#define PAPOUCH_SB422_2_PID 0x0105 /* Papouch SB422 USB-RS422 Converter */ -#define PAPOUCH_SB485S_PID 0x0106 /* Papouch SB485S USB-485/422 Converter */ -#define PAPOUCH_SB485C_PID 0x0107 /* Papouch SB485C USB-485/422 Converter */ -#define PAPOUCH_LEC_PID 0x0300 /* LEC USB Converter */ -#define PAPOUCH_SB232_PID 0x0301 /* Papouch SB232 USB-RS232 Converter */ -#define PAPOUCH_TMU_PID 0x0400 /* TMU USB Thermometer */ -#define PAPOUCH_IRAMP_PID 0x0500 /* Papouch IRAmp Duplex */ -#define PAPOUCH_DRAK5_PID 0x0700 /* Papouch DRAK5 */ -#define PAPOUCH_QUIDO8x8_PID 0x0800 /* Papouch Quido 8/8 Module */ -#define PAPOUCH_QUIDO4x4_PID 0x0900 /* Papouch Quido 4/4 Module */ -#define PAPOUCH_QUIDO2x2_PID 0x0a00 /* Papouch Quido 2/2 Module */ -#define PAPOUCH_QUIDO10x1_PID 0x0b00 /* Papouch Quido 10/1 Module */ -#define PAPOUCH_QUIDO30x3_PID 0x0c00 /* Papouch Quido 30/3 Module */ -#define PAPOUCH_QUIDO60x3_PID 0x0d00 /* Papouch Quido 60(100)/3 Module */ -#define PAPOUCH_QUIDO2x16_PID 0x0e00 /* Papouch Quido 2/16 Module */ -#define PAPOUCH_QUIDO3x32_PID 0x0f00 /* Papouch Quido 3/32 Module */ -#define PAPOUCH_DRAK6_PID 0x1000 /* Papouch DRAK6 */ -#define PAPOUCH_UPSUSB_PID 0x8000 /* Papouch UPS-USB adapter */ -#define PAPOUCH_MU_PID 0x8001 /* MU controller */ -#define PAPOUCH_SIMUKEY_PID 0x8002 /* Papouch SimuKey */ -#define PAPOUCH_AD4USB_PID 0x8003 /* AD4USB Measurement Module */ -#define PAPOUCH_GMUX_PID 0x8004 /* Papouch GOLIATH MUX */ -#define PAPOUCH_GMSR_PID 0x8005 /* Papouch GOLIATH MSR */ - -/* - * Marvell SheevaPlug - */ -#define MARVELL_VID 0x9e88 -#define MARVELL_SHEEVAPLUG_PID 0x9e8f - -/* - * Evolution Robotics products (http://www.evolution.com/). - * Submitted by Shawn M. Lavelle. - */ -#define EVOLUTION_VID 0xDEEE /* Vendor ID */ -#define EVOLUTION_ER1_PID 0x0300 /* ER1 Control Module */ -#define EVO_8U232AM_PID 0x02FF /* Evolution robotics RCM2 (FT232AM)*/ -#define EVO_HYBRID_PID 0x0302 /* Evolution robotics RCM4 PID (FT232BM)*/ -#define EVO_RCM4_PID 0x0303 /* Evolution robotics RCM4 PID */ - -/* - * MJS Gadgets HD Radio / XM Radio / Sirius Radio interfaces (using VID 0x0403) - */ -#define MJSG_GENERIC_PID 0x9378 -#define MJSG_SR_RADIO_PID 0x9379 -#define MJSG_XM_RADIO_PID 0x937A -#define MJSG_HD_RADIO_PID 0x937C - -/* - * D.O.Tec products (http://www.directout.eu) - */ -#define FTDI_DOTEC_PID 0x9868 - -/* - * Xverve Signalyzer tools (http://www.signalyzer.com/) - */ -#define XVERVE_SIGNALYZER_ST_PID 0xBCA0 -#define XVERVE_SIGNALYZER_SLITE_PID 0xBCA1 -#define XVERVE_SIGNALYZER_SH2_PID 0xBCA2 -#define XVERVE_SIGNALYZER_SH4_PID 0xBCA4 - -/* - * Segway Robotic Mobility Platform USB interface (using VID 0x0403) - * Submitted by John G. Rogers - */ -#define SEGWAY_RMP200_PID 0xe729 - - -/* - * Accesio USB Data Acquisition products (http://www.accesio.com/) - */ -#define ACCESIO_COM4SM_PID 0xD578 - -/* www.sciencescope.co.uk educational dataloggers */ -#define FTDI_SCIENCESCOPE_LOGBOOKML_PID 0xFF18 -#define FTDI_SCIENCESCOPE_LS_LOGBOOK_PID 0xFF1C -#define FTDI_SCIENCESCOPE_HS_LOGBOOK_PID 0xFF1D - -/* - * Milkymist One JTAG/Serial - */ -#define QIHARDWARE_VID 0x20B7 -#define MILKYMISTONE_JTAGSERIAL_PID 0x0713 - -/* - * CTI GmbH RS485 Converter http://www.cti-lean.com/ - */ -/* USB-485-Mini*/ -#define FTDI_CTI_MINI_PID 0xF608 -/* USB-Nano-485*/ -#define FTDI_CTI_NANO_PID 0xF60B - -/* - * ZeitControl cardsystems GmbH rfid-readers http://zeitconrol.de - */ -/* TagTracer MIFARE*/ -#define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0 - -/* - * Rainforest Automation - */ -/* ZigBee controller */ -#define FTDI_RF_R106 0x8A28 - -/* - * Product: HCP HIT GPRS modem - * Manufacturer: HCP d.o.o. - * ATI command output: Cinterion MC55i - */ -#define FTDI_CINTERION_MC55I_PID 0xA951 diff --git a/qemu/hw/usb/quirks-pl2303-ids.h b/qemu/hw/usb/quirks-pl2303-ids.h deleted file mode 100644 index 8dbdb46ff..000000000 --- a/qemu/hw/usb/quirks-pl2303-ids.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Prolific PL2303 USB to serial adaptor driver header file - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#define BENQ_VENDOR_ID 0x04a5 -#define BENQ_PRODUCT_ID_S81 0x4027 - -#define PL2303_VENDOR_ID 0x067b -#define PL2303_PRODUCT_ID 0x2303 -#define PL2303_PRODUCT_ID_RSAQ2 0x04bb -#define PL2303_PRODUCT_ID_DCU11 0x1234 -#define PL2303_PRODUCT_ID_PHAROS 0xaaa0 -#define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 -#define PL2303_PRODUCT_ID_ALDIGA 0x0611 -#define PL2303_PRODUCT_ID_MMX 0x0612 -#define PL2303_PRODUCT_ID_GPRS 0x0609 -#define PL2303_PRODUCT_ID_HCR331 0x331a -#define PL2303_PRODUCT_ID_MOTOROLA 0x0307 - -#define ATEN_VENDOR_ID 0x0557 -#define ATEN_VENDOR_ID2 0x0547 -#define ATEN_PRODUCT_ID 0x2008 - -#define IODATA_VENDOR_ID 0x04bb -#define IODATA_PRODUCT_ID 0x0a03 -#define IODATA_PRODUCT_ID_RSAQ5 0x0a0e - -#define ELCOM_VENDOR_ID 0x056e -#define ELCOM_PRODUCT_ID 0x5003 -#define ELCOM_PRODUCT_ID_UCSGT 0x5004 - -#define ITEGNO_VENDOR_ID 0x0eba -#define ITEGNO_PRODUCT_ID 0x1080 -#define ITEGNO_PRODUCT_ID_2080 0x2080 - -#define MA620_VENDOR_ID 0x0df7 -#define MA620_PRODUCT_ID 0x0620 - -#define RATOC_VENDOR_ID 0x0584 -#define RATOC_PRODUCT_ID 0xb000 - -#define TRIPP_VENDOR_ID 0x2478 -#define TRIPP_PRODUCT_ID 0x2008 - -#define RADIOSHACK_VENDOR_ID 0x1453 -#define RADIOSHACK_PRODUCT_ID 0x4026 - -#define DCU10_VENDOR_ID 0x0731 -#define DCU10_PRODUCT_ID 0x0528 - -#define SITECOM_VENDOR_ID 0x6189 -#define SITECOM_PRODUCT_ID 0x2068 - -/* Alcatel OT535/735 USB cable */ -#define ALCATEL_VENDOR_ID 0x11f7 -#define ALCATEL_PRODUCT_ID 0x02df - -/* Samsung I330 phone cradle */ -#define SAMSUNG_VENDOR_ID 0x04e8 -#define SAMSUNG_PRODUCT_ID 0x8001 - -#define SIEMENS_VENDOR_ID 0x11f5 -#define SIEMENS_PRODUCT_ID_SX1 0x0001 -#define SIEMENS_PRODUCT_ID_X65 0x0003 -#define SIEMENS_PRODUCT_ID_X75 0x0004 -#define SIEMENS_PRODUCT_ID_EF81 0x0005 - -#define SYNTECH_VENDOR_ID 0x0745 -#define SYNTECH_PRODUCT_ID 0x0001 - -/* Nokia CA-42 Cable */ -#define NOKIA_CA42_VENDOR_ID 0x078b -#define NOKIA_CA42_PRODUCT_ID 0x1234 - -/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */ -#define CA_42_CA42_VENDOR_ID 0x10b5 -#define CA_42_CA42_PRODUCT_ID 0xac70 - -#define SAGEM_VENDOR_ID 0x079b -#define SAGEM_PRODUCT_ID 0x0027 - -/* Leadtek GPS 9531 (ID 0413:2101) */ -#define LEADTEK_VENDOR_ID 0x0413 -#define LEADTEK_9531_PRODUCT_ID 0x2101 - -/* USB GSM cable from Speed Dragon Multimedia, Ltd */ -#define SPEEDDRAGON_VENDOR_ID 0x0e55 -#define SPEEDDRAGON_PRODUCT_ID 0x110b - -/* DATAPILOT Universal-2 Phone Cable */ -#define DATAPILOT_U2_VENDOR_ID 0x0731 -#define DATAPILOT_U2_PRODUCT_ID 0x2003 - -/* Belkin "F5U257" Serial Adapter */ -#define BELKIN_VENDOR_ID 0x050d -#define BELKIN_PRODUCT_ID 0x0257 - -/* Alcor Micro Corp. USB 2.0 TO RS-232 */ -#define ALCOR_VENDOR_ID 0x058F -#define ALCOR_PRODUCT_ID 0x9720 - -/* Willcom WS002IN Data Driver (by NetIndex Inc.) */ -#define WS002IN_VENDOR_ID 0x11f6 -#define WS002IN_PRODUCT_ID 0x2001 - -/* Corega CG-USBRS232R Serial Adapter */ -#define COREGA_VENDOR_ID 0x07aa -#define COREGA_PRODUCT_ID 0x002a - -/* Y.C. Cable U.S.A., Inc - USB to RS-232 */ -#define YCCABLE_VENDOR_ID 0x05ad -#define YCCABLE_PRODUCT_ID 0x0fba - -/* "Superial" USB - Serial */ -#define SUPERIAL_VENDOR_ID 0x5372 -#define SUPERIAL_PRODUCT_ID 0x2303 - -/* Hewlett-Packard LD220-HP POS Pole Display */ -#define HP_VENDOR_ID 0x03f0 -#define HP_LD220_PRODUCT_ID 0x3524 - -/* Cressi Edy (diving computer) PC interface */ -#define CRESSI_VENDOR_ID 0x04b8 -#define CRESSI_EDY_PRODUCT_ID 0x0521 - -/* Zeagle dive computer interface */ -#define ZEAGLE_VENDOR_ID 0x04b8 -#define ZEAGLE_N2ITION3_PRODUCT_ID 0x0522 - -/* Sony, USB data cable for CMD-Jxx mobile phones */ -#define SONY_VENDOR_ID 0x054c -#define SONY_QN3USB_PRODUCT_ID 0x0437 - -/* Sanwa KB-USB2 multimeter cable (ID: 11ad:0001) */ -#define SANWA_VENDOR_ID 0x11ad -#define SANWA_PRODUCT_ID 0x0001 - -/* ADLINK ND-6530 RS232,RS485 and RS422 adapter */ -#define ADLINK_VENDOR_ID 0x0b63 -#define ADLINK_ND6530_PRODUCT_ID 0x6530 - -/* SMART USB Serial Adapter */ -#define SMART_VENDOR_ID 0x0b8c -#define SMART_PRODUCT_ID 0x2303 diff --git a/qemu/hw/usb/quirks.c b/qemu/hw/usb/quirks.c deleted file mode 100644 index 38a9c5634..000000000 --- a/qemu/hw/usb/quirks.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * USB quirk handling - * - * Copyright (c) 2012 Red Hat, Inc. - * - * Red Hat Authors: - * Hans de Goede <hdegoede@redhat.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "quirks.h" -#include "hw/usb.h" - -static bool usb_id_match(const struct usb_device_id *ids, - uint16_t vendor_id, uint16_t product_id, - uint8_t interface_class, uint8_t interface_subclass, - uint8_t interface_protocol) { - int i; - - for (i = 0; ids[i].vendor_id != -1; i++) { - if (ids[i].vendor_id == vendor_id && - ids[i].product_id == product_id && - (ids[i].interface_class == -1 || - (ids[i].interface_class == interface_class && - ids[i].interface_subclass == interface_subclass && - ids[i].interface_protocol == interface_protocol))) { - return true; - } - } - return false; -} - -int usb_get_quirks(uint16_t vendor_id, uint16_t product_id, - uint8_t interface_class, uint8_t interface_subclass, - uint8_t interface_protocol) -{ - int quirks = 0; - - if (usb_id_match(usbredir_raw_serial_ids, vendor_id, product_id, - interface_class, interface_subclass, interface_protocol)) { - quirks |= USB_QUIRK_BUFFER_BULK_IN; - } - if (usb_id_match(usbredir_ftdi_serial_ids, vendor_id, product_id, - interface_class, interface_subclass, interface_protocol)) { - quirks |= USB_QUIRK_BUFFER_BULK_IN | USB_QUIRK_IS_FTDI; - } - - return quirks; -} diff --git a/qemu/hw/usb/quirks.h b/qemu/hw/usb/quirks.h deleted file mode 100644 index 8dc606552..000000000 --- a/qemu/hw/usb/quirks.h +++ /dev/null @@ -1,910 +0,0 @@ -/* - * USB quirk handling - * - * Copyright (c) 2012 Red Hat, Inc. - * - * Red Hat Authors: - * Hans de Goede <hdegoede@redhat.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -/* 1 on 1 copy of linux/drivers/usb/serial/ftdi_sio_ids.h */ -#include "quirks-ftdi-ids.h" -/* 1 on 1 copy of linux/drivers/usb/serial/pl2303.h */ -#include "quirks-pl2303-ids.h" - -struct usb_device_id { - int vendor_id; - int product_id; - int interface_class; - int interface_subclass; - int interface_protocol; -}; - -#define USB_DEVICE(vendor, product) \ - .vendor_id = vendor, .product_id = product, .interface_class = -1, - -#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, iclass, isubclass, iproto) \ - .vendor_id = vend, .product_id = prod, .interface_class = iclass, \ - .interface_subclass = isubclass, .interface_protocol = iproto - -static const struct usb_device_id usbredir_raw_serial_ids[] = { - /* - * Silicon Laboratories CP210x USB to RS232 serial adapter ids - * copied from linux/drivers/usb/serial/cp210x.c - * - * Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk) - */ - { USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */ - { USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */ - { USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ - { USB_DEVICE(0x0489, 0xE003) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ - { USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */ - { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */ - { USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */ - { USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */ - { USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */ - { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ - { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */ - { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */ - { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */ - { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ - { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ - { USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */ - { USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */ - { USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */ - { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ - { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ - { USB_DEVICE(0x10C4, 0x8044) }, /* Cygnal Debug Adapter */ - { USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */ - { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ - { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */ - { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ - { USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */ - { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ - { USB_DEVICE(0x10C4, 0x80C4) }, /* Cygnal Integrated Products, Inc., Optris infrared thermometer */ - { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ - { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */ - { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */ - { USB_DEVICE(0x10C4, 0x8115) }, /* Arygon NFC/Mifare Reader */ - { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */ - { USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */ - { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */ - { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */ - { USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */ - { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ - { USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */ - { USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */ - { USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */ - { USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */ - { USB_DEVICE(0x10C4, 0x81A9) }, /* Multiplex RC Interface */ - { USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */ - { USB_DEVICE(0x10C4, 0x81AD) }, /* INSYS USB Modem */ - { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */ - { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ - { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ - { USB_DEVICE(0x10C4, 0x81E8) }, /* Zephyr Bioharness */ - { USB_DEVICE(0x10C4, 0x81F2) }, /* C1007 HF band RFID controller */ - { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ - { USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */ - { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */ - { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */ - { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */ - { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ - { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */ - { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ - { USB_DEVICE(0x10C4, 0x83D8) }, /* DekTec DTA Plus VHF/UHF Booster/Attenuator */ - { USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */ - { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ - { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ - { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ - { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ - { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ - { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ - { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ - { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ - { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ - { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */ - { USB_DEVICE(0x10C4, 0xEA80) }, /* Silicon Labs factory default */ - { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ - { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ - { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */ - { USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */ - { USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */ - { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */ - { USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */ - { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ - { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */ - { USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */ - { USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */ - { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ - { USB_DEVICE(0x166A, 0x0304) }, /* Clipsal 5000CT2 C-Bus Black and White Touchscreen */ - { USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */ - { USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */ - { USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */ - { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ - { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */ - { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */ - { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */ - { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */ - { USB_DEVICE(0x17A8, 0x0001) }, /* Kamstrup Optical Eye/3-wire */ - { USB_DEVICE(0x17A8, 0x0005) }, /* Kamstrup M-Bus Master MultiPort 250D */ - { USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */ - { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ - { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ - { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ - { USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */ - { USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */ - { USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */ - { USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */ - { USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */ - { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ - - /* - * Prolific pl2303 USB to RS232 serial adapter ids - * copied from linux/drivers/usb/serial/pl2303.c - * - * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2003 IBM Corp. - */ - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) }, - { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, - { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, - { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, - { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, - { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, - { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, - { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, - { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) }, - { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, - { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, - { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, - { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, - { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, - { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) }, - { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) }, - { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) }, - { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */ - { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) }, - { USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) }, - { USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) }, - { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) }, - { USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) }, - { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) }, - { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) }, - { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) }, - { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) }, - { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) }, - { USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) }, - { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) }, - { USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) }, - { USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) }, - { USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) }, - { USB_DEVICE(ZEAGLE_VENDOR_ID, ZEAGLE_N2ITION3_PRODUCT_ID) }, - { USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) }, - { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, - { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, - { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, - - { USB_DEVICE(-1, -1) } /* Terminating Entry */ -}; - -static const struct usb_device_id usbredir_ftdi_serial_ids[] = { - /* - * FTDI USB to RS232 serial adapter ids - * copied from linux/drivers/usb/serial/ftdi_sio.c - * - * Copyright (C) 2009 - 2010 - * Johan Hovold (jhovold@gmail.com) - * Copyright (C) 1999 - 2001 - * Greg Kroah-Hartman (greg@kroah.com) - * Bill Ryder (bryder@sgi.com) - * Copyright (C) 2002 - * Kuba Ober (kuba@mareimbrium.org) - */ - { USB_DEVICE(FTDI_VID, FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_3_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_4_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_5_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_6_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_7_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USINT_CAT_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USINT_WKEY_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USINT_RS232_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IPLUS2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_DMX4ALL) }, - { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_4232H_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_232H_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_FTX_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_SNIFFER_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_THROTTLE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GATEWAY_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GBM_PID) }, - { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, - { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SPROG_II) }, - { USB_DEVICE(FTDI_VID, FTDI_LENZ_LIUSB_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_547_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_633_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_631_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_635_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_640_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_642_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_URBAN_0_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_URBAN_1_PID) }, - { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_0_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_3_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_4_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_5_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_R2000KU_TRUE_RNG) }, - { USB_DEVICE(FTDI_VID, FTDI_VARDAAN_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0100_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0101_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0102_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0103_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0104_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0105_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0106_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0107_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0108_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0109_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0110_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0111_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0112_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0113_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0114_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0115_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0116_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0117_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0118_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0119_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0120_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0121_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0122_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0123_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0124_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0125_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0126_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0128_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0129_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0130_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0131_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0132_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0133_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0134_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0135_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0136_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0137_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0138_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0139_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0140_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0141_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0142_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0143_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0144_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0145_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0146_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0147_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0148_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0149_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0150_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0151_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0152_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0159_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0160_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0161_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0162_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0163_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0164_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0165_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0166_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0167_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0168_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0169_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0170_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0171_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0172_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0173_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0174_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0175_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0176_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0177_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0178_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0179_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0180_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0181_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0182_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0183_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0184_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0185_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0186_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0187_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0188_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0189_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0190_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0191_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0192_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0193_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0194_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0195_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0196_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0197_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0198_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0199_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01ED_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USBX_707_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2106_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_5_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_6_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_7_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_8_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_5_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_6_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_7_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_8_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_5_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_6_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_7_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_4_PID) }, - { USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) }, - { USB_DEVICE(OCT_VID, OCT_US101_PID) }, - { USB_DEVICE(OCT_VID, OCT_DK201_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) }, - { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) }, - { USB_DEVICE(FTDI_VID, PROTEGO_R2X0) }, - { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) }, - { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_4) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E808_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E809_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80A_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80B_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80C_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80D_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80E_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80F_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E888_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E889_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88A_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88B_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88C_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88D_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88E_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88F_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UO100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UM100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UR100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_ALC8500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PYRAMID_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1000PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_US485_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PICPRO_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PCMCIA_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PK1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_RS232MON_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID) }, - /* - * ELV devices: - */ - { USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS550_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_EC3000_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS888_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_TWS550_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FEM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_CLI7000_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_PPS7330_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_TFM100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UDF77_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UIO88_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UAD8_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UDA7_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_USI2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_T1100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_PCD200_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_ULA200_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_CSI8_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_EM1000DL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_PCK100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_RFP500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FS20SIG_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UTP8_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS444PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_EM1010PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_HS485_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UMS100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_TFD128_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FM3RX_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS777_PID) }, - { USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) }, - { USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) }, - { USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) }, - { USB_DEVICE(FTDI_VID, LINX_FUTURE_1_PID) }, - { USB_DEVICE(FTDI_VID, LINX_FUTURE_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSMACHX_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSLOAD_N_GO_3_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSICDU64_4_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSPRIME8_5_PID) }, - { USB_DEVICE(FTDI_VID, INSIDE_ACCESSO) }, - { USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) }, - { USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) }, - { USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) }, - { USB_DEVICE(FALCOM_VID, FALCOM_SAMBA_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OCEANIC_PID) }, - { USB_DEVICE(TTI_VID, TTI_QL355P_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) }, - { USB_DEVICE(ACTON_VID, ACTON_SPECTRAPRO_PID) }, - { USB_DEVICE(CONTEC_VID, CONTEC_COM1USBH_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USOPTL4_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USPTL4_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_2_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR2_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_485USB9F_2W_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_485USB9F_4W_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_232USB9M_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_485USBTB_2W_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_485USBTB_4W_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_TTL5USB9M_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_TTL3USB9M_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_ZZ_PROG1_USB_PID) }, - { USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) }, - { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_3_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_0_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_1_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_2_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_3_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_4_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) }, - { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_KW_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_YS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y6_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y8_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_IC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_DB9_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_RS232_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y9_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_VCP_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_D2XX_PID) }, - { USB_DEVICE(EVOLUTION_VID, EVOLUTION_ER1_PID) }, - { USB_DEVICE(EVOLUTION_VID, EVO_HYBRID_PID) }, - { USB_DEVICE(EVOLUTION_VID, EVO_RCM4_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ARTEMIS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16C_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HR_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HRC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16IC_PID) }, - { USB_DEVICE(KOBIL_VID, KOBIL_CONV_B1_PID) }, - { USB_DEVICE(KOBIL_VID, KOBIL_CONV_KAAN_PID) }, - { USB_DEVICE(POSIFLEX_VID, POSIFLEX_PP7000_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TTUSB_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ECLO_COM_1WIRE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_777_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NZR_SEM_USB_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_1_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_OPC_U_UC_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2C1_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2C2_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2D_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2VT_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2VR_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVT_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVR_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVT_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVR_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ACG_HFDUAL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_YEI_SERVOCENTER31_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) }, - { USB_DEVICE(TESTO_VID, TESTO_USB_INTERFACE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GAMMA_SCOUT_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13M_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, - { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID) }, - { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, - { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_SERIAL_VX7_PID) }, - { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_CT29B_PID) }, - { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_RTS01_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) }, - { USB_DEVICE(TML_VID, TML_USB_SERIAL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, - { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID) }, - { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID) }, - { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID) }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID) }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID) }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID) }, - { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, - { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, - - /* Papouch devices based on FTDI chip */ - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AP485_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB422_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485_2_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AP485_2_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB422_2_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485S_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485C_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_LEC_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB232_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_IRAMP_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_DRAK5_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO8x8_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO2x2_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO10x1_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO30x3_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO60x3_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO2x16_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO3x32_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_DRAK6_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_UPSUSB_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_MU_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SIMUKEY_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AD4USB_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_GMUX_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_GMSR_PID) }, - - { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, - { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, - { USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) }, - { USB_DEVICE(ATMEL_VID, STK541_PID) }, - { USB_DEVICE(DE_VID, STB_PID) }, - { USB_DEVICE(DE_VID, WHT_PID) }, - { USB_DEVICE(ADI_VID, ADI_GNICE_PID) }, - { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID) }, - { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID, - 0xff, 0xff, 0x00) }, - { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, - { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID) }, - { USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) }, - { USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) }, - { USB_DEVICE(FTDI_VID, PI_C865_PID) }, - { USB_DEVICE(FTDI_VID, PI_C857_PID) }, - { USB_DEVICE(PI_VID, PI_C866_PID) }, - { USB_DEVICE(PI_VID, PI_C663_PID) }, - { USB_DEVICE(PI_VID, PI_C725_PID) }, - { USB_DEVICE(PI_VID, PI_E517_PID) }, - { USB_DEVICE(PI_VID, PI_C863_PID) }, - { USB_DEVICE(PI_VID, PI_E861_PID) }, - { USB_DEVICE(PI_VID, PI_C867_PID) }, - { USB_DEVICE(PI_VID, PI_E609_PID) }, - { USB_DEVICE(PI_VID, PI_E709_PID) }, - { USB_DEVICE(PI_VID, PI_100F_PID) }, - { USB_DEVICE(PI_VID, PI_1011_PID) }, - { USB_DEVICE(PI_VID, PI_1012_PID) }, - { USB_DEVICE(PI_VID, PI_1013_PID) }, - { USB_DEVICE(PI_VID, PI_1014_PID) }, - { USB_DEVICE(PI_VID, PI_1015_PID) }, - { USB_DEVICE(PI_VID, PI_1016_PID) }, - { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) }, - { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, - { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID) }, - { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID) }, - { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, - { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, - { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, - { USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) }, - { USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) }, - { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) }, - { USB_DEVICE(FTDI_VID, MJSG_HD_RADIO_PID) }, - { USB_DEVICE(FTDI_VID, MJSG_XM_RADIO_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_ST_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID) }, - { USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) }, - { USB_DEVICE(FTDI_VID, ACCESIO_COM4SM_PID) }, - { USB_DEVICE(IONICS_VID, IONICS_PLUGCOMPUTER_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_24_MASTER_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_PC_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_USB_DMX_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MIDI_TIMECODE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MINI_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MAXI_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MEDIA_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LOGBOOKML_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, - { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID) }, - { USB_DEVICE(ST_VID, ST_STMCLT1030_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, - { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, - - { USB_DEVICE(-1, -1) } /* Terminating Entry */ -}; - -#undef USB_DEVICE -#undef USB_DEVICE_AND_INTERFACE_INFO diff --git a/qemu/hw/usb/redirect.c b/qemu/hw/usb/redirect.c deleted file mode 100644 index 8d8054037..000000000 --- a/qemu/hw/usb/redirect.c +++ /dev/null @@ -1,2526 +0,0 @@ -/* - * USB redirector usb-guest - * - * Copyright (c) 2011-2012 Red Hat, Inc. - * - * Red Hat Authors: - * Hans de Goede <hdegoede@redhat.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "qapi/qmp/qerror.h" -#include "qemu/error-report.h" -#include "qemu/iov.h" -#include "sysemu/char.h" - -#include <usbredirparser.h> -#include <usbredirfilter.h> - -#include "hw/usb.h" - -/* ERROR is defined below. Remove any previous definition. */ -#undef ERROR - -#define MAX_ENDPOINTS 32 -#define NO_INTERFACE_INFO 255 /* Valid interface_count always <= 32 */ -#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) -#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) -#define USBEP2I(usb_ep) (((usb_ep)->pid == USB_TOKEN_IN) ? \ - ((usb_ep)->nr | 0x10) : ((usb_ep)->nr)) -#define I2USBEP(d, i) (usb_ep_get(&(d)->dev, \ - ((i) & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, \ - (i) & 0x0f)) - -#ifndef USBREDIR_VERSION /* This is not defined in older usbredir versions */ -#define USBREDIR_VERSION 0 -#endif - -typedef struct USBRedirDevice USBRedirDevice; - -/* Struct to hold buffered packets */ -struct buf_packet { - uint8_t *data; - void *free_on_destroy; - uint16_t len; - uint16_t offset; - uint8_t status; - QTAILQ_ENTRY(buf_packet)next; -}; - -struct endp_data { - USBRedirDevice *dev; - uint8_t type; - uint8_t interval; - uint8_t interface; /* bInterfaceNumber this ep belongs to */ - uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */ - uint32_t max_streams; - uint8_t iso_started; - uint8_t iso_error; /* For reporting iso errors to the HC */ - uint8_t interrupt_started; - uint8_t interrupt_error; - uint8_t bulk_receiving_enabled; - uint8_t bulk_receiving_started; - uint8_t bufpq_prefilled; - uint8_t bufpq_dropping_packets; - QTAILQ_HEAD(, buf_packet) bufpq; - int32_t bufpq_size; - int32_t bufpq_target_size; - USBPacket *pending_async_packet; -}; - -struct PacketIdQueueEntry { - uint64_t id; - QTAILQ_ENTRY(PacketIdQueueEntry)next; -}; - -struct PacketIdQueue { - USBRedirDevice *dev; - const char *name; - QTAILQ_HEAD(, PacketIdQueueEntry) head; - int size; -}; - -struct USBRedirDevice { - USBDevice dev; - /* Properties */ - CharDriverState *cs; - uint8_t debug; - char *filter_str; - int32_t bootindex; - /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ - const uint8_t *read_buf; - int read_buf_size; - /* Active chardev-watch-tag */ - guint watch; - /* For async handling of close / reject */ - QEMUBH *chardev_close_bh; - QEMUBH *device_reject_bh; - /* To delay the usb attach in case of quick chardev close + open */ - QEMUTimer *attach_timer; - int64_t next_attach_time; - struct usbredirparser *parser; - struct endp_data endpoint[MAX_ENDPOINTS]; - struct PacketIdQueue cancelled; - struct PacketIdQueue already_in_flight; - void (*buffered_bulk_in_complete)(USBRedirDevice *, USBPacket *, uint8_t); - /* Data for device filtering */ - struct usb_redir_device_connect_header device_info; - struct usb_redir_interface_info_header interface_info; - struct usbredirfilter_rule *filter_rules; - int filter_rules_count; - int compatible_speedmask; -}; - -#define TYPE_USB_REDIR "usb-redir" -#define USB_REDIRECT(obj) OBJECT_CHECK(USBRedirDevice, (obj), TYPE_USB_REDIR) - -static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); -static void usbredir_device_connect(void *priv, - struct usb_redir_device_connect_header *device_connect); -static void usbredir_device_disconnect(void *priv); -static void usbredir_interface_info(void *priv, - struct usb_redir_interface_info_header *interface_info); -static void usbredir_ep_info(void *priv, - struct usb_redir_ep_info_header *ep_info); -static void usbredir_configuration_status(void *priv, uint64_t id, - struct usb_redir_configuration_status_header *configuration_status); -static void usbredir_alt_setting_status(void *priv, uint64_t id, - struct usb_redir_alt_setting_status_header *alt_setting_status); -static void usbredir_iso_stream_status(void *priv, uint64_t id, - struct usb_redir_iso_stream_status_header *iso_stream_status); -static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, - struct usb_redir_interrupt_receiving_status_header - *interrupt_receiving_status); -static void usbredir_bulk_streams_status(void *priv, uint64_t id, - struct usb_redir_bulk_streams_status_header *bulk_streams_status); -static void usbredir_bulk_receiving_status(void *priv, uint64_t id, - struct usb_redir_bulk_receiving_status_header *bulk_receiving_status); -static void usbredir_control_packet(void *priv, uint64_t id, - struct usb_redir_control_packet_header *control_packet, - uint8_t *data, int data_len); -static void usbredir_bulk_packet(void *priv, uint64_t id, - struct usb_redir_bulk_packet_header *bulk_packet, - uint8_t *data, int data_len); -static void usbredir_iso_packet(void *priv, uint64_t id, - struct usb_redir_iso_packet_header *iso_packet, - uint8_t *data, int data_len); -static void usbredir_interrupt_packet(void *priv, uint64_t id, - struct usb_redir_interrupt_packet_header *interrupt_header, - uint8_t *data, int data_len); -static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, - struct usb_redir_buffered_bulk_packet_header *buffered_bulk_packet, - uint8_t *data, int data_len); - -static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p, - int status); - -#define VERSION "qemu usb-redir guest " QEMU_VERSION - -/* - * Logging stuff - */ - -#define ERROR(...) \ - do { \ - if (dev->debug >= usbredirparser_error) { \ - error_report("usb-redir error: " __VA_ARGS__); \ - } \ - } while (0) -#define WARNING(...) \ - do { \ - if (dev->debug >= usbredirparser_warning) { \ - error_report("usb-redir warning: " __VA_ARGS__); \ - } \ - } while (0) -#define INFO(...) \ - do { \ - if (dev->debug >= usbredirparser_info) { \ - error_report("usb-redir: " __VA_ARGS__); \ - } \ - } while (0) -#define DPRINTF(...) \ - do { \ - if (dev->debug >= usbredirparser_debug) { \ - error_report("usb-redir: " __VA_ARGS__); \ - } \ - } while (0) -#define DPRINTF2(...) \ - do { \ - if (dev->debug >= usbredirparser_debug_data) { \ - error_report("usb-redir: " __VA_ARGS__); \ - } \ - } while (0) - -static void usbredir_log(void *priv, int level, const char *msg) -{ - USBRedirDevice *dev = priv; - - if (dev->debug < level) { - return; - } - - error_report("%s", msg); -} - -static void usbredir_log_data(USBRedirDevice *dev, const char *desc, - const uint8_t *data, int len) -{ - int i, j, n; - - if (dev->debug < usbredirparser_debug_data) { - return; - } - - for (i = 0; i < len; i += j) { - char buf[128]; - - n = sprintf(buf, "%s", desc); - for (j = 0; j < 8 && i + j < len; j++) { - n += sprintf(buf + n, " %02X", data[i + j]); - } - error_report("%s", buf); - } -} - -/* - * usbredirparser io functions - */ - -static int usbredir_read(void *priv, uint8_t *data, int count) -{ - USBRedirDevice *dev = priv; - - if (dev->read_buf_size < count) { - count = dev->read_buf_size; - } - - memcpy(data, dev->read_buf, count); - - dev->read_buf_size -= count; - if (dev->read_buf_size) { - dev->read_buf += count; - } else { - dev->read_buf = NULL; - } - - return count; -} - -static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond, - void *opaque) -{ - USBRedirDevice *dev = opaque; - - dev->watch = 0; - usbredirparser_do_write(dev->parser); - - return FALSE; -} - -static int usbredir_write(void *priv, uint8_t *data, int count) -{ - USBRedirDevice *dev = priv; - int r; - - if (!dev->cs->be_open) { - return 0; - } - - /* Don't send new data to the chardev until our state is fully synced */ - if (!runstate_check(RUN_STATE_RUNNING)) { - return 0; - } - - r = qemu_chr_fe_write(dev->cs, data, count); - if (r < count) { - if (!dev->watch) { - dev->watch = qemu_chr_fe_add_watch(dev->cs, G_IO_OUT|G_IO_HUP, - usbredir_write_unblocked, dev); - } - if (r < 0) { - r = 0; - } - } - return r; -} - -/* - * Cancelled and buffered packets helpers - */ - -static void packet_id_queue_init(struct PacketIdQueue *q, - USBRedirDevice *dev, const char *name) -{ - q->dev = dev; - q->name = name; - QTAILQ_INIT(&q->head); - q->size = 0; -} - -static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id) -{ - USBRedirDevice *dev = q->dev; - struct PacketIdQueueEntry *e; - - DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name); - - e = g_new0(struct PacketIdQueueEntry, 1); - e->id = id; - QTAILQ_INSERT_TAIL(&q->head, e, next); - q->size++; -} - -static int packet_id_queue_remove(struct PacketIdQueue *q, uint64_t id) -{ - USBRedirDevice *dev = q->dev; - struct PacketIdQueueEntry *e; - - QTAILQ_FOREACH(e, &q->head, next) { - if (e->id == id) { - DPRINTF("removing packet id %"PRIu64" from %s queue\n", - id, q->name); - QTAILQ_REMOVE(&q->head, e, next); - q->size--; - g_free(e); - return 1; - } - } - return 0; -} - -static void packet_id_queue_empty(struct PacketIdQueue *q) -{ - USBRedirDevice *dev = q->dev; - struct PacketIdQueueEntry *e, *next_e; - - DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name); - - QTAILQ_FOREACH_SAFE(e, &q->head, next, next_e) { - QTAILQ_REMOVE(&q->head, e, next); - g_free(e); - } - q->size = 0; -} - -static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - int i = USBEP2I(p->ep); - - if (p->combined) { - usb_combined_packet_cancel(udev, p); - return; - } - - if (dev->endpoint[i].pending_async_packet) { - assert(dev->endpoint[i].pending_async_packet == p); - dev->endpoint[i].pending_async_packet = NULL; - return; - } - - packet_id_queue_add(&dev->cancelled, p->id); - usbredirparser_send_cancel_data_packet(dev->parser, p->id); - usbredirparser_do_write(dev->parser); -} - -static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) -{ - if (!dev->dev.attached) { - return 1; /* Treat everything as cancelled after a disconnect */ - } - return packet_id_queue_remove(&dev->cancelled, id); -} - -static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev, - struct USBEndpoint *ep) -{ - static USBPacket *p; - - /* async handled packets for bulk receiving eps do not count as inflight */ - if (dev->endpoint[USBEP2I(ep)].bulk_receiving_started) { - return; - } - - QTAILQ_FOREACH(p, &ep->queue, queue) { - /* Skip combined packets, except for the first */ - if (p->combined && p != p->combined->first) { - continue; - } - if (p->state == USB_PACKET_ASYNC) { - packet_id_queue_add(&dev->already_in_flight, p->id); - } - } -} - -static void usbredir_fill_already_in_flight(USBRedirDevice *dev) -{ - int ep; - struct USBDevice *udev = &dev->dev; - - usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_ctl); - - for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { - usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]); - usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]); - } -} - -static int usbredir_already_in_flight(USBRedirDevice *dev, uint64_t id) -{ - return packet_id_queue_remove(&dev->already_in_flight, id); -} - -static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, - uint8_t ep, uint64_t id) -{ - USBPacket *p; - - if (usbredir_is_cancelled(dev, id)) { - return NULL; - } - - p = usb_ep_find_packet_by_id(&dev->dev, - (ep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT, - ep & 0x0f, id); - if (p == NULL) { - ERROR("could not find packet with id %"PRIu64"\n", id); - } - return p; -} - -static int bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, - uint8_t status, uint8_t ep, void *free_on_destroy) -{ - struct buf_packet *bufp; - - if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets && - dev->endpoint[EP2I(ep)].bufpq_size > - 2 * dev->endpoint[EP2I(ep)].bufpq_target_size) { - DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep); - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1; - } - /* Since we're interupting the stream anyways, drop enough packets to get - back to our target buffer size */ - if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) { - if (dev->endpoint[EP2I(ep)].bufpq_size > - dev->endpoint[EP2I(ep)].bufpq_target_size) { - free(data); - return -1; - } - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; - } - - bufp = g_new(struct buf_packet, 1); - bufp->data = data; - bufp->len = len; - bufp->offset = 0; - bufp->status = status; - bufp->free_on_destroy = free_on_destroy; - QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); - dev->endpoint[EP2I(ep)].bufpq_size++; - return 0; -} - -static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp, - uint8_t ep) -{ - QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); - dev->endpoint[EP2I(ep)].bufpq_size--; - free(bufp->free_on_destroy); - g_free(bufp); -} - -static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep) -{ - struct buf_packet *buf, *buf_next; - - QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) { - bufp_free(dev, buf, ep); - } -} - -/* - * USBDevice callbacks - */ - -static void usbredir_handle_reset(USBDevice *udev) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - - DPRINTF("reset device\n"); - usbredirparser_send_reset(dev->parser); - usbredirparser_do_write(dev->parser); -} - -static void usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, - uint8_t ep) -{ - int status, len; - if (!dev->endpoint[EP2I(ep)].iso_started && - !dev->endpoint[EP2I(ep)].iso_error) { - struct usb_redir_start_iso_stream_header start_iso = { - .endpoint = ep, - }; - int pkts_per_sec; - - if (dev->dev.speed == USB_SPEED_HIGH) { - pkts_per_sec = 8000 / dev->endpoint[EP2I(ep)].interval; - } else { - pkts_per_sec = 1000 / dev->endpoint[EP2I(ep)].interval; - } - /* Testing has shown that we need circa 60 ms buffer */ - dev->endpoint[EP2I(ep)].bufpq_target_size = (pkts_per_sec * 60) / 1000; - - /* Aim for approx 100 interrupts / second on the client to - balance latency and interrupt load */ - start_iso.pkts_per_urb = pkts_per_sec / 100; - if (start_iso.pkts_per_urb < 1) { - start_iso.pkts_per_urb = 1; - } else if (start_iso.pkts_per_urb > 32) { - start_iso.pkts_per_urb = 32; - } - - start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size + - start_iso.pkts_per_urb - 1) / - start_iso.pkts_per_urb; - /* Output endpoints pre-fill only 1/2 of the packets, keeping the rest - as overflow buffer. Also see the usbredir protocol documentation */ - if (!(ep & USB_DIR_IN)) { - start_iso.no_urbs *= 2; - } - if (start_iso.no_urbs > 16) { - start_iso.no_urbs = 16; - } - - /* No id, we look at the ep when receiving a status back */ - usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso); - usbredirparser_do_write(dev->parser); - DPRINTF("iso stream started pkts/sec %d pkts/urb %d urbs %d ep %02X\n", - pkts_per_sec, start_iso.pkts_per_urb, start_iso.no_urbs, ep); - dev->endpoint[EP2I(ep)].iso_started = 1; - dev->endpoint[EP2I(ep)].bufpq_prefilled = 0; - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; - } - - if (ep & USB_DIR_IN) { - struct buf_packet *isop; - - if (dev->endpoint[EP2I(ep)].iso_started && - !dev->endpoint[EP2I(ep)].bufpq_prefilled) { - if (dev->endpoint[EP2I(ep)].bufpq_size < - dev->endpoint[EP2I(ep)].bufpq_target_size) { - return; - } - dev->endpoint[EP2I(ep)].bufpq_prefilled = 1; - } - - isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); - if (isop == NULL) { - DPRINTF("iso-token-in ep %02X, no isop, iso_error: %d\n", - ep, dev->endpoint[EP2I(ep)].iso_error); - /* Re-fill the buffer */ - dev->endpoint[EP2I(ep)].bufpq_prefilled = 0; - /* Check iso_error for stream errors, otherwise its an underrun */ - status = dev->endpoint[EP2I(ep)].iso_error; - dev->endpoint[EP2I(ep)].iso_error = 0; - p->status = status ? USB_RET_IOERROR : USB_RET_SUCCESS; - return; - } - 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); - - status = isop->status; - len = isop->len; - if (len > p->iov.size) { - ERROR("received iso data is larger then packet ep %02X (%d > %d)\n", - ep, len, (int)p->iov.size); - len = p->iov.size; - status = usb_redir_babble; - } - usb_packet_copy(p, isop->data, len); - bufp_free(dev, isop, ep); - usbredir_handle_status(dev, p, status); - } else { - /* If the stream was not started because of a pending error don't - send the packet to the usb-host */ - if (dev->endpoint[EP2I(ep)].iso_started) { - struct usb_redir_iso_packet_header iso_packet = { - .endpoint = ep, - .length = p->iov.size - }; - uint8_t buf[p->iov.size]; - /* No id, we look at the ep when receiving a status back */ - usb_packet_copy(p, buf, p->iov.size); - usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet, - buf, p->iov.size); - usbredirparser_do_write(dev->parser); - } - status = dev->endpoint[EP2I(ep)].iso_error; - dev->endpoint[EP2I(ep)].iso_error = 0; - DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status, - p->iov.size); - usbredir_handle_status(dev, p, status); - } -} - -static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep) -{ - struct usb_redir_stop_iso_stream_header stop_iso_stream = { - .endpoint = ep - }; - if (dev->endpoint[EP2I(ep)].iso_started) { - usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream); - DPRINTF("iso stream stopped ep %02X\n", ep); - dev->endpoint[EP2I(ep)].iso_started = 0; - } - dev->endpoint[EP2I(ep)].iso_error = 0; - usbredir_free_bufpq(dev, ep); -} - -/* - * The usb-host may poll the endpoint faster then our guest, resulting in lots - * of smaller bulkp-s. The below buffered_bulk_in_complete* functions combine - * data from multiple bulkp-s into a single packet, avoiding bufpq overflows. - */ -static void usbredir_buffered_bulk_add_data_to_packet(USBRedirDevice *dev, - struct buf_packet *bulkp, int count, USBPacket *p, uint8_t ep) -{ - usb_packet_copy(p, bulkp->data + bulkp->offset, count); - bulkp->offset += count; - if (bulkp->offset == bulkp->len) { - /* Store status in the last packet with data from this bulkp */ - usbredir_handle_status(dev, p, bulkp->status); - bufp_free(dev, bulkp, ep); - } -} - -static void usbredir_buffered_bulk_in_complete_raw(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - struct buf_packet *bulkp; - int count; - - while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) && - p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) { - count = bulkp->len - bulkp->offset; - if (count > (p->iov.size - p->actual_length)) { - count = p->iov.size - p->actual_length; - } - usbredir_buffered_bulk_add_data_to_packet(dev, bulkp, count, p, ep); - } -} - -static void usbredir_buffered_bulk_in_complete_ftdi(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - const int maxp = dev->endpoint[EP2I(ep)].max_packet_size; - uint8_t header[2] = { 0, 0 }; - struct buf_packet *bulkp; - int count; - - while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) && - p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) { - if (bulkp->len < 2) { - WARNING("malformed ftdi bulk in packet\n"); - bufp_free(dev, bulkp, ep); - continue; - } - - if ((p->actual_length % maxp) == 0) { - usb_packet_copy(p, bulkp->data, 2); - memcpy(header, bulkp->data, 2); - } else { - if (bulkp->data[0] != header[0] || bulkp->data[1] != header[1]) { - break; /* Different header, add to next packet */ - } - } - - if (bulkp->offset == 0) { - bulkp->offset = 2; /* Skip header */ - } - count = bulkp->len - bulkp->offset; - /* Must repeat the header at maxp interval */ - if (count > (maxp - (p->actual_length % maxp))) { - count = maxp - (p->actual_length % maxp); - } - usbredir_buffered_bulk_add_data_to_packet(dev, bulkp, count, p, ep); - } -} - -static void usbredir_buffered_bulk_in_complete(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - dev->buffered_bulk_in_complete(dev, p, ep); - DPRINTF("bulk-token-in ep %02X status %d len %d id %"PRIu64"\n", - ep, p->status, p->actual_length, p->id); -} - -static void usbredir_handle_buffered_bulk_in_data(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - /* Input bulk endpoint, buffered packet input */ - if (!dev->endpoint[EP2I(ep)].bulk_receiving_started) { - int bpt; - struct usb_redir_start_bulk_receiving_header start = { - .endpoint = ep, - .stream_id = 0, - .no_transfers = 5, - }; - /* Round bytes_per_transfer up to a multiple of max_packet_size */ - bpt = 512 + dev->endpoint[EP2I(ep)].max_packet_size - 1; - bpt /= dev->endpoint[EP2I(ep)].max_packet_size; - bpt *= dev->endpoint[EP2I(ep)].max_packet_size; - start.bytes_per_transfer = bpt; - /* No id, we look at the ep when receiving a status back */ - usbredirparser_send_start_bulk_receiving(dev->parser, 0, &start); - usbredirparser_do_write(dev->parser); - DPRINTF("bulk receiving started bytes/transfer %u count %d ep %02X\n", - start.bytes_per_transfer, start.no_transfers, ep); - dev->endpoint[EP2I(ep)].bulk_receiving_started = 1; - /* We don't really want to drop bulk packets ever, but - having some upper limit to how much we buffer is good. */ - dev->endpoint[EP2I(ep)].bufpq_target_size = 5000; - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; - } - - if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) { - DPRINTF("bulk-token-in ep %02X, no bulkp\n", ep); - assert(dev->endpoint[EP2I(ep)].pending_async_packet == NULL); - dev->endpoint[EP2I(ep)].pending_async_packet = p; - p->status = USB_RET_ASYNC; - return; - } - usbredir_buffered_bulk_in_complete(dev, p, ep); -} - -static void usbredir_stop_bulk_receiving(USBRedirDevice *dev, uint8_t ep) -{ - struct usb_redir_stop_bulk_receiving_header stop_bulk = { - .endpoint = ep, - .stream_id = 0, - }; - if (dev->endpoint[EP2I(ep)].bulk_receiving_started) { - usbredirparser_send_stop_bulk_receiving(dev->parser, 0, &stop_bulk); - DPRINTF("bulk receiving stopped ep %02X\n", ep); - dev->endpoint[EP2I(ep)].bulk_receiving_started = 0; - } - usbredir_free_bufpq(dev, ep); -} - -static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, - uint8_t ep) -{ - struct usb_redir_bulk_packet_header bulk_packet; - size_t size = usb_packet_size(p); - const int maxp = dev->endpoint[EP2I(ep)].max_packet_size; - - if (usbredir_already_in_flight(dev, p->id)) { - p->status = USB_RET_ASYNC; - return; - } - - if (dev->endpoint[EP2I(ep)].bulk_receiving_enabled) { - if (size != 0 && (size % maxp) == 0) { - usbredir_handle_buffered_bulk_in_data(dev, p, ep); - return; - } - WARNING("bulk recv invalid size %zd ep %02x, disabling\n", size, ep); - assert(dev->endpoint[EP2I(ep)].pending_async_packet == NULL); - usbredir_stop_bulk_receiving(dev, ep); - dev->endpoint[EP2I(ep)].bulk_receiving_enabled = 0; - } - - DPRINTF("bulk-out ep %02X stream %u len %zd id %"PRIu64"\n", - ep, p->stream, size, p->id); - - bulk_packet.endpoint = ep; - bulk_packet.length = size; - bulk_packet.stream_id = p->stream; - bulk_packet.length_high = size >> 16; - assert(bulk_packet.length_high == 0 || - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_32bits_bulk_length)); - - if (ep & USB_DIR_IN) { - usbredirparser_send_bulk_packet(dev->parser, p->id, - &bulk_packet, NULL, 0); - } else { - uint8_t buf[size]; - usb_packet_copy(p, buf, size); - usbredir_log_data(dev, "bulk data out:", buf, size); - usbredirparser_send_bulk_packet(dev->parser, p->id, - &bulk_packet, buf, size); - } - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_handle_interrupt_in_data(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - /* Input interrupt endpoint, buffered packet input */ - struct buf_packet *intp; - int status, len; - - if (!dev->endpoint[EP2I(ep)].interrupt_started && - !dev->endpoint[EP2I(ep)].interrupt_error) { - struct usb_redir_start_interrupt_receiving_header start_int = { - .endpoint = ep, - }; - /* No id, we look at the ep when receiving a status back */ - usbredirparser_send_start_interrupt_receiving(dev->parser, 0, - &start_int); - usbredirparser_do_write(dev->parser); - DPRINTF("interrupt recv started ep %02X\n", ep); - dev->endpoint[EP2I(ep)].interrupt_started = 1; - /* We don't really want to drop interrupt packets ever, but - having some upper limit to how much we buffer is good. */ - dev->endpoint[EP2I(ep)].bufpq_target_size = 1000; - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; - } - - intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); - if (intp == NULL) { - DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep); - /* Check interrupt_error for stream errors */ - status = dev->endpoint[EP2I(ep)].interrupt_error; - dev->endpoint[EP2I(ep)].interrupt_error = 0; - if (status) { - usbredir_handle_status(dev, p, status); - } else { - p->status = USB_RET_NAK; - } - return; - } - DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, - intp->status, intp->len); - - status = intp->status; - len = intp->len; - if (len > p->iov.size) { - ERROR("received int data is larger then packet ep %02X\n", ep); - len = p->iov.size; - status = usb_redir_babble; - } - usb_packet_copy(p, intp->data, len); - bufp_free(dev, intp, ep); - usbredir_handle_status(dev, p, status); -} - -/* - * Handle interrupt out data, the usbredir protocol expects us to do this - * async, so that it can report back a completion status. But guests will - * expect immediate completion for an interrupt endpoint, and handling this - * async causes migration issues. So we report success directly, counting - * on the fact that output interrupt packets normally always succeed. - */ -static void usbredir_handle_interrupt_out_data(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - struct usb_redir_interrupt_packet_header interrupt_packet; - uint8_t buf[p->iov.size]; - - DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep, - p->iov.size, p->id); - - interrupt_packet.endpoint = ep; - interrupt_packet.length = p->iov.size; - - usb_packet_copy(p, buf, p->iov.size); - usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size); - usbredirparser_send_interrupt_packet(dev->parser, p->id, - &interrupt_packet, buf, p->iov.size); - usbredirparser_do_write(dev->parser); -} - -static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev, - uint8_t ep) -{ - struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = { - .endpoint = ep - }; - if (dev->endpoint[EP2I(ep)].interrupt_started) { - usbredirparser_send_stop_interrupt_receiving(dev->parser, 0, - &stop_interrupt_recv); - DPRINTF("interrupt recv stopped ep %02X\n", ep); - dev->endpoint[EP2I(ep)].interrupt_started = 0; - } - dev->endpoint[EP2I(ep)].interrupt_error = 0; - usbredir_free_bufpq(dev, ep); -} - -static void usbredir_handle_data(USBDevice *udev, USBPacket *p) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - uint8_t ep; - - ep = p->ep->nr; - if (p->pid == USB_TOKEN_IN) { - ep |= USB_DIR_IN; - } - - switch (dev->endpoint[EP2I(ep)].type) { - case USB_ENDPOINT_XFER_CONTROL: - ERROR("handle_data called for control transfer on ep %02X\n", ep); - p->status = USB_RET_NAK; - break; - case USB_ENDPOINT_XFER_BULK: - if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN && - p->ep->pipeline) { - p->status = USB_RET_ADD_TO_QUEUE; - break; - } - usbredir_handle_bulk_data(dev, p, ep); - break; - case USB_ENDPOINT_XFER_ISOC: - usbredir_handle_iso_data(dev, p, ep); - break; - case USB_ENDPOINT_XFER_INT: - if (ep & USB_DIR_IN) { - usbredir_handle_interrupt_in_data(dev, p, ep); - } else { - usbredir_handle_interrupt_out_data(dev, p, ep); - } - break; - default: - ERROR("handle_data ep %02X has unknown type %d\n", ep, - dev->endpoint[EP2I(ep)].type); - p->status = USB_RET_NAK; - } -} - -static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) -{ - if (ep->pid == USB_TOKEN_IN && ep->pipeline) { - usb_ep_combine_input_packets(ep); - } -} - -static void usbredir_stop_ep(USBRedirDevice *dev, int i) -{ - uint8_t ep = I2EP(i); - - switch (dev->endpoint[i].type) { - case USB_ENDPOINT_XFER_BULK: - if (ep & USB_DIR_IN) { - usbredir_stop_bulk_receiving(dev, ep); - } - break; - case USB_ENDPOINT_XFER_ISOC: - usbredir_stop_iso_stream(dev, ep); - break; - case USB_ENDPOINT_XFER_INT: - if (ep & USB_DIR_IN) { - usbredir_stop_interrupt_receiving(dev, ep); - } - break; - } - usbredir_free_bufpq(dev, ep); -} - -static void usbredir_ep_stopped(USBDevice *udev, USBEndpoint *uep) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - - usbredir_stop_ep(dev, USBEP2I(uep)); - usbredirparser_do_write(dev->parser); -} - -static void usbredir_set_config(USBRedirDevice *dev, USBPacket *p, - int config) -{ - struct usb_redir_set_configuration_header set_config; - int i; - - DPRINTF("set config %d id %"PRIu64"\n", config, p->id); - - for (i = 0; i < MAX_ENDPOINTS; i++) { - usbredir_stop_ep(dev, i); - } - - set_config.configuration = config; - usbredirparser_send_set_configuration(dev->parser, p->id, &set_config); - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_get_config(USBRedirDevice *dev, USBPacket *p) -{ - DPRINTF("get config id %"PRIu64"\n", p->id); - - usbredirparser_send_get_configuration(dev->parser, p->id); - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, - int interface, int alt) -{ - struct usb_redir_set_alt_setting_header set_alt; - int i; - - DPRINTF("set interface %d alt %d id %"PRIu64"\n", interface, alt, p->id); - - for (i = 0; i < MAX_ENDPOINTS; i++) { - if (dev->endpoint[i].interface == interface) { - usbredir_stop_ep(dev, i); - } - } - - set_alt.interface = interface; - set_alt.alt = alt; - usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt); - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, - int interface) -{ - struct usb_redir_get_alt_setting_header get_alt; - - DPRINTF("get interface %d id %"PRIu64"\n", interface, p->id); - - get_alt.interface = interface; - usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt); - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_handle_control(USBDevice *udev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - struct usb_redir_control_packet_header control_packet; - - if (usbredir_already_in_flight(dev, p->id)) { - p->status = USB_RET_ASYNC; - return; - } - - /* Special cases for certain standard device requests */ - switch (request) { - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - DPRINTF("set address %d\n", value); - dev->dev.addr = value; - return; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - usbredir_set_config(dev, p, value & 0xff); - return; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - usbredir_get_config(dev, p); - return; - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - usbredir_set_interface(dev, p, index, value); - return; - case InterfaceRequest | USB_REQ_GET_INTERFACE: - usbredir_get_interface(dev, p, index); - return; - } - - /* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */ - DPRINTF( - "ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %"PRIu64"\n", - request >> 8, request & 0xff, value, index, length, p->id); - - control_packet.request = request & 0xFF; - control_packet.requesttype = request >> 8; - control_packet.endpoint = control_packet.requesttype & USB_DIR_IN; - control_packet.value = value; - control_packet.index = index; - control_packet.length = length; - - if (control_packet.requesttype & USB_DIR_IN) { - usbredirparser_send_control_packet(dev->parser, p->id, - &control_packet, NULL, 0); - } else { - usbredir_log_data(dev, "ctrl data out:", data, length); - usbredirparser_send_control_packet(dev->parser, p->id, - &control_packet, data, length); - } - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static int usbredir_alloc_streams(USBDevice *udev, USBEndpoint **eps, - int nr_eps, int streams) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); -#if USBREDIR_VERSION >= 0x000700 - struct usb_redir_alloc_bulk_streams_header alloc_streams; - int i; - - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_bulk_streams)) { - ERROR("peer does not support streams\n"); - goto reject; - } - - if (streams == 0) { - ERROR("request to allocate 0 streams\n"); - return -1; - } - - alloc_streams.no_streams = streams; - alloc_streams.endpoints = 0; - for (i = 0; i < nr_eps; i++) { - alloc_streams.endpoints |= 1 << USBEP2I(eps[i]); - } - usbredirparser_send_alloc_bulk_streams(dev->parser, 0, &alloc_streams); - usbredirparser_do_write(dev->parser); - - return 0; -#else - ERROR("usbredir_alloc_streams not implemented\n"); - goto reject; -#endif -reject: - ERROR("streams are not available, disconnecting\n"); - qemu_bh_schedule(dev->device_reject_bh); - return -1; -} - -static void usbredir_free_streams(USBDevice *udev, USBEndpoint **eps, - int nr_eps) -{ -#if USBREDIR_VERSION >= 0x000700 - USBRedirDevice *dev = USB_REDIRECT(udev); - struct usb_redir_free_bulk_streams_header free_streams; - int i; - - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_bulk_streams)) { - return; - } - - free_streams.endpoints = 0; - for (i = 0; i < nr_eps; i++) { - free_streams.endpoints |= 1 << USBEP2I(eps[i]); - } - usbredirparser_send_free_bulk_streams(dev->parser, 0, &free_streams); - usbredirparser_do_write(dev->parser); -#endif -} - -/* - * Close events can be triggered by usbredirparser_do_write which gets called - * from within the USBDevice data / control packet callbacks and doing a - * usb_detach from within these callbacks is not a good idea. - * - * So we use a bh handler to take care of close events. - */ -static void usbredir_chardev_close_bh(void *opaque) -{ - USBRedirDevice *dev = opaque; - - qemu_bh_cancel(dev->device_reject_bh); - usbredir_device_disconnect(dev); - - if (dev->parser) { - DPRINTF("destroying usbredirparser\n"); - usbredirparser_destroy(dev->parser); - dev->parser = NULL; - } - if (dev->watch) { - g_source_remove(dev->watch); - dev->watch = 0; - } -} - -static void usbredir_create_parser(USBRedirDevice *dev) -{ - uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; - int flags = 0; - - DPRINTF("creating usbredirparser\n"); - - dev->parser = qemu_oom_check(usbredirparser_create()); - dev->parser->priv = dev; - dev->parser->log_func = usbredir_log; - dev->parser->read_func = usbredir_read; - dev->parser->write_func = usbredir_write; - dev->parser->hello_func = usbredir_hello; - dev->parser->device_connect_func = usbredir_device_connect; - dev->parser->device_disconnect_func = usbredir_device_disconnect; - dev->parser->interface_info_func = usbredir_interface_info; - dev->parser->ep_info_func = usbredir_ep_info; - dev->parser->configuration_status_func = usbredir_configuration_status; - dev->parser->alt_setting_status_func = usbredir_alt_setting_status; - dev->parser->iso_stream_status_func = usbredir_iso_stream_status; - dev->parser->interrupt_receiving_status_func = - usbredir_interrupt_receiving_status; - dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; - dev->parser->bulk_receiving_status_func = usbredir_bulk_receiving_status; - dev->parser->control_packet_func = usbredir_control_packet; - dev->parser->bulk_packet_func = usbredir_bulk_packet; - dev->parser->iso_packet_func = usbredir_iso_packet; - dev->parser->interrupt_packet_func = usbredir_interrupt_packet; - dev->parser->buffered_bulk_packet_func = usbredir_buffered_bulk_packet; - dev->read_buf = NULL; - dev->read_buf_size = 0; - - usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); - usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); - usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); - usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); - usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length); - usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving); -#if USBREDIR_VERSION >= 0x000700 - usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams); -#endif - - if (runstate_check(RUN_STATE_INMIGRATE)) { - flags |= usbredirparser_fl_no_hello; - } - usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, - flags); - usbredirparser_do_write(dev->parser); -} - -static void usbredir_reject_device(USBRedirDevice *dev) -{ - 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); - } -} - -/* - * We may need to reject the device when the hcd calls alloc_streams, doing - * an usb_detach from within a hcd call is not a good idea, hence this bh. - */ -static void usbredir_device_reject_bh(void *opaque) -{ - USBRedirDevice *dev = opaque; - - usbredir_reject_device(dev); -} - -static void usbredir_do_attach(void *opaque) -{ - USBRedirDevice *dev = opaque; - Error *local_err = NULL; - - /* In order to work properly with XHCI controllers we need these caps */ - if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !( - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size) && - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_32bits_bulk_length) && - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_64bits_ids))) { - ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n"); - usbredir_reject_device(dev); - return; - } - - usb_device_attach(&dev->dev, &local_err); - if (local_err) { - error_report_err(local_err); - WARNING("rejecting device due to speed mismatch\n"); - usbredir_reject_device(dev); - } -} - -/* - * chardev callbacks - */ - -static int usbredir_chardev_can_read(void *opaque) -{ - USBRedirDevice *dev = opaque; - - if (!dev->parser) { - WARNING("chardev_can_read called on non open chardev!\n"); - return 0; - } - - /* Don't read new data from the chardev until our state is fully synced */ - if (!runstate_check(RUN_STATE_RUNNING)) { - return 0; - } - - /* usbredir_parser_do_read will consume *all* data we give it */ - return 1024 * 1024; -} - -static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) -{ - USBRedirDevice *dev = opaque; - - /* No recursion allowed! */ - assert(dev->read_buf == NULL); - - dev->read_buf = buf; - dev->read_buf_size = size; - - usbredirparser_do_read(dev->parser); - /* Send any acks, etc. which may be queued now */ - usbredirparser_do_write(dev->parser); -} - -static void usbredir_chardev_event(void *opaque, int event) -{ - USBRedirDevice *dev = opaque; - - switch (event) { - case CHR_EVENT_OPENED: - DPRINTF("chardev open\n"); - /* Make sure any pending closes are handled (no-op if none pending) */ - usbredir_chardev_close_bh(dev); - qemu_bh_cancel(dev->chardev_close_bh); - usbredir_create_parser(dev); - break; - case CHR_EVENT_CLOSED: - DPRINTF("chardev close\n"); - qemu_bh_schedule(dev->chardev_close_bh); - break; - } -} - -/* - * init + destroy - */ - -static void usbredir_vm_state_change(void *priv, int running, RunState state) -{ - USBRedirDevice *dev = priv; - - if (state == RUN_STATE_RUNNING && dev->parser != NULL) { - usbredirparser_do_write(dev->parser); /* Flush any pending writes */ - } -} - -static void usbredir_init_endpoints(USBRedirDevice *dev) -{ - int i; - - usb_ep_init(&dev->dev); - memset(dev->endpoint, 0, sizeof(dev->endpoint)); - for (i = 0; i < MAX_ENDPOINTS; i++) { - dev->endpoint[i].dev = dev; - QTAILQ_INIT(&dev->endpoint[i].bufpq); - } -} - -static void usbredir_realize(USBDevice *udev, Error **errp) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - int i; - - if (dev->cs == NULL) { - error_setg(errp, QERR_MISSING_PARAMETER, "chardev"); - return; - } - - if (dev->filter_str) { - i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|", - &dev->filter_rules, - &dev->filter_rules_count); - if (i) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "filter", - "a usb device filter string"); - return; - } - } - - dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); - dev->device_reject_bh = qemu_bh_new(usbredir_device_reject_bh, dev); - dev->attach_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, usbredir_do_attach, dev); - - packet_id_queue_init(&dev->cancelled, dev, "cancelled"); - packet_id_queue_init(&dev->already_in_flight, dev, "already-in-flight"); - usbredir_init_endpoints(dev); - - /* We'll do the attach once we receive the speed from the usb-host */ - udev->auto_attach = 0; - - /* Will be cleared during setup when we find conflicts */ - dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; - - /* Let the backend know we are ready */ - qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, - usbredir_chardev_read, usbredir_chardev_event, dev); - - qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev); -} - -static void usbredir_cleanup_device_queues(USBRedirDevice *dev) -{ - int i; - - packet_id_queue_empty(&dev->cancelled); - packet_id_queue_empty(&dev->already_in_flight); - for (i = 0; i < MAX_ENDPOINTS; i++) { - usbredir_free_bufpq(dev, I2EP(i)); - } -} - -static void usbredir_handle_destroy(USBDevice *udev) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - - qemu_chr_delete(dev->cs); - dev->cs = NULL; - /* Note must be done after qemu_chr_close, as that causes a close event */ - qemu_bh_delete(dev->chardev_close_bh); - qemu_bh_delete(dev->device_reject_bh); - - timer_del(dev->attach_timer); - timer_free(dev->attach_timer); - - usbredir_cleanup_device_queues(dev); - - if (dev->parser) { - usbredirparser_destroy(dev->parser); - } - if (dev->watch) { - g_source_remove(dev->watch); - } - - free(dev->filter_rules); -} - -static int usbredir_check_filter(USBRedirDevice *dev) -{ - if (dev->interface_info.interface_count == NO_INTERFACE_INFO) { - ERROR("No interface info for device\n"); - goto error; - } - - if (dev->filter_rules) { - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_connect_device_version)) { - ERROR("Device filter specified and peer does not have the " - "connect_device_version capability\n"); - goto error; - } - - if (usbredirfilter_check( - dev->filter_rules, - dev->filter_rules_count, - dev->device_info.device_class, - dev->device_info.device_subclass, - dev->device_info.device_protocol, - dev->interface_info.interface_class, - dev->interface_info.interface_subclass, - dev->interface_info.interface_protocol, - dev->interface_info.interface_count, - dev->device_info.vendor_id, - dev->device_info.product_id, - dev->device_info.device_version_bcd, - 0) != 0) { - goto error; - } - } - - return 0; - -error: - usbredir_reject_device(dev); - return -1; -} - -static void usbredir_check_bulk_receiving(USBRedirDevice *dev) -{ - int i, j, quirks; - - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_bulk_receiving)) { - return; - } - - for (i = EP2I(USB_DIR_IN); i < MAX_ENDPOINTS; i++) { - dev->endpoint[i].bulk_receiving_enabled = 0; - } - for (i = 0; i < dev->interface_info.interface_count; i++) { - quirks = usb_get_quirks(dev->device_info.vendor_id, - dev->device_info.product_id, - dev->interface_info.interface_class[i], - dev->interface_info.interface_subclass[i], - dev->interface_info.interface_protocol[i]); - if (!(quirks & USB_QUIRK_BUFFER_BULK_IN)) { - continue; - } - if (quirks & USB_QUIRK_IS_FTDI) { - dev->buffered_bulk_in_complete = - usbredir_buffered_bulk_in_complete_ftdi; - } else { - dev->buffered_bulk_in_complete = - usbredir_buffered_bulk_in_complete_raw; - } - - for (j = EP2I(USB_DIR_IN); j < MAX_ENDPOINTS; j++) { - if (dev->endpoint[j].interface == - dev->interface_info.interface[i] && - dev->endpoint[j].type == USB_ENDPOINT_XFER_BULK && - dev->endpoint[j].max_packet_size != 0) { - dev->endpoint[j].bulk_receiving_enabled = 1; - /* - * With buffering pipelining is not necessary. Also packet - * combining and bulk in buffering don't play nice together! - */ - I2USBEP(dev, j)->pipeline = false; - break; /* Only buffer for the first ep of each intf */ - } - } - } -} - -/* - * usbredirparser packet complete callbacks - */ - -static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p, - int status) -{ - switch (status) { - case usb_redir_success: - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - break; - case usb_redir_stall: - p->status = USB_RET_STALL; - break; - case usb_redir_cancelled: - /* - * When the usbredir-host unredirects a device, it will report a status - * of cancelled for all pending packets, followed by a disconnect msg. - */ - p->status = USB_RET_IOERROR; - break; - case usb_redir_inval: - WARNING("got invalid param error from usb-host?\n"); - p->status = USB_RET_IOERROR; - break; - case usb_redir_babble: - p->status = USB_RET_BABBLE; - break; - case usb_redir_ioerror: - case usb_redir_timeout: - default: - p->status = USB_RET_IOERROR; - } -} - -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, - struct usb_redir_device_connect_header *device_connect) -{ - USBRedirDevice *dev = priv; - const char *speed; - - if (timer_pending(dev->attach_timer) || dev->dev.attached) { - ERROR("Received device connect while already connected\n"); - return; - } - - switch (device_connect->speed) { - case usb_redir_speed_low: - speed = "low speed"; - dev->dev.speed = USB_SPEED_LOW; - dev->compatible_speedmask &= ~USB_SPEED_MASK_FULL; - dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH; - break; - case usb_redir_speed_full: - speed = "full speed"; - dev->dev.speed = USB_SPEED_FULL; - dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH; - break; - case usb_redir_speed_high: - speed = "high speed"; - dev->dev.speed = USB_SPEED_HIGH; - break; - case usb_redir_speed_super: - speed = "super speed"; - dev->dev.speed = USB_SPEED_SUPER; - break; - default: - speed = "unknown speed"; - dev->dev.speed = USB_SPEED_FULL; - } - - if (usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_connect_device_version)) { - INFO("attaching %s device %04x:%04x version %d.%d class %02x\n", - speed, device_connect->vendor_id, device_connect->product_id, - ((device_connect->device_version_bcd & 0xf000) >> 12) * 10 + - ((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); - } else { - INFO("attaching %s device %04x:%04x class %02x\n", speed, - device_connect->vendor_id, device_connect->product_id, - device_connect->device_class); - } - - dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask; - dev->device_info = *device_connect; - - if (usbredir_check_filter(dev)) { - WARNING("Device %04x:%04x rejected by device filter, not attaching\n", - device_connect->vendor_id, device_connect->product_id); - return; - } - - usbredir_check_bulk_receiving(dev); - timer_mod(dev->attach_timer, dev->next_attach_time); -} - -static void usbredir_device_disconnect(void *priv) -{ - USBRedirDevice *dev = priv; - - /* Stop any pending attaches */ - timer_del(dev->attach_timer); - - if (dev->dev.attached) { - DPRINTF("detaching device\n"); - usb_device_detach(&dev->dev); - /* - * Delay next usb device attach to give the guest a chance to see - * see the detach / attach in case of quick close / open succession - */ - dev->next_attach_time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 200; - } - - /* Reset state so that the next dev connected starts with a clean slate */ - usbredir_cleanup_device_queues(dev); - usbredir_init_endpoints(dev); - dev->interface_info.interface_count = NO_INTERFACE_INFO; - dev->dev.addr = 0; - dev->dev.speed = 0; - dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; -} - -static void usbredir_interface_info(void *priv, - struct usb_redir_interface_info_header *interface_info) -{ - USBRedirDevice *dev = priv; - - dev->interface_info = *interface_info; - - /* - * If we receive interface info after the device has already been - * connected (ie on a set_config), re-check interface dependent things. - */ - if (timer_pending(dev->attach_timer) || dev->dev.attached) { - usbredir_check_bulk_receiving(dev); - if (usbredir_check_filter(dev)) { - ERROR("Device no longer matches filter after interface info " - "change, disconnecting!\n"); - } - } -} - -static void usbredir_mark_speed_incompatible(USBRedirDevice *dev, int speed) -{ - dev->compatible_speedmask &= ~(1 << speed); - dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask; -} - -static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep) -{ - if (uep->type != USB_ENDPOINT_XFER_BULK) { - return; - } - if (uep->pid == USB_TOKEN_OUT) { - uep->pipeline = true; - } - if (uep->pid == USB_TOKEN_IN && uep->max_packet_size != 0 && - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_32bits_bulk_length)) { - uep->pipeline = true; - } -} - -static void usbredir_setup_usb_eps(USBRedirDevice *dev) -{ - struct USBEndpoint *usb_ep; - int i; - - for (i = 0; i < MAX_ENDPOINTS; i++) { - usb_ep = I2USBEP(dev, i); - usb_ep->type = dev->endpoint[i].type; - usb_ep->ifnum = dev->endpoint[i].interface; - usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; - usb_ep->max_streams = dev->endpoint[i].max_streams; - usbredir_set_pipeline(dev, usb_ep); - } -} - -static void usbredir_ep_info(void *priv, - struct usb_redir_ep_info_header *ep_info) -{ - USBRedirDevice *dev = priv; - int i; - - for (i = 0; i < MAX_ENDPOINTS; i++) { - dev->endpoint[i].type = ep_info->type[i]; - dev->endpoint[i].interval = ep_info->interval[i]; - dev->endpoint[i].interface = ep_info->interface[i]; - if (usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size)) { - dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i]; - } -#if USBREDIR_VERSION >= 0x000700 - if (usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_bulk_streams)) { - dev->endpoint[i].max_streams = ep_info->max_streams[i]; - } -#endif - switch (dev->endpoint[i].type) { - case usb_redir_type_invalid: - break; - case usb_redir_type_iso: - usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); - usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH); - /* Fall through */ - case usb_redir_type_interrupt: - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size) || - ep_info->max_packet_size[i] > 64) { - usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); - } - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size) || - ep_info->max_packet_size[i] > 1024) { - usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH); - } - if (dev->endpoint[i].interval == 0) { - ERROR("Received 0 interval for isoc or irq endpoint\n"); - usbredir_reject_device(dev); - return; - } - /* Fall through */ - case usb_redir_type_control: - case usb_redir_type_bulk: - DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i), - dev->endpoint[i].type, dev->endpoint[i].interface); - break; - default: - ERROR("Received invalid endpoint type\n"); - usbredir_reject_device(dev); - return; - } - } - /* The new ep info may have caused a speed incompatibility, recheck */ - if (dev->dev.attached && - !(dev->dev.port->speedmask & dev->dev.speedmask)) { - ERROR("Device no longer matches speed after endpoint info change, " - "disconnecting!\n"); - usbredir_reject_device(dev); - return; - } - usbredir_setup_usb_eps(dev); - usbredir_check_bulk_receiving(dev); -} - -static void usbredir_configuration_status(void *priv, uint64_t id, - struct usb_redir_configuration_status_header *config_status) -{ - USBRedirDevice *dev = priv; - USBPacket *p; - - DPRINTF("set config status %d config %d id %"PRIu64"\n", - config_status->status, config_status->configuration, id); - - p = usbredir_find_packet_by_id(dev, 0, id); - if (p) { - if (dev->dev.setup_buf[0] & USB_DIR_IN) { - dev->dev.data_buf[0] = config_status->configuration; - p->actual_length = 1; - } - usbredir_handle_status(dev, p, config_status->status); - usb_generic_async_ctrl_complete(&dev->dev, p); - } -} - -static void usbredir_alt_setting_status(void *priv, uint64_t id, - struct usb_redir_alt_setting_status_header *alt_setting_status) -{ - USBRedirDevice *dev = priv; - USBPacket *p; - - DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n", - alt_setting_status->status, alt_setting_status->interface, - alt_setting_status->alt, id); - - p = usbredir_find_packet_by_id(dev, 0, id); - if (p) { - if (dev->dev.setup_buf[0] & USB_DIR_IN) { - dev->dev.data_buf[0] = alt_setting_status->alt; - p->actual_length = 1; - } - usbredir_handle_status(dev, p, alt_setting_status->status); - usb_generic_async_ctrl_complete(&dev->dev, p); - } -} - -static void usbredir_iso_stream_status(void *priv, uint64_t id, - struct usb_redir_iso_stream_status_header *iso_stream_status) -{ - USBRedirDevice *dev = priv; - uint8_t ep = iso_stream_status->endpoint; - - DPRINTF("iso status %d ep %02X id %"PRIu64"\n", iso_stream_status->status, - ep, id); - - if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) { - return; - } - - dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status; - if (iso_stream_status->status == usb_redir_stall) { - DPRINTF("iso stream stopped by peer ep %02X\n", ep); - dev->endpoint[EP2I(ep)].iso_started = 0; - } -} - -static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, - struct usb_redir_interrupt_receiving_status_header - *interrupt_receiving_status) -{ - USBRedirDevice *dev = priv; - uint8_t ep = interrupt_receiving_status->endpoint; - - DPRINTF("interrupt recv status %d ep %02X id %"PRIu64"\n", - interrupt_receiving_status->status, ep, id); - - if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) { - return; - } - - dev->endpoint[EP2I(ep)].interrupt_error = - interrupt_receiving_status->status; - if (interrupt_receiving_status->status == usb_redir_stall) { - DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep); - dev->endpoint[EP2I(ep)].interrupt_started = 0; - } -} - -static void usbredir_bulk_streams_status(void *priv, uint64_t id, - struct usb_redir_bulk_streams_status_header *bulk_streams_status) -{ -#if USBREDIR_VERSION >= 0x000700 - USBRedirDevice *dev = priv; - - if (bulk_streams_status->status == usb_redir_success) { - DPRINTF("bulk streams status %d eps %08x\n", - bulk_streams_status->status, bulk_streams_status->endpoints); - } else { - ERROR("bulk streams %s failed status %d eps %08x\n", - (bulk_streams_status->no_streams == 0) ? "free" : "alloc", - bulk_streams_status->status, bulk_streams_status->endpoints); - ERROR("usb-redir-host does not provide streams, disconnecting\n"); - usbredir_reject_device(dev); - } -#endif -} - -static void usbredir_bulk_receiving_status(void *priv, uint64_t id, - struct usb_redir_bulk_receiving_status_header *bulk_receiving_status) -{ - USBRedirDevice *dev = priv; - uint8_t ep = bulk_receiving_status->endpoint; - - DPRINTF("bulk recv status %d ep %02X id %"PRIu64"\n", - bulk_receiving_status->status, ep, id); - - if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].bulk_receiving_started) { - return; - } - - if (bulk_receiving_status->status == usb_redir_stall) { - DPRINTF("bulk receiving stopped by peer ep %02X\n", ep); - dev->endpoint[EP2I(ep)].bulk_receiving_started = 0; - } -} - -static void usbredir_control_packet(void *priv, uint64_t id, - struct usb_redir_control_packet_header *control_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - USBPacket *p; - int len = control_packet->length; - - DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status, - len, id); - - /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices - * to work redirected to a not superspeed capable hcd */ - if (dev->dev.speed == USB_SPEED_SUPER && - !((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER)) && - control_packet->requesttype == 0x80 && - control_packet->request == 6 && - control_packet->value == 0x100 && control_packet->index == 0 && - data_len >= 18 && data[7] == 9) { - data[7] = 64; - } - - p = usbredir_find_packet_by_id(dev, 0, id); - if (p) { - usbredir_handle_status(dev, p, control_packet->status); - if (data_len > 0) { - usbredir_log_data(dev, "ctrl data in:", data, data_len); - if (data_len > sizeof(dev->dev.data_buf)) { - ERROR("ctrl buffer too small (%d > %zu)\n", - data_len, sizeof(dev->dev.data_buf)); - p->status = USB_RET_STALL; - data_len = len = sizeof(dev->dev.data_buf); - } - memcpy(dev->dev.data_buf, data, data_len); - } - p->actual_length = len; - usb_generic_async_ctrl_complete(&dev->dev, p); - } - free(data); -} - -static void usbredir_bulk_packet(void *priv, uint64_t id, - struct usb_redir_bulk_packet_header *bulk_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - uint8_t ep = bulk_packet->endpoint; - int len = (bulk_packet->length_high << 16) | bulk_packet->length; - USBPacket *p; - - DPRINTF("bulk-in status %d ep %02X stream %u len %d id %"PRIu64"\n", - bulk_packet->status, ep, bulk_packet->stream_id, len, id); - - p = usbredir_find_packet_by_id(dev, ep, id); - if (p) { - size_t size = usb_packet_size(p); - usbredir_handle_status(dev, p, bulk_packet->status); - if (data_len > 0) { - usbredir_log_data(dev, "bulk data in:", data, data_len); - if (data_len > size) { - ERROR("bulk got more data then requested (%d > %zd)\n", - data_len, p->iov.size); - p->status = USB_RET_BABBLE; - data_len = len = size; - } - usb_packet_copy(p, data, data_len); - } - p->actual_length = len; - if (p->pid == USB_TOKEN_IN && p->ep->pipeline) { - usb_combined_input_packet_complete(&dev->dev, p); - } else { - usb_packet_complete(&dev->dev, p); - } - } - free(data); -} - -static void usbredir_iso_packet(void *priv, uint64_t id, - struct usb_redir_iso_packet_header *iso_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - uint8_t ep = iso_packet->endpoint; - - DPRINTF2("iso-in status %d ep %02X len %d id %"PRIu64"\n", - iso_packet->status, ep, data_len, id); - - if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) { - ERROR("received iso packet for non iso endpoint %02X\n", ep); - free(data); - return; - } - - if (dev->endpoint[EP2I(ep)].iso_started == 0) { - DPRINTF("received iso packet for non started stream ep %02X\n", ep); - free(data); - return; - } - - /* bufp_alloc also adds the packet to the ep queue */ - bufp_alloc(dev, data, data_len, iso_packet->status, ep, data); -} - -static void usbredir_interrupt_packet(void *priv, uint64_t id, - struct usb_redir_interrupt_packet_header *interrupt_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - uint8_t ep = interrupt_packet->endpoint; - - DPRINTF("interrupt-in status %d ep %02X len %d id %"PRIu64"\n", - interrupt_packet->status, ep, data_len, id); - - if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) { - ERROR("received int packet for non interrupt endpoint %02X\n", ep); - free(data); - return; - } - - if (ep & USB_DIR_IN) { - if (dev->endpoint[EP2I(ep)].interrupt_started == 0) { - DPRINTF("received int packet while not started ep %02X\n", ep); - free(data); - return; - } - - if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) { - usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f), 0); - } - - /* bufp_alloc also adds the packet to the ep queue */ - bufp_alloc(dev, data, data_len, interrupt_packet->status, ep, data); - } else { - /* - * We report output interrupt packets as completed directly upon - * submission, so all we can do here if one failed is warn. - */ - if (interrupt_packet->status) { - WARNING("interrupt output failed status %d ep %02X id %"PRIu64"\n", - interrupt_packet->status, ep, id); - } - } -} - -static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, - struct usb_redir_buffered_bulk_packet_header *buffered_bulk_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - uint8_t status, ep = buffered_bulk_packet->endpoint; - void *free_on_destroy; - int i, len; - - DPRINTF("buffered-bulk-in status %d ep %02X len %d id %"PRIu64"\n", - buffered_bulk_packet->status, ep, data_len, id); - - if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_BULK) { - ERROR("received buffered-bulk packet for non bulk ep %02X\n", ep); - free(data); - return; - } - - if (dev->endpoint[EP2I(ep)].bulk_receiving_started == 0) { - DPRINTF("received buffered-bulk packet on not started ep %02X\n", ep); - free(data); - return; - } - - /* Data must be in maxp chunks for buffered_bulk_add_*_data_to_packet */ - len = dev->endpoint[EP2I(ep)].max_packet_size; - status = usb_redir_success; - free_on_destroy = NULL; - for (i = 0; i < data_len; i += len) { - int r; - if (len >= (data_len - i)) { - len = data_len - i; - status = buffered_bulk_packet->status; - free_on_destroy = data; - } - /* bufp_alloc also adds the packet to the ep queue */ - r = bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); - if (r) { - break; - } - } - - if (dev->endpoint[EP2I(ep)].pending_async_packet) { - USBPacket *p = dev->endpoint[EP2I(ep)].pending_async_packet; - dev->endpoint[EP2I(ep)].pending_async_packet = NULL; - usbredir_buffered_bulk_in_complete(dev, p, ep); - usb_packet_complete(&dev->dev, p); - } -} - -/* - * Migration code - */ - -static void usbredir_pre_save(void *priv) -{ - USBRedirDevice *dev = priv; - - usbredir_fill_already_in_flight(dev); -} - -static int usbredir_post_load(void *priv, int version_id) -{ - USBRedirDevice *dev = priv; - - if (dev->parser == NULL) { - return 0; - } - - switch (dev->device_info.speed) { - case usb_redir_speed_low: - dev->dev.speed = USB_SPEED_LOW; - break; - case usb_redir_speed_full: - dev->dev.speed = USB_SPEED_FULL; - break; - case usb_redir_speed_high: - dev->dev.speed = USB_SPEED_HIGH; - break; - case usb_redir_speed_super: - dev->dev.speed = USB_SPEED_SUPER; - break; - default: - dev->dev.speed = USB_SPEED_FULL; - } - dev->dev.speedmask = (1 << dev->dev.speed); - - usbredir_setup_usb_eps(dev); - usbredir_check_bulk_receiving(dev); - - return 0; -} - -/* For usbredirparser migration */ -static void usbredir_put_parser(QEMUFile *f, void *priv, size_t unused) -{ - USBRedirDevice *dev = priv; - uint8_t *data; - int len; - - if (dev->parser == NULL) { - qemu_put_be32(f, 0); - return; - } - - usbredirparser_serialize(dev->parser, &data, &len); - qemu_oom_check(data); - - qemu_put_be32(f, len); - qemu_put_buffer(f, data, len); - - free(data); -} - -static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused) -{ - USBRedirDevice *dev = priv; - uint8_t *data; - int len, ret; - - len = qemu_get_be32(f); - if (len == 0) { - return 0; - } - - /* - * If our chardev is not open already at this point the usbredir connection - * has been broken (non seamless migration, or restore from disk). - * - * In this case create a temporary parser to receive the migration data, - * and schedule the close_bh to report the device as disconnected to the - * guest and to destroy the parser again. - */ - if (dev->parser == NULL) { - WARNING("usb-redir connection broken during migration\n"); - usbredir_create_parser(dev); - qemu_bh_schedule(dev->chardev_close_bh); - } - - data = g_malloc(len); - qemu_get_buffer(f, data, len); - - ret = usbredirparser_unserialize(dev->parser, data, len); - - g_free(data); - - return ret; -} - -static const VMStateInfo usbredir_parser_vmstate_info = { - .name = "usb-redir-parser", - .put = usbredir_put_parser, - .get = usbredir_get_parser, -}; - - -/* For buffered packets (iso/irq) queue migration */ -static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused) -{ - struct endp_data *endp = priv; - USBRedirDevice *dev = endp->dev; - struct buf_packet *bufp; - int len, i = 0; - - qemu_put_be32(f, endp->bufpq_size); - QTAILQ_FOREACH(bufp, &endp->bufpq, next) { - len = bufp->len - bufp->offset; - DPRINTF("put_bufpq %d/%d len %d status %d\n", i + 1, endp->bufpq_size, - len, bufp->status); - qemu_put_be32(f, len); - qemu_put_be32(f, bufp->status); - qemu_put_buffer(f, bufp->data + bufp->offset, len); - i++; - } - assert(i == endp->bufpq_size); -} - -static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused) -{ - struct endp_data *endp = priv; - USBRedirDevice *dev = endp->dev; - struct buf_packet *bufp; - int i; - - endp->bufpq_size = qemu_get_be32(f); - for (i = 0; i < endp->bufpq_size; i++) { - bufp = g_new(struct buf_packet, 1); - bufp->len = qemu_get_be32(f); - bufp->status = qemu_get_be32(f); - bufp->offset = 0; - bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */ - bufp->free_on_destroy = bufp->data; - qemu_get_buffer(f, bufp->data, bufp->len); - QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next); - DPRINTF("get_bufpq %d/%d len %d status %d\n", i + 1, endp->bufpq_size, - bufp->len, bufp->status); - } - return 0; -} - -static const VMStateInfo usbredir_ep_bufpq_vmstate_info = { - .name = "usb-redir-bufpq", - .put = usbredir_put_bufpq, - .get = usbredir_get_bufpq, -}; - - -/* For endp_data migration */ -static bool usbredir_bulk_receiving_needed(void *priv) -{ - struct endp_data *endp = priv; - - return endp->bulk_receiving_started; -} - -static const VMStateDescription usbredir_bulk_receiving_vmstate = { - .name = "usb-redir-ep/bulk-receiving", - .version_id = 1, - .minimum_version_id = 1, - .needed = usbredir_bulk_receiving_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(bulk_receiving_started, struct endp_data), - VMSTATE_END_OF_LIST() - } -}; - -static bool usbredir_stream_needed(void *priv) -{ - struct endp_data *endp = priv; - - return endp->max_streams; -} - -static const VMStateDescription usbredir_stream_vmstate = { - .name = "usb-redir-ep/stream-state", - .version_id = 1, - .minimum_version_id = 1, - .needed = usbredir_stream_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT32(max_streams, struct endp_data), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription usbredir_ep_vmstate = { - .name = "usb-redir-ep", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(type, struct endp_data), - VMSTATE_UINT8(interval, struct endp_data), - VMSTATE_UINT8(interface, struct endp_data), - VMSTATE_UINT16(max_packet_size, struct endp_data), - VMSTATE_UINT8(iso_started, struct endp_data), - VMSTATE_UINT8(iso_error, struct endp_data), - VMSTATE_UINT8(interrupt_started, struct endp_data), - VMSTATE_UINT8(interrupt_error, struct endp_data), - VMSTATE_UINT8(bufpq_prefilled, struct endp_data), - VMSTATE_UINT8(bufpq_dropping_packets, struct endp_data), - { - .name = "bufpq", - .version_id = 0, - .field_exists = NULL, - .size = 0, - .info = &usbredir_ep_bufpq_vmstate_info, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_INT32(bufpq_target_size, struct endp_data), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &usbredir_bulk_receiving_vmstate, - &usbredir_stream_vmstate, - NULL - } -}; - - -/* For PacketIdQueue migration */ -static void usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused) -{ - struct PacketIdQueue *q = priv; - USBRedirDevice *dev = q->dev; - struct PacketIdQueueEntry *e; - int remain = q->size; - - DPRINTF("put_packet_id_q %s size %d\n", q->name, q->size); - qemu_put_be32(f, q->size); - QTAILQ_FOREACH(e, &q->head, next) { - qemu_put_be64(f, e->id); - remain--; - } - assert(remain == 0); -} - -static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused) -{ - struct PacketIdQueue *q = priv; - USBRedirDevice *dev = q->dev; - int i, size; - uint64_t id; - - size = qemu_get_be32(f); - DPRINTF("get_packet_id_q %s size %d\n", q->name, size); - for (i = 0; i < size; i++) { - id = qemu_get_be64(f); - packet_id_queue_add(q, id); - } - assert(q->size == size); - return 0; -} - -static const VMStateInfo usbredir_ep_packet_id_q_vmstate_info = { - .name = "usb-redir-packet-id-q", - .put = usbredir_put_packet_id_q, - .get = usbredir_get_packet_id_q, -}; - -static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = { - .name = "usb-redir-packet-id-queue", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - { - .name = "queue", - .version_id = 0, - .field_exists = NULL, - .size = 0, - .info = &usbredir_ep_packet_id_q_vmstate_info, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_END_OF_LIST() - } -}; - - -/* For usb_redir_device_connect_header migration */ -static const VMStateDescription usbredir_device_info_vmstate = { - .name = "usb-redir-device-info", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(speed, struct usb_redir_device_connect_header), - VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header), - VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header), - VMSTATE_UINT8(device_protocol, struct usb_redir_device_connect_header), - VMSTATE_UINT16(vendor_id, struct usb_redir_device_connect_header), - VMSTATE_UINT16(product_id, struct usb_redir_device_connect_header), - VMSTATE_UINT16(device_version_bcd, - struct usb_redir_device_connect_header), - VMSTATE_END_OF_LIST() - } -}; - - -/* For usb_redir_interface_info_header migration */ -static const VMStateDescription usbredir_interface_info_vmstate = { - .name = "usb-redir-interface-info", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(interface_count, - struct usb_redir_interface_info_header), - VMSTATE_UINT8_ARRAY(interface, - struct usb_redir_interface_info_header, 32), - VMSTATE_UINT8_ARRAY(interface_class, - struct usb_redir_interface_info_header, 32), - VMSTATE_UINT8_ARRAY(interface_subclass, - struct usb_redir_interface_info_header, 32), - VMSTATE_UINT8_ARRAY(interface_protocol, - struct usb_redir_interface_info_header, 32), - VMSTATE_END_OF_LIST() - } -}; - - -/* And finally the USBRedirDevice vmstate itself */ -static const VMStateDescription usbredir_vmstate = { - .name = "usb-redir", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = usbredir_pre_save, - .post_load = usbredir_post_load, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, USBRedirDevice), - VMSTATE_TIMER_PTR(attach_timer, USBRedirDevice), - { - .name = "parser", - .version_id = 0, - .field_exists = NULL, - .size = 0, - .info = &usbredir_parser_vmstate_info, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_STRUCT_ARRAY(endpoint, USBRedirDevice, MAX_ENDPOINTS, 1, - usbredir_ep_vmstate, struct endp_data), - VMSTATE_STRUCT(cancelled, USBRedirDevice, 1, - usbredir_ep_packet_id_queue_vmstate, - struct PacketIdQueue), - VMSTATE_STRUCT(already_in_flight, USBRedirDevice, 1, - usbredir_ep_packet_id_queue_vmstate, - struct PacketIdQueue), - VMSTATE_STRUCT(device_info, USBRedirDevice, 1, - usbredir_device_info_vmstate, - struct usb_redir_device_connect_header), - VMSTATE_STRUCT(interface_info, USBRedirDevice, 1, - usbredir_interface_info_vmstate, - struct usb_redir_interface_info_header), - VMSTATE_END_OF_LIST() - } -}; - -static Property usbredir_properties[] = { - DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), - DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning), - DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usbredir_class_initfn(ObjectClass *klass, void *data) -{ - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - uc->realize = usbredir_realize; - uc->product_desc = "USB Redirection Device"; - uc->handle_destroy = usbredir_handle_destroy; - uc->cancel_packet = usbredir_cancel_packet; - uc->handle_reset = usbredir_handle_reset; - uc->handle_data = usbredir_handle_data; - uc->handle_control = usbredir_handle_control; - uc->flush_ep_queue = usbredir_flush_ep_queue; - uc->ep_stopped = usbredir_ep_stopped; - uc->alloc_streams = usbredir_alloc_streams; - uc->free_streams = usbredir_free_streams; - dc->vmsd = &usbredir_vmstate; - dc->props = usbredir_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static void usbredir_instance_init(Object *obj) -{ - USBDevice *udev = USB_DEVICE(obj); - USBRedirDevice *dev = USB_REDIRECT(udev); - - device_add_bootindex_property(obj, &dev->bootindex, - "bootindex", NULL, - &udev->qdev, NULL); -} - -static const TypeInfo usbredir_dev_info = { - .name = TYPE_USB_REDIR, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBRedirDevice), - .class_init = usbredir_class_initfn, - .instance_init = usbredir_instance_init, -}; - -static void usbredir_register_types(void) -{ - type_register_static(&usbredir_dev_info); -} - -type_init(usbredir_register_types) diff --git a/qemu/hw/usb/tusb6010.c b/qemu/hw/usb/tusb6010.c deleted file mode 100644 index 8f593a6fd..000000000 --- a/qemu/hw/usb/tusb6010.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * Texas Instruments TUSB6010 emulation. - * Based on reverse-engineering of a linux driver. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski <andrew@openedhand.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/arm/omap.h" -#include "hw/irq.h" -#include "hw/devices.h" -#include "hw/sysbus.h" - -#define TYPE_TUSB6010 "tusb6010" -#define TUSB(obj) OBJECT_CHECK(TUSBState, (obj), TYPE_TUSB6010) - -typedef struct TUSBState { - SysBusDevice parent_obj; - - MemoryRegion iomem[2]; - qemu_irq irq; - MUSBState *musb; - QEMUTimer *otg_timer; - QEMUTimer *pwr_timer; - - int power; - uint32_t scratch; - uint16_t test_reset; - uint32_t prcm_config; - uint32_t prcm_mngmt; - uint16_t otg_status; - uint32_t dev_config; - int host_mode; - uint32_t intr; - uint32_t intr_ok; - uint32_t mask; - uint32_t usbip_intr; - uint32_t usbip_mask; - uint32_t gpio_intr; - uint32_t gpio_mask; - uint32_t gpio_config; - uint32_t dma_intr; - uint32_t dma_mask; - uint32_t dma_map; - uint32_t dma_config; - uint32_t ep0_config; - uint32_t rx_config[15]; - uint32_t tx_config[15]; - uint32_t wkup_mask; - uint32_t pullup[2]; - uint32_t control_config; - uint32_t otg_timer_val; -} TUSBState; - -#define TUSB_DEVCLOCK 60000000 /* 60 MHz */ - -#define TUSB_VLYNQ_CTRL 0x004 - -/* Mentor Graphics OTG core registers. */ -#define TUSB_BASE_OFFSET 0x400 - -/* FIFO registers, 32-bit. */ -#define TUSB_FIFO_BASE 0x600 - -/* Device System & Control registers, 32-bit. */ -#define TUSB_SYS_REG_BASE 0x800 - -#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000) -#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16) -#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15) -#define TUSB_DEV_CONF_SOFT_ID (1 << 1) -#define TUSB_DEV_CONF_ID_SEL (1 << 0) - -#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004) -#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008) -#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24) -#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23) -#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19) -#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18) -#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17) -#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16) -#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15) -#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14) -#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13) -#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12) -#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11) -#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10) -#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9) -#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7) -#define TUSB_PHY_OTG_CTRL_PD (1 << 6) -#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5) -#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4) -#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3) -#define TUSB_PHY_OTG_CTRL_RESET (1 << 2) -#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1) -#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0) - -/* OTG status register */ -#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c) -#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8) -#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7) -#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6) -#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) -#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) -#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) -#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2) -#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) -#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) -#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) - -#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010) -#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31) -#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff) -#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014) - -/* PRCM configuration register */ -#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018) -#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24) -#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16) - -/* PRCM management register */ -#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c) -#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25) -#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24) -#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20) -#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19) -#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18) -#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17) -#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10) -#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9) -#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8) -#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4) -#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3) -#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2) -#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1) -#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0) - -/* Wake-up source clear and mask registers */ -#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020) -#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028) -#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c) -#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13) -#define TUSB_PRCM_WGPIO_7 (1 << 12) -#define TUSB_PRCM_WGPIO_6 (1 << 11) -#define TUSB_PRCM_WGPIO_5 (1 << 10) -#define TUSB_PRCM_WGPIO_4 (1 << 9) -#define TUSB_PRCM_WGPIO_3 (1 << 8) -#define TUSB_PRCM_WGPIO_2 (1 << 7) -#define TUSB_PRCM_WGPIO_1 (1 << 6) -#define TUSB_PRCM_WGPIO_0 (1 << 5) -#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */ -#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */ -#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */ -#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */ -#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */ - -#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030) -#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034) -#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038) -#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c) -#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040) -#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044) -#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048) -#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c) -#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050) -#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054) -#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058) -#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c) -#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060) -#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064) -#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068) -#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c) - -/* NOR flash interrupt source registers */ -#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070) -#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074) -#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078) -#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c) -#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24) -#define TUSB_INT_SRC_USB_IP_CORE (1 << 17) -#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16) -#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15) -#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14) -#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13) -#define TUSB_INT_SRC_DEV_READY (1 << 12) -#define TUSB_INT_SRC_USB_IP_TX (1 << 9) -#define TUSB_INT_SRC_USB_IP_RX (1 << 8) -#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7) -#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6) -#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5) -#define TUSB_INT_SRC_USB_IP_CONN (1 << 4) -#define TUSB_INT_SRC_USB_IP_SOF (1 << 3) -#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2) -#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1) -#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0) - -#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080) -#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084) -#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100) -#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104) -#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108) -#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c) -#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148) -#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c) -#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188) -#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4) -#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8) -#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8) - -#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8) -#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc) - -/* Device System & Control register bitfields */ -#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18) -#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17) -#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16) -#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24) -#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26) -#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20) -#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16) -#define TUSB_EP0_CONFIG_SW_EN (1 << 8) -#define TUSB_EP0_CONFIG_DIR_TX (1 << 7) -#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f) -#define TUSB_EP_CONFIG_SW_EN (1 << 31) -#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) -#define TUSB_PROD_TEST_RESET_VAL 0xa596 - -static void tusb_intr_update(TUSBState *s) -{ - if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY) - qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok); - else - qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok); -} - -static void tusb_usbip_intr_update(TUSBState *s) -{ - /* TX interrupt in the MUSB */ - if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_TX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_TX; - - /* RX interrupt in the MUSB */ - if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_RX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_RX; - - /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */ - - tusb_intr_update(s); -} - -static void tusb_dma_intr_update(TUSBState *s) -{ - if (s->dma_intr & ~s->dma_mask) - s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE; - else - s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE; - - tusb_intr_update(s); -} - -static void tusb_gpio_intr_update(TUSBState *s) -{ - /* TODO: How is this signalled? */ -} - -static uint32_t tusb_async_readb(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[0](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readh(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[1](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readw(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - uint32_t ret; - - switch (offset) { - case TUSB_DEV_CONF: - return s->dev_config; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[2](s->musb, offset & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return 0x00; /* TODO */ - - case TUSB_DEV_OTG_STAT: - ret = s->otg_status; -#if 0 - if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) - ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; -#endif - return ret; - case TUSB_DEV_OTG_TIMER: - return s->otg_timer_val; - - case TUSB_PRCM_REV: - return 0x20; - case TUSB_PRCM_CONF: - return s->prcm_config; - case TUSB_PRCM_MNGMT: - return s->prcm_mngmt; - case TUSB_PRCM_WAKEUP_SOURCE: - case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */ - return 0x00000000; - case TUSB_PRCM_WAKEUP_MASK: - return s->wkup_mask; - - case TUSB_PULLUP_1_CTRL: - return s->pullup[0]; - case TUSB_PULLUP_2_CTRL: - return s->pullup[1]; - - case TUSB_INT_CTRL_REV: - return 0x20; - case TUSB_INT_CTRL_CONF: - return s->control_config; - - case TUSB_USBIP_INT_SRC: - case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */ - case TUSB_USBIP_INT_CLEAR: - return s->usbip_intr; - case TUSB_USBIP_INT_MASK: - return s->usbip_mask; - - case TUSB_DMA_INT_SRC: - case TUSB_DMA_INT_SET: /* TODO: What do these two return? */ - case TUSB_DMA_INT_CLEAR: - return s->dma_intr; - case TUSB_DMA_INT_MASK: - return s->dma_mask; - - case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */ - case TUSB_GPIO_INT_SET: - case TUSB_GPIO_INT_CLEAR: - return s->gpio_intr; - case TUSB_GPIO_INT_MASK: - return s->gpio_mask; - - case TUSB_INT_SRC: - case TUSB_INT_SRC_SET: /* TODO: What do these two return? */ - case TUSB_INT_SRC_CLEAR: - return s->intr; - case TUSB_INT_MASK: - return s->mask; - - case TUSB_GPIO_REV: - return 0x30; - case TUSB_GPIO_CONF: - return s->gpio_config; - - case TUSB_DMA_CTRL_REV: - return 0x30; - case TUSB_DMA_REQ_CONF: - return s->dma_config; - case TUSB_EP0_CONF: - return s->ep0_config; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - return s->tx_config[epnum]; - case TUSB_DMA_EP_MAP: - return s->dma_map; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - return s->rx_config[epnum]; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return 0x00000000; /* TODO */ - case TUSB_WAIT_COUNT: - return 0x00; /* TODO */ - - case TUSB_SCRATCH_PAD: - return s->scratch; - - case TUSB_PROD_TEST_RESET: - return s->test_reset; - - /* DIE IDs */ - case TUSB_DIDR1_LO: - return 0xa9453c59; - case TUSB_DIDR1_HI: - return 0x54059adf; - } - - printf("%s: unknown register at %03x\n", __FUNCTION__, offset); - return 0; -} - -static void tusb_async_writeb(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[0](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writeh(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[1](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writew(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - - switch (offset) { - case TUSB_VLYNQ_CTRL: - break; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[2](s->musb, offset & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - case TUSB_DEV_CONF: - s->dev_config = value; - s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE); - if (value & TUSB_DEV_CONF_PROD_TEST_MODE) - hw_error("%s: Product Test mode not allowed\n", __FUNCTION__); - break; - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return; /* TODO */ - case TUSB_DEV_OTG_TIMER: - s->otg_timer_val = value; - if (value & TUSB_DEV_OTG_TIMER_ENABLE) - timer_mod(s->otg_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(TUSB_DEV_OTG_TIMER_VAL(value), - NANOSECONDS_PER_SECOND, TUSB_DEVCLOCK)); - else - timer_del(s->otg_timer); - break; - - case TUSB_PRCM_CONF: - s->prcm_config = value; - break; - case TUSB_PRCM_MNGMT: - s->prcm_mngmt = value; - break; - case TUSB_PRCM_WAKEUP_CLEAR: - break; - case TUSB_PRCM_WAKEUP_MASK: - s->wkup_mask = value; - break; - - case TUSB_PULLUP_1_CTRL: - s->pullup[0] = value; - break; - case TUSB_PULLUP_2_CTRL: - s->pullup[1] = value; - break; - case TUSB_INT_CTRL_CONF: - s->control_config = value; - tusb_intr_update(s); - break; - - case TUSB_USBIP_INT_SET: - s->usbip_intr |= value; - tusb_usbip_intr_update(s); - break; - case TUSB_USBIP_INT_CLEAR: - s->usbip_intr &= ~value; - tusb_usbip_intr_update(s); - musb_core_intr_clear(s->musb, ~value); - break; - case TUSB_USBIP_INT_MASK: - s->usbip_mask = value; - tusb_usbip_intr_update(s); - break; - - case TUSB_DMA_INT_SET: - s->dma_intr |= value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_CLEAR: - s->dma_intr &= ~value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_MASK: - s->dma_mask = value; - tusb_dma_intr_update(s); - break; - - case TUSB_GPIO_INT_SET: - s->gpio_intr |= value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_CLEAR: - s->gpio_intr &= ~value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_MASK: - s->gpio_mask = value; - tusb_gpio_intr_update(s); - break; - - case TUSB_INT_SRC_SET: - s->intr |= value; - tusb_intr_update(s); - break; - case TUSB_INT_SRC_CLEAR: - s->intr &= ~value; - tusb_intr_update(s); - break; - case TUSB_INT_MASK: - s->mask = value; - tusb_intr_update(s); - break; - - case TUSB_GPIO_CONF: - s->gpio_config = value; - break; - case TUSB_DMA_REQ_CONF: - s->dma_config = value; - break; - case TUSB_EP0_CONF: - s->ep0_config = value & 0x1ff; - musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value), - value & TUSB_EP0_CONFIG_DIR_TX); - break; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - s->tx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1); - break; - case TUSB_DMA_EP_MAP: - s->dma_map = value; - break; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - s->rx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0); - break; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return; /* TODO */ - case TUSB_WAIT_COUNT: - return; /* TODO */ - - case TUSB_SCRATCH_PAD: - s->scratch = value; - break; - - case TUSB_PROD_TEST_RESET: - s->test_reset = value; - break; - - default: - printf("%s: unknown register at %03x\n", __FUNCTION__, offset); - return; - } -} - -static const MemoryRegionOps tusb_async_ops = { - .old_mmio = { - .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, }, - .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void tusb_otg_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - s->otg_timer_val = 0; - s->intr |= TUSB_INT_SRC_OTG_TIMEOUT; - tusb_intr_update(s); -} - -static void tusb_power_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - if (s->power) { - s->intr_ok = ~0; - tusb_intr_update(s); - } -} - -static void tusb_musb_core_intr(void *opaque, int source, int level) -{ - TUSBState *s = (TUSBState *) opaque; - uint16_t otg_status = s->otg_status; - - switch (source) { - case musb_set_vbus: - if (level) - otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID; - else - otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; - - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */ - if (s->otg_status != otg_status) { - s->otg_status = otg_status; - s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG; - tusb_intr_update(s); - } - break; - - case musb_set_session: - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */ - if (level) { - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END; - } else { - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END; - } - - /* XXX: some IRQ or anything? */ - break; - - case musb_irq_tx: - case musb_irq_rx: - s->usbip_intr = musb_core_intr_get(s->musb); - /* Fall through. */ - default: - if (level) - s->intr |= 1 << source; - else - s->intr &= ~(1 << source); - tusb_intr_update(s); - break; - } -} - -static void tusb6010_power(TUSBState *s, int on) -{ - if (!on) { - s->power = 0; - } else if (!s->power && on) { - s->power = 1; - /* Pull the interrupt down after TUSB6010 comes up. */ - s->intr_ok = 0; - tusb_intr_update(s); - timer_mod(s->pwr_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND / 2); - } -} - -static void tusb6010_irq(void *opaque, int source, int level) -{ - if (source) { - tusb_musb_core_intr(opaque, source - 1, level); - } else { - tusb6010_power(opaque, level); - } -} - -static void tusb6010_reset(DeviceState *dev) -{ - TUSBState *s = TUSB(dev); - int i; - - s->test_reset = TUSB_PROD_TEST_RESET_VAL; - s->host_mode = 0; - s->dev_config = 0; - s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */ - s->power = 0; - s->mask = 0xffffffff; - s->intr = 0x00000000; - s->otg_timer_val = 0; - s->scratch = 0; - s->prcm_config = 0; - s->prcm_mngmt = 0; - s->intr_ok = 0; - s->usbip_intr = 0; - s->usbip_mask = 0; - s->gpio_intr = 0; - s->gpio_mask = 0; - s->gpio_config = 0; - s->dma_intr = 0; - s->dma_mask = 0; - s->dma_map = 0; - s->dma_config = 0; - s->ep0_config = 0; - s->wkup_mask = 0; - s->pullup[0] = s->pullup[1] = 0; - s->control_config = 0; - for (i = 0; i < 15; i++) { - s->rx_config[i] = s->tx_config[i] = 0; - } - musb_reset(s->musb); -} - -static int tusb6010_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - TUSBState *s = TUSB(dev); - - s->otg_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_otg_tick, s); - s->pwr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_power_tick, s); - memory_region_init_io(&s->iomem[1], OBJECT(s), &tusb_async_ops, s, - "tusb-async", UINT32_MAX); - sysbus_init_mmio(sbd, &s->iomem[0]); - sysbus_init_mmio(sbd, &s->iomem[1]); - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_in(dev, tusb6010_irq, musb_irq_max + 1); - s->musb = musb_init(dev, 1); - return 0; -} - -static void tusb6010_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = tusb6010_init; - dc->reset = tusb6010_reset; -} - -static const TypeInfo tusb6010_info = { - .name = TYPE_TUSB6010, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(TUSBState), - .class_init = tusb6010_class_init, -}; - -static void tusb6010_register_types(void) -{ - type_register_static(&tusb6010_info); -} - -type_init(tusb6010_register_types) |