mirror of
https://github.com/qemu/qemu.git
synced 2024-11-25 20:03:37 +08:00
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJSmEXxAAoJEEy22O7T6HE47t0QALonQORRj0IUAH0cOdfAhlQ3 tGMQksBCYevBatKt4iZQgkw6H0jwse6QfsgsG2dfznEO+ZWsrt9cxe1UrqxbK2PN 2PY/I9Ke1iP6tjcf9ftjqt+mZcAg/FHrbua5hb8zXRQnqu2jr0y3Cp7k2Jax4j4d Zl2FJ+sd4lGNR3Qpb85Muxtii8XERmMqvAit72VN4VAW4iE+SQAFSOgzBC512b55 wLVc6DrbnM8I4AVJQ8RH2pMQau0/aBHFbU8By2RKbymkJmIG2nFqLH6eSJ19QgzY CmX8yGDJM5LGAGRZCeDSeuilxFU/WCSoTtkL8cPcYUv4cSTm+forzxhVz+CVOeVu JJsWNkaIxu4mxfRyADjUKkWoKX7ACro3ErfAWHdv8hwuhZ4uD6cf2++nXVDK9dq4 yLL2nR4YG0NTOdQNKrsUbltf9gC5cWqNRgVMJ5VfqIBGtjXdTbpGpcUEFuDDegjk GhfN8lcpqgnFj0U4fAGLxHYXHvJRpNeWzEEANPuEYnWr2tSrgBWKkYLaooTDHt5r FUE6lmKL+BzQYnXfWWqh1fZoiBzzrMaT3OkHc2vx/SrGLuO/rVWTzXsFQI+NGPHp XxuyocFoKZA2yGr9h6eBBp9mtd5y0oOVxBR0WbkgvmbyxkX7Zq9r2PSoDOm26oE3 5kmApAnSij83aT06Qe8P =2yvC -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
e679f05248
@ -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
|
||||
|
18
hw/usb/bus.c
18
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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
156
hw/usb/dev-uas.c
156
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
16
trace-events
16
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
|
||||
|
Loading…
Reference in New Issue
Block a user