Merge remote-tracking branch 'kraxel/usb.78' into staging
# By Gerd Hoffmann # Via Gerd Hoffmann * kraxel/usb.78: uas-uas: usb3 streams usb-xhci: usb3 streams usb-core: usb3 streams usb: fix endpoint descriptor ordering usb-redir: simplify packet copy usb: make usb_packet_copy operate on combined packets usb: add usb_ep_set_halted usb-host: remove usb_host_device_close usb-host: move legacy cmd line bits usb-storage: use scsi_req_enqueue return value allow disabling usb smartcard support make usb devices configurable fix scripts/make_device_config.sh usb: Makefile cleanup
This commit is contained in:
commit
70aa41b56c
2
configure
vendored
2
configure
vendored
@ -3723,7 +3723,7 @@ fi
|
||||
# USB host support
|
||||
case "$usb" in
|
||||
linux)
|
||||
echo "HOST_USB=linux" >> $config_host_mak
|
||||
echo "HOST_USB=linux legacy" >> $config_host_mak
|
||||
;;
|
||||
bsd)
|
||||
echo "HOST_USB=bsd" >> $config_host_mak
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for alpha-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_I8254=y
|
||||
CONFIG_PCKBD=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for arm-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_VGA=y
|
||||
CONFIG_ISA_MMIO=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for i386-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_VGA=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_VGA_ISA=y
|
||||
|
@ -1,5 +1,6 @@
|
||||
# Default configuration for m68k-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_PTIMER=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for mips-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESP=y
|
||||
CONFIG_VGA=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for mips64-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESP=y
|
||||
CONFIG_VGA=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for mips64el-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESP=y
|
||||
CONFIG_VGA=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for mipsel-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESP=y
|
||||
CONFIG_VGA=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for ppc-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESCC=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for ppc64-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESCC=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for ppcemb-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_GDBSTUB_XML=y
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_ESCC=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for sh4-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PTIMER=y
|
||||
CONFIG_PFLASH_CFI02=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for sh4eb-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PTIMER=y
|
||||
CONFIG_PFLASH_CFI02=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for sparc64-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_ISA_MMIO=y
|
||||
CONFIG_M48T59=y
|
||||
CONFIG_PTIMER=y
|
||||
|
8
default-configs/usb.mak
Normal file
8
default-configs/usb.mak
Normal file
@ -0,0 +1,8 @@
|
||||
CONFIG_USB_TABLET_WACOM=y
|
||||
CONFIG_USB_STORAGE_BOT=y
|
||||
CONFIG_USB_STORAGE_UAS=y
|
||||
CONFIG_USB_SMARTCARD=y
|
||||
CONFIG_USB_AUDIO=y
|
||||
CONFIG_USB_SERIAL=y
|
||||
CONFIG_USB_NETWORK=y
|
||||
CONFIG_USB_BLUETOOTH=y
|
@ -1,6 +1,7 @@
|
||||
# Default configuration for x86_64-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_VGA=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_VGA_ISA=y
|
||||
|
@ -38,8 +38,10 @@ common-obj-$(CONFIG_DMA) += dma.o
|
||||
common-obj-$(CONFIG_I82374) += i82374.o
|
||||
common-obj-$(CONFIG_HPET) += hpet.o
|
||||
common-obj-$(CONFIG_APPLESMC) += applesmc.o
|
||||
ifeq ($(CONFIG_USB_SMARTCARD),y)
|
||||
common-obj-y += ccid-card-passthru.o
|
||||
common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
|
||||
endif
|
||||
common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
|
||||
common-obj-y += fifo.o
|
||||
common-obj-y += pam.o
|
||||
|
13
hw/usb.h
13
hw/usb.h
@ -361,6 +361,7 @@ struct USBPacket {
|
||||
int pid;
|
||||
uint64_t id;
|
||||
USBEndpoint *ep;
|
||||
unsigned int stream;
|
||||
QEMUIOVector iov;
|
||||
uint64_t parameter; /* control transfers */
|
||||
bool short_not_ok;
|
||||
@ -383,13 +384,15 @@ struct USBCombinedPacket {
|
||||
void usb_packet_init(USBPacket *p);
|
||||
void usb_packet_set_state(USBPacket *p, USBPacketState state);
|
||||
void usb_packet_check_state(USBPacket *p, USBPacketState expected);
|
||||
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
|
||||
bool short_not_ok, bool int_req);
|
||||
void usb_packet_setup(USBPacket *p, int pid,
|
||||
USBEndpoint *ep, unsigned int stream,
|
||||
uint64_t id, bool short_not_ok, bool int_req);
|
||||
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
|
||||
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
|
||||
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl);
|
||||
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes);
|
||||
void usb_packet_skip(USBPacket *p, size_t bytes);
|
||||
size_t usb_packet_size(USBPacket *p);
|
||||
void usb_packet_cleanup(USBPacket *p);
|
||||
|
||||
static inline bool usb_packet_is_inflight(USBPacket *p)
|
||||
@ -417,6 +420,7 @@ 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_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,
|
||||
uint64_t id);
|
||||
|
||||
@ -428,13 +432,12 @@ void usb_attach(USBPort *port);
|
||||
void usb_detach(USBPort *port);
|
||||
void usb_port_reset(USBPort *port);
|
||||
void usb_device_reset(USBDevice *dev);
|
||||
void usb_wakeup(USBEndpoint *ep);
|
||||
void usb_wakeup(USBEndpoint *ep, unsigned int stream);
|
||||
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
|
||||
int set_usb_string(uint8_t *buf, const char *str);
|
||||
|
||||
/* usb-linux.c */
|
||||
USBDevice *usb_host_device_open(USBBus *bus, const char *devname);
|
||||
int usb_host_device_close(const char *devname);
|
||||
void usb_host_info(Monitor *mon, const QDict *qdict);
|
||||
|
||||
/* usb-bt.c */
|
||||
@ -488,7 +491,7 @@ struct USBBus {
|
||||
struct USBBusOps {
|
||||
int (*register_companion)(USBBus *bus, USBPort *ports[],
|
||||
uint32_t portcount, uint32_t firstport);
|
||||
void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep);
|
||||
void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep, unsigned int stream);
|
||||
};
|
||||
|
||||
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
|
||||
|
@ -1,14 +1,30 @@
|
||||
# usb subsystem core
|
||||
common-obj-y += core.o combined-packet.o bus.o desc.o
|
||||
common-obj-y += libhw.o
|
||||
|
||||
# usb host adapters
|
||||
common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o
|
||||
common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o
|
||||
common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o hcd-ehci-sysbus.o
|
||||
common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
|
||||
common-obj-y += libhw.o
|
||||
|
||||
# emulated usb devices
|
||||
common-obj-y += dev-hub.o
|
||||
common-obj-y += dev-hid.o
|
||||
common-obj-$(CONFIG_USB_TABLET_WACOM) += dev-wacom.o
|
||||
common-obj-$(CONFIG_USB_STORAGE_BOT) += dev-storage.o
|
||||
common-obj-$(CONFIG_USB_STORAGE_UAS) += dev-uas.o
|
||||
common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o
|
||||
common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o
|
||||
common-obj-$(CONFIG_USB_NETWORK) += dev-network.o
|
||||
|
||||
# FIXME: make configurable too
|
||||
CONFIG_USB_BLUETOOTH := y
|
||||
common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o
|
||||
common-obj-$(CONFIG_USB_SMARTCARD) += dev-smartcard-reader.o
|
||||
|
||||
# usb redirection
|
||||
common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o
|
||||
|
||||
common-obj-y += core.o combined-packet.o bus.o desc.o dev-hub.o
|
||||
common-obj-y += host-$(HOST_USB).o dev-bluetooth.o
|
||||
common-obj-y += dev-hid.o dev-storage.o dev-wacom.o
|
||||
common-obj-y += dev-serial.o dev-network.o dev-audio.o
|
||||
common-obj-y += dev-smartcard-reader.o
|
||||
common-obj-y += dev-uas.o
|
||||
# usb pass-through
|
||||
common-obj-y += $(patsubst %,host-%.o,$(HOST_USB))
|
||||
|
@ -71,7 +71,7 @@ void usb_device_reset(USBDevice *dev)
|
||||
usb_device_handle_reset(dev);
|
||||
}
|
||||
|
||||
void usb_wakeup(USBEndpoint *ep)
|
||||
void usb_wakeup(USBEndpoint *ep, unsigned int stream)
|
||||
{
|
||||
USBDevice *dev = ep->dev;
|
||||
USBBus *bus = usb_bus_from_device(dev);
|
||||
@ -80,7 +80,7 @@ void usb_wakeup(USBEndpoint *ep)
|
||||
dev->port->ops->wakeup(dev->port);
|
||||
}
|
||||
if (bus->ops->wakeup_endpoint) {
|
||||
bus->ops->wakeup_endpoint(bus, ep);
|
||||
bus->ops->wakeup_endpoint(bus, ep, stream);
|
||||
}
|
||||
}
|
||||
|
||||
@ -545,14 +545,16 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
|
||||
p->state = state;
|
||||
}
|
||||
|
||||
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
|
||||
bool short_not_ok, bool int_req)
|
||||
void usb_packet_setup(USBPacket *p, int pid,
|
||||
USBEndpoint *ep, unsigned int stream,
|
||||
uint64_t id, bool short_not_ok, bool int_req)
|
||||
{
|
||||
assert(!usb_packet_is_inflight(p));
|
||||
assert(p->iov.iov != NULL);
|
||||
p->id = id;
|
||||
p->pid = pid;
|
||||
p->ep = ep;
|
||||
p->stream = stream;
|
||||
p->status = USB_RET_SUCCESS;
|
||||
p->actual_length = 0;
|
||||
p->parameter = 0;
|
||||
@ -570,15 +572,17 @@ void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
|
||||
|
||||
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
|
||||
{
|
||||
QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
|
||||
|
||||
assert(p->actual_length >= 0);
|
||||
assert(p->actual_length + bytes <= p->iov.size);
|
||||
assert(p->actual_length + bytes <= iov->size);
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
case USB_TOKEN_OUT:
|
||||
iov_to_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
|
||||
iov_to_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes);
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
iov_from_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
|
||||
iov_from_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
|
||||
@ -589,14 +593,21 @@ void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
|
||||
|
||||
void usb_packet_skip(USBPacket *p, size_t bytes)
|
||||
{
|
||||
QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
|
||||
|
||||
assert(p->actual_length >= 0);
|
||||
assert(p->actual_length + bytes <= p->iov.size);
|
||||
assert(p->actual_length + bytes <= iov->size);
|
||||
if (p->pid == USB_TOKEN_IN) {
|
||||
iov_memset(p->iov.iov, p->iov.niov, p->actual_length, 0, bytes);
|
||||
iov_memset(iov->iov, iov->niov, p->actual_length, 0, bytes);
|
||||
}
|
||||
p->actual_length += bytes;
|
||||
}
|
||||
|
||||
size_t usb_packet_size(USBPacket *p)
|
||||
{
|
||||
return p->combined ? p->combined->iov.size : p->iov.size;
|
||||
}
|
||||
|
||||
void usb_packet_cleanup(USBPacket *p)
|
||||
{
|
||||
assert(!usb_packet_is_inflight(p));
|
||||
@ -755,6 +766,12 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled)
|
||||
uep->pipeline = enabled;
|
||||
}
|
||||
|
||||
void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted)
|
||||
{
|
||||
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
|
||||
uep->halted = halted;
|
||||
}
|
||||
|
||||
USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
|
||||
uint64_t id)
|
||||
{
|
||||
|
@ -225,12 +225,9 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
|
||||
d->u.endpoint.bRefresh = ep->bRefresh;
|
||||
d->u.endpoint.bSynchAddress = ep->bSynchAddress;
|
||||
}
|
||||
if (ep->extra) {
|
||||
memcpy(dest + bLength, ep->extra, extralen);
|
||||
}
|
||||
|
||||
if (superlen) {
|
||||
USBDescriptor *d = (void *)(dest + bLength + extralen);
|
||||
USBDescriptor *d = (void *)(dest + bLength);
|
||||
|
||||
d->bLength = 0x06;
|
||||
d->bDescriptorType = USB_DT_ENDPOINT_COMPANION;
|
||||
@ -243,6 +240,10 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
|
||||
usb_hi(ep->wBytesPerInterval);
|
||||
}
|
||||
|
||||
if (ep->extra) {
|
||||
memcpy(dest + bLength + superlen, ep->extra, extralen);
|
||||
}
|
||||
|
||||
return bLength + extralen + superlen;
|
||||
}
|
||||
|
||||
|
@ -478,7 +478,7 @@ static void usb_bt_out_hci_packet_event(void *opaque,
|
||||
struct USBBtState *s = (struct USBBtState *) opaque;
|
||||
|
||||
if (s->evt.len == 0) {
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
usb_bt_fifo_enqueue(&s->evt, data, len);
|
||||
}
|
||||
|
@ -423,7 +423,7 @@ static void usb_hid_changed(HIDState *hs)
|
||||
{
|
||||
USBHIDState *us = container_of(hs, USBHIDState, hid);
|
||||
|
||||
usb_wakeup(us->intr);
|
||||
usb_wakeup(us->intr, 0);
|
||||
}
|
||||
|
||||
static void usb_hid_handle_reset(USBDevice *dev)
|
||||
|
@ -164,7 +164,7 @@ static void usb_hub_attach(USBPort *port1)
|
||||
} else {
|
||||
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
|
||||
}
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
|
||||
static void usb_hub_detach(USBPort *port1)
|
||||
@ -173,7 +173,7 @@ static void usb_hub_detach(USBPort *port1)
|
||||
USBHubPort *port = &s->ports[port1->index];
|
||||
|
||||
trace_usb_hub_detach(s->dev.addr, port1->index + 1);
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
|
||||
/* Let upstream know the device on this port is gone */
|
||||
s->dev.port->ops->child_detach(s->dev.port, port1->dev);
|
||||
@ -184,7 +184,7 @@ static void usb_hub_detach(USBPort *port1)
|
||||
port->wPortStatus &= ~PORT_STAT_ENABLE;
|
||||
port->wPortChange |= PORT_STAT_C_ENABLE;
|
||||
}
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
|
||||
static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
|
||||
@ -202,7 +202,7 @@ static void usb_hub_wakeup(USBPort *port1)
|
||||
|
||||
if (port->wPortStatus & PORT_STAT_SUSPEND) {
|
||||
port->wPortChange |= PORT_STAT_C_SUSPEND;
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,7 +364,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
||||
port->wPortChange |= PORT_STAT_C_RESET;
|
||||
/* set enable bit */
|
||||
port->wPortStatus |= PORT_STAT_ENABLE;
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
break;
|
||||
case PORT_POWER:
|
||||
|
@ -855,7 +855,7 @@ static void *rndis_queue_response(USBNetState *s, unsigned int length)
|
||||
g_malloc0(sizeof(struct rndis_response) + length);
|
||||
|
||||
if (QTAILQ_EMPTY(&s->rndis_resp)) {
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries);
|
||||
|
@ -839,7 +839,7 @@ static void ccid_on_slot_change(USBCCIDState *s, bool full)
|
||||
s->bmSlotICCState |= SLOT_0_CHANGED_MASK;
|
||||
}
|
||||
s->notify_slot_change = true;
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
|
||||
static void ccid_write_data_block_error(
|
||||
|
@ -400,6 +400,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
struct usb_msd_cbw cbw;
|
||||
uint8_t devep = p->ep->nr;
|
||||
SCSIDevice *scsi_dev;
|
||||
uint32_t len;
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
@ -441,8 +442,8 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
#ifdef DEBUG_MSD
|
||||
scsi_req_print(s->req);
|
||||
#endif
|
||||
scsi_req_enqueue(s->req);
|
||||
if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) {
|
||||
len = scsi_req_enqueue(s->req);
|
||||
if (len) {
|
||||
scsi_req_continue(s->req);
|
||||
}
|
||||
break;
|
||||
|
247
hw/usb/dev-uas.c
247
hw/usb/dev-uas.c
@ -99,6 +99,9 @@ typedef struct {
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define UAS_STREAM_BM_ATTR 4
|
||||
#define UAS_MAX_STREAMS (1 << UAS_STREAM_BM_ATTR)
|
||||
|
||||
typedef struct UASDevice UASDevice;
|
||||
typedef struct UASRequest UASRequest;
|
||||
typedef struct UASStatus UASStatus;
|
||||
@ -106,12 +109,18 @@ typedef struct UASStatus UASStatus;
|
||||
struct UASDevice {
|
||||
USBDevice dev;
|
||||
SCSIBus bus;
|
||||
UASRequest *datain;
|
||||
UASRequest *dataout;
|
||||
USBPacket *status;
|
||||
QEMUBH *status_bh;
|
||||
QTAILQ_HEAD(, UASStatus) results;
|
||||
QTAILQ_HEAD(, UASRequest) requests;
|
||||
|
||||
/* usb 2.0 only */
|
||||
USBPacket *status2;
|
||||
UASRequest *datain2;
|
||||
UASRequest *dataout2;
|
||||
|
||||
/* usb 3.0 only */
|
||||
USBPacket *data3[UAS_MAX_STREAMS];
|
||||
USBPacket *status3[UAS_MAX_STREAMS];
|
||||
};
|
||||
|
||||
struct UASRequest {
|
||||
@ -132,6 +141,7 @@ struct UASRequest {
|
||||
};
|
||||
|
||||
struct UASStatus {
|
||||
uint32_t stream;
|
||||
uas_ui status;
|
||||
uint32_t length;
|
||||
QTAILQ_ENTRY(UASStatus) next;
|
||||
@ -144,6 +154,7 @@ enum {
|
||||
STR_PRODUCT,
|
||||
STR_SERIALNUMBER,
|
||||
STR_CONFIG_HIGH,
|
||||
STR_CONFIG_SUPER,
|
||||
};
|
||||
|
||||
static const USBDescStrings desc_strings = {
|
||||
@ -151,6 +162,7 @@ static const USBDescStrings desc_strings = {
|
||||
[STR_PRODUCT] = "USB Attached SCSI HBA",
|
||||
[STR_SERIALNUMBER] = "27842",
|
||||
[STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
|
||||
[STR_CONFIG_SUPER] = "Super speed config (usb 3.0)",
|
||||
};
|
||||
|
||||
static const USBDescIface desc_iface_high = {
|
||||
@ -204,6 +216,64 @@ static const USBDescIface desc_iface_high = {
|
||||
}
|
||||
};
|
||||
|
||||
static const USBDescIface desc_iface_super = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 4,
|
||||
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
|
||||
.bInterfaceSubClass = 0x06, /* SCSI */
|
||||
.bInterfaceProtocol = 0x62, /* UAS */
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 1024,
|
||||
.bMaxBurst = 15,
|
||||
.extra = (uint8_t[]) {
|
||||
0x04, /* u8 bLength */
|
||||
0x24, /* u8 bDescriptorType */
|
||||
UAS_PIPE_ID_COMMAND,
|
||||
0x00, /* u8 bReserved */
|
||||
},
|
||||
},{
|
||||
.bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 1024,
|
||||
.bMaxBurst = 15,
|
||||
.bmAttributes_super = UAS_STREAM_BM_ATTR,
|
||||
.extra = (uint8_t[]) {
|
||||
0x04, /* u8 bLength */
|
||||
0x24, /* u8 bDescriptorType */
|
||||
UAS_PIPE_ID_STATUS,
|
||||
0x00, /* u8 bReserved */
|
||||
},
|
||||
},{
|
||||
.bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 1024,
|
||||
.bMaxBurst = 15,
|
||||
.bmAttributes_super = UAS_STREAM_BM_ATTR,
|
||||
.extra = (uint8_t[]) {
|
||||
0x04, /* u8 bLength */
|
||||
0x24, /* u8 bDescriptorType */
|
||||
UAS_PIPE_ID_DATA_IN,
|
||||
0x00, /* u8 bReserved */
|
||||
},
|
||||
},{
|
||||
.bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 1024,
|
||||
.bMaxBurst = 15,
|
||||
.bmAttributes_super = UAS_STREAM_BM_ATTR,
|
||||
.extra = (uint8_t[]) {
|
||||
0x04, /* u8 bLength */
|
||||
0x24, /* u8 bDescriptorType */
|
||||
UAS_PIPE_ID_DATA_OUT,
|
||||
0x00, /* u8 bReserved */
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static const USBDescDevice desc_device_high = {
|
||||
.bcdUSB = 0x0200,
|
||||
.bMaxPacketSize0 = 64,
|
||||
@ -220,6 +290,22 @@ static const USBDescDevice desc_device_high = {
|
||||
},
|
||||
};
|
||||
|
||||
static const USBDescDevice desc_device_super = {
|
||||
.bcdUSB = 0x0300,
|
||||
.bMaxPacketSize0 = 64,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = STR_CONFIG_SUPER,
|
||||
.bmAttributes = 0xc0,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_super,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const USBDesc desc = {
|
||||
.id = {
|
||||
.idVendor = 0x46f4, /* CRC16() of "QEMU" */
|
||||
@ -229,45 +315,68 @@ static const USBDesc desc = {
|
||||
.iProduct = STR_PRODUCT,
|
||||
.iSerialNumber = STR_SERIALNUMBER,
|
||||
},
|
||||
.high = &desc_device_high,
|
||||
.str = desc_strings,
|
||||
.high = &desc_device_high,
|
||||
.super = &desc_device_super,
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static UASStatus *usb_uas_alloc_status(uint8_t id, uint16_t tag)
|
||||
static bool uas_using_streams(UASDevice *uas)
|
||||
{
|
||||
return uas->dev.speed == USB_SPEED_SUPER;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag)
|
||||
{
|
||||
UASStatus *st = g_new0(UASStatus, 1);
|
||||
|
||||
st->status.hdr.id = id;
|
||||
st->status.hdr.tag = cpu_to_be16(tag);
|
||||
st->length = sizeof(uas_ui_header);
|
||||
if (uas_using_streams(uas)) {
|
||||
st->stream = tag;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
static void usb_uas_send_status_bh(void *opaque)
|
||||
{
|
||||
UASDevice *uas = opaque;
|
||||
UASStatus *st = QTAILQ_FIRST(&uas->results);
|
||||
USBPacket *p = uas->status;
|
||||
UASStatus *st;
|
||||
USBPacket *p;
|
||||
|
||||
assert(p != NULL);
|
||||
assert(st != NULL);
|
||||
while ((st = QTAILQ_FIRST(&uas->results)) != NULL) {
|
||||
if (uas_using_streams(uas)) {
|
||||
p = uas->status3[st->stream];
|
||||
uas->status3[st->stream] = NULL;
|
||||
} else {
|
||||
p = uas->status2;
|
||||
uas->status2 = NULL;
|
||||
}
|
||||
if (p == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
uas->status = NULL;
|
||||
usb_packet_copy(p, &st->status, st->length);
|
||||
QTAILQ_REMOVE(&uas->results, st, next);
|
||||
g_free(st);
|
||||
usb_packet_copy(p, &st->status, st->length);
|
||||
QTAILQ_REMOVE(&uas->results, st, next);
|
||||
g_free(st);
|
||||
|
||||
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
|
||||
usb_packet_complete(&uas->dev, p);
|
||||
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
|
||||
usb_packet_complete(&uas->dev, p);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
|
||||
{
|
||||
USBPacket *p = uas_using_streams(uas) ?
|
||||
uas->status3[st->stream] : uas->status2;
|
||||
|
||||
st->length += length;
|
||||
QTAILQ_INSERT_TAIL(&uas->results, st, next);
|
||||
if (uas->status) {
|
||||
if (p) {
|
||||
/*
|
||||
* Just schedule bh make sure any in-flight data transaction
|
||||
* is finished before completing (sending) the status packet.
|
||||
@ -276,14 +385,14 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
|
||||
} else {
|
||||
USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN,
|
||||
UAS_PIPE_ID_STATUS);
|
||||
usb_wakeup(ep);
|
||||
usb_wakeup(ep, st->stream);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
|
||||
uint8_t code, uint16_t add_info)
|
||||
{
|
||||
UASStatus *st = usb_uas_alloc_status(UAS_UI_RESPONSE, tag);
|
||||
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;
|
||||
@ -293,7 +402,7 @@ static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
|
||||
|
||||
static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
|
||||
{
|
||||
UASStatus *st = usb_uas_alloc_status(UAS_UI_SENSE, req->tag);
|
||||
UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_SENSE, req->tag);
|
||||
int len, slen = 0;
|
||||
|
||||
trace_usb_uas_sense(req->uas->dev.addr, req->tag, status);
|
||||
@ -310,7 +419,8 @@ static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
|
||||
|
||||
static void usb_uas_queue_read_ready(UASRequest *req)
|
||||
{
|
||||
UASStatus *st = usb_uas_alloc_status(UAS_UI_READ_READY, req->tag);
|
||||
UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY,
|
||||
req->tag);
|
||||
|
||||
trace_usb_uas_read_ready(req->uas->dev.addr, req->tag);
|
||||
usb_uas_queue_status(req->uas, st, 0);
|
||||
@ -318,7 +428,8 @@ static void usb_uas_queue_read_ready(UASRequest *req)
|
||||
|
||||
static void usb_uas_queue_write_ready(UASRequest *req)
|
||||
{
|
||||
UASStatus *st = usb_uas_alloc_status(UAS_UI_WRITE_READY, req->tag);
|
||||
UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_WRITE_READY,
|
||||
req->tag);
|
||||
|
||||
trace_usb_uas_write_ready(req->uas->dev.addr, req->tag);
|
||||
usb_uas_queue_status(req->uas, st, 0);
|
||||
@ -381,18 +492,22 @@ static void usb_uas_start_next_transfer(UASDevice *uas)
|
||||
{
|
||||
UASRequest *req;
|
||||
|
||||
if (uas_using_streams(uas)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(req, &uas->requests, next) {
|
||||
if (req->active || req->complete) {
|
||||
continue;
|
||||
}
|
||||
if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain == NULL) {
|
||||
uas->datain = req;
|
||||
if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) {
|
||||
uas->datain2 = req;
|
||||
usb_uas_queue_read_ready(req);
|
||||
req->active = true;
|
||||
return;
|
||||
}
|
||||
if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout == NULL) {
|
||||
uas->dataout = req;
|
||||
if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) {
|
||||
uas->dataout2 = req;
|
||||
usb_uas_queue_write_ready(req);
|
||||
req->active = true;
|
||||
return;
|
||||
@ -417,11 +532,11 @@ static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
|
||||
UASRequest *req = priv;
|
||||
UASDevice *uas = req->uas;
|
||||
|
||||
if (req == uas->datain) {
|
||||
uas->datain = NULL;
|
||||
if (req == uas->datain2) {
|
||||
uas->datain2 = NULL;
|
||||
}
|
||||
if (req == uas->dataout) {
|
||||
uas->dataout = NULL;
|
||||
if (req == uas->dataout2) {
|
||||
uas->dataout2 = NULL;
|
||||
}
|
||||
QTAILQ_REMOVE(&uas->requests, req, next);
|
||||
g_free(req);
|
||||
@ -522,12 +637,25 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
|
||||
UASRequest *req, *nreq;
|
||||
int i;
|
||||
|
||||
if (uas->status == p) {
|
||||
uas->status = NULL;
|
||||
if (uas->status2 == p) {
|
||||
uas->status2 = NULL;
|
||||
qemu_bh_cancel(uas->status_bh);
|
||||
return;
|
||||
}
|
||||
if (uas_using_streams(uas)) {
|
||||
for (i = 0; i < UAS_MAX_STREAMS; i++) {
|
||||
if (uas->status3[i] == p) {
|
||||
uas->status3[i] = NULL;
|
||||
return;
|
||||
}
|
||||
if (uas->data3[i] == p) {
|
||||
uas->data3[i] = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
|
||||
if (req->data == p) {
|
||||
req->data = NULL;
|
||||
@ -555,9 +683,18 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui)
|
||||
usb_uas_get_lun(req->lun),
|
||||
req->lun >> 32, req->lun & 0xffffffff);
|
||||
QTAILQ_INSERT_TAIL(&uas->requests, req, next);
|
||||
if (uas_using_streams(uas) && uas->data3[req->tag] != NULL) {
|
||||
req->data = uas->data3[req->tag];
|
||||
req->data_async = true;
|
||||
uas->data3[req->tag] = NULL;
|
||||
}
|
||||
|
||||
req->req = scsi_req_new(req->dev, req->tag,
|
||||
usb_uas_get_lun(req->lun),
|
||||
ui->command.cdb, req);
|
||||
#if 1
|
||||
scsi_req_print(req->req);
|
||||
#endif
|
||||
len = scsi_req_enqueue(req->req);
|
||||
if (len) {
|
||||
req->data_size = len;
|
||||
@ -669,12 +806,26 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
|
||||
}
|
||||
break;
|
||||
case UAS_PIPE_ID_STATUS:
|
||||
st = QTAILQ_FIRST(&uas->results);
|
||||
if (st == NULL) {
|
||||
assert(uas->status == NULL);
|
||||
uas->status = p;
|
||||
p->status = USB_RET_ASYNC;
|
||||
break;
|
||||
if (p->stream) {
|
||||
QTAILQ_FOREACH(st, &uas->results, next) {
|
||||
if (st->stream == p->stream) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (st == NULL) {
|
||||
assert(uas->status3[p->stream] == NULL);
|
||||
uas->status3[p->stream] = p;
|
||||
p->status = USB_RET_ASYNC;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
st = QTAILQ_FIRST(&uas->results);
|
||||
if (st == NULL) {
|
||||
assert(uas->status2 == NULL);
|
||||
uas->status2 = p;
|
||||
p->status = USB_RET_ASYNC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
usb_packet_copy(p, &st->status, st->length);
|
||||
QTAILQ_REMOVE(&uas->results, st, next);
|
||||
@ -682,11 +833,23 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
|
||||
break;
|
||||
case UAS_PIPE_ID_DATA_IN:
|
||||
case UAS_PIPE_ID_DATA_OUT:
|
||||
req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) ? uas->datain : uas->dataout;
|
||||
if (p->stream) {
|
||||
req = usb_uas_find_request(uas, p->stream);
|
||||
} else {
|
||||
req = (p->ep->nr == UAS_PIPE_ID_DATA_IN)
|
||||
? uas->datain2 : uas->dataout2;
|
||||
}
|
||||
if (req == NULL) {
|
||||
fprintf(stderr, "%s: no inflight request\n", __func__);
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
if (p->stream) {
|
||||
assert(uas->data3[p->stream] == NULL);
|
||||
uas->data3[p->stream] = p;
|
||||
p->status = USB_RET_ASYNC;
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "%s: no inflight request\n", __func__);
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
scsi_req_ref(req->req);
|
||||
req->data = p;
|
||||
|
@ -138,7 +138,7 @@ static void usb_mouse_event(void *opaque,
|
||||
s->dz += dz1;
|
||||
s->buttons_state = buttons_state;
|
||||
s->changed = 1;
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
|
||||
static void usb_wacom_event(void *opaque,
|
||||
@ -152,7 +152,7 @@ static void usb_wacom_event(void *opaque,
|
||||
s->dz += dz;
|
||||
s->buttons_state = buttons_state;
|
||||
s->changed = 1;
|
||||
usb_wakeup(s->intr);
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
|
||||
static inline int int_clamp(int val, int vmin, int vmax)
|
||||
|
@ -874,7 +874,8 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[],
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
|
||||
static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
|
||||
unsigned int stream)
|
||||
{
|
||||
EHCIState *s = container_of(bus, EHCIState, bus);
|
||||
uint32_t portsc = s->portsc[ep->dev->port->index];
|
||||
@ -1420,7 +1421,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
||||
}
|
||||
|
||||
spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0);
|
||||
usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr, spd,
|
||||
usb_packet_setup(&p->packet, p->pid, ep, 0, p->qtdaddr, spd,
|
||||
(p->qtd.token & QTD_TOKEN_IOC) != 0);
|
||||
usb_packet_map(&p->packet, &p->sgl);
|
||||
p->async = EHCI_ASYNC_INITIALIZED;
|
||||
@ -1493,7 +1494,7 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
dev = ehci_find_device(ehci, devaddr);
|
||||
ep = usb_ep_get(dev, pid, endp);
|
||||
if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) {
|
||||
usb_packet_setup(&ehci->ipacket, pid, ep, addr, false,
|
||||
usb_packet_setup(&ehci->ipacket, pid, ep, 0, addr, false,
|
||||
(itd->transact[i] & ITD_XACT_IOC) != 0);
|
||||
usb_packet_map(&ehci->ipacket, &ehci->isgl);
|
||||
usb_handle_packet(dev, &ehci->ipacket);
|
||||
|
@ -625,7 +625,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
||||
/* A wild guess on the FADDR semantics... */
|
||||
dev = usb_find_device(&s->port, ep->faddr[idx]);
|
||||
uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
|
||||
usb_packet_setup(&ep->packey[dir].p, pid, uep,
|
||||
usb_packet_setup(&ep->packey[dir].p, pid, uep, 0,
|
||||
(dev->addr << 16) | (uep->nr << 8) | pid, false, true);
|
||||
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
|
||||
ep->packey[dir].ep = ep;
|
||||
|
@ -830,7 +830,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
||||
OHCI_BM(iso_td.flags, TD_DI) == 0;
|
||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||
usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false, int_req);
|
||||
usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req);
|
||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
|
||||
usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ohci->usb_packet.status == USB_RET_ASYNC) {
|
||||
@ -1034,7 +1034,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
}
|
||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||
usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r,
|
||||
usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r,
|
||||
OHCI_BM(td.flags, TD_DI) == 0);
|
||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
|
||||
usb_handle_packet(dev, &ohci->usb_packet);
|
||||
|
@ -879,7 +879,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
|
||||
|
||||
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
||||
spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
|
||||
usb_packet_setup(&async->packet, pid, q->ep, td_addr, spd,
|
||||
usb_packet_setup(&async->packet, pid, q->ep, 0, td_addr, spd,
|
||||
(td->ctrl & TD_CTRL_IOC) != 0);
|
||||
qemu_sglist_add(&async->sgl, td->buffer, max_len);
|
||||
usb_packet_map(&async->packet, &async->sgl);
|
||||
|
@ -34,8 +34,8 @@
|
||||
#else
|
||||
#define DPRINTF(...) do {} while (0)
|
||||
#endif
|
||||
#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
|
||||
__func__, __LINE__); abort(); } while (0)
|
||||
#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \
|
||||
__func__, __LINE__, _msg); abort(); } while (0)
|
||||
|
||||
#define MAXPORTS_2 15
|
||||
#define MAXPORTS_3 15
|
||||
@ -301,6 +301,8 @@ typedef enum TRBCCode {
|
||||
#define SLOT_CONTEXT_ENTRIES_SHIFT 27
|
||||
|
||||
typedef struct XHCIState XHCIState;
|
||||
typedef struct XHCIStreamContext XHCIStreamContext;
|
||||
typedef struct XHCIEPContext XHCIEPContext;
|
||||
|
||||
#define get_field(data, field) \
|
||||
(((data) >> field##_SHIFT) & field##_MASK)
|
||||
@ -351,6 +353,7 @@ typedef struct XHCITransfer {
|
||||
unsigned int iso_pkts;
|
||||
unsigned int slotid;
|
||||
unsigned int epid;
|
||||
unsigned int streamid;
|
||||
bool in_xfer;
|
||||
bool iso_xfer;
|
||||
|
||||
@ -367,7 +370,14 @@ typedef struct XHCITransfer {
|
||||
uint64_t mfindex_kick;
|
||||
} XHCITransfer;
|
||||
|
||||
typedef struct XHCIEPContext {
|
||||
struct XHCIStreamContext {
|
||||
dma_addr_t pctx;
|
||||
unsigned int sct;
|
||||
XHCIRing ring;
|
||||
XHCIStreamContext *sstreams;
|
||||
};
|
||||
|
||||
struct XHCIEPContext {
|
||||
XHCIState *xhci;
|
||||
unsigned int slotid;
|
||||
unsigned int epid;
|
||||
@ -382,11 +392,17 @@ typedef struct XHCIEPContext {
|
||||
unsigned int max_psize;
|
||||
uint32_t state;
|
||||
|
||||
/* streams */
|
||||
unsigned int max_pstreams;
|
||||
bool lsa;
|
||||
unsigned int nr_pstreams;
|
||||
XHCIStreamContext *pstreams;
|
||||
|
||||
/* iso xfer scheduling */
|
||||
unsigned int interval;
|
||||
int64_t mfindex_last;
|
||||
QEMUTimer *kick_timer;
|
||||
} XHCIEPContext;
|
||||
};
|
||||
|
||||
typedef struct XHCISlot {
|
||||
bool enabled;
|
||||
@ -482,7 +498,7 @@ enum xhci_flags {
|
||||
};
|
||||
|
||||
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
|
||||
unsigned int epid);
|
||||
unsigned int epid, unsigned int streamid);
|
||||
static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
unsigned int epid);
|
||||
static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
|
||||
@ -1068,18 +1084,116 @@ static void xhci_stop(XHCIState *xhci)
|
||||
xhci->crcr_low &= ~CRCR_CRR;
|
||||
}
|
||||
|
||||
static XHCIStreamContext *xhci_alloc_stream_contexts(unsigned count,
|
||||
dma_addr_t base)
|
||||
{
|
||||
XHCIStreamContext *stctx;
|
||||
unsigned int i;
|
||||
|
||||
stctx = g_new0(XHCIStreamContext, count);
|
||||
for (i = 0; i < count; i++) {
|
||||
stctx[i].pctx = base + i * 16;
|
||||
stctx[i].sct = -1;
|
||||
}
|
||||
return stctx;
|
||||
}
|
||||
|
||||
static void xhci_reset_streams(XHCIEPContext *epctx)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < epctx->nr_pstreams; i++) {
|
||||
epctx->pstreams[i].sct = -1;
|
||||
g_free(epctx->pstreams[i].sstreams);
|
||||
}
|
||||
}
|
||||
|
||||
static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base)
|
||||
{
|
||||
assert(epctx->pstreams == NULL);
|
||||
epctx->nr_pstreams = 2 << epctx->max_pstreams;
|
||||
epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base);
|
||||
}
|
||||
|
||||
static void xhci_free_streams(XHCIEPContext *epctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(epctx->pstreams != NULL);
|
||||
|
||||
if (!epctx->lsa) {
|
||||
for (i = 0; i < epctx->nr_pstreams; i++) {
|
||||
g_free(epctx->pstreams[i].sstreams);
|
||||
}
|
||||
}
|
||||
g_free(epctx->pstreams);
|
||||
epctx->pstreams = NULL;
|
||||
epctx->nr_pstreams = 0;
|
||||
}
|
||||
|
||||
static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx,
|
||||
unsigned int streamid,
|
||||
uint32_t *cc_error)
|
||||
{
|
||||
XHCIStreamContext *sctx;
|
||||
dma_addr_t base;
|
||||
uint32_t ctx[2], sct;
|
||||
|
||||
assert(streamid != 0);
|
||||
if (epctx->lsa) {
|
||||
if (streamid >= epctx->nr_pstreams) {
|
||||
*cc_error = CC_INVALID_STREAM_ID_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
sctx = epctx->pstreams + streamid;
|
||||
} else {
|
||||
FIXME("secondary streams not implemented yet");
|
||||
}
|
||||
|
||||
if (sctx->sct == -1) {
|
||||
xhci_dma_read_u32s(epctx->xhci, sctx->pctx, ctx, sizeof(ctx));
|
||||
fprintf(stderr, "%s: init sctx #%d @ %lx: %08x %08x\n", __func__,
|
||||
streamid, sctx->pctx, ctx[0], ctx[1]);
|
||||
sct = (ctx[0] >> 1) & 0x07;
|
||||
if (epctx->lsa && sct != 1) {
|
||||
*cc_error = CC_INVALID_STREAM_TYPE_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
sctx->sct = sct;
|
||||
base = xhci_addr64(ctx[0] & ~0xf, ctx[1]);
|
||||
xhci_ring_init(epctx->xhci, &sctx->ring, base);
|
||||
}
|
||||
return sctx;
|
||||
}
|
||||
|
||||
static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
|
||||
uint32_t state)
|
||||
XHCIStreamContext *sctx, uint32_t state)
|
||||
{
|
||||
uint32_t ctx[5];
|
||||
uint32_t ctx2[2];
|
||||
|
||||
fprintf(stderr, "%s: epid %d, state %d\n",
|
||||
__func__, epctx->epid, state);
|
||||
xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
|
||||
ctx[0] &= ~EP_STATE_MASK;
|
||||
ctx[0] |= state;
|
||||
ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
|
||||
ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
|
||||
DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
|
||||
epctx->pctx, state, ctx[3], ctx[2]);
|
||||
|
||||
/* update ring dequeue ptr */
|
||||
if (epctx->nr_pstreams) {
|
||||
if (sctx != NULL) {
|
||||
xhci_dma_read_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2));
|
||||
ctx2[0] &= 0xe;
|
||||
ctx2[0] |= sctx->ring.dequeue | sctx->ring.ccs;
|
||||
ctx2[1] = (sctx->ring.dequeue >> 16) >> 16;
|
||||
xhci_dma_write_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2));
|
||||
}
|
||||
} else {
|
||||
ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
|
||||
ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
|
||||
DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
|
||||
epctx->pctx, state, ctx[3], ctx[2]);
|
||||
}
|
||||
|
||||
xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
|
||||
epctx->state = state;
|
||||
}
|
||||
@ -1087,7 +1201,7 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
|
||||
static void xhci_ep_kick_timer(void *opaque)
|
||||
{
|
||||
XHCIEPContext *epctx = opaque;
|
||||
xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid);
|
||||
xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0);
|
||||
}
|
||||
|
||||
static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
@ -1117,16 +1231,22 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
slot->eps[epid-1] = epctx;
|
||||
|
||||
dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]);
|
||||
xhci_ring_init(xhci, &epctx->ring, dequeue);
|
||||
epctx->ring.ccs = ctx[2] & 1;
|
||||
|
||||
epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK;
|
||||
DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type);
|
||||
epctx->pctx = pctx;
|
||||
epctx->max_psize = ctx[1]>>16;
|
||||
epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
|
||||
epctx->max_pstreams = (ctx[0] >> 10) & 0xf;
|
||||
epctx->lsa = (ctx[0] >> 15) & 1;
|
||||
DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n",
|
||||
epid/2, epid%2, epctx->max_psize);
|
||||
if (epctx->max_pstreams) {
|
||||
xhci_alloc_streams(epctx, dequeue);
|
||||
} else {
|
||||
xhci_ring_init(xhci, &epctx->ring, dequeue);
|
||||
epctx->ring.ccs = ctx[2] & 1;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
|
||||
usb_packet_init(&epctx->transfers[i].packet);
|
||||
}
|
||||
@ -1227,7 +1347,11 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
|
||||
epctx = slot->eps[epid-1];
|
||||
|
||||
xhci_set_ep_state(xhci, epctx, EP_DISABLED);
|
||||
if (epctx->nr_pstreams) {
|
||||
xhci_free_streams(epctx);
|
||||
}
|
||||
|
||||
xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED);
|
||||
|
||||
qemu_free_timer(epctx->kick_timer);
|
||||
g_free(epctx);
|
||||
@ -1264,7 +1388,11 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
|
||||
|
||||
epctx = slot->eps[epid-1];
|
||||
|
||||
xhci_set_ep_state(xhci, epctx, EP_STOPPED);
|
||||
xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED);
|
||||
|
||||
if (epctx->nr_pstreams) {
|
||||
xhci_reset_streams(epctx);
|
||||
}
|
||||
|
||||
return CC_SUCCESS;
|
||||
}
|
||||
@ -1315,16 +1443,22 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
|
||||
return CC_USB_TRANSACTION_ERROR;
|
||||
}
|
||||
|
||||
xhci_set_ep_state(xhci, epctx, EP_STOPPED);
|
||||
xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED);
|
||||
|
||||
if (epctx->nr_pstreams) {
|
||||
xhci_reset_streams(epctx);
|
||||
}
|
||||
|
||||
return CC_SUCCESS;
|
||||
}
|
||||
|
||||
static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
|
||||
unsigned int epid, uint64_t pdequeue)
|
||||
unsigned int epid, unsigned int streamid,
|
||||
uint64_t pdequeue)
|
||||
{
|
||||
XHCISlot *slot;
|
||||
XHCIEPContext *epctx;
|
||||
XHCIStreamContext *sctx;
|
||||
dma_addr_t dequeue;
|
||||
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
@ -1334,7 +1468,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
|
||||
return CC_TRB_ERROR;
|
||||
}
|
||||
|
||||
trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue);
|
||||
trace_usb_xhci_ep_set_dequeue(slotid, epid, streamid, pdequeue);
|
||||
dequeue = xhci_mask64(pdequeue);
|
||||
|
||||
slot = &xhci->slots[slotid-1];
|
||||
@ -1346,16 +1480,26 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
|
||||
|
||||
epctx = slot->eps[epid-1];
|
||||
|
||||
|
||||
if (epctx->state != EP_STOPPED) {
|
||||
fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid);
|
||||
return CC_CONTEXT_STATE_ERROR;
|
||||
}
|
||||
|
||||
xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
|
||||
epctx->ring.ccs = dequeue & 1;
|
||||
if (epctx->nr_pstreams) {
|
||||
uint32_t err;
|
||||
sctx = xhci_find_stream(epctx, streamid, &err);
|
||||
if (sctx == NULL) {
|
||||
return err;
|
||||
}
|
||||
xhci_ring_init(xhci, &sctx->ring, dequeue & ~0xf);
|
||||
sctx->ring.ccs = dequeue & 1;
|
||||
} else {
|
||||
sctx = NULL;
|
||||
xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
|
||||
epctx->ring.ccs = dequeue & 1;
|
||||
}
|
||||
|
||||
xhci_set_ep_state(xhci, epctx, EP_STOPPED);
|
||||
xhci_set_ep_state(xhci, epctx, sctx, EP_STOPPED);
|
||||
|
||||
return CC_SUCCESS;
|
||||
}
|
||||
@ -1484,12 +1628,22 @@ static void xhci_stall_ep(XHCITransfer *xfer)
|
||||
XHCIState *xhci = xfer->xhci;
|
||||
XHCISlot *slot = &xhci->slots[xfer->slotid-1];
|
||||
XHCIEPContext *epctx = slot->eps[xfer->epid-1];
|
||||
uint32_t err;
|
||||
XHCIStreamContext *sctx;
|
||||
|
||||
epctx->ring.dequeue = xfer->trbs[0].addr;
|
||||
epctx->ring.ccs = xfer->trbs[0].ccs;
|
||||
xhci_set_ep_state(xhci, epctx, EP_HALTED);
|
||||
DPRINTF("xhci: stalled slot %d ep %d\n", xfer->slotid, xfer->epid);
|
||||
DPRINTF("xhci: will continue at "DMA_ADDR_FMT"\n", epctx->ring.dequeue);
|
||||
if (epctx->nr_pstreams) {
|
||||
sctx = xhci_find_stream(epctx, xfer->streamid, &err);
|
||||
if (sctx == NULL) {
|
||||
return;
|
||||
}
|
||||
sctx->ring.dequeue = xfer->trbs[0].addr;
|
||||
sctx->ring.ccs = xfer->trbs[0].ccs;
|
||||
xhci_set_ep_state(xhci, epctx, sctx, EP_HALTED);
|
||||
} else {
|
||||
epctx->ring.dequeue = xfer->trbs[0].addr;
|
||||
epctx->ring.ccs = xfer->trbs[0].ccs;
|
||||
xhci_set_ep_state(xhci, epctx, NULL, EP_HALTED);
|
||||
}
|
||||
}
|
||||
|
||||
static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
|
||||
@ -1518,8 +1672,8 @@ static int xhci_setup_packet(XHCITransfer *xfer)
|
||||
}
|
||||
|
||||
xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
|
||||
usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false,
|
||||
xfer->int_req);
|
||||
usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid,
|
||||
xfer->trbs[0].addr, false, xfer->int_req);
|
||||
usb_packet_map(&xfer->packet, &xfer->sgl);
|
||||
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
|
||||
xfer->packet.pid, dev->addr, ep->nr);
|
||||
@ -1572,7 +1726,7 @@ static int xhci_complete_packet(XHCITransfer *xfer)
|
||||
default:
|
||||
fprintf(stderr, "%s: FIXME: status = %d\n", __func__,
|
||||
xfer->packet.status);
|
||||
FIXME();
|
||||
FIXME("unhandled USB_RET_*");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1585,7 +1739,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
|
||||
trb_setup = &xfer->trbs[0];
|
||||
trb_status = &xfer->trbs[xfer->trb_count-1];
|
||||
|
||||
trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
|
||||
trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid);
|
||||
|
||||
/* at most one Event Data TRB allowed after STATUS */
|
||||
if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
|
||||
@ -1627,7 +1781,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
|
||||
|
||||
xhci_complete_packet(xfer);
|
||||
if (!xfer->running_async && !xfer->running_retry) {
|
||||
xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
|
||||
xhci_kick_ep(xhci, xfer->slotid, xfer->epid, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1710,26 +1864,29 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
|
||||
|
||||
xhci_complete_packet(xfer);
|
||||
if (!xfer->running_async && !xfer->running_retry) {
|
||||
xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
|
||||
xhci_kick_ep(xhci, xfer->slotid, xfer->epid, xfer->streamid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
|
||||
{
|
||||
trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
|
||||
trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid);
|
||||
return xhci_submit(xhci, xfer, epctx);
|
||||
}
|
||||
|
||||
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
|
||||
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
|
||||
unsigned int epid, unsigned int streamid)
|
||||
{
|
||||
XHCIStreamContext *stctx;
|
||||
XHCIEPContext *epctx;
|
||||
XHCIRing *ring;
|
||||
USBEndpoint *ep = NULL;
|
||||
uint64_t mfindex;
|
||||
int length;
|
||||
int i;
|
||||
|
||||
trace_usb_xhci_ep_kick(slotid, epid);
|
||||
trace_usb_xhci_ep_kick(slotid, epid, streamid);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
assert(epid >= 1 && epid <= 31);
|
||||
|
||||
@ -1782,14 +1939,28 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
|
||||
return;
|
||||
}
|
||||
|
||||
xhci_set_ep_state(xhci, epctx, EP_RUNNING);
|
||||
|
||||
if (epctx->nr_pstreams) {
|
||||
uint32_t err;
|
||||
stctx = xhci_find_stream(epctx, streamid, &err);
|
||||
if (stctx == NULL) {
|
||||
return;
|
||||
}
|
||||
ring = &stctx->ring;
|
||||
xhci_set_ep_state(xhci, epctx, stctx, EP_RUNNING);
|
||||
} else {
|
||||
ring = &epctx->ring;
|
||||
streamid = 0;
|
||||
xhci_set_ep_state(xhci, epctx, NULL, EP_RUNNING);
|
||||
}
|
||||
assert(ring->base != 0);
|
||||
|
||||
while (1) {
|
||||
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
|
||||
if (xfer->running_async || xfer->running_retry) {
|
||||
break;
|
||||
}
|
||||
length = xhci_ring_chain_length(xhci, &epctx->ring);
|
||||
length = xhci_ring_chain_length(xhci, ring);
|
||||
if (length < 0) {
|
||||
break;
|
||||
} else if (length == 0) {
|
||||
@ -1808,11 +1979,12 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
|
||||
xfer->trb_count = length;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
assert(xhci_ring_fetch(xhci, &epctx->ring, &xfer->trbs[i], NULL));
|
||||
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) {
|
||||
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
|
||||
@ -1977,7 +2149,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
||||
DPRINTF("xhci: device address is %d\n", slot->devaddr);
|
||||
usb_device_reset(dev);
|
||||
usb_packet_setup(&p, USB_TOKEN_OUT,
|
||||
usb_ep_get(dev, USB_TOKEN_OUT, 0),
|
||||
usb_ep_get(dev, USB_TOKEN_OUT, 0), 0,
|
||||
0, false, false);
|
||||
usb_device_handle_control(dev, &p,
|
||||
DeviceOutRequest | USB_REQ_SET_ADDRESS,
|
||||
@ -2357,11 +2529,14 @@ static void xhci_process_commands(XHCIState *xhci)
|
||||
}
|
||||
break;
|
||||
case CR_SET_TR_DEQUEUE:
|
||||
fprintf(stderr, "%s: CR_SET_TR_DEQUEUE\n", __func__);
|
||||
slotid = xhci_get_slot(xhci, &event, &trb);
|
||||
if (slotid) {
|
||||
unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
|
||||
& TRB_CR_EPID_MASK;
|
||||
event.ccode = xhci_set_ep_dequeue(xhci, slotid, epid,
|
||||
unsigned int streamid = (trb.status >> 16) & 0xffff;
|
||||
event.ccode = xhci_set_ep_dequeue(xhci, slotid,
|
||||
epid, streamid,
|
||||
trb.parameter);
|
||||
}
|
||||
break;
|
||||
@ -2554,9 +2729,9 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
|
||||
break;
|
||||
case 0x10: /* HCCPARAMS */
|
||||
if (sizeof(dma_addr_t) == 4) {
|
||||
ret = 0x00081000;
|
||||
ret = 0x00087000;
|
||||
} else {
|
||||
ret = 0x00081001;
|
||||
ret = 0x00087001;
|
||||
}
|
||||
break;
|
||||
case 0x14: /* DBOFF */
|
||||
@ -2880,6 +3055,7 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
XHCIState *xhci = ptr;
|
||||
unsigned int epid, streamid;
|
||||
|
||||
trace_usb_xhci_doorbell_write(reg, val);
|
||||
|
||||
@ -2898,13 +3074,15 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
|
||||
(uint32_t)val);
|
||||
}
|
||||
} else {
|
||||
epid = val & 0xff;
|
||||
streamid = (val >> 16) & 0xffff;
|
||||
if (reg > xhci->numslots) {
|
||||
fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg);
|
||||
} else if (val > 31) {
|
||||
} else if (epid > 31) {
|
||||
fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n",
|
||||
(int)reg, (uint32_t)val);
|
||||
} else {
|
||||
xhci_kick_ep(xhci, reg, val);
|
||||
xhci_kick_ep(xhci, reg, epid, streamid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2988,7 +3166,7 @@ static void xhci_complete(USBPort *port, USBPacket *packet)
|
||||
return;
|
||||
}
|
||||
xhci_complete_packet(xfer);
|
||||
xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
|
||||
xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid, xfer->streamid);
|
||||
}
|
||||
|
||||
static void xhci_child_detach(USBPort *uport, USBDevice *child)
|
||||
@ -3033,7 +3211,8 @@ static int xhci_find_epid(USBEndpoint *ep)
|
||||
}
|
||||
}
|
||||
|
||||
static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
|
||||
static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
|
||||
unsigned int stream)
|
||||
{
|
||||
XHCIState *xhci = container_of(bus, XHCIState, bus);
|
||||
int slotid;
|
||||
@ -3044,7 +3223,7 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
|
||||
DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
|
||||
return;
|
||||
}
|
||||
xhci_kick_ep(xhci, slotid, xhci_find_epid(ep));
|
||||
xhci_kick_ep(xhci, slotid, xhci_find_epid(ep), stream);
|
||||
}
|
||||
|
||||
static USBBusOps xhci_bus_ops = {
|
||||
|
@ -637,9 +637,3 @@ void usb_host_info(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
usb_host_scan(mon, usb_host_info_device);
|
||||
}
|
||||
|
||||
/* XXX add this */
|
||||
int usb_host_device_close(const char *devname)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
144
hw/usb/host-legacy.c
Normal file
144
hw/usb/host-legacy.c
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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 "hw/usb.h"
|
||||
#include "hw/usb/host.h"
|
||||
|
||||
/*
|
||||
* Autoconnect filter
|
||||
* Format:
|
||||
* auto:bus:dev[:vid:pid]
|
||||
* auto:bus.dev[:vid:pid]
|
||||
*
|
||||
* bus - bus number (dec, * means any)
|
||||
* dev - device number (dec, * means any)
|
||||
* vid - vendor id (hex, * means any)
|
||||
* pid - product id (hex, * means any)
|
||||
*
|
||||
* See 'lsusb' output.
|
||||
*/
|
||||
static int parse_filter(const char *spec, struct USBAutoFilter *f)
|
||||
{
|
||||
enum { BUS, DEV, VID, PID, DONE };
|
||||
const char *p = spec;
|
||||
int i;
|
||||
|
||||
f->bus_num = 0;
|
||||
f->addr = 0;
|
||||
f->vendor_id = 0;
|
||||
f->product_id = 0;
|
||||
|
||||
for (i = BUS; i < DONE; i++) {
|
||||
p = strpbrk(p, ":.");
|
||||
if (!p) {
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
|
||||
if (*p == '*') {
|
||||
continue;
|
||||
}
|
||||
switch (i) {
|
||||
case BUS:
|
||||
f->bus_num = strtol(p, NULL, 10);
|
||||
break;
|
||||
case DEV:
|
||||
f->addr = strtol(p, NULL, 10);
|
||||
break;
|
||||
case VID:
|
||||
f->vendor_id = strtol(p, NULL, 16);
|
||||
break;
|
||||
case PID:
|
||||
f->product_id = strtol(p, NULL, 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < DEV) {
|
||||
fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
|
||||
{
|
||||
struct USBAutoFilter filter;
|
||||
USBDevice *dev;
|
||||
char *p;
|
||||
|
||||
dev = usb_create(bus, "usb-host");
|
||||
|
||||
if (strstr(devname, "auto:")) {
|
||||
if (parse_filter(devname, &filter) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
p = strchr(devname, '.');
|
||||
if (p) {
|
||||
filter.bus_num = strtoul(devname, NULL, 0);
|
||||
filter.addr = strtoul(p + 1, NULL, 0);
|
||||
filter.vendor_id = 0;
|
||||
filter.product_id = 0;
|
||||
} else {
|
||||
p = strchr(devname, ':');
|
||||
if (p) {
|
||||
filter.bus_num = 0;
|
||||
filter.addr = 0;
|
||||
filter.vendor_id = strtoul(devname, NULL, 16);
|
||||
filter.product_id = strtoul(p + 1, NULL, 16);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num);
|
||||
qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr);
|
||||
qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id);
|
||||
qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id);
|
||||
qdev_init_nofail(&dev->qdev);
|
||||
return dev;
|
||||
|
||||
fail:
|
||||
qdev_free(&dev->qdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void usb_host_register_types(void)
|
||||
{
|
||||
usb_legacy_register("usb-host", "host", usb_host_device_open);
|
||||
}
|
||||
|
||||
type_init(usb_host_register_types)
|
@ -43,6 +43,7 @@
|
||||
#include <linux/version.h>
|
||||
#include "hw/usb.h"
|
||||
#include "hw/usb/desc.h"
|
||||
#include "hw/usb/host.h"
|
||||
|
||||
/* We redefine it to avoid version problems */
|
||||
struct usb_ctrltransfer {
|
||||
@ -87,14 +88,6 @@ struct endp_data {
|
||||
int inflight;
|
||||
};
|
||||
|
||||
struct USBAutoFilter {
|
||||
uint32_t bus_num;
|
||||
uint32_t addr;
|
||||
char *port;
|
||||
uint32_t vendor_id;
|
||||
uint32_t product_id;
|
||||
};
|
||||
|
||||
enum USBHostDeviceOptions {
|
||||
USB_HOST_OPT_PIPELINE,
|
||||
};
|
||||
@ -131,7 +124,6 @@ typedef struct USBHostDevice {
|
||||
static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
|
||||
|
||||
static int usb_host_close(USBHostDevice *dev);
|
||||
static int parse_filter(const char *spec, struct USBAutoFilter *f);
|
||||
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);
|
||||
@ -1544,75 +1536,10 @@ static const TypeInfo usb_host_dev_info = {
|
||||
static void usb_host_register_types(void)
|
||||
{
|
||||
type_register_static(&usb_host_dev_info);
|
||||
usb_legacy_register("usb-host", "host", usb_host_device_open);
|
||||
}
|
||||
|
||||
type_init(usb_host_register_types)
|
||||
|
||||
USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
|
||||
{
|
||||
struct USBAutoFilter filter;
|
||||
USBDevice *dev;
|
||||
char *p;
|
||||
|
||||
dev = usb_create(bus, "usb-host");
|
||||
|
||||
if (strstr(devname, "auto:")) {
|
||||
if (parse_filter(devname, &filter) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
if ((p = strchr(devname, '.'))) {
|
||||
filter.bus_num = strtoul(devname, NULL, 0);
|
||||
filter.addr = strtoul(p + 1, NULL, 0);
|
||||
filter.vendor_id = 0;
|
||||
filter.product_id = 0;
|
||||
} else if ((p = strchr(devname, ':'))) {
|
||||
filter.bus_num = 0;
|
||||
filter.addr = 0;
|
||||
filter.vendor_id = strtoul(devname, NULL, 16);
|
||||
filter.product_id = strtoul(p + 1, NULL, 16);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num);
|
||||
qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr);
|
||||
qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id);
|
||||
qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id);
|
||||
qdev_init_nofail(&dev->qdev);
|
||||
return dev;
|
||||
|
||||
fail:
|
||||
qdev_free(&dev->qdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int usb_host_device_close(const char *devname)
|
||||
{
|
||||
#if 0
|
||||
char product_name[PRODUCT_NAME_SZ];
|
||||
int bus_num, addr;
|
||||
USBHostDevice *s;
|
||||
|
||||
if (strstr(devname, "auto:")) {
|
||||
return usb_host_auto_del(devname);
|
||||
}
|
||||
if (usb_host_find_device(&bus_num, &addr, product_name,
|
||||
sizeof(product_name), devname) < 0) {
|
||||
return -1;
|
||||
}
|
||||
s = hostdev_find(bus_num, addr);
|
||||
if (s) {
|
||||
usb_device_delete_addr(s->bus_num, s->dev.addr);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read sys file-system device file
|
||||
*
|
||||
@ -1840,56 +1767,6 @@ static void usb_host_auto_check(void *unused)
|
||||
qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Autoconnect filter
|
||||
* Format:
|
||||
* auto:bus:dev[:vid:pid]
|
||||
* auto:bus.dev[:vid:pid]
|
||||
*
|
||||
* bus - bus number (dec, * means any)
|
||||
* dev - device number (dec, * means any)
|
||||
* vid - vendor id (hex, * means any)
|
||||
* pid - product id (hex, * means any)
|
||||
*
|
||||
* See 'lsusb' output.
|
||||
*/
|
||||
static int parse_filter(const char *spec, struct USBAutoFilter *f)
|
||||
{
|
||||
enum { BUS, DEV, VID, PID, DONE };
|
||||
const char *p = spec;
|
||||
int i;
|
||||
|
||||
f->bus_num = 0;
|
||||
f->addr = 0;
|
||||
f->vendor_id = 0;
|
||||
f->product_id = 0;
|
||||
|
||||
for (i = BUS; i < DONE; i++) {
|
||||
p = strpbrk(p, ":.");
|
||||
if (!p) {
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
|
||||
if (*p == '*') {
|
||||
continue;
|
||||
}
|
||||
switch(i) {
|
||||
case BUS: f->bus_num = strtol(p, NULL, 10); break;
|
||||
case DEV: f->addr = strtol(p, NULL, 10); break;
|
||||
case VID: f->vendor_id = strtol(p, NULL, 16); break;
|
||||
case PID: f->product_id = strtol(p, NULL, 16); break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < DEV) {
|
||||
fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************/
|
||||
/* USB host device info */
|
||||
|
||||
|
@ -45,8 +45,3 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int usb_host_device_close(const char *devname)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
44
hw/usb/host.h
Normal file
44
hw/usb/host.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Linux host USB redirector
|
||||
*
|
||||
* Copyright (c) 2005 Fabrice Bellard
|
||||
*
|
||||
* Copyright (c) 2008 Max Krasnyansky
|
||||
* Support for host device auto connect & disconnect
|
||||
* Major rewrite to support fully async operation
|
||||
*
|
||||
* Copyright 2008 TJ <linux@tjworld.net>
|
||||
* Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
|
||||
* to the legacy /proc/bus/usb USB device discovery and handling
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef QEMU_USB_HOST_H
|
||||
#define QEMU_USB_HOST_H
|
||||
|
||||
struct USBAutoFilter {
|
||||
uint32_t bus_num;
|
||||
uint32_t addr;
|
||||
char *port;
|
||||
uint32_t vendor_id;
|
||||
uint32_t product_id;
|
||||
};
|
||||
|
||||
#endif /* QEMU_USB_HOST_H */
|
@ -737,7 +737,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
||||
uint8_t ep)
|
||||
{
|
||||
struct usb_redir_bulk_packet_header bulk_packet;
|
||||
size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
|
||||
size_t size = usb_packet_size(p);
|
||||
const int maxp = dev->endpoint[EP2I(ep)].max_packet_size;
|
||||
|
||||
if (usbredir_already_in_flight(dev, p->id)) {
|
||||
@ -771,12 +771,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
||||
&bulk_packet, NULL, 0);
|
||||
} else {
|
||||
uint8_t buf[size];
|
||||
if (p->combined) {
|
||||
iov_to_buf(p->combined->iov.iov, p->combined->iov.niov,
|
||||
0, buf, size);
|
||||
} else {
|
||||
usb_packet_copy(p, buf, size);
|
||||
}
|
||||
usb_packet_copy(p, buf, size);
|
||||
usbredir_log_data(dev, "bulk data out:", buf, size);
|
||||
usbredirparser_send_bulk_packet(dev->parser, p->id,
|
||||
&bulk_packet, buf, size);
|
||||
@ -1830,7 +1825,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
|
||||
|
||||
p = usbredir_find_packet_by_id(dev, ep, id);
|
||||
if (p) {
|
||||
size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
|
||||
size_t size = usb_packet_size(p);
|
||||
usbredir_handle_status(dev, p, bulk_packet->status);
|
||||
if (data_len > 0) {
|
||||
usbredir_log_data(dev, "bulk data in:", data, data_len);
|
||||
@ -1840,12 +1835,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
|
||||
p->status = USB_RET_BABBLE;
|
||||
data_len = len = size;
|
||||
}
|
||||
if (p->combined) {
|
||||
iov_from_buf(p->combined->iov.iov, p->combined->iov.niov,
|
||||
0, data, data_len);
|
||||
} else {
|
||||
usb_packet_copy(p, data, data_len);
|
||||
}
|
||||
usb_packet_copy(p, data, data_len);
|
||||
}
|
||||
p->actual_length = len;
|
||||
if (p->pid == USB_TOKEN_IN && p->ep->pipeline) {
|
||||
|
@ -18,7 +18,7 @@ process_includes () {
|
||||
|
||||
f=$src
|
||||
while [ -n "$f" ] ; do
|
||||
f=`tr -d '\r' < $f | awk '/^include / {printf "'$src_dir'/%s", $2}'`
|
||||
f=`cat $f | tr -d '\r' | awk '/^include / {printf "'$src_dir'/%s ", $2}'`
|
||||
[ $? = 0 ] || exit 1
|
||||
all_includes="$all_includes $f"
|
||||
done
|
||||
|
@ -370,11 +370,11 @@ usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d"
|
||||
usb_xhci_slot_reset(uint32_t slotid) "slotid %d"
|
||||
usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
||||
usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
||||
usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint64_t param) "slotid %d, epid %d, ptr %016" PRIx64
|
||||
usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
||||
usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint32_t streamid, uint64_t param) "slotid %d, epid %d, streamid %d, ptr %016" PRIx64
|
||||
usb_xhci_ep_kick(uint32_t slotid, uint32_t epid, uint32_t streamid) "slotid %d, epid %d, streamid %d"
|
||||
usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
||||
usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
|
||||
usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid) "%p: slotid %d, epid %d"
|
||||
usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t streamid) "%p: slotid %d, epid %d, streamid %d"
|
||||
usb_xhci_xfer_async(void *xfer) "%p"
|
||||
usb_xhci_xfer_nak(void *xfer) "%p"
|
||||
usb_xhci_xfer_retry(void *xfer) "%p"
|
||||
|
Loading…
Reference in New Issue
Block a user