aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/block/xen_disk.c7
-rw-r--r--hw/display/Makefile.objs3
-rw-r--r--hw/display/qxl.c12
-rw-r--r--hw/display/qxl.h3
-rw-r--r--hw/i386/pc_sysfw.c5
-rw-r--r--hw/scsi/lsi53c895a.c51
-rw-r--r--hw/scsi/scsi-bus.c2
-rw-r--r--hw/scsi/spapr_vscsi.c195
-rw-r--r--hw/scsi/srp.h7
-rw-r--r--hw/scsi/virtio-scsi.c1
-rw-r--r--hw/usb/combined-packet.c1
-rw-r--r--hw/usb/core.c3
-rw-r--r--hw/usb/hcd-xhci.c45
-rw-r--r--hw/usb/host-bsd.c639
-rw-r--r--hw/usb/host-linux.c1911
15 files changed, 257 insertions, 2628 deletions
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index 668cc069ff..f35fc5944a 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -809,10 +809,15 @@ static int blk_connect(struct XenDevice *xendev)
xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
blkdev->bs = bdrv_new(blkdev->dev);
if (blkdev->bs) {
+ Error *local_err = NULL;
BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto,
readonly);
if (bdrv_open(blkdev->bs,
- blkdev->filename, NULL, qflags, drv) != 0) {
+ blkdev->filename, NULL, qflags, drv, &local_err) != 0)
+ {
+ xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
+ error_get_pretty(local_err));
+ error_free(local_err);
bdrv_unref(blkdev->bs);
blkdev->bs = NULL;
}
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 6e9fb3b695..540df82600 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -31,5 +31,4 @@ obj-$(CONFIG_TCX) += tcx.o
obj-$(CONFIG_VGA) += vga.o
-common-obj-$(CONFIG_QXL) += qxl-logger.o qxl-render.o
-obj-$(CONFIG_QXL) += qxl.o
+common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index c50e285e21..ee2db0da1a 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -313,9 +313,7 @@ static ram_addr_t qxl_rom_size(void)
sizeof(qxl_modes);
uint32_t rom_size = 8192; /* two pages */
- required_rom_size = MAX(required_rom_size, TARGET_PAGE_SIZE);
- required_rom_size = msb_mask(required_rom_size * 2 - 1);
- assert(required_rom_size <= rom_size);
+ QEMU_BUILD_BUG_ON(required_rom_size > rom_size);
return rom_size;
}
@@ -364,7 +362,7 @@ static void init_qxl_rom(PCIQXLDevice *d)
num_pages = d->vga.vram_size;
num_pages -= ram_header_size;
num_pages -= surface0_area_size;
- num_pages = num_pages / TARGET_PAGE_SIZE;
+ num_pages = num_pages / QXL_PAGE_SIZE;
rom->draw_area_offset = cpu_to_le32(0);
rom->surface0_area_size = cpu_to_le32(surface0_area_size);
@@ -416,9 +414,8 @@ static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr)
intptr_t offset;
offset = ptr - base;
- offset &= ~(TARGET_PAGE_SIZE-1);
assert(offset < qxl->vga.vram_size);
- qxl_set_dirty(&qxl->vga.vram, offset, offset + TARGET_PAGE_SIZE);
+ qxl_set_dirty(&qxl->vga.vram, offset, offset + 3);
}
/* can be called from spice server thread context */
@@ -528,7 +525,8 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
info->num_memslots = NUM_MEMSLOTS;
info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
info->internal_groupslot_id = 0;
- info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS;
+ info->qxl_ram_size =
+ le32_to_cpu(qxl->shadow_rom.num_pages) << QXL_PAGE_BITS;
info->n_surfaces = qxl->ssd.num_surfaces;
}
diff --git a/hw/display/qxl.h b/hw/display/qxl.h
index 8e9b0c299e..84f0182383 100644
--- a/hw/display/qxl.h
+++ b/hw/display/qxl.h
@@ -27,6 +27,9 @@ enum qxl_mode {
#define QXL_NUM_DIRTY_RECTS 64
+#define QXL_PAGE_BITS 12
+#define QXL_PAGE_SIZE (1 << QXL_PAGE_BITS);
+
typedef struct PCIQXLDevice {
PCIDevice pci;
SimpleSpiceDisplay ssd;
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index 8246a1bdd4..e917c83540 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -53,10 +53,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory,
flash_size = memory_region_size(flash_mem);
/* map the last 128KB of the BIOS in ISA space */
- isa_bios_size = flash_size;
- if (isa_bios_size > (128 * 1024)) {
- isa_bios_size = 128 * 1024;
- }
+ isa_bios_size = MIN(flash_size, 128 * 1024);
isa_bios = g_malloc(sizeof(*isa_bios));
memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size);
vmstate_register_ram_global(isa_bios);
diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c
index 0c36842729..36e5f50360 100644
--- a/hw/scsi/lsi53c895a.c
+++ b/hw/scsi/lsi53c895a.c
@@ -7,8 +7,11 @@
* This code is licensed under the LGPL.
*/
-/* ??? Need to check if the {read,write}[wl] routines work properly on
- big-endian targets. */
+/* Note:
+ * LSI53C810 emulation is incorrect, in the sense that it supports
+ * features added in later evolutions. This should not be a problem,
+ * as well-behaved operating systems will not try to use them.
+ */
#include <assert.h>
@@ -278,6 +281,7 @@ typedef struct {
uint32_t script_ram[2048];
} LSIState;
+#define TYPE_LSI53C810 "lsi53c810"
#define TYPE_LSI53C895A "lsi53c895a"
#define LSI53C895A(obj) \
@@ -998,12 +1002,6 @@ bad:
s->msg_action = 0;
}
-/* Sign extend a 24-bit value. */
-static inline int32_t sxt24(int32_t n)
-{
- return (n << 8) >> 8;
-}
-
#define LSI_BUF_SIZE 4096
static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
{
@@ -1083,7 +1081,7 @@ again:
/* Table indirect addressing. */
/* 32-bit Table indirect */
- offset = sxt24(addr);
+ offset = sextract32(addr, 0, 24);
pci_dma_read(pci_dev, s->dsa + offset, buf, 8);
/* byte count is stored in bits 0:23 only */
s->dbc = cpu_to_le32(buf[0]) & 0xffffff;
@@ -1183,13 +1181,13 @@ again:
uint32_t id;
if (insn & (1 << 25)) {
- id = read_dword(s, s->dsa + sxt24(insn));
+ id = read_dword(s, s->dsa + sextract32(insn, 0, 24));
} else {
id = insn;
}
id = (id >> 16) & 0xf;
if (insn & (1 << 26)) {
- addr = s->dsp + sxt24(addr);
+ addr = s->dsp + sextract32(addr, 0, 24);
}
s->dnad = addr;
switch (opcode) {
@@ -1385,7 +1383,7 @@ again:
if (cond == jmp) {
if (insn & (1 << 23)) {
/* Relative address. */
- addr = s->dsp + sxt24(addr);
+ addr = s->dsp + sextract32(addr, 0, 24);
}
switch ((insn >> 27) & 7) {
case 0: /* Jump */
@@ -1438,7 +1436,7 @@ again:
int i;
if (insn & (1 << 28)) {
- addr = s->dsa + sxt24(addr);
+ addr = s->dsa + sextract32(addr, 0, 24);
}
n = (insn & 7);
reg = (insn >> 16) & 0xff;
@@ -1521,7 +1519,7 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
used for diagnostics, so should be ok. */
return 0;
case 0xc: /* DSTAT */
- tmp = s->dstat | 0x80;
+ tmp = s->dstat | LSI_DSTAT_DFE;
if ((s->istat0 & LSI_ISTAT0_INTF) == 0)
s->dstat = 0;
lsi_update_irq(s);
@@ -1705,8 +1703,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
s->sxfer = val;
break;
case 0x06: /* SDID */
- if ((val & 0xf) != (s->ssid & 0xf))
+ if ((s->ssid & 0x80) && (val & 0xf) != (s->ssid & 0xf)) {
BADF("Destination ID does not match SSID\n");
+ }
s->sdid = val & 0xf;
break;
case 0x07: /* GPREG0 */
@@ -1748,6 +1747,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
case 0x17: /* MBOX1 */
s->mbox1 = val;
break;
+ case 0x18: /* CTEST0 */
+ /* nothing to do */
+ break;
case 0x1a: /* CTEST2 */
s->ctest2 = val & LSI_CTEST2_PCICIE;
break;
@@ -1876,8 +1878,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
int shift;
n = (offset - 0x58) >> 2;
shift = (offset & 3) * 8;
- s->scratch[n] &= ~(0xff << shift);
- s->scratch[n] |= (val & 0xff) << shift;
+ s->scratch[n] = deposit32(s->scratch[n], shift, 8, val);
} else {
BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val);
}
@@ -2113,7 +2114,7 @@ static int lsi_scsi_init(PCIDevice *dev)
"lsi-io", 256);
pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io);
- pci_register_bar(dev, 1, 0, &s->mmio_io);
+ pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio_io);
pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io);
QTAILQ_INIT(&s->queue);
@@ -2151,9 +2152,23 @@ static const TypeInfo lsi_info = {
.class_init = lsi_class_init,
};
+static void lsi53c810_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->device_id = PCI_DEVICE_ID_LSI_53C810;
+}
+
+static TypeInfo lsi53c810_info = {
+ .name = TYPE_LSI53C810,
+ .parent = TYPE_LSI53C895A,
+ .class_init = lsi53c810_class_init,
+};
+
static void lsi53c895a_register_types(void)
{
type_register_static(&lsi_info);
+ type_register_static(&lsi53c810_info);
}
type_init(lsi53c895a_register_types)
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 5cd6137046..4d36841d40 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -224,7 +224,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
if (object_property_find(OBJECT(dev), "removable", NULL)) {
qdev_prop_set_bit(dev, "removable", removable);
}
- if (serial) {
+ if (serial && object_property_find(OBJECT(dev), "serial", NULL)) {
qdev_prop_set_string(dev, "serial", serial);
}
if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index b2fcd4b3e3..2a26042701 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -117,6 +117,20 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
return NULL;
}
+static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag)
+{
+ vscsi_req *req;
+ int i;
+
+ for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+ req = &s->reqs[i];
+ if (req->iu.srp.cmd.tag == srp_tag) {
+ return req;
+ }
+ }
+ return NULL;
+}
+
static void vscsi_put_req(vscsi_req *req)
{
if (req->sreq != NULL) {
@@ -755,40 +769,91 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
{
union viosrp_iu *iu = &req->iu;
- int fn;
+ vscsi_req *tmpreq;
+ int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE;
+ SCSIDevice *d;
+ uint64_t tag = iu->srp.rsp.tag;
+ uint8_t sol_not = iu->srp.cmd.sol_not;
fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n",
iu->srp.tsk_mgmt.tsk_mgmt_func);
- switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
-#if 0 /* We really don't deal with these for now */
- case SRP_TSK_ABORT_TASK:
- fn = ABORT_TASK;
- break;
- case SRP_TSK_ABORT_TASK_SET:
- fn = ABORT_TASK_SET;
- break;
- case SRP_TSK_CLEAR_TASK_SET:
- fn = CLEAR_TASK_SET;
- break;
- case SRP_TSK_LUN_RESET:
- fn = LOGICAL_UNIT_RESET;
- break;
- case SRP_TSK_CLEAR_ACA:
- fn = CLEAR_ACA;
- break;
-#endif
- default:
- fn = 0;
+ d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun);
+ if (!d) {
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ } else {
+ switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
+ case SRP_TSK_ABORT_TASK:
+ if (d->lun != lun) {
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ break;
+ }
+
+ tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag);
+ if (tmpreq && tmpreq->sreq) {
+ assert(tmpreq->sreq->hba_private);
+ scsi_req_cancel(tmpreq->sreq);
+ }
+ break;
+
+ case SRP_TSK_LUN_RESET:
+ if (d->lun != lun) {
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ break;
+ }
+
+ qdev_reset_all(&d->qdev);
+ break;
+
+ case SRP_TSK_ABORT_TASK_SET:
+ case SRP_TSK_CLEAR_TASK_SET:
+ if (d->lun != lun) {
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ break;
+ }
+
+ for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+ tmpreq = &s->reqs[i];
+ if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) {
+ continue;
+ }
+ if (!tmpreq->active || !tmpreq->sreq) {
+ continue;
+ }
+ assert(tmpreq->sreq->hba_private);
+ scsi_req_cancel(tmpreq->sreq);
+ }
+ break;
+
+ case SRP_TSK_CLEAR_ACA:
+ resp = SRP_TSK_MGMT_NOT_SUPPORTED;
+ break;
+
+ default:
+ resp = SRP_TSK_MGMT_FIELDS_INVALID;
+ break;
+ }
}
- if (fn) {
- /* XXX Send/Handle target task management */
- ;
+
+ /* Compose the response here as */
+ memset(iu, 0, sizeof(struct srp_rsp) + 4);
+ iu->srp.rsp.opcode = SRP_RSP;
+ iu->srp.rsp.req_lim_delta = cpu_to_be32(1);
+ iu->srp.rsp.tag = tag;
+ iu->srp.rsp.flags |= SRP_RSP_FLAG_RSPVALID;
+ iu->srp.rsp.resp_data_len = cpu_to_be32(4);
+ if (resp) {
+ iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2;
} else {
- vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0);
- vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+ iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1;
}
- return !fn;
+
+ iu->srp.rsp.status = GOOD;
+ iu->srp.rsp.data[3] = resp;
+
+ vscsi_send_iu(s, req, sizeof(iu->srp.rsp) + 4, VIOSRP_SRP_FORMAT);
+
+ return 1;
}
static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req)
@@ -858,29 +923,97 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req)
return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT);
}
+static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req)
+{
+ struct viosrp_capabilities *vcap;
+ struct capabilities cap = { };
+ uint16_t len, req_len;
+ uint64_t buffer;
+ int rc;
+
+ vcap = &req->iu.mad.capabilities;
+ req_len = len = be16_to_cpu(vcap->common.length);
+ buffer = be64_to_cpu(vcap->buffer);
+ if (len > sizeof(cap)) {
+ fprintf(stderr, "vscsi_send_capabilities: capabilities size mismatch !\n");
+
+ /*
+ * Just read and populate the structure that is known.
+ * Zero rest of the structure.
+ */
+ len = sizeof(cap);
+ }
+ rc = spapr_vio_dma_read(&s->vdev, buffer, &cap, len);
+ if (rc) {
+ fprintf(stderr, "vscsi_send_capabilities: DMA read failure !\n");
+ }
+
+ /*
+ * Current implementation does not suppport any migration or
+ * reservation capabilities. Construct the response telling the
+ * guest not to use them.
+ */
+ cap.flags = 0;
+ cap.migration.ecl = 0;
+ cap.reserve.type = 0;
+ cap.migration.common.server_support = 0;
+ cap.reserve.common.server_support = 0;
+
+ rc = spapr_vio_dma_write(&s->vdev, buffer, &cap, len);
+ if (rc) {
+ fprintf(stderr, "vscsi_send_capabilities: DMA write failure !\n");
+ }
+ if (req_len > len) {
+ /*
+ * Being paranoid and lets not worry about the error code
+ * here. Actual write of the cap is done above.
+ */
+ spapr_vio_dma_set(&s->vdev, (buffer + len), 0, (req_len - len));
+ }
+ vcap->common.status = rc ? cpu_to_be32(1) : 0;
+ return vscsi_send_iu(s, req, sizeof(*vcap), VIOSRP_MAD_FORMAT);
+}
+
static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req)
{
union mad_iu *mad = &req->iu.mad;
+ bool request_handled = false;
+ uint64_t retlen = 0;
switch (be32_to_cpu(mad->empty_iu.common.type)) {
case VIOSRP_EMPTY_IU_TYPE:
fprintf(stderr, "Unsupported EMPTY MAD IU\n");
+ retlen = sizeof(mad->empty_iu);
break;
case VIOSRP_ERROR_LOG_TYPE:
fprintf(stderr, "Unsupported ERROR LOG MAD IU\n");
- mad->error_log.common.status = cpu_to_be16(1);
- vscsi_send_iu(s, req, sizeof(mad->error_log), VIOSRP_MAD_FORMAT);
+ retlen = sizeof(mad->error_log);
break;
case VIOSRP_ADAPTER_INFO_TYPE:
vscsi_send_adapter_info(s, req);
+ request_handled = true;
break;
case VIOSRP_HOST_CONFIG_TYPE:
- mad->host_config.common.status = cpu_to_be16(1);
- vscsi_send_iu(s, req, sizeof(mad->host_config), VIOSRP_MAD_FORMAT);
+ retlen = sizeof(mad->host_config);
+ break;
+ case VIOSRP_CAPABILITIES_TYPE:
+ vscsi_send_capabilities(s, req);
+ request_handled = true;
break;
default:
fprintf(stderr, "VSCSI: Unknown MAD type %02x\n",
be32_to_cpu(mad->empty_iu.common.type));
+ /*
+ * PAPR+ says that "The length field is set to the length
+ * of the data structure(s) used in the command".
+ * As we did not recognize the request type, put zero there.
+ */
+ retlen = 0;
+ }
+
+ if (!request_handled) {
+ mad->empty_iu.common.status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED);
+ vscsi_send_iu(s, req, retlen, VIOSRP_MAD_FORMAT);
}
return 1;
diff --git a/hw/scsi/srp.h b/hw/scsi/srp.h
index 5e0cad5c19..d27f31d2d5 100644
--- a/hw/scsi/srp.h
+++ b/hw/scsi/srp.h
@@ -90,6 +90,13 @@ enum {
SRP_REV16A_IB_IO_CLASS = 0x0100
};
+enum {
+ SRP_TSK_MGMT_COMPLETE = 0x00,
+ SRP_TSK_MGMT_FIELDS_INVALID = 0x02,
+ SRP_TSK_MGMT_NOT_SUPPORTED = 0x04,
+ SRP_TSK_MGMT_FAILED = 0x05
+};
+
struct srp_direct_buf {
uint64_t va;
uint32_t key;
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 3bd690d128..26d95a14ec 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -693,6 +693,7 @@ static const TypeInfo virtio_scsi_common_info = {
.name = TYPE_VIRTIO_SCSI_COMMON,
.parent = TYPE_VIRTIO_DEVICE,
.instance_size = sizeof(VirtIOSCSICommon),
+ .abstract = true,
.class_init = virtio_scsi_common_class_init,
};
diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c
index 13f6602ad2..ad77705f8c 100644
--- a/hw/usb/combined-packet.c
+++ b/hw/usb/combined-packet.c
@@ -39,6 +39,7 @@ static void usb_combined_packet_remove(USBCombinedPacket *combined,
p->combined = NULL;
QTAILQ_REMOVE(&combined->packets, p, combined_entry);
if (QTAILQ_EMPTY(&combined->packets)) {
+ qemu_iovec_destroy(&combined->iov);
g_free(combined);
}
}
diff --git a/hw/usb/core.c b/hw/usb/core.c
index 31960c28a8..cf59a1abcf 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -622,6 +622,7 @@ void usb_ep_reset(USBDevice *dev)
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.dev = dev;
dev->ep_ctl.pipeline = false;
for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
@@ -633,6 +634,8 @@ void usb_ep_reset(USBDevice *dev)
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].dev = dev;
dev->ep_out[ep].dev = dev;
dev->ep_in[ep].pipeline = false;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index f02231dc87..469c24d768 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -509,6 +509,8 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
unsigned int epid);
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",
@@ -1138,7 +1140,7 @@ static void xhci_reset_streams(XHCIEPContext *epctx)
static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base)
{
assert(epctx->pstreams == NULL);
- epctx->nr_pstreams = 2 << epctx->max_pstreams;
+ epctx->nr_pstreams = 2 << (epctx->max_pstreams + 1);
epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base);
}
@@ -1245,6 +1247,9 @@ static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci,
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);
@@ -1358,13 +1363,12 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
xferi = epctx->next_xfer;
for (i = 0; i < TD_QUEUE; i++) {
- if (epctx->transfers[xferi].packet.ep) {
- ep = epctx->transfers[xferi].packet.ep;
- }
killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]);
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);
}
@@ -1376,6 +1380,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
{
XHCISlot *slot;
XHCIEPContext *epctx;
+ int i;
trace_usb_xhci_ep_disable(slotid, epid);
assert(slotid >= 1 && slotid <= xhci->numslots);
@@ -1396,6 +1401,10 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
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);
@@ -1696,7 +1705,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
static int xhci_setup_packet(XHCITransfer *xfer)
{
XHCIState *xhci = xfer->xhci;
- USBDevice *dev;
USBEndpoint *ep;
int dir;
@@ -1704,15 +1712,13 @@ static int xhci_setup_packet(XHCITransfer *xfer)
if (xfer->packet.ep) {
ep = xfer->packet.ep;
- dev = ep->dev;
} else {
- if (!xhci->slots[xfer->slotid-1].uport) {
+ ep = xhci_epid_to_usbep(xhci, xfer->slotid, xfer->epid);
+ if (!ep) {
fprintf(stderr, "xhci: slot %d has no device\n",
xfer->slotid);
return -1;
}
- dev = xhci->slots[xfer->slotid-1].uport->dev;
- ep = usb_ep_get(dev, dir, xfer->epid >> 1);
}
xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
@@ -1720,7 +1726,7 @@ static int xhci_setup_packet(XHCITransfer *xfer)
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, dev->addr, ep->nr);
+ xfer->packet.pid, ep->dev->addr, ep->nr);
return 0;
}
@@ -2060,9 +2066,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
for (i = 0; i < length; i++) {
assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL));
}
- xfer->xhci = xhci;
- xfer->epid = epid;
- xfer->slotid = slotid;
xfer->streamid = streamid;
if (epid == 1) {
@@ -2075,7 +2078,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
} else {
if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
- ep = xfer->packet.ep;
} else {
if (!xfer->timed_xfer) {
fprintf(stderr, "xhci: error firing data transfer\n");
@@ -2092,6 +2094,8 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
break;
}
}
+
+ ep = xhci_epid_to_usbep(xhci, slotid, epid);
if (ep) {
usb_device_flush_ep_queue(ep->dev, ep);
}
@@ -3321,6 +3325,19 @@ static int xhci_find_epid(USBEndpoint *ep)
}
}
+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)
{
diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c
deleted file mode 100644
index 39f22810b3..0000000000
--- a/hw/usb/host-bsd.c
+++ /dev/null
@@ -1,639 +0,0 @@
-/*
- * BSD host USB redirector
- *
- * Copyright (c) 2006 Lonnie Mendez
- * Portions of code and concepts borrowed from
- * usb-linux.c and libusb's bsd.c and are copyright their respective owners.
- *
- * 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-common.h"
-#include "monitor/monitor.h"
-#include "hw/usb.h"
-
-/* usb.h declares these */
-#undef USB_SPEED_HIGH
-#undef USB_SPEED_FULL
-#undef USB_SPEED_LOW
-
-#include <sys/ioctl.h>
-#ifndef __DragonFly__
-#include <dev/usb/usb.h>
-#else
-#include <bus/usb/usb.h>
-#endif
-
-/* This value has maximum potential at 16.
- * You should also set hw.usb.debug to gain
- * more detailed view.
- */
-//#define DEBUG
-#define UGEN_DEBUG_LEVEL 0
-
-
-typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
- int vendor_id, int product_id,
- const char *product_name, int speed);
-static int usb_host_find_device(int *pbus_num, int *paddr,
- const char *devname);
-
-typedef struct USBHostDevice {
- USBDevice dev;
- int ep_fd[USB_MAX_ENDPOINTS];
- int devfd;
- char devpath[32];
-} USBHostDevice;
-
-
-static int ensure_ep_open(USBHostDevice *dev, int ep, int mode)
-{
- char buf[32];
- int fd;
-
- /* Get the address for this endpoint */
- ep = UE_GET_ADDR(ep);
-
- if (dev->ep_fd[ep] < 0) {
-#if defined(__FreeBSD__) || defined(__DragonFly__)
- snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep);
-#else
- snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep);
-#endif
- /* Try to open it O_RDWR first for those devices which have in and out
- * endpoints with the same address (eg 0x02 and 0x82)
- */
- fd = open(buf, O_RDWR);
- if (fd < 0 && errno == ENXIO)
- fd = open(buf, mode);
- if (fd < 0) {
-#ifdef DEBUG
- printf("ensure_ep_open: failed to open device endpoint %s: %s\n",
- buf, strerror(errno));
-#endif
- }
- dev->ep_fd[ep] = fd;
- }
-
- return dev->ep_fd[ep];
-}
-
-static void ensure_eps_closed(USBHostDevice *dev)
-{
- int epnum = 1;
-
- if (!dev)
- return;
-
- while (epnum < USB_MAX_ENDPOINTS) {
- if (dev->ep_fd[epnum] >= 0) {
- close(dev->ep_fd[epnum]);
- dev->ep_fd[epnum] = -1;
- }
- epnum++;
- }
-}
-
-static void usb_host_handle_reset(USBDevice *dev)
-{
-#if 0
- USBHostDevice *s = (USBHostDevice *)dev;
-#endif
-}
-
-/* XXX:
- * -check device states against transfer requests
- * and return appropriate response
- */
-static void usb_host_handle_control(USBDevice *dev,
- USBPacket *p,
- int request,
- int value,
- int index,
- int length,
- uint8_t *data)
-{
- USBHostDevice *s = (USBHostDevice *)dev;
- struct usb_ctl_request req;
- struct usb_alt_interface aiface;
- int ret, timeout = 50;
-
- if ((request >> 8) == UT_WRITE_DEVICE &&
- (request & 0xff) == UR_SET_ADDRESS) {
-
- /* specific SET_ADDRESS support */
- dev->addr = value;
- } else if ((request >> 8) == UT_WRITE_DEVICE &&
- (request & 0xff) == UR_SET_CONFIG) {
-
- ensure_eps_closed(s); /* can't do this without all eps closed */
-
- ret = ioctl(s->devfd, USB_SET_CONFIG, &value);
- if (ret < 0) {
-#ifdef DEBUG
- printf("handle_control: failed to set configuration - %s\n",
- strerror(errno));
-#endif
- p->status = USB_RET_STALL;
- }
- } else if ((request >> 8) == UT_WRITE_INTERFACE &&
- (request & 0xff) == UR_SET_INTERFACE) {
-
- aiface.uai_interface_index = index;
- aiface.uai_alt_no = value;
-
- ensure_eps_closed(s); /* can't do this without all eps closed */
- ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface);
- if (ret < 0) {
-#ifdef DEBUG
- printf("handle_control: failed to set alternate interface - %s\n",
- strerror(errno));
-#endif
- p->status = USB_RET_STALL;
- }
- } else {
- req.ucr_request.bmRequestType = request >> 8;
- req.ucr_request.bRequest = request & 0xff;
- USETW(req.ucr_request.wValue, value);
- USETW(req.ucr_request.wIndex, index);
- USETW(req.ucr_request.wLength, length);
- req.ucr_data = data;
- req.ucr_flags = USBD_SHORT_XFER_OK;
-
- ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout);
-#if defined(__NetBSD__) || defined(__OpenBSD__)
- if (ret < 0 && errno != EINVAL) {
-#else
- if (ret < 0) {
-#endif
-#ifdef DEBUG
- printf("handle_control: setting timeout failed - %s\n",
- strerror(errno));
-#endif
- }
-
- ret = ioctl(s->devfd, USB_DO_REQUEST, &req);
- /* ugen returns EIO for usbd_do_request_ no matter what
- * happens with the transfer */
- if (ret < 0) {
-#ifdef DEBUG
- printf("handle_control: error after request - %s\n",
- strerror(errno));
-#endif
- p->status = USB_RET_NAK; /* STALL */
- } else {
- p->actual_length = req.ucr_actlen;
- }
- }
-}
-
-static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
-{
- USBHostDevice *s = (USBHostDevice *)dev;
- int ret, fd, mode;
- int one = 1, shortpacket = 0, timeout = 50;
- sigset_t new_mask, old_mask;
- uint8_t devep = p->ep->nr;
-
- /* protect data transfers from SIGALRM signal */
- sigemptyset(&new_mask);
- sigaddset(&new_mask, SIGALRM);
- sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
-
- if (p->pid == USB_TOKEN_IN) {
- devep |= 0x80;
- mode = O_RDONLY;
- shortpacket = 1;
- } else {
- mode = O_WRONLY;
- }
-
- fd = ensure_ep_open(s, devep, mode);
- if (fd < 0) {
- sigprocmask(SIG_SETMASK, &old_mask, NULL);
- p->status = USB_RET_NODEV;
- return;
- }
-
- if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
-#ifdef DEBUG
- printf("handle_data: failed to set timeout - %s\n",
- strerror(errno));
-#endif
- }
-
- if (shortpacket) {
- if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) {
-#ifdef DEBUG
- printf("handle_data: failed to set short xfer mode - %s\n",
- strerror(errno));
-#endif
- sigprocmask(SIG_SETMASK, &old_mask, NULL);
- }
- }
-
- if (p->pid == USB_TOKEN_IN)
- ret = readv(fd, p->iov.iov, p->iov.niov);
- else
- ret = writev(fd, p->iov.iov, p->iov.niov);
-
- sigprocmask(SIG_SETMASK, &old_mask, NULL);
-
- if (ret < 0) {
-#ifdef DEBUG
- printf("handle_data: error after %s data - %s\n",
- pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno));
-#endif
- switch(errno) {
- case ETIMEDOUT:
- case EINTR:
- p->status = USB_RET_NAK;
- break;
- default:
- p->status = USB_RET_STALL;
- }
- } else {
- p->actual_length = ret;
- }
-}
-
-static void usb_host_handle_destroy(USBDevice *opaque)
-{
- USBHostDevice *s = (USBHostDevice *)opaque;
- int i;
-
- for (i = 0; i < USB_MAX_ENDPOINTS; i++)
- if (s->ep_fd[i] >= 0)
- close(s->ep_fd[i]);
-
- if (s->devfd < 0)
- return;
-
- close(s->devfd);
-
- g_free(s);
-}
-
-static int usb_host_initfn(USBDevice *dev)
-{
- dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
- return 0;
-}
-
-USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname)
-{
- struct usb_device_info bus_info, dev_info;
- USBDevice *d = NULL, *ret = NULL;
- USBHostDevice *dev;
- char ctlpath[PATH_MAX + 1];
- char buspath[PATH_MAX + 1];
- int bfd, dfd, bus, address, i;
- int ugendebug = UGEN_DEBUG_LEVEL;
-
- if (usb_host_find_device(&bus, &address, devname) < 0) {
- goto fail;
- }
-
- snprintf(buspath, PATH_MAX, "/dev/usb%d", bus);
-
- bfd = open(buspath, O_RDWR);
- if (bfd < 0) {
-#ifdef DEBUG
- printf("usb_host_device_open: failed to open usb bus - %s\n",
- strerror(errno));
-#endif
- goto fail;
- }
-
- bus_info.udi_addr = address;
- if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) {
-#ifdef DEBUG
- printf("usb_host_device_open: failed to grab bus information - %s\n",
- strerror(errno));
-#endif
- goto fail_bfd;
- }
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
- snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]);
-#else
- snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]);
-#endif
-
- dfd = open(ctlpath, O_RDWR);
- if (dfd < 0) {
- dfd = open(ctlpath, O_RDONLY);
- if (dfd < 0) {
-#ifdef DEBUG
- printf("usb_host_device_open: failed to open usb device %s - %s\n",
- ctlpath, strerror(errno));
-#endif
- }
- goto fail_dfd;
- }
-
- if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) {
-#ifdef DEBUG
- printf("usb_host_device_open: failed to grab device info - %s\n",
- strerror(errno));
-#endif
- goto fail_dfd;
- }
-
- d = usb_create(guest_bus, "usb-host");
- dev = DO_UPCAST(USBHostDevice, dev, d);
-
- if (dev_info.udi_speed == 1) {
- dev->dev.speed = USB_SPEED_LOW - 1;
- dev->dev.speedmask = USB_SPEED_MASK_LOW;
- } else {
- dev->dev.speed = USB_SPEED_FULL - 1;
- dev->dev.speedmask = USB_SPEED_MASK_FULL;
- }
-
- if (strncmp(dev_info.udi_product, "product", 7) != 0) {
- pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
- dev_info.udi_product);
- } else {
- snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
- "host:%s", devname);
- }
-
- pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/");
- pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]);
-
- /* Mark the endpoints as not yet open */
- for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
- dev->ep_fd[i] = -1;
- }
-
- ioctl(dfd, USB_SETDEBUG, &ugendebug);
-
- ret = (USBDevice *)dev;
-
-fail_dfd:
- close(dfd);
-fail_bfd:
- close(bfd);
-fail:
- return ret;
-}
-
-static void usb_host_class_initfn(ObjectClass *klass, void *data)
-{
- USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
- uc->product_desc = "USB Host Device";
- uc->init = usb_host_initfn;
- uc->handle_reset = usb_host_handle_reset;
- uc->handle_control = usb_host_handle_control;
- uc->handle_data = usb_host_handle_data;
- uc->handle_destroy = usb_host_handle_destroy;
-}
-
-static const TypeInfo usb_host_dev_info = {
- .name = "usb-host",
- .parent = TYPE_USB_DEVICE,
- .instance_size = sizeof(USBHostDevice),
- .class_init = usb_host_class_initfn,
-};
-
-static void usb_host_register_types(void)
-{
- type_register_static(&usb_host_dev_info);
-}
-
-type_init(usb_host_register_types)
-
-static int usb_host_scan(void *opaque, USBScanFunc *func)
-{
- struct usb_device_info bus_info;
- struct usb_device_info dev_info;
- uint16_t vendor_id, product_id, class_id, speed;
- int bfd, dfd, bus, address;
- char busbuf[20], devbuf[20], product_name[256];
- int ret = 0;
-
- for (bus = 0; bus < 10; bus++) {
-
- snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus);
- bfd = open(busbuf, O_RDWR);
- if (bfd < 0)
- continue;
-
- for (address = 1; address < 127; address++) {
-
- bus_info.udi_addr = address;
- if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0)
- continue;
-
- /* only list devices that can be used by generic layer */
- if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0)
- continue;
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
- snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]);
-#else
- snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]);
-#endif
-
- dfd = open(devbuf, O_RDONLY);
- if (dfd < 0) {
-#ifdef DEBUG
- printf("usb_host_scan: couldn't open device %s - %s\n", devbuf,
- strerror(errno));
-#endif
- continue;
- }
-
- if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0)
- printf("usb_host_scan: couldn't get device information for %s - %s\n",
- devbuf, strerror(errno));
-
- /* XXX: might need to fixup endianness of word values before copying over */
-
- vendor_id = dev_info.udi_vendorNo;
- product_id = dev_info.udi_productNo;
- class_id = dev_info.udi_class;
- speed = dev_info.udi_speed;
-
- if (strncmp(dev_info.udi_product, "product", 7) != 0)
- pstrcpy(product_name, sizeof(product_name),
- dev_info.udi_product);
- else
- product_name[0] = '\0';
-
- ret = func(opaque, bus, address, class_id, vendor_id,
- product_id, product_name, speed);
-
- close(dfd);
-
- if (ret)
- goto the_end;
- }
-
- close(bfd);
- }
-
-the_end:
- return ret;
-}
-
-typedef struct FindDeviceState {
- int vendor_id;
- int product_id;
- int bus_num;
- int addr;
-} FindDeviceState;
-
-static int usb_host_find_device_scan(void *opaque, int bus_num, int addr,
- int class_id,
- int vendor_id, int product_id,
- const char *product_name, int speed)
-{
- FindDeviceState *s = opaque;
- if (vendor_id == s->vendor_id &&
- product_id == s->product_id) {
- s->bus_num = bus_num;
- s->addr = addr;
- return 1;
- } else {
- return 0;
- }
-}
-
-
-/* the syntax is :
- 'bus.addr' (decimal numbers) or
- 'vendor_id:product_id' (hexa numbers) */
-static int usb_host_find_device(int *pbus_num, int *paddr,
- const char *devname)
-{
- const char *p;
- int ret;
- FindDeviceState fs;
-
- p = strchr(devname, '.');
- if (p) {
- *pbus_num = strtoul(devname, NULL, 0);
- *paddr = strtoul(p + 1, NULL, 0);
- return 0;
- }
- p = strchr(devname, ':');
- if (p) {
- fs.vendor_id = strtoul(devname, NULL, 16);
- fs.product_id = strtoul(p + 1, NULL, 16);
- ret = usb_host_scan(&fs, usb_host_find_device_scan);
- if (ret) {
- *pbus_num = fs.bus_num;
- *paddr = fs.addr;
- return 0;
- }
- }
- return -1;
-}
-
-/**********************/
-/* USB host device info */
-
-struct usb_class_info {
- int class;
- const char *class_name;
-};
-
-static const struct usb_class_info usb_class_info[] = {
- { USB_CLASS_AUDIO, "Audio"},
- { USB_CLASS_COMM, "Communication"},
- { USB_CLASS_HID, "HID"},
- { USB_CLASS_HUB, "Hub" },
- { USB_CLASS_PHYSICAL, "Physical" },
- { USB_CLASS_PRINTER, "Printer" },
- { USB_CLASS_MASS_STORAGE, "Storage" },
- { USB_CLASS_CDC_DATA, "Data" },
- { USB_CLASS_APP_SPEC, "Application Specific" },
- { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
- { USB_CLASS_STILL_IMAGE, "Still Image" },
- { USB_CLASS_CSCID, "Smart Card" },
- { USB_CLASS_CONTENT_SEC, "Content Security" },
- { -1, NULL }
-};
-
-static const char *usb_class_str(uint8_t class)
-{
- const struct usb_class_info *p;
- for (p = usb_class_info; p->class != -1; p++) {
- if (p->class == class)
- break;
- }
- return p->class_name;
-}
-
-static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
- int vendor_id, int product_id,
- const char *product_name,
- int speed)
-{
- const char *class_str, *speed_str;
-
- switch(speed) {
- case USB_SPEED_LOW:
- speed_str = "1.5";
- break;
- case USB_SPEED_FULL:
- speed_str = "12";
- break;
- case USB_SPEED_HIGH:
- speed_str = "480";
- break;
- default:
- speed_str = "?";
- break;
- }
-
- monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n",
- bus_num, addr, speed_str);
- class_str = usb_class_str(class_id);
- if (class_str)
- monitor_printf(mon, " %s:", class_str);
- else
- monitor_printf(mon, " Class %02x:", class_id);
- monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
- if (product_name[0] != '\0')
- monitor_printf(mon, ", %s", product_name);
- monitor_printf(mon, "\n");
-}
-
-static int usb_host_info_device(void *opaque,
- int bus_num, int addr,
- int class_id,
- int vendor_id, int product_id,
- const char *product_name,
- int speed)
-{
- Monitor *mon = opaque;
-
- usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id,
- product_name, speed);
- return 0;
-}
-
-void usb_host_info(Monitor *mon, const QDict *qdict)
-{
- usb_host_scan(mon, usb_host_info_device);
-}
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
deleted file mode 100644
index 65cd3b444c..0000000000
--- a/hw/usb/host-linux.c
+++ /dev/null
@@ -1,1911 +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-common.h"
-#include "qemu/timer.h"
-#include "monitor/monitor.h"
-#include "sysemu/sysemu.h"
-#include "trace.h"
-
-#include <dirent.h>
-#include <sys/ioctl.h>
-
-#include <linux/usbdevice_fs.h>
-#include <linux/version.h>
-#include "hw/usb.h"
-#include "hw/usb/desc.h"
-#include "hw/usb/host.h"
-
-#ifdef CONFIG_USB_LIBUSB
-# define DEVNAME "usb-host-linux"
-#else
-# define DEVNAME "usb-host"
-#endif
-
-/* We redefine it to avoid version problems */
-struct usb_ctrltransfer {
- uint8_t bRequestType;
- uint8_t bRequest;
- uint16_t wValue;
- uint16_t wIndex;
- uint16_t wLength;
- uint32_t timeout;
- void *data;
-};
-
-typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
- int class_id, int vendor_id, int product_id,
- const char *product_name, int speed);
-
-//#define DEBUG
-
-#ifdef DEBUG
-#define DPRINTF printf
-#else
-#define DPRINTF(...)
-#endif
-
-#define PRODUCT_NAME_SZ 32
-#define MAX_PORTLEN 16
-
-/* endpoint association data */
-#define ISO_FRAME_DESC_PER_URB 32
-
-/* devio.c limits single requests to 16k */
-#define MAX_USBFS_BUFFER_SIZE 16384
-
-typedef struct AsyncURB AsyncURB;
-
-struct endp_data {
- uint8_t halted;
- uint8_t iso_started;
- AsyncURB *iso_urb;
- int iso_urb_idx;
- int iso_buffer_used;
- int inflight;
-};
-
-enum USBHostDeviceOptions {
- USB_HOST_OPT_PIPELINE,
-};
-
-typedef struct USBHostDevice {
- USBDevice dev;
- int fd;
- int hub_fd;
- int hub_port;
-
- uint8_t descr[8192];
- int descr_len;
- int closing;
- uint32_t iso_urb_count;
- uint32_t options;
- Notifier exit;
- QEMUBH *bh;
-
- struct endp_data ep_in[USB_MAX_ENDPOINTS];
- struct endp_data ep_out[USB_MAX_ENDPOINTS];
- QLIST_HEAD(, AsyncURB) aurbs;
-
- /* Host side address */
- int bus_num;
- int addr;
- char port[MAX_PORTLEN];
- struct USBAutoFilter match;
- int32_t bootindex;
- int seen, errcount;
-
- QTAILQ_ENTRY(USBHostDevice) next;
-} USBHostDevice;
-
-static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
-
-static int usb_host_close(USBHostDevice *dev);
-static void usb_host_auto_check(void *unused);
-static int usb_host_read_file(char *line, size_t line_size,
- const char *device_file, const char *device_name);
-static void usb_linux_update_endp_table(USBHostDevice *s);
-
-static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p)
-{
- static const int usbfs[] = {
- [USB_ENDPOINT_XFER_CONTROL] = USBDEVFS_URB_TYPE_CONTROL,
- [USB_ENDPOINT_XFER_ISOC] = USBDEVFS_URB_TYPE_ISO,
- [USB_ENDPOINT_XFER_BULK] = USBDEVFS_URB_TYPE_BULK,
- [USB_ENDPOINT_XFER_INT] = USBDEVFS_URB_TYPE_INTERRUPT,
- };
- uint8_t type = p->ep->type;
- assert(type < ARRAY_SIZE(usbfs));
- return usbfs[type];
-}
-
-static int usb_host_do_reset(USBHostDevice *dev)
-{
- struct timeval s, e;
- uint32_t usecs;
- int ret;
-
- gettimeofday(&s, NULL);
- ret = ioctl(dev->fd, USBDEVFS_RESET);
- gettimeofday(&e, NULL);
- usecs = (e.tv_sec - s.tv_sec) * 1000000;
- usecs += e.tv_usec - s.tv_usec;
- if (usecs > 1000000) {
- /* more than a second, something is fishy, broken usb device? */
- fprintf(stderr, "husb: device %d:%d reset took %d.%06d seconds\n",
- dev->bus_num, dev->addr, usecs / 1000000, usecs % 1000000);
- }
- return ret;
-}
-
-static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
-{
- struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
- assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
- assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
- return eps + ep - 1;
-}
-
-static int is_isoc(USBHostDevice *s, int pid, int ep)
-{
- return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC;
-}
-
-static int is_valid(USBHostDevice *s, int pid, int ep)
-{
- return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID;
-}
-
-static int is_halted(USBHostDevice *s, int pid, int ep)
-{
- return get_endp(s, pid, ep)->halted;
-}
-
-static void clear_halt(USBHostDevice *s, int pid, int ep)
-{
- trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep);
- get_endp(s, pid, ep)->halted = 0;
-}
-
-static void set_halt(USBHostDevice *s, int pid, int ep)
-{
- if (ep != 0) {
- trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep);
- get_endp(s, pid, ep)->halted = 1;
- }
-}
-
-static int is_iso_started(USBHostDevice *s, int pid, int ep)
-{
- return get_endp(s, pid, ep)->iso_started;
-}
-
-static void clear_iso_started(USBHostDevice *s, int pid, int ep)
-{
- trace_usb_host_iso_stop(s->bus_num, s->addr, ep);
- get_endp(s, pid, ep)->iso_started = 0;
-}
-
-static void set_iso_started(USBHostDevice *s, int pid, int ep)
-{
- struct endp_data *e = get_endp(s, pid, ep);
-
- trace_usb_host_iso_start(s->bus_num, s->addr, ep);
- if (!e->iso_started) {
- e->iso_started = 1;
- e->inflight = 0;
- }
-}
-
-static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value)
-{
- struct endp_data *e = get_endp(s, pid, ep);
-
- e->inflight += value;
- return e->inflight;
-}
-
-static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb)
-{
- get_endp(s, pid, ep)->iso_urb = iso_urb;
-}
-
-static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep)
-{
- return get_endp(s, pid, ep)->iso_urb;
-}
-
-static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i)
-{
- get_endp(s, pid, ep)->iso_urb_idx = i;
-}
-
-static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep)
-{
- return get_endp(s, pid, ep)->iso_urb_idx;
-}
-
-static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i)
-{
- get_endp(s, pid, ep)->iso_buffer_used = i;
-}
-
-static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
-{
- return get_endp(s, pid, ep)->iso_buffer_used;
-}
-
-/*
- * Async URB state.
- * We always allocate iso packet descriptors even for bulk transfers
- * to simplify allocation and casts.
- */
-struct AsyncURB
-{
- struct usbdevfs_urb urb;
- struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
- USBHostDevice *hdev;
- QLIST_ENTRY(AsyncURB) next;
-
- /* For regular async urbs */
- USBPacket *packet;
- int more; /* large transfer, more urbs follow */
-
- /* For buffered iso handling */
- int iso_frame_idx; /* -1 means in flight */
-};
-
-static AsyncURB *async_alloc(USBHostDevice *s)
-{
- AsyncURB *aurb = g_malloc0(sizeof(AsyncURB));
- aurb->hdev = s;
- QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
- return aurb;
-}
-
-static void async_free(AsyncURB *aurb)
-{
- QLIST_REMOVE(aurb, next);
- g_free(aurb);
-}
-
-static void do_disconnect(USBHostDevice *s)
-{
- usb_host_close(s);
- usb_host_auto_check(NULL);
-}
-
-static void async_complete(void *opaque)
-{
- USBHostDevice *s = opaque;
- AsyncURB *aurb;
- int urbs = 0;
-
- while (1) {
- USBPacket *p;
-
- int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
- if (r < 0) {
- if (errno == EAGAIN) {
- if (urbs > 2) {
- /* indicates possible latency issues */
- trace_usb_host_iso_many_urbs(s->bus_num, s->addr, urbs);
- }
- return;
- }
- if (errno == ENODEV) {
- if (!s->closing) {
- trace_usb_host_disconnect(s->bus_num, s->addr);
- do_disconnect(s);
- }
- return;
- }
-
- perror("USBDEVFS_REAPURBNDELAY");
- return;
- }
-
- DPRINTF("husb: async completed. aurb %p status %d alen %d\n",
- aurb, aurb->urb.status, aurb->urb.actual_length);
-
- /* If this is a buffered iso urb mark it as complete and don't do
- anything else (it is handled further in usb_host_handle_iso_data) */
- if (aurb->iso_frame_idx == -1) {
- int inflight;
- int pid = (aurb->urb.endpoint & USB_DIR_IN) ?
- USB_TOKEN_IN : USB_TOKEN_OUT;
- int ep = aurb->urb.endpoint & 0xf;
- if (aurb->urb.status == -EPIPE) {
- set_halt(s, pid, ep);
- }
- aurb->iso_frame_idx = 0;
- urbs++;
- inflight = change_iso_inflight(s, pid, ep, -1);
- if (inflight == 0 && is_iso_started(s, pid, ep)) {
- /* can be latency issues, or simply end of stream */
- trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, ep);
- }
- continue;
- }
-
- p = aurb->packet;
- trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status,
- aurb->urb.actual_length, aurb->more);
-
- if (p) {
- switch (aurb->urb.status) {
- case 0:
- p->actual_length += aurb->urb.actual_length;
- if (!aurb->more) {
- /* Clear previous ASYNC status */
- p->status = USB_RET_SUCCESS;
- }
- break;
-
- case -EPIPE:
- set_halt(s, p->pid, p->ep->nr);
- p->status = USB_RET_STALL;
- break;
-
- case -EOVERFLOW:
- p->status = USB_RET_BABBLE;
- break;
-
- default:
- p->status = USB_RET_IOERROR;
- break;
- }
-
- if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
- trace_usb_host_req_complete(s->bus_num, s->addr, p,
- p->status, aurb->urb.actual_length);
- usb_generic_async_ctrl_complete(&s->dev, p);
- } else if (!aurb->more) {
- trace_usb_host_req_complete(s->bus_num, s->addr, p,
- p->status, aurb->urb.actual_length);
- usb_packet_complete(&s->dev, p);
- }
- }
-
- async_free(aurb);
- }
-}
-
-static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
-{
- USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
- AsyncURB *aurb;
-
- trace_usb_host_req_canceled(s->bus_num, s->addr, p);
-
- QLIST_FOREACH(aurb, &s->aurbs, next) {
- if (p != aurb->packet) {
- continue;
- }
-
- trace_usb_host_urb_canceled(s->bus_num, s->addr, aurb);
-
- /* Mark it as dead (see async_complete above) */
- aurb->packet = NULL;
-
- int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
- if (r < 0) {
- DPRINTF("husb: async. discard urb failed errno %d\n", errno);
- }
- }
-}
-
-static int usb_host_open_device(int bus, int addr)
-{
- const char *usbfs = NULL;
- char filename[32];
- struct stat st;
- int fd, rc;
-
- rc = stat("/dev/bus/usb", &st);
- if (rc == 0 && S_ISDIR(st.st_mode)) {
- /* udev-created device nodes available */
- usbfs = "/dev/bus/usb";
- } else {
- /* fallback: usbfs mounted below /proc */
- usbfs = "/proc/bus/usb";
- }
-
- snprintf(filename, sizeof(filename), "%s/%03d/%03d",
- usbfs, bus, addr);
- fd = open(filename, O_RDWR | O_NONBLOCK);
- if (fd < 0) {
- fprintf(stderr, "husb: open %s: %s\n", filename, strerror(errno));
- }
- return fd;
-}
-
-static int usb_host_claim_port(USBHostDevice *s)
-{
-#ifdef USBDEVFS_CLAIM_PORT
- char *h, hub_name[64], line[1024];
- int hub_addr, ret;
-
- snprintf(hub_name, sizeof(hub_name), "%d-%s",
- s->match.bus_num, s->match.port);
-
- /* try strip off last ".$portnr" to get hub */
- h = strrchr(hub_name, '.');
- if (h != NULL) {
- s->hub_port = atoi(h+1);
- *h = '\0';
- } else {
- /* no dot in there -> it is the root hub */
- snprintf(hub_name, sizeof(hub_name), "usb%d",
- s->match.bus_num);
- s->hub_port = atoi(s->match.port);
- }
-
- if (!usb_host_read_file(line, sizeof(line), "devnum",
- hub_name)) {
- return -1;
- }
- if (sscanf(line, "%d", &hub_addr) != 1) {
- return -1;
- }
-
- s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr);
- if (s->hub_fd < 0) {
- return -1;
- }
-
- ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &s->hub_port);
- if (ret < 0) {
- close(s->hub_fd);
- s->hub_fd = -1;
- return -1;
- }
-
- trace_usb_host_claim_port(s->match.bus_num, hub_addr, s->hub_port);
- return 0;
-#else
- return -1;
-#endif
-}
-
-static void usb_host_release_port(USBHostDevice *s)
-{
- if (s->hub_fd == -1) {
- return;
- }
-#ifdef USBDEVFS_RELEASE_PORT
- ioctl(s->hub_fd, USBDEVFS_RELEASE_PORT, &s->hub_port);
-#endif
- close(s->hub_fd);
- s->hub_fd = -1;
-}
-
-static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces)
-{
- /* earlier Linux 2.4 do not support that */
-#ifdef USBDEVFS_DISCONNECT
- struct usbdevfs_ioctl ctrl;
- int ret, interface;
-
- for (interface = 0; interface < nb_interfaces; interface++) {
- ctrl.ioctl_code = USBDEVFS_DISCONNECT;
- ctrl.ifno = interface;
- ctrl.data = 0;
- ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
- if (ret < 0 && errno != ENODATA) {
- perror("USBDEVFS_DISCONNECT");
- return -1;
- }
- }
-#endif
- return 0;
-}
-
-static int usb_linux_get_num_interfaces(USBHostDevice *s)
-{
- char device_name[64], line[1024];
- int num_interfaces = 0;
-
- sprintf(device_name, "%d-%s", s->bus_num, s->port);
- if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
- device_name)) {
- return -1;
- }
- if (sscanf(line, "%d", &num_interfaces) != 1) {
- return -1;
- }
- return num_interfaces;
-}
-
-static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
-{
- const char *op = NULL;
- int dev_descr_len, config_descr_len;
- int interface, nb_interfaces;
- int ret, i;
-
- for (i = 0; i < USB_MAX_INTERFACES; i++) {
- dev->dev.altsetting[i] = 0;
- }
-
- if (configuration == 0) { /* address state - ignore */
- dev->dev.ninterfaces = 0;
- dev->dev.configuration = 0;
- return 1;
- }
-
- DPRINTF("husb: claiming interfaces. config %d\n", configuration);
-
- i = 0;
- dev_descr_len = dev->descr[0];
- if (dev_descr_len > dev->descr_len) {
- fprintf(stderr, "husb: update iface failed. descr too short\n");
- return 0;
- }
-
- i += dev_descr_len;
- while (i < dev->descr_len) {
- DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n",
- i, dev->descr_len,
- dev->descr[i], dev->descr[i+1]);
-
- if (dev->descr[i+1] != USB_DT_CONFIG) {
- i += dev->descr[i];
- continue;
- }
- config_descr_len = dev->descr[i];
-
- DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
-
- if (configuration == dev->descr[i + 5]) {
- configuration = dev->descr[i + 5];
- break;
- }
-
- i += config_descr_len;
- }
-
- if (i >= dev->descr_len) {
- fprintf(stderr,
- "husb: update iface failed. no matching configuration\n");
- return 0;
- }
- nb_interfaces = dev->descr[i + 4];
-
- if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) {
- goto fail;
- }
-
- /* XXX: only grab if all interfaces are free */
- for (interface = 0; interface < nb_interfaces; interface++) {
- op = "USBDEVFS_CLAIMINTERFACE";
- ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
- if (ret < 0) {
- goto fail;
- }
- }
-
- trace_usb_host_claim_interfaces(dev->bus_num, dev->addr,
- nb_interfaces, configuration);
-
- dev->dev.ninterfaces = nb_interfaces;
- dev->dev.configuration = configuration;
- return 1;
-
-fail:
- if (errno == ENODEV) {
- do_disconnect(dev);
- }
- perror(op);
- return 0;
-}
-
-static int usb_host_release_interfaces(USBHostDevice *s)
-{
- int ret, i;
-
- trace_usb_host_release_interfaces(s->bus_num, s->addr);
-
- for (i = 0; i < s->dev.ninterfaces; i++) {
- ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
- if (ret < 0) {
- perror("USBDEVFS_RELEASEINTERFACE");
- return 0;
- }
- }
- return 1;
-}
-
-static void usb_host_handle_reset(USBDevice *dev)
-{
- USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-
- trace_usb_host_reset(s->bus_num, s->addr);
-
- usb_host_do_reset(s);
-
- usb_host_claim_interfaces(s, 0);
- usb_linux_update_endp_table(s);
-}
-
-static void usb_host_handle_destroy(USBDevice *dev)
-{
- USBHostDevice *s = (USBHostDevice *)dev;
-
- usb_host_release_port(s);
- usb_host_close(s);
- QTAILQ_REMOVE(&hostdevs, s, next);
- qemu_remove_exit_notifier(&s->exit);
-}
-
-/* iso data is special, we need to keep enough urbs in flight to make sure
- that the controller never runs out of them, otherwise the device will
- likely suffer a buffer underrun / overrun. */
-static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
-{
- AsyncURB *aurb;
- int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep);
-
- aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb));
- for (i = 0; i < s->iso_urb_count; i++) {
- aurb[i].urb.endpoint = ep;
- aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len;
- aurb[i].urb.buffer = g_malloc(aurb[i].urb.buffer_length);
- aurb[i].urb.type = USBDEVFS_URB_TYPE_ISO;
- aurb[i].urb.flags = USBDEVFS_URB_ISO_ASAP;
- aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB;
- for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++)
- aurb[i].urb.iso_frame_desc[j].length = len;
- if (pid == USB_TOKEN_IN) {
- aurb[i].urb.endpoint |= 0x80;
- /* Mark as fully consumed (idle) */
- aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB;
- }
- }
- set_iso_urb(s, pid, ep, aurb);
-
- return aurb;
-}
-
-static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
-{
- AsyncURB *aurb;
- int i, ret, killed = 0, free = 1;
-
- aurb = get_iso_urb(s, pid, ep);
- if (!aurb) {
- return;
- }
-
- for (i = 0; i < s->iso_urb_count; i++) {
- /* in flight? */
- if (aurb[i].iso_frame_idx == -1) {
- ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]);
- if (ret < 0) {
- perror("USBDEVFS_DISCARDURB");
- free = 0;
- continue;
- }
- killed++;
- }
- }
-
- /* Make sure any urbs we've killed are reaped before we free them */
- if (killed) {
- async_complete(s);
- }
-
- for (i = 0; i < s->iso_urb_count; i++) {
- g_free(aurb[i].urb.buffer);
- }
-
- if (free)
- g_free(aurb);
- else
- printf("husb: leaking iso urbs because of discard failure\n");
- set_iso_urb(s, pid, ep, NULL);
- set_iso_urb_idx(s, pid, ep, 0);
- clear_iso_started(s, pid, ep);
-}
-
-static void urb_status_to_usb_ret(int status, USBPacket *p)
-{
- switch (status) {
- case -EPIPE:
- p->status = USB_RET_STALL;
- break;
- case -EOVERFLOW:
- p->status = USB_RET_BABBLE;
- break;
- default:
- p->status = USB_RET_IOERROR;
- }
-}
-
-static void usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
-{
- AsyncURB *aurb;
- int i, j, max_packet_size, offset, len;
- uint8_t *buf;
-
- max_packet_size = p->ep->max_packet_size;
- if (max_packet_size == 0) {
- p->status = USB_RET_NAK;
- return;
- }
-
- aurb = get_iso_urb(s, p->pid, p->ep->nr);
- if (!aurb) {
- aurb = usb_host_alloc_iso(s, p->pid, p->ep->nr);
- }
-
- i = get_iso_urb_idx(s, p->pid, p->ep->nr);
- j = aurb[i].iso_frame_idx;
- if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
- if (in) {
- /* Check urb status */
- if (aurb[i].urb.status) {
- urb_status_to_usb_ret(aurb[i].urb.status, p);
- /* Move to the next urb */
- aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
- /* Check frame status */
- } else if (aurb[i].urb.iso_frame_desc[j].status) {
- urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status, p);
- /* Check the frame fits */
- } else if (aurb[i].urb.iso_frame_desc[j].actual_length
- > p->iov.size) {
- printf("husb: received iso data is larger then packet\n");
- p->status = USB_RET_BABBLE;
- /* All good copy data over */
- } else {
- len = aurb[i].urb.iso_frame_desc[j].actual_length;
- buf = aurb[i].urb.buffer +
- j * aurb[i].urb.iso_frame_desc[0].length;
- usb_packet_copy(p, buf, len);
- }
- } else {
- len = p->iov.size;
- offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr);
-
- /* Check the frame fits */
- if (len > max_packet_size) {
- printf("husb: send iso data is larger then max packet size\n");
- p->status = USB_RET_NAK;
- return;
- }
-
- /* All good copy data over */
- usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
- aurb[i].urb.iso_frame_desc[j].length = len;
- offset += len;
- set_iso_buffer_used(s, p->pid, p->ep->nr, offset);
-
- /* Start the stream once we have buffered enough data */
- if (!is_iso_started(s, p->pid, p->ep->nr) && i == 1 && j == 8) {
- set_iso_started(s, p->pid, p->ep->nr);
- }
- }
- aurb[i].iso_frame_idx++;
- if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
- i = (i + 1) % s->iso_urb_count;
- set_iso_urb_idx(s, p->pid, p->ep->nr, i);
- }
- } else {
- if (in) {
- set_iso_started(s, p->pid, p->ep->nr);
- } else {
- DPRINTF("hubs: iso out error no free buffer, dropping packet\n");
- }
- }
-
- if (is_iso_started(s, p->pid, p->ep->nr)) {
- /* (Re)-submit all fully consumed / filled urbs */
- for (i = 0; i < s->iso_urb_count; i++) {
- if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
- if (ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]) < 0) {
- perror("USBDEVFS_SUBMITURB");
- if (!in || p->status == USB_RET_SUCCESS) {
- switch(errno) {
- case ETIMEDOUT:
- p->status = USB_RET_NAK;
- break;
- case EPIPE:
- default:
- p->status = USB_RET_STALL;
- }
- }
- break;
- }
- aurb[i].iso_frame_idx = -1;
- change_iso_inflight(s, p->pid, p->ep->nr, 1);
- }
- }
- }
-}
-
-static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
-{
- USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
- struct usbdevfs_urb *urb;
- AsyncURB *aurb;
- int ret, rem, prem, v;
- uint8_t *pbuf;
- uint8_t ep;
-
- trace_usb_host_req_data(s->bus_num, s->addr, p,
- p->pid == USB_TOKEN_IN,
- p->ep->nr, p->iov.size);
-
- if (!is_valid(s, p->pid, p->ep->nr)) {
- p->status = USB_RET_NAK;
- trace_usb_host_req_complete(s->bus_num, s->addr, p,
- p->status, p->actual_length);
- return;
- }
-
- if (p->pid == USB_TOKEN_IN) {
- ep = p->ep->nr | 0x80;
- } else {
- ep = p->ep->nr;
- }
-
- if (is_halted(s, p->pid, p->ep->nr)) {
- unsigned int arg = ep;
- ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
- if (ret < 0) {
- perror("USBDEVFS_CLEAR_HALT");
- p->status = USB_RET_NAK;
- trace_usb_host_req_complete(s->bus_num, s->addr, p,
- p->status, p->actual_length);
- return;
- }
- clear_halt(s, p->pid, p->ep->nr);
- }
-
- if (is_isoc(s, p->pid, p->ep->nr)) {
- usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
- return;
- }
-
- v = 0;
- prem = 0;
- pbuf = NULL;
- rem = p->iov.size;
- do {
- if (prem == 0 && rem > 0) {
- assert(v < p->iov.niov);
- prem = p->iov.iov[v].iov_len;
- pbuf = p->iov.iov[v].iov_base;
- assert(prem <= rem);
- v++;
- }
- aurb = async_alloc(s);
- aurb->packet = p;
-
- urb = &aurb->urb;
- urb->endpoint = ep;
- urb->type = usb_host_usbfs_type(s, p);
- urb->usercontext = s;
- urb->buffer = pbuf;
- urb->buffer_length = prem;
-
- if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) {
- urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
- }
- pbuf += urb->buffer_length;
- prem -= urb->buffer_length;
- rem -= urb->buffer_length;
- if (rem) {
- aurb->more = 1;
- }
-
- trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
- urb->buffer_length, aurb->more);
- ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
-
- DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
- urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
-
- if (ret < 0) {
- perror("USBDEVFS_SUBMITURB");
- async_free(aurb);
-
- switch(errno) {
- case ETIMEDOUT:
- p->status = USB_RET_NAK;
- trace_usb_host_req_complete(s->bus_num, s->addr, p,
- p->status, p->actual_length);
- break;
- case EPIPE:
- default:
- p->status = USB_RET_STALL;
- trace_usb_host_req_complete(s->bus_num, s->addr, p,
- p->status, p->actual_length);
- }
- return;
- }
- } while (rem > 0);
-
- p->status = USB_RET_ASYNC;
-}
-
-static int ctrl_error(void)
-{
- if (errno == ETIMEDOUT) {
- return USB_RET_NAK;
- } else {
- return USB_RET_STALL;
- }
-}
-
-static void usb_host_set_address(USBHostDevice *s, int addr)
-{
- trace_usb_host_set_address(s->bus_num, s->addr, addr);
- s->dev.addr = addr;
-}
-
-static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p)
-{
- int ret, first = 1;
-
- trace_usb_host_set_config(s->bus_num, s->addr, config);
-
- usb_host_release_interfaces(s);
-
-again:
- ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
-
- DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
-
- if (ret < 0 && errno == EBUSY && first) {
- /* happens if usb device is in use by host drivers */
- int count = usb_linux_get_num_interfaces(s);
- if (count > 0) {
- DPRINTF("husb: busy -> disconnecting %d interfaces\n", count);
- usb_host_disconnect_ifaces(s, count);
- first = 0;
- goto again;
- }
- }
-
- if (ret < 0) {
- p->status = ctrl_error();
- return;
- }
- usb_host_claim_interfaces(s, config);
- usb_linux_update_endp_table(s);
-}
-
-static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
- USBPacket *p)
-{
- struct usbdevfs_setinterface si;
- int i, ret;
-
- trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
-
- for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
- if (is_isoc(s, USB_TOKEN_IN, i)) {
- usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i);
- }
- if (is_isoc(s, USB_TOKEN_OUT, i)) {
- usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i);
- }
- }
-
- if (iface >= USB_MAX_INTERFACES) {
- p->status = USB_RET_STALL;
- return;
- }
-
- si.interface = iface;
- si.altsetting = alt;
- ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
-
- DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n",
- iface, alt, ret, errno);
-
- if (ret < 0) {
- p->status = ctrl_error();
- return;
- }
-
- s->dev.altsetting[iface] = alt;
- usb_linux_update_endp_table(s);
-}
-
-static void usb_host_handle_control(USBDevice *dev, USBPacket *p,
- int request, int value, int index, int length, uint8_t *data)
-{
- USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
- struct usbdevfs_urb *urb;
- AsyncURB *aurb;
- int ret;
-
- /*
- * Process certain standard device requests.
- * These are infrequent and are processed synchronously.
- */
-
- /* Note request is (bRequestType << 8) | bRequest */
- trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
-
- 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;
- ioctl(s->fd, USBDEVFS_CLEAR_HALT, &index);
- clear_halt(s, pid, index & 0x0f);
- trace_usb_host_req_emulated(s->bus_num, s->addr, p, 0);
- return;
- }
- }
-
- /* The rest are asynchronous */
- if (length > sizeof(dev->data_buf)) {
- fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
- length, sizeof(dev->data_buf));
- p->status = USB_RET_STALL;
- return;
- }
-
- aurb = async_alloc(s);
- aurb->packet = p;
-
- /*
- * Setup ctrl transfer.
- *
- * s->ctrl is laid out such that data buffer immediately follows
- * 'req' struct which is exactly what usbdevfs expects.
- */
- urb = &aurb->urb;
-
- urb->type = USBDEVFS_URB_TYPE_CONTROL;
- urb->endpoint = p->ep->nr;
-
- urb->buffer = &dev->setup_buf;
- urb->buffer_length = length + 8;
-
- urb->usercontext = s;
-
- trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
- urb->buffer_length, aurb->more);
- ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
-
- DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
-
- if (ret < 0) {
- DPRINTF("husb: submit failed. errno %d\n", errno);
- async_free(aurb);
-
- switch(errno) {
- case ETIMEDOUT:
- p->status = USB_RET_NAK;
- break;
- case EPIPE:
- default:
- p->status = USB_RET_STALL;
- break;
- }
- return;
- }
-
- p->status = USB_RET_ASYNC;
-}
-
-static void usb_linux_update_endp_table(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",
- };
- uint8_t devep, type;
- uint16_t mps, v, p;
- int ep, pid;
- unsigned int i, configuration = -1, interface = -1, altsetting = -1;
- struct endp_data *epd;
- USBDescriptor *d;
- bool active = false;
-
- usb_ep_reset(&s->dev);
-
- for (i = 0;; i += d->bLength) {
- if (i+2 >= s->descr_len) {
- break;
- }
- d = (void *)(s->descr + i);
- if (d->bLength < 2) {
- trace_usb_host_parse_error(s->bus_num, s->addr,
- "descriptor too short");
- return;
- }
- if (i + d->bLength > s->descr_len) {
- trace_usb_host_parse_error(s->bus_num, s->addr,
- "descriptor too long");
- return;
- }
- switch (d->bDescriptorType) {
- case 0:
- trace_usb_host_parse_error(s->bus_num, s->addr,
- "invalid descriptor type");
- return;
- case USB_DT_DEVICE:
- if (d->bLength < 0x12) {
- trace_usb_host_parse_error(s->bus_num, s->addr,
- "device descriptor too short");
- return;
- }
- v = (d->u.device.idVendor_hi << 8) | d->u.device.idVendor_lo;
- p = (d->u.device.idProduct_hi << 8) | d->u.device.idProduct_lo;
- trace_usb_host_parse_device(s->bus_num, s->addr, v, p);
- break;
- case USB_DT_CONFIG:
- if (d->bLength < 0x09) {
- trace_usb_host_parse_error(s->bus_num, s->addr,
- "config descriptor too short");
- return;
- }
- configuration = d->u.config.bConfigurationValue;
- active = (configuration == s->dev.configuration);
- trace_usb_host_parse_config(s->bus_num, s->addr,
- configuration, active);
- break;
- case USB_DT_INTERFACE:
- if (d->bLength < 0x09) {
- trace_usb_host_parse_error(s->bus_num, s->addr,
- "interface descriptor too short");
- return;
- }
- interface = d->u.interface.bInterfaceNumber;
- altsetting = d->u.interface.bAlternateSetting;
- active = (configuration == s->dev.configuration) &&
- (altsetting == s->dev.altsetting[interface]);
- trace_usb_host_parse_interface(s->bus_num, s->addr,
- interface, altsetting, active);
- break;
- case USB_DT_ENDPOINT:
- if (d->bLength < 0x07) {
- trace_usb_host_parse_error(s->bus_num, s->addr,
- "endpoint descriptor too short");
- return;
- }
- devep = d->u.endpoint.bEndpointAddress;
- pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
- ep = devep & 0xf;
- if (ep == 0) {
- trace_usb_host_parse_error(s->bus_num, s->addr,
- "invalid endpoint address");
- return;
- }
-
- type = d->u.endpoint.bmAttributes & 0x3;
- mps = d->u.endpoint.wMaxPacketSize_lo |
- (d->u.endpoint.wMaxPacketSize_hi << 8);
- trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep,
- (devep & USB_DIR_IN) ? "in" : "out",
- tname[type], active);
-
- if (active) {
- usb_ep_set_max_packet_size(&s->dev, pid, ep, mps);
- assert(usb_ep_get_type(&s->dev, pid, ep) ==
- USB_ENDPOINT_XFER_INVALID);
- usb_ep_set_type(&s->dev, pid, ep, type);
- usb_ep_set_ifnum(&s->dev, pid, ep, interface);
- if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) &&
- (type == USB_ENDPOINT_XFER_BULK) &&
- (pid == USB_TOKEN_OUT)) {
- usb_ep_set_pipeline(&s->dev, pid, ep, true);
- }
-
- epd = get_endp(s, pid, ep);
- epd->halted = 0;
- }
-
- break;
- default:
- trace_usb_host_parse_unknown(s->bus_num, s->addr,
- d->bLength, d->bDescriptorType);
- break;
- }
- }
-}
-
-/*
- * Check if we can safely redirect a usb2 device to a usb1 virtual controller,
- * this function assumes this is safe, if:
- * 1) There are no isoc endpoints
- * 2) There are no interrupt endpoints with a max_packet_size > 64
- * Note bulk endpoints with a max_packet_size > 64 in theory also are not
- * usb1 compatible, but in practice this seems to work fine.
- */
-static int usb_linux_full_speed_compat(USBHostDevice *dev)
-{
- int i, packet_size;
-
- /*
- * usb_linux_update_endp_table only registers info about ep in the current
- * interface altsettings, so we need to parse the descriptors again.
- */
- for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) {
- if (dev->descr[i + 1] == USB_DT_ENDPOINT) {
- switch (dev->descr[i + 3] & 0x3) {
- case 0x00: /* CONTROL */
- break;
- case 0x01: /* ISO */
- return 0;
- case 0x02: /* BULK */
- break;
- case 0x03: /* INTERRUPT */
- packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8);
- if (packet_size > 64)
- return 0;
- break;
- }
- }
- }
- return 1;
-}
-
-static int usb_host_open(USBHostDevice *dev, int bus_num,
- int addr, const char *port,
- const char *prod_name, int speed)
-{
- int fd = -1, ret;
-
- trace_usb_host_open_started(bus_num, addr);
-
- if (dev->fd != -1) {
- goto fail;
- }
-
- fd = usb_host_open_device(bus_num, addr);
- if (fd < 0) {
- goto fail;
- }
- DPRINTF("husb: opened %s\n", buf);
-
- dev->bus_num = bus_num;
- dev->addr = addr;
- pstrcpy(dev->port, sizeof(dev->port), port);
- dev->fd = fd;
-
- /* read the device description */
- dev->descr_len = read(fd, dev->descr, sizeof(dev->descr));
- if (dev->descr_len <= 0) {
- perror("husb: reading device data failed");
- goto fail;
- }
-
-#ifdef DEBUG
- {
- int x;
- printf("=== begin dumping device descriptor data ===\n");
- for (x = 0; x < dev->descr_len; x++) {
- printf("%02x ", dev->descr[x]);
- }
- printf("\n=== end dumping device descriptor data ===\n");
- }
-#endif
-
-
- /* start unconfigured -- we'll wait for the guest to set a configuration */
- if (!usb_host_claim_interfaces(dev, 0)) {
- goto fail;
- }
-
- usb_ep_init(&dev->dev);
- usb_linux_update_endp_table(dev);
-
- if (speed == -1) {
- struct usbdevfs_connectinfo ci;
-
- ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
- if (ret < 0) {
- perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
- goto fail;
- }
-
- if (ci.slow) {
- speed = USB_SPEED_LOW;
- } else {
- speed = USB_SPEED_HIGH;
- }
- }
- dev->dev.speed = speed;
- dev->dev.speedmask = (1 << speed);
- if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) {
- dev->dev.speedmask |= USB_SPEED_MASK_FULL;
- }
-
- trace_usb_host_open_success(bus_num, addr);
-
- if (!prod_name || prod_name[0] == '\0') {
- snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
- "host:%d.%d", bus_num, addr);
- } else {
- pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
- prod_name);
- }
-
- ret = usb_device_attach(&dev->dev);
- if (ret) {
- goto fail;
- }
-
- /* USB devio uses 'write' flag to check for async completions */
- qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
-
- return 0;
-
-fail:
- trace_usb_host_open_failure(bus_num, addr);
- if (dev->fd != -1) {
- close(dev->fd);
- dev->fd = -1;
- }
- return -1;
-}
-
-static int usb_host_close(USBHostDevice *dev)
-{
- int i;
-
- if (dev->fd == -1) {
- return -1;
- }
-
- trace_usb_host_close(dev->bus_num, dev->addr);
-
- qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
- dev->closing = 1;
- for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
- if (is_isoc(dev, USB_TOKEN_IN, i)) {
- usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i);
- }
- if (is_isoc(dev, USB_TOKEN_OUT, i)) {
- usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i);
- }
- }
- async_complete(dev);
- dev->closing = 0;
- if (dev->dev.attached) {
- usb_device_detach(&dev->dev);
- }
- usb_host_do_reset(dev);
- close(dev->fd);
- dev->fd = -1;
- return 0;
-}
-
-static void usb_host_exit_notifier(struct Notifier *n, void *data)
-{
- USBHostDevice *s = container_of(n, USBHostDevice, exit);
-
- usb_host_release_port(s);
- if (s->fd != -1) {
- usb_host_do_reset(s);
- }
-}
-
-/*
- * 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 to to 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;
-
- if (dev->fd != -1) {
- usb_host_close(dev);
- }
- if (dev->dev.attached) {
- usb_device_detach(&dev->dev);
- }
- usb_host_auto_check(NULL);
-}
-
-static int usb_host_post_load(void *opaque, int version_id)
-{
- USBHostDevice *dev = opaque;
-
- qemu_bh_schedule(dev->bh);
- return 0;
-}
-
-static int usb_host_initfn(USBDevice *dev)
-{
- USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-
- dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
- dev->auto_attach = 0;
- s->fd = -1;
- s->hub_fd = -1;
-
- QTAILQ_INSERT_TAIL(&hostdevs, s, next);
- s->exit.notify = usb_host_exit_notifier;
- qemu_add_exit_notifier(&s->exit);
- s->bh = qemu_bh_new(usb_host_post_load_bh, s);
- usb_host_auto_check(NULL);
-
- if (s->match.bus_num != 0 && s->match.port != NULL) {
- usb_host_claim_port(s);
- }
- add_boot_device_path(s->bootindex, &dev->qdev, NULL);
- return 0;
-}
-
-static const VMStateDescription vmstate_usb_host = {
- .name = DEVNAME,
- .version_id = 1,
- .minimum_version_id = 1,
- .post_load = usb_host_post_load,
- .fields = (VMStateField[]) {
- VMSTATE_USB_DEVICE(dev, 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_HEX32("vendorid", USBHostDevice, match.vendor_id, 0),
- DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
- DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4),
- DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex, -1),
- 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->init = usb_host_initfn;
- uc->product_desc = "USB Host Device";
- uc->cancel_packet = usb_host_async_cancel;
- 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;
- dc->vmsd = &vmstate_usb_host;
- dc->props = usb_host_dev_properties;
- set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
-}
-
-static const TypeInfo usb_host_dev_info = {
- .name = DEVNAME,
- .parent = TYPE_USB_DEVICE,
- .instance_size = sizeof(USBHostDevice),
- .class_init = usb_host_class_initfn,
-};
-
-static void usb_host_register_types(void)
-{
- type_register_static(&usb_host_dev_info);
-}
-
-type_init(usb_host_register_types)
-
-/*
- * Read sys file-system device file
- *
- * @line address of buffer to put file contents in
- * @line_size size of line
- * @device_file path to device file (printf format string)
- * @device_name device being opened (inserted into device_file)
- *
- * @return 0 failed, 1 succeeded ('line' contains data)
- */
-static int usb_host_read_file(char *line, size_t line_size,
- const char *device_file, const char *device_name)
-{
- FILE *f;
- int ret = 0;
- char filename[PATH_MAX];
-
- snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name,
- device_file);
- f = fopen(filename, "r");
- if (f) {
- ret = fgets(line, line_size, f) != NULL;
- fclose(f);
- }
-
- return ret;
-}
-
-/*
- * Use /sys/bus/usb/devices/ directory to determine host's USB
- * devices.
- *
- * This code is based on Robert Schiele's original patches posted to
- * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950
- */
-static int usb_host_scan(void *opaque, USBScanFunc *func)
-{
- DIR *dir = NULL;
- char line[1024];
- int bus_num, addr, speed, class_id, product_id, vendor_id;
- int ret = 0;
- char port[MAX_PORTLEN];
- char product_name[512];
- struct dirent *de;
-
- dir = opendir("/sys/bus/usb/devices");
- if (!dir) {
- perror("husb: opendir /sys/bus/usb/devices");
- fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n");
- goto the_end;
- }
-
- while ((de = readdir(dir))) {
- if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
- if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) {
- continue;
- }
-
- if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
- goto the_end;
- }
- if (sscanf(line, "%d", &addr) != 1) {
- goto the_end;
- }
- if (!usb_host_read_file(line, sizeof(line), "bDeviceClass",
- de->d_name)) {
- goto the_end;
- }
- if (sscanf(line, "%x", &class_id) != 1) {
- goto the_end;
- }
-
- if (!usb_host_read_file(line, sizeof(line), "idVendor",
- de->d_name)) {
- goto the_end;
- }
- if (sscanf(line, "%x", &vendor_id) != 1) {
- goto the_end;
- }
- if (!usb_host_read_file(line, sizeof(line), "idProduct",
- de->d_name)) {
- goto the_end;
- }
- if (sscanf(line, "%x", &product_id) != 1) {
- goto the_end;
- }
- if (!usb_host_read_file(line, sizeof(line), "product",
- de->d_name)) {
- *product_name = 0;
- } else {
- if (strlen(line) > 0) {
- line[strlen(line) - 1] = '\0';
- }
- pstrcpy(product_name, sizeof(product_name), line);
- }
-
- if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) {
- goto the_end;
- }
- if (!strcmp(line, "5000\n")) {
- speed = USB_SPEED_SUPER;
- } else if (!strcmp(line, "480\n")) {
- speed = USB_SPEED_HIGH;
- } else if (!strcmp(line, "1.5\n")) {
- speed = USB_SPEED_LOW;
- } else {
- speed = USB_SPEED_FULL;
- }
-
- ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
- product_id, product_name, speed);
- if (ret) {
- goto the_end;
- }
- }
- }
- the_end:
- if (dir) {
- closedir(dir);
- }
- return ret;
-}
-
-static QEMUTimer *usb_auto_timer;
-static VMChangeStateEntry *usb_vmstate;
-
-static int usb_host_auto_scan(void *opaque, int bus_num,
- int addr, const char *port,
- int class_id, int vendor_id, int product_id,
- const char *product_name, int speed)
-{
- struct USBAutoFilter *f;
- struct USBHostDevice *s;
-
- /* Ignore hubs */
- if (class_id == 9)
- return 0;
-
- QTAILQ_FOREACH(s, &hostdevs, next) {
- f = &s->match;
-
- if (f->bus_num > 0 && f->bus_num != bus_num) {
- continue;
- }
- if (f->addr > 0 && f->addr != addr) {
- continue;
- }
- if (f->port != NULL && strcmp(f->port, port) != 0) {
- continue;
- }
-
- if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
- continue;
- }
-
- if (f->product_id > 0 && f->product_id != product_id) {
- continue;
- }
- /* We got a match */
- s->seen++;
- if (s->errcount >= 3) {
- return 0;
- }
-
- /* Already attached ? */
- if (s->fd != -1) {
- return 0;
- }
- DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
-
- if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) {
- s->errcount++;
- }
- break;
- }
-
- return 0;
-}
-
-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;
- int unconnected = 0;
-
- if (runstate_is_running()) {
- usb_host_scan(NULL, usb_host_auto_scan);
-
- QTAILQ_FOREACH(s, &hostdevs, next) {
- if (s->fd == -1) {
- unconnected++;
- }
- if (s->seen == 0) {
- s->errcount = 0;
- }
- s->seen = 0;
- }
-
- if (unconnected == 0) {
- /* nothing to watch */
- if (usb_auto_timer) {
- timer_del(usb_auto_timer);
- trace_usb_host_auto_scan_disabled();
- }
- return;
- }
- }
-
- 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);
-}
-
-#ifndef CONFIG_USB_LIBUSB
-
-/**********************/
-/* USB host device info */
-
-struct usb_class_info {
- int class;
- const char *class_name;
-};
-
-static const struct usb_class_info usb_class_info[] = {
- { USB_CLASS_AUDIO, "Audio"},
- { USB_CLASS_COMM, "Communication"},
- { USB_CLASS_HID, "HID"},
- { USB_CLASS_HUB, "Hub" },
- { USB_CLASS_PHYSICAL, "Physical" },
- { USB_CLASS_PRINTER, "Printer" },
- { USB_CLASS_MASS_STORAGE, "Storage" },
- { USB_CLASS_CDC_DATA, "Data" },
- { USB_CLASS_APP_SPEC, "Application Specific" },
- { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
- { USB_CLASS_STILL_IMAGE, "Still Image" },
- { USB_CLASS_CSCID, "Smart Card" },
- { USB_CLASS_CONTENT_SEC, "Content Security" },
- { -1, NULL }
-};
-
-static const char *usb_class_str(uint8_t class)
-{
- const struct usb_class_info *p;
- for(p = usb_class_info; p->class != -1; p++) {
- if (p->class == class) {
- break;
- }
- }
- return p->class_name;
-}
-
-static void usb_info_device(Monitor *mon, int bus_num,
- int addr, const char *port,
- int class_id, int vendor_id, int product_id,
- const char *product_name,
- int speed)
-{
- const char *class_str, *speed_str;
-
- switch(speed) {
- case USB_SPEED_LOW:
- speed_str = "1.5";
- break;
- case USB_SPEED_FULL:
- speed_str = "12";
- break;
- case USB_SPEED_HIGH:
- speed_str = "480";
- break;
- case USB_SPEED_SUPER:
- speed_str = "5000";
- break;
- default:
- speed_str = "?";
- break;
- }
-
- monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
- bus_num, addr, port, speed_str);
- class_str = usb_class_str(class_id);
- if (class_str) {
- monitor_printf(mon, " %s:", class_str);
- } else {
- monitor_printf(mon, " Class %02x:", class_id);
- }
- monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
- if (product_name[0] != '\0') {
- monitor_printf(mon, ", %s", product_name);
- }
- monitor_printf(mon, "\n");
-}
-
-static int usb_host_info_device(void *opaque, int bus_num, int addr,
- const char *path, int class_id,
- int vendor_id, int product_id,
- const char *product_name,
- int speed)
-{
- Monitor *mon = opaque;
-
- usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
- product_name, speed);
- return 0;
-}
-
-static void dec2str(int val, char *str, size_t size)
-{
- if (val == 0) {
- snprintf(str, size, "*");
- } else {
- snprintf(str, size, "%d", val);
- }
-}
-
-static void hex2str(int val, char *str, size_t size)
-{
- if (val == 0) {
- snprintf(str, size, "*");
- } else {
- snprintf(str, size, "%04x", val);
- }
-}
-
-void usb_host_info(Monitor *mon, const QDict *qdict)
-{
- struct USBAutoFilter *f;
- struct USBHostDevice *s;
-
- usb_host_scan(mon, usb_host_info_device);
-
- if (QTAILQ_EMPTY(&hostdevs)) {
- return;
- }
-
- monitor_printf(mon, " Auto filters:\n");
- QTAILQ_FOREACH(s, &hostdevs, next) {
- char bus[10], addr[10], vid[10], pid[10];
- f = &s->match;
- dec2str(f->bus_num, bus, sizeof(bus));
- dec2str(f->addr, addr, sizeof(addr));
- hex2str(f->vendor_id, vid, sizeof(vid));
- hex2str(f->product_id, pid, sizeof(pid));
- monitor_printf(mon, " Bus %s, Addr %s, Port %s, ID %s:%s\n",
- bus, addr, f->port ? f->port : "*", vid, pid);
- }
-}
-
-#endif