diff options
author | Anthony Liguori <aliguori@amazon.com> | 2013-12-06 12:54:35 -0800 |
---|---|---|
committer | Anthony Liguori <aliguori@amazon.com> | 2013-12-06 12:54:36 -0800 |
commit | e679f05248e220b8ef587e8fd6c64ffe83b1e16f (patch) | |
tree | b0a6f7e6d54c230848bf9af901ee584480eae199 | |
parent | 783eb67e7a125eaf226e5ea4f5df97c1ea42311e (diff) | |
parent | 0b1fa34e1dd2764ee7ae2be849e1c5ba5e8724ca (diff) | |
download | hqemu-e679f05248e220b8ef587e8fd6c64ffe83b1e16f.zip hqemu-e679f05248e220b8ef587e8fd6c64ffe83b1e16f.tar.gz |
Merge remote-tracking branch 'kraxel/tags/pull-usb-1' into staging
Improvements for usb3 bulk stream (usb core, xhci).
Bugfixes for uas emulation.
Add remote wakeup support for ehci.
Add suspend support for xhci.
Misc minor tweaks and fixes.
# gpg: Signature made Thu 28 Nov 2013 11:44:49 PM PST using RSA key ID D3E87138
# gpg: Can't check signature: public key not found
# By Hans de Goede (11) and others
# Via Gerd Hoffmann
* kraxel/tags/pull-usb-1:
usb: move usb_{hi,lo} helpers to header file.
usb: add vendor request defines
trace-events: Clean up after removal of old usb-host code
Revert "usb-tablet: Don't claim wakeup capability for USB-2 version"
ehci: implement port wakeup
xhci: Call usb_device_alloc/free_streams
usb: Add usb_device_alloc/free_streams
usb: Add max_streams attribute to endpoint info
uas: s/ui/iu/
uas: Fix response iu struct definition
uas: Bounds check tags when using streams
uas: Streams are numbered 1-y, rather then 0-x
uas: Fix / cleanup usb_uas_task error handling
uas: Only use report iu-s for task_mgmt status reporting
scsi: Add 2 new sense codes needed by uas
xhci: add support for suspend/resume
xhci: Add a few missing checks for disconnected devices
Message-id: 1385712381-30918-1-git-send-email-kraxel@redhat.com
Signed-off-by: Anthony Liguori <aliguori@amazon.com>
-rw-r--r-- | hw/scsi/scsi-bus.c | 10 | ||||
-rw-r--r-- | hw/usb/bus.c | 18 | ||||
-rw-r--r-- | hw/usb/core.c | 22 | ||||
-rw-r--r-- | hw/usb/desc.c | 12 | ||||
-rw-r--r-- | hw/usb/desc.h | 11 | ||||
-rw-r--r-- | hw/usb/dev-hid.c | 2 | ||||
-rw-r--r-- | hw/usb/dev-uas.c | 156 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.c | 18 | ||||
-rw-r--r-- | hw/usb/hcd-xhci.c | 138 | ||||
-rw-r--r-- | include/hw/scsi/scsi.h | 4 | ||||
-rw-r--r-- | include/hw/usb.h | 32 | ||||
-rw-r--r-- | trace-events | 16 |
12 files changed, 335 insertions, 104 deletions
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index ea916d1..3e5e31d 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1293,6 +1293,11 @@ const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 }; +/* Illegal request, Invalid Transfer Tag */ +const struct SCSISense sense_code_INVALID_TAG = { + .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 +}; + /* Command aborted, I/O process terminated */ const struct SCSISense sense_code_IO_ERROR = { .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 @@ -1308,6 +1313,11 @@ const struct SCSISense sense_code_LUN_FAILURE = { .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 }; +/* Command aborted, Overlapped Commands Attempted */ +const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { + .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 +}; + /* Unit attention, Capacity data has changed */ const struct SCSISense sense_code_CAPACITY_CHANGED = { .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 diff --git a/hw/usb/bus.c b/hw/usb/bus.c index ca329be..09848c6 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -203,6 +203,24 @@ void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *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 int usb_qdev_init(DeviceState *qdev) { USBDevice *dev = USB_DEVICE(qdev); diff --git a/hw/usb/core.c b/hw/usb/core.c index cf59a1a..67ba7d6 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -623,6 +623,7 @@ void usb_ep_reset(USBDevice *dev) 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++) { @@ -636,6 +637,8 @@ void usb_ep_reset(USBDevice *dev) 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; @@ -764,6 +767,25 @@ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep) return uep->max_packet_size; } +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; + } +} + +int usb_ep_get_max_streams(USBDevice *dev, int pid, int ep) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + return uep->max_streams; +} + void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) { struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); diff --git a/hw/usb/desc.c b/hw/usb/desc.c index bf6c522..f18a043 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -6,16 +6,6 @@ /* ------------------------------------------------------------------ */ -static uint8_t usb_lo(uint16_t val) -{ - return val & 0xff; -} - -static uint8_t usb_hi(uint16_t val) -{ - return (val >> 8) & 0xff; -} - int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, uint8_t *dest, size_t len) { @@ -385,6 +375,8 @@ static void usb_desc_ep_init(USBDevice *dev) 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); } } } diff --git a/hw/usb/desc.h b/hw/usb/desc.h index ddd3e74..81327b0 100644 --- a/hw/usb/desc.h +++ b/hw/usb/desc.h @@ -194,6 +194,17 @@ struct USBDesc { #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, uint8_t *dest, size_t len); diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 5956720..5e667f0 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -236,7 +236,7 @@ static const USBDescDevice desc_device_tablet2 = { .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = STR_CONFIG_TABLET, - .bmAttributes = 0x80, + .bmAttributes = 0xa0, .bMaxPower = 50, .nif = 1, .ifs = &desc_iface_tablet2, diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 70ed2d1..997b715 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -55,7 +55,7 @@ typedef struct { uint8_t id; uint8_t reserved; uint16_t tag; -} QEMU_PACKED uas_ui_header; +} QEMU_PACKED uas_iu_header; typedef struct { uint8_t prio_taskattr; /* 6:3 priority, 2:0 task attribute */ @@ -65,7 +65,7 @@ typedef struct { uint64_t lun; uint8_t cdb[16]; uint8_t add_cdb[]; -} QEMU_PACKED uas_ui_command; +} QEMU_PACKED uas_iu_command; typedef struct { uint16_t status_qualifier; @@ -73,29 +73,29 @@ typedef struct { uint8_t reserved[7]; uint16_t sense_length; uint8_t sense_data[18]; -} QEMU_PACKED uas_ui_sense; +} QEMU_PACKED uas_iu_sense; typedef struct { - uint16_t add_response_info; + uint8_t add_response_info[3]; uint8_t response_code; -} QEMU_PACKED uas_ui_response; +} QEMU_PACKED uas_iu_response; typedef struct { uint8_t function; uint8_t reserved; uint16_t task_tag; uint64_t lun; -} QEMU_PACKED uas_ui_task_mgmt; +} QEMU_PACKED uas_iu_task_mgmt; typedef struct { - uas_ui_header hdr; + uas_iu_header hdr; union { - uas_ui_command command; - uas_ui_sense sense; - uas_ui_task_mgmt task; - uas_ui_response response; + uas_iu_command command; + uas_iu_sense sense; + uas_iu_task_mgmt task; + uas_iu_response response; }; -} QEMU_PACKED uas_ui; +} QEMU_PACKED uas_iu; /* --------------------------------------------------------------------- */ @@ -122,8 +122,8 @@ struct UASDevice { UASRequest *dataout2; /* usb 3.0 only */ - USBPacket *data3[UAS_MAX_STREAMS]; - USBPacket *status3[UAS_MAX_STREAMS]; + USBPacket *data3[UAS_MAX_STREAMS + 1]; + USBPacket *status3[UAS_MAX_STREAMS + 1]; }; struct UASRequest { @@ -145,7 +145,7 @@ struct UASRequest { struct UASStatus { uint32_t stream; - uas_ui status; + uas_iu status; uint32_t length; QTAILQ_ENTRY(UASStatus) next; }; @@ -338,7 +338,7 @@ static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag) st->status.hdr.id = id; st->status.hdr.tag = cpu_to_be16(tag); - st->length = sizeof(uas_ui_header); + st->length = sizeof(uas_iu_header); if (uas_using_streams(uas)) { st->stream = tag; } @@ -392,15 +392,13 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) } } -static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, - uint8_t code, uint16_t add_info) +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; - st->status.response.add_response_info = cpu_to_be16(add_info); - usb_uas_queue_status(uas, st, sizeof(uas_ui_response)); + usb_uas_queue_status(uas, st, sizeof(uas_iu_response)); } static void usb_uas_queue_sense(UASRequest *req, uint8_t status) @@ -416,10 +414,28 @@ static void usb_uas_queue_sense(UASRequest *req, uint8_t status) sizeof(st->status.sense.sense_data)); st->status.sense.sense_length = cpu_to_be16(slen); } - len = sizeof(uas_ui_sense) - sizeof(st->status.sense.sense_data) + 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, @@ -518,14 +534,14 @@ static void usb_uas_start_next_transfer(UASDevice *uas) } } -static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_ui *ui) +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(ui->hdr.tag); - req->lun = be64_to_cpu(ui->command.lun); + 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; } @@ -648,7 +664,7 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) return; } if (uas_using_streams(uas)) { - for (i = 0; i < UAS_MAX_STREAMS; i++) { + for (i = 0; i <= UAS_MAX_STREAMS; i++) { if (uas->status3[i] == p) { uas->status3[i] = NULL; return; @@ -668,16 +684,20 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) assert(!"canceled usb packet not found"); } -static void usb_uas_command(UASDevice *uas, uas_ui *ui) +static void usb_uas_command(UASDevice *uas, uas_iu *iu) { UASRequest *req; uint32_t len; + uint16_t tag = be16_to_cpu(iu->hdr.tag); - req = usb_uas_find_request(uas, be16_to_cpu(ui->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, ui); + req = usb_uas_alloc_request(uas, iu); if (req->dev == NULL) { goto bad_target; } @@ -694,7 +714,7 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui) req->req = scsi_req_new(req->dev, req->tag, usb_uas_get_lun(req->lun), - ui->command.cdb, req); + iu->command.cdb, req); if (uas->requestlog) { scsi_req_print(req->req); } @@ -705,105 +725,97 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui) } return; +invalid_tag: + usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG); + return; + overlapped_tag: - usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0); + usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS); return; bad_target: - /* - * FIXME: Seems to upset linux, is this wrong? - * NOTE: Happens only with no scsi devices at the bus, not sure - * this is a valid UAS setup in the first place. - */ - usb_uas_queue_response(uas, req->tag, UAS_RC_INVALID_INFO_UNIT, 0); + usb_uas_queue_fake_sense(uas, tag, sense_code_LUN_NOT_SUPPORTED); g_free(req); } -static void usb_uas_task(UASDevice *uas, uas_ui *ui) +static void usb_uas_task(UASDevice *uas, uas_iu *iu) { - uint16_t tag = be16_to_cpu(ui->hdr.tag); - uint64_t lun64 = be64_to_cpu(ui->task.lun); + 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; - req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.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 (ui->task.function) { + switch (iu->task.function) { case UAS_TMF_ABORT_TASK: - task_tag = be16_to_cpu(ui->task.task_tag); + task_tag = be16_to_cpu(iu->task.task_tag); trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag); - if (dev == NULL) { - goto bad_target; - } - if (dev->lun != lun) { - goto incorrect_lun; - } 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, 0); + 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); - if (dev == NULL) { - goto bad_target; - } - if (dev->lun != lun) { - goto incorrect_lun; - } qdev_reset_all(&dev->qdev); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0); + usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); break; default: - trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, ui->task.function); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED, 0); + 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; -overlapped_tag: - usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0); +invalid_tag: + usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT); return; -bad_target: - /* FIXME: correct? [see long comment in usb_uas_command()] */ - usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT, 0); +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, 0); + usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN); } static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) { UASDevice *uas = DO_UPCAST(UASDevice, dev, dev); - uas_ui ui; + uas_iu iu; UASStatus *st; UASRequest *req; int length; switch (p->ep->nr) { case UAS_PIPE_ID_COMMAND: - length = MIN(sizeof(ui), p->iov.size); - usb_packet_copy(p, &ui, length); - switch (ui.hdr.id) { + 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, &ui); + usb_uas_command(uas, &iu); break; case UAS_UI_TASK_MGMT: - usb_uas_task(uas, &ui); + usb_uas_task(uas, &iu); break; default: - fprintf(stderr, "%s: unknown command ui: id 0x%x\n", - __func__, ui.hdr.id); + fprintf(stderr, "%s: unknown command iu: id 0x%x\n", + __func__, iu.hdr.id); p->status = USB_RET_STALL; break; } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 0ba38c9..355bbd6 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -827,9 +827,9 @@ static void ehci_child_detach(USBPort *port, USBDevice *child) static void ehci_wakeup(USBPort *port) { EHCIState *s = port->opaque; - uint32_t portsc = s->portsc[port->index]; + uint32_t *portsc = &s->portsc[port->index]; - if (portsc & PORTSC_POWNER) { + if (*portsc & PORTSC_POWNER) { USBPort *companion = s->companion_ports[port->index]; if (companion->ops->wakeup) { companion->ops->wakeup(companion); @@ -837,6 +837,12 @@ static void ehci_wakeup(USBPort *port) 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); } @@ -1068,6 +1074,14 @@ static void ehci_port_write(void *ptr, hwaddr addr, } } + 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); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 835f65e..bafe085 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1150,6 +1150,111 @@ static void xhci_free_streams(XHCIEPContext *epctx) 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 & (1 << 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) { + fprintf(stderr, "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) @@ -1495,7 +1600,8 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, } if (!xhci->slots[slotid-1].uport || - !xhci->slots[slotid-1].uport->dev) { + !xhci->slots[slotid-1].uport->dev || + !xhci->slots[slotid-1].uport->dev->attached) { return CC_USB_TRANSACTION_ERROR; } @@ -1982,6 +2088,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int 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; @@ -2206,7 +2320,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, trace_usb_xhci_slot_address(slotid, uport->path); dev = uport->dev; - if (!dev) { + if (!dev || !dev->attached) { fprintf(stderr, "xhci: port %s not connected\n", uport->path); return CC_USB_TRANSACTION_ERROR; } @@ -2313,6 +2427,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, 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); @@ -2334,6 +2450,16 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, } } + res = xhci_alloc_device_streams(xhci, slotid, ictl_ctx[1]); + if (res != CC_SUCCESS) { + for (i = 2; i <= 31; i++) { + if (ictl_ctx[1] & (1 << 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); @@ -3016,6 +3142,14 @@ static void xhci_oper_write(void *ptr, hwaddr reg, } 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) { diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 76f6ac2..bf6da3d 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -199,12 +199,16 @@ extern const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED; extern const struct SCSISense sense_code_INCOMPATIBLE_FORMAT; /* Illegal request, medium removal prevented */ extern const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED; +/* Illegal request, Invalid Transfer Tag */ +extern const struct SCSISense sense_code_INVALID_TAG; /* Command aborted, I/O process terminated */ extern const struct SCSISense sense_code_IO_ERROR; /* Command aborted, I_T Nexus loss occurred */ extern const struct SCSISense sense_code_I_T_NEXUS_LOSS; /* Command aborted, Logical Unit failure */ extern const struct SCSISense sense_code_LUN_FAILURE; +/* Command aborted, Overlapped Commands Attempted */ +extern const struct SCSISense sense_code_OVERLAPPED_COMMANDS; /* LUN not ready, Capacity data has changed */ extern const struct SCSISense sense_code_CAPACITY_CHANGED; /* LUN not ready, Medium not present */ diff --git a/include/hw/usb.h b/include/hw/usb.h index a7680d4..2a3ea0c 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -102,17 +102,26 @@ #define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) #define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) -#define InterfaceRequest \ +#define VendorDeviceRequest ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) +#define VendorDeviceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) + +#define InterfaceRequest \ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) #define InterfaceOutRequest \ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) -#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) -#define EndpointOutRequest \ - ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) #define ClassInterfaceRequest \ ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) #define ClassInterfaceOutRequest \ ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) +#define VendorInterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8) +#define VendorInterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8) + +#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) +#define EndpointOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) #define USB_REQ_GET_STATUS 0x00 #define USB_REQ_CLEAR_FEATURE 0x01 @@ -189,6 +198,7 @@ struct USBEndpoint { uint8_t type; uint8_t ifnum; int max_packet_size; + int max_streams; bool pipeline; bool halted; USBDevice *dev; @@ -314,6 +324,14 @@ typedef struct USBDeviceClass { */ void (*ep_stopped)(USBDevice *dev, USBEndpoint *ep); + /* + * Called by the hcd to alloc / free streams on a bulk endpoint. + * Optional may be NULL. + */ + int (*alloc_streams)(USBDevice *dev, USBEndpoint **eps, int nr_eps, + int streams); + void (*free_streams)(USBDevice *dev, USBEndpoint **eps, int nr_eps); + const char *product_desc; const USBDesc *usb_desc; } USBDeviceClass; @@ -421,6 +439,8 @@ void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum); void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, uint16_t raw); int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep); +void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw); +int usb_ep_get_max_streams(USBDevice *dev, int pid, int ep); void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled); void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted); USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, @@ -550,6 +570,10 @@ void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep); void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep); +int usb_device_alloc_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps, + int streams); +void usb_device_free_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps); + const char *usb_device_get_product_desc(USBDevice *dev); const USBDesc *usb_device_get_usb_desc(USBDevice *dev); diff --git a/trace-events b/trace-events index bc6788f..e78a8d3 100644 --- a/trace-events +++ b/trace-events @@ -309,6 +309,9 @@ usb_ehci_sitd(uint32_t addr, uint32_t nxt, uint32_t active) "ITD @ %08x: next %0 usb_ehci_port_attach(uint32_t port, const char *owner, const char *device) "attach port #%d, owner %s, device %s" usb_ehci_port_detach(uint32_t port, const char *owner) "detach port #%d, owner %s" usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" +usb_ehci_port_suspend(uint32_t port) "port #%d" +usb_ehci_port_wakeup(uint32_t port) "port #%d" +usb_ehci_port_resume(uint32_t port) "port #%d" usb_ehci_queue_action(void *q, const char *action) "q %p: %s" usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s" usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x" @@ -427,45 +430,32 @@ usb_uas_tmf_abort_task(int addr, uint16_t tag, uint16_t task_tag) "dev %d, tag 0 usb_uas_tmf_logical_unit_reset(int addr, uint16_t tag, int lun) "dev %d, tag 0x%x, lun %d" usb_uas_tmf_unsupported(int addr, uint16_t tag, uint32_t function) "dev %d, tag 0x%x, function 0x%x" -# hw/usb/host-linux.c # hw/usb/host-libusb.c usb_host_open_started(int bus, int addr) "dev %d:%d" usb_host_open_success(int bus, int addr) "dev %d:%d" usb_host_open_failure(int bus, int addr) "dev %d:%d" -usb_host_disconnect(int bus, int addr) "dev %d:%d" usb_host_close(int bus, int addr) "dev %d:%d" usb_host_attach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d" usb_host_detach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d" usb_host_set_address(int bus, int addr, int config) "dev %d:%d, address %d" usb_host_set_config(int bus, int addr, int config) "dev %d:%d, config %d" usb_host_set_interface(int bus, int addr, int interface, int alt) "dev %d:%d, interface %d, alt %d" -usb_host_claim_interfaces(int bus, int addr, int config, int nif) "dev %d:%d, config %d, nif %d" usb_host_claim_interface(int bus, int addr, int config, int interface) "dev %d:%d, config %d, if %d" -usb_host_release_interfaces(int bus, int addr) "dev %d:%d" usb_host_release_interface(int bus, int addr, int interface) "dev %d:%d, if %d" usb_host_req_control(int bus, int addr, void *p, int req, int value, int index) "dev %d:%d, packet %p, req 0x%x, value %d, index %d" usb_host_req_data(int bus, int addr, void *p, int in, int ep, int size) "dev %d:%d, packet %p, in %d, ep %d, size %d" usb_host_req_complete(int bus, int addr, void *p, int status, int length) "dev %d:%d, packet %p, status %d, length %d" usb_host_req_emulated(int bus, int addr, void *p, int status) "dev %d:%d, packet %p, status %d" usb_host_req_canceled(int bus, int addr, void *p) "dev %d:%d, packet %p" -usb_host_urb_submit(int bus, int addr, void *aurb, int length, int more) "dev %d:%d, aurb %p, length %d, more %d" -usb_host_urb_complete(int bus, int addr, void *aurb, int status, int length, int more) "dev %d:%d, aurb %p, status %d, length %d, more %d" -usb_host_urb_canceled(int bus, int addr, void *aurb) "dev %d:%d, aurb %p" -usb_host_ep_set_halt(int bus, int addr, int ep) "dev %d:%d, ep %d" -usb_host_ep_clear_halt(int bus, int addr, int ep) "dev %d:%d, ep %d" usb_host_iso_start(int bus, int addr, int ep) "dev %d:%d, ep %d" usb_host_iso_stop(int bus, int addr, int ep) "dev %d:%d, ep %d" usb_host_iso_out_of_bufs(int bus, int addr, int ep) "dev %d:%d, ep %d" -usb_host_iso_many_urbs(int bus, int addr, int count) "dev %d:%d, count %d" usb_host_reset(int bus, int addr) "dev %d:%d" usb_host_auto_scan_enabled(void) usb_host_auto_scan_disabled(void) -usb_host_claim_port(int bus, int hub, int port) "bus %d, hub addr %d, port %d" -usb_host_parse_device(int bus, int addr, int vendor, int product) "dev %d:%d, id %04x:%04x" usb_host_parse_config(int bus, int addr, int value, int active) "dev %d:%d, value %d, active %d" usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev %d:%d, num %d, alt %d, active %d" usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d" -usb_host_parse_unknown(int bus, int addr, int len, int type) "dev %d:%d, len %d, type %d" usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s" # hw/scsi/scsi-bus.c |