mirror of https://github.com/xemu-project/xemu.git
Merge remote-tracking branch 'kraxel/usb.7.pull' into staging
This commit is contained in:
commit
a69fb35079
14
hw/usb-hub.c
14
hw/usb-hub.c
|
@ -256,6 +256,19 @@ static void usb_hub_wakeup(USBDevice *dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void usb_hub_complete(USBDevice *dev, USBPacket *packet)
|
||||
{
|
||||
USBHubState *s = dev->port->opaque;
|
||||
|
||||
/*
|
||||
* Just pass it along upstream for now.
|
||||
*
|
||||
* If we ever inplement usb 2.0 split transactions this will
|
||||
* become a little more complicated ...
|
||||
*/
|
||||
usb_packet_complete(&s->dev, packet);
|
||||
}
|
||||
|
||||
static void usb_hub_handle_attach(USBDevice *dev)
|
||||
{
|
||||
USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
|
||||
|
@ -524,6 +537,7 @@ static USBPortOps usb_hub_port_ops = {
|
|||
.attach = usb_hub_attach,
|
||||
.detach = usb_hub_detach,
|
||||
.wakeup = usb_hub_wakeup,
|
||||
.complete = usb_hub_complete,
|
||||
};
|
||||
|
||||
static int usb_hub_initfn(USBDevice *dev)
|
||||
|
|
|
@ -241,7 +241,7 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
|||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(p);
|
||||
usb_packet_complete(&s->dev, p);
|
||||
} else if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
|||
usb_packet_complete returns. */
|
||||
DPRINTF("Packet complete %p\n", p);
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(p);
|
||||
usb_packet_complete(&s->dev, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -364,6 +364,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
|
||||
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
|
||||
s->residue = 0;
|
||||
s->scsi_len = 0;
|
||||
s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
|
||||
/* ??? Should check that USB and SCSI data transfer
|
||||
directions match. */
|
||||
|
|
|
@ -261,13 +261,24 @@
|
|||
|
||||
static void musb_attach(USBPort *port);
|
||||
static void musb_detach(USBPort *port);
|
||||
static void musb_schedule_cb(USBDevice *dev, USBPacket *p);
|
||||
|
||||
static USBPortOps musb_port_ops = {
|
||||
.attach = musb_attach,
|
||||
.detach = musb_detach,
|
||||
.complete = musb_schedule_cb,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
typedef struct MUSBPacket MUSBPacket;
|
||||
typedef struct MUSBEndPoint MUSBEndPoint;
|
||||
|
||||
struct MUSBPacket {
|
||||
USBPacket p;
|
||||
MUSBEndPoint *ep;
|
||||
int dir;
|
||||
};
|
||||
|
||||
struct MUSBEndPoint {
|
||||
uint16_t faddr[2];
|
||||
uint8_t haddr[2];
|
||||
uint8_t hport[2];
|
||||
|
@ -284,7 +295,7 @@ typedef struct {
|
|||
int fifolen[2];
|
||||
int fifostart[2];
|
||||
int fifoaddr[2];
|
||||
USBPacket packey[2];
|
||||
MUSBPacket packey[2];
|
||||
int status[2];
|
||||
int ext_size[2];
|
||||
|
||||
|
@ -294,7 +305,7 @@ typedef struct {
|
|||
MUSBState *musb;
|
||||
USBCallback *delayed_cb[2];
|
||||
QEMUTimer *intv_timer[2];
|
||||
} MUSBEndPoint;
|
||||
};
|
||||
|
||||
struct MUSBState {
|
||||
qemu_irq *irqs;
|
||||
|
@ -321,7 +332,9 @@ struct MUSBState {
|
|||
/* Duplicating the world since 2008!... probably we should have 32
|
||||
* logical, single endpoints instead. */
|
||||
MUSBEndPoint ep[16];
|
||||
} *musb_init(qemu_irq *irqs)
|
||||
};
|
||||
|
||||
struct MUSBState *musb_init(qemu_irq *irqs)
|
||||
{
|
||||
MUSBState *s = qemu_mallocz(sizeof(*s));
|
||||
int i;
|
||||
|
@ -488,21 +501,23 @@ static inline void musb_cb_tick0(void *opaque)
|
|||
{
|
||||
MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
|
||||
|
||||
ep->delayed_cb[0](&ep->packey[0], opaque);
|
||||
ep->delayed_cb[0](&ep->packey[0].p, opaque);
|
||||
}
|
||||
|
||||
static inline void musb_cb_tick1(void *opaque)
|
||||
{
|
||||
MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
|
||||
|
||||
ep->delayed_cb[1](&ep->packey[1], opaque);
|
||||
ep->delayed_cb[1](&ep->packey[1].p, opaque);
|
||||
}
|
||||
|
||||
#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0)
|
||||
|
||||
static inline void musb_schedule_cb(USBPacket *packey, void *opaque, int dir)
|
||||
static inline void musb_schedule_cb(USBDevice *dev, USBPacket *packey)
|
||||
{
|
||||
MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
|
||||
MUSBPacket *p = container_of(packey, MUSBPacket, p);
|
||||
MUSBEndPoint *ep = p->ep;
|
||||
int dir = p->dir;
|
||||
int timeout = 0;
|
||||
|
||||
if (ep->status[dir] == USB_RET_NAK)
|
||||
|
@ -510,25 +525,15 @@ static inline void musb_schedule_cb(USBPacket *packey, void *opaque, int dir)
|
|||
else if (ep->interrupt[dir])
|
||||
timeout = 8;
|
||||
else
|
||||
return musb_cb_tick(opaque);
|
||||
return musb_cb_tick(ep);
|
||||
|
||||
if (!ep->intv_timer[dir])
|
||||
ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, opaque);
|
||||
ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, ep);
|
||||
|
||||
qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock_ns(vm_clock) +
|
||||
muldiv64(timeout, get_ticks_per_sec(), 8000));
|
||||
}
|
||||
|
||||
static void musb_schedule0_cb(USBPacket *packey, void *opaque)
|
||||
{
|
||||
return musb_schedule_cb(packey, opaque, 0);
|
||||
}
|
||||
|
||||
static void musb_schedule1_cb(USBPacket *packey, void *opaque)
|
||||
{
|
||||
return musb_schedule_cb(packey, opaque, 1);
|
||||
}
|
||||
|
||||
static int musb_timeout(int ttype, int speed, int val)
|
||||
{
|
||||
#if 1
|
||||
|
@ -585,19 +590,18 @@ static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
|||
ep->type[idx] >> 6, ep->interval[idx]);
|
||||
ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT;
|
||||
ep->delayed_cb[dir] = cb;
|
||||
cb = dir ? musb_schedule1_cb : musb_schedule0_cb;
|
||||
|
||||
ep->packey[dir].pid = pid;
|
||||
ep->packey[dir].p.pid = pid;
|
||||
/* A wild guess on the FADDR semantics... */
|
||||
ep->packey[dir].devaddr = ep->faddr[idx];
|
||||
ep->packey[dir].devep = ep->type[idx] & 0xf;
|
||||
ep->packey[dir].data = (void *) ep->buf[idx];
|
||||
ep->packey[dir].len = len;
|
||||
ep->packey[dir].complete_cb = cb;
|
||||
ep->packey[dir].complete_opaque = ep;
|
||||
ep->packey[dir].p.devaddr = ep->faddr[idx];
|
||||
ep->packey[dir].p.devep = ep->type[idx] & 0xf;
|
||||
ep->packey[dir].p.data = (void *) ep->buf[idx];
|
||||
ep->packey[dir].p.len = len;
|
||||
ep->packey[dir].ep = ep;
|
||||
ep->packey[dir].dir = dir;
|
||||
|
||||
if (s->port.dev)
|
||||
ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir]);
|
||||
ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir].p);
|
||||
else
|
||||
ret = USB_RET_NODEV;
|
||||
|
||||
|
@ -607,7 +611,7 @@ static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
|||
}
|
||||
|
||||
ep->status[dir] = ret;
|
||||
usb_packet_complete(&ep->packey[dir]);
|
||||
usb_packet_complete(s->port.dev, &ep->packey[dir].p);
|
||||
}
|
||||
|
||||
static void musb_tx_packet_complete(USBPacket *packey, void *opaque)
|
||||
|
@ -821,14 +825,14 @@ static void musb_rx_req(MUSBState *s, int epnum)
|
|||
|
||||
/* If we already have a packet, which didn't fit into the
|
||||
* 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */
|
||||
if (ep->packey[1].pid == USB_TOKEN_IN && ep->status[1] >= 0 &&
|
||||
if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 &&
|
||||
(ep->fifostart[1]) + ep->rxcount <
|
||||
ep->packey[1].len) {
|
||||
ep->packey[1].p.len) {
|
||||
TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount );
|
||||
ep->fifostart[1] += ep->rxcount;
|
||||
ep->fifolen[1] = 0;
|
||||
|
||||
ep->rxcount = MIN(ep->packey[0].len - (ep->fifostart[1]),
|
||||
ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]),
|
||||
ep->maxp[1]);
|
||||
|
||||
ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;
|
||||
|
@ -866,10 +870,11 @@ static void musb_rx_req(MUSBState *s, int epnum)
|
|||
#ifdef SETUPLEN_HACK
|
||||
/* Why should *we* do that instead of Linux? */
|
||||
if (!epnum) {
|
||||
if (ep->packey[0].devaddr == 2)
|
||||
if (ep->packey[0].p.devaddr == 2) {
|
||||
total = MIN(s->setup_len, 8);
|
||||
else
|
||||
} else {
|
||||
total = MIN(s->setup_len, 64);
|
||||
}
|
||||
s->setup_len -= total;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -575,9 +575,9 @@ static void ohci_copy_iso_td(OHCIState *ohci,
|
|||
|
||||
static void ohci_process_lists(OHCIState *ohci, int completion);
|
||||
|
||||
static void ohci_async_complete_packet(USBPacket *packet, void *opaque)
|
||||
static void ohci_async_complete_packet(USBDevice *dev, USBPacket *packet)
|
||||
{
|
||||
OHCIState *ohci = opaque;
|
||||
OHCIState *ohci = container_of(packet, OHCIState, usb_packet);
|
||||
#ifdef DEBUG_PACKET
|
||||
DPRINTF("Async packet complete\n");
|
||||
#endif
|
||||
|
@ -748,8 +748,6 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
|
||||
ohci->usb_packet.data = ohci->usb_buf;
|
||||
ohci->usb_packet.len = len;
|
||||
ohci->usb_packet.complete_cb = ohci_async_complete_packet;
|
||||
ohci->usb_packet.complete_opaque = ohci;
|
||||
ret = dev->info->handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
|
@ -946,8 +944,6 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
|
||||
ohci->usb_packet.data = ohci->usb_buf;
|
||||
ohci->usb_packet.len = len;
|
||||
ohci->usb_packet.complete_cb = ohci_async_complete_packet;
|
||||
ohci->usb_packet.complete_opaque = ohci;
|
||||
ret = dev->info->handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
|
@ -1665,6 +1661,7 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={
|
|||
static USBPortOps ohci_port_ops = {
|
||||
.attach = ohci_attach,
|
||||
.detach = ohci_detach,
|
||||
.complete = ohci_async_complete_packet,
|
||||
};
|
||||
|
||||
static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||
|
|
|
@ -106,6 +106,8 @@ static void dump_data(const uint8_t *data, int len)
|
|||
static void dump_data(const uint8_t *data, int len) {}
|
||||
#endif
|
||||
|
||||
typedef struct UHCIState UHCIState;
|
||||
|
||||
/*
|
||||
* Pending async transaction.
|
||||
* 'packet' must be the first field because completion
|
||||
|
@ -113,7 +115,8 @@ static void dump_data(const uint8_t *data, int len) {}
|
|||
*/
|
||||
typedef struct UHCIAsync {
|
||||
USBPacket packet;
|
||||
struct UHCIAsync *next;
|
||||
UHCIState *uhci;
|
||||
QTAILQ_ENTRY(UHCIAsync) next;
|
||||
uint32_t td;
|
||||
uint32_t token;
|
||||
int8_t valid;
|
||||
|
@ -127,7 +130,7 @@ typedef struct UHCIPort {
|
|||
uint16_t ctrl;
|
||||
} UHCIPort;
|
||||
|
||||
typedef struct UHCIState {
|
||||
struct UHCIState {
|
||||
PCIDevice dev;
|
||||
USBBus bus;
|
||||
uint16_t cmd; /* cmd register */
|
||||
|
@ -145,10 +148,9 @@ typedef struct UHCIState {
|
|||
uint32_t pending_int_mask;
|
||||
|
||||
/* Active packets */
|
||||
UHCIAsync *async_pending;
|
||||
UHCIAsync *async_pool;
|
||||
QTAILQ_HEAD(,UHCIAsync) async_pending;
|
||||
uint8_t num_ports_vmstate;
|
||||
} UHCIState;
|
||||
};
|
||||
|
||||
typedef struct UHCI_TD {
|
||||
uint32_t link;
|
||||
|
@ -167,12 +169,12 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s)
|
|||
UHCIAsync *async = qemu_malloc(sizeof(UHCIAsync));
|
||||
|
||||
memset(&async->packet, 0, sizeof(async->packet));
|
||||
async->uhci = s;
|
||||
async->valid = 0;
|
||||
async->td = 0;
|
||||
async->token = 0;
|
||||
async->done = 0;
|
||||
async->isoc = 0;
|
||||
async->next = NULL;
|
||||
|
||||
return async;
|
||||
}
|
||||
|
@ -184,24 +186,12 @@ static void uhci_async_free(UHCIState *s, UHCIAsync *async)
|
|||
|
||||
static void uhci_async_link(UHCIState *s, UHCIAsync *async)
|
||||
{
|
||||
async->next = s->async_pending;
|
||||
s->async_pending = async;
|
||||
QTAILQ_INSERT_HEAD(&s->async_pending, async, next);
|
||||
}
|
||||
|
||||
static void uhci_async_unlink(UHCIState *s, UHCIAsync *async)
|
||||
{
|
||||
UHCIAsync *curr = s->async_pending;
|
||||
UHCIAsync **prev = &s->async_pending;
|
||||
|
||||
while (curr) {
|
||||
if (curr == async) {
|
||||
*prev = curr->next;
|
||||
return;
|
||||
}
|
||||
|
||||
prev = &curr->next;
|
||||
curr = curr->next;
|
||||
}
|
||||
QTAILQ_REMOVE(&s->async_pending, async, next);
|
||||
}
|
||||
|
||||
static void uhci_async_cancel(UHCIState *s, UHCIAsync *async)
|
||||
|
@ -220,11 +210,10 @@ static void uhci_async_cancel(UHCIState *s, UHCIAsync *async)
|
|||
*/
|
||||
static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
|
||||
{
|
||||
UHCIAsync *async = s->async_pending;
|
||||
UHCIAsync *async;
|
||||
|
||||
while (async) {
|
||||
QTAILQ_FOREACH(async, &s->async_pending, next) {
|
||||
async->valid--;
|
||||
async = async->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -234,47 +223,30 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
|
|||
*/
|
||||
static void uhci_async_validate_end(UHCIState *s)
|
||||
{
|
||||
UHCIAsync *curr = s->async_pending;
|
||||
UHCIAsync **prev = &s->async_pending;
|
||||
UHCIAsync *next;
|
||||
UHCIAsync *curr, *n;
|
||||
|
||||
while (curr) {
|
||||
QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
|
||||
if (curr->valid > 0) {
|
||||
prev = &curr->next;
|
||||
curr = curr->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
next = curr->next;
|
||||
|
||||
/* Unlink */
|
||||
*prev = next;
|
||||
|
||||
uhci_async_unlink(s, curr);
|
||||
uhci_async_cancel(s, curr);
|
||||
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void uhci_async_cancel_all(UHCIState *s)
|
||||
{
|
||||
UHCIAsync *curr = s->async_pending;
|
||||
UHCIAsync *next;
|
||||
|
||||
while (curr) {
|
||||
next = curr->next;
|
||||
UHCIAsync *curr, *n;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
|
||||
uhci_async_unlink(s, curr);
|
||||
uhci_async_cancel(s, curr);
|
||||
|
||||
curr = next;
|
||||
}
|
||||
|
||||
s->async_pending = NULL;
|
||||
}
|
||||
|
||||
static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token)
|
||||
{
|
||||
UHCIAsync *async = s->async_pending;
|
||||
UHCIAsync *async;
|
||||
UHCIAsync *match = NULL;
|
||||
int count = 0;
|
||||
|
||||
|
@ -291,7 +263,7 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token
|
|||
* If we ever do we'd want to optimize this algorithm.
|
||||
*/
|
||||
|
||||
while (async) {
|
||||
QTAILQ_FOREACH(async, &s->async_pending, next) {
|
||||
if (async->token == token) {
|
||||
/* Good match */
|
||||
match = async;
|
||||
|
@ -301,8 +273,6 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async = async->next;
|
||||
count++;
|
||||
}
|
||||
|
||||
|
@ -672,7 +642,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void uhci_async_complete(USBPacket * packet, void *opaque);
|
||||
static void uhci_async_complete(USBDevice *dev, USBPacket *packet);
|
||||
static void uhci_process_frame(UHCIState *s);
|
||||
|
||||
/* return -1 if fatal error (frame must be stopped)
|
||||
|
@ -825,8 +795,6 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
|
|||
async->packet.devep = (td->token >> 15) & 0xf;
|
||||
async->packet.data = async->buffer;
|
||||
async->packet.len = max_len;
|
||||
async->packet.complete_cb = uhci_async_complete;
|
||||
async->packet.complete_opaque = s;
|
||||
|
||||
switch(pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
|
@ -862,10 +830,10 @@ done:
|
|||
return len;
|
||||
}
|
||||
|
||||
static void uhci_async_complete(USBPacket *packet, void *opaque)
|
||||
static void uhci_async_complete(USBDevice *dev, USBPacket *packet)
|
||||
{
|
||||
UHCIState *s = opaque;
|
||||
UHCIAsync *async = (UHCIAsync *) packet;
|
||||
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
|
||||
UHCIState *s = async->uhci;
|
||||
|
||||
DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
|
||||
|
||||
|
@ -1113,6 +1081,7 @@ static USBPortOps uhci_port_ops = {
|
|||
.attach = uhci_attach,
|
||||
.detach = uhci_detach,
|
||||
.wakeup = uhci_wakeup,
|
||||
.complete = uhci_async_complete,
|
||||
};
|
||||
|
||||
static int usb_uhci_common_initfn(UHCIState *s)
|
||||
|
@ -1137,6 +1106,7 @@ static int usb_uhci_common_initfn(UHCIState *s)
|
|||
s->expire_time = qemu_get_clock_ns(vm_clock) +
|
||||
(get_ticks_per_sec() / FRAME_TIMER_FREQ);
|
||||
s->num_ports_vmstate = NB_PORTS;
|
||||
QTAILQ_INIT(&s->async_pending);
|
||||
|
||||
qemu_register_reset(uhci_reset, s);
|
||||
|
||||
|
|
6
hw/usb.c
6
hw/usb.c
|
@ -93,6 +93,12 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
|
|||
s->setup_len = ret;
|
||||
s->setup_state = SETUP_STATE_DATA;
|
||||
} else {
|
||||
if (s->setup_len > sizeof(s->data_buf)) {
|
||||
fprintf(stderr,
|
||||
"usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
|
||||
s->setup_len, sizeof(s->data_buf));
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
if (s->setup_len == 0)
|
||||
s->setup_state = SETUP_STATE_ACK;
|
||||
else
|
||||
|
|
9
hw/usb.h
9
hw/usb.h
|
@ -167,7 +167,7 @@ struct USBDevice {
|
|||
|
||||
int32_t state;
|
||||
uint8_t setup_buf[8];
|
||||
uint8_t data_buf[1024];
|
||||
uint8_t data_buf[4096];
|
||||
int32_t remote_wakeup;
|
||||
int32_t setup_state;
|
||||
int32_t setup_len;
|
||||
|
@ -235,6 +235,7 @@ typedef struct USBPortOps {
|
|||
void (*attach)(USBPort *port);
|
||||
void (*detach)(USBPort *port);
|
||||
void (*wakeup)(USBDevice *dev);
|
||||
void (*complete)(USBDevice *dev, USBPacket *p);
|
||||
} USBPortOps;
|
||||
|
||||
/* USB port on which a device can be connected */
|
||||
|
@ -259,8 +260,6 @@ struct USBPacket {
|
|||
uint8_t *data;
|
||||
int len;
|
||||
/* Internal use by the USB layer. */
|
||||
USBCallback *complete_cb;
|
||||
void *complete_opaque;
|
||||
USBCallback *cancel_cb;
|
||||
void *cancel_opaque;
|
||||
};
|
||||
|
@ -278,9 +277,9 @@ static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
|
|||
/* Notify the controller that an async packet is complete. This should only
|
||||
be called for packets previously deferred with usb_defer_packet, and
|
||||
should never be called from within handle_packet. */
|
||||
static inline void usb_packet_complete(USBPacket *p)
|
||||
static inline void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
p->complete_cb(p, p->complete_opaque);
|
||||
dev->port->ops->complete(dev, p);
|
||||
}
|
||||
|
||||
/* Cancel an active packet. The packed must have been deferred with
|
||||
|
|
410
usb-linux.c
410
usb-linux.c
|
@ -78,7 +78,7 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
|
|||
|
||||
#define USBPROCBUS_PATH "/proc/bus/usb"
|
||||
#define PRODUCT_NAME_SZ 32
|
||||
#define MAX_ENDPOINTS 16
|
||||
#define MAX_ENDPOINTS 15
|
||||
#define USBDEVBUS_PATH "/dev/bus/usb"
|
||||
#define USBSYSBUS_PATH "/sys/bus/usb"
|
||||
|
||||
|
@ -92,9 +92,20 @@ static char *usb_host_device_path;
|
|||
static int usb_fs_type;
|
||||
|
||||
/* endpoint association data */
|
||||
#define ISO_FRAME_DESC_PER_URB 32
|
||||
#define ISO_URB_COUNT 3
|
||||
#define INVALID_EP_TYPE 255
|
||||
|
||||
typedef struct AsyncURB AsyncURB;
|
||||
|
||||
struct endp_data {
|
||||
uint8_t type;
|
||||
uint8_t halted;
|
||||
uint8_t iso_started;
|
||||
AsyncURB *iso_urb;
|
||||
int iso_urb_idx;
|
||||
int iso_buffer_used;
|
||||
int max_packet_size;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -160,6 +171,11 @@ static int is_isoc(USBHostDevice *s, int ep)
|
|||
return s->endp_table[ep - 1].type == USBDEVFS_URB_TYPE_ISO;
|
||||
}
|
||||
|
||||
static int is_valid(USBHostDevice *s, int ep)
|
||||
{
|
||||
return s->endp_table[ep - 1].type != INVALID_EP_TYPE;
|
||||
}
|
||||
|
||||
static int is_halted(USBHostDevice *s, int ep)
|
||||
{
|
||||
return s->endp_table[ep - 1].halted;
|
||||
|
@ -175,19 +191,73 @@ static void set_halt(USBHostDevice *s, int ep)
|
|||
s->endp_table[ep - 1].halted = 1;
|
||||
}
|
||||
|
||||
static int is_iso_started(USBHostDevice *s, int ep)
|
||||
{
|
||||
return s->endp_table[ep - 1].iso_started;
|
||||
}
|
||||
|
||||
static void clear_iso_started(USBHostDevice *s, int ep)
|
||||
{
|
||||
s->endp_table[ep - 1].iso_started = 0;
|
||||
}
|
||||
|
||||
static void set_iso_started(USBHostDevice *s, int ep)
|
||||
{
|
||||
s->endp_table[ep - 1].iso_started = 1;
|
||||
}
|
||||
|
||||
static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb)
|
||||
{
|
||||
s->endp_table[ep - 1].iso_urb = iso_urb;
|
||||
}
|
||||
|
||||
static AsyncURB *get_iso_urb(USBHostDevice *s, int ep)
|
||||
{
|
||||
return s->endp_table[ep - 1].iso_urb;
|
||||
}
|
||||
|
||||
static void set_iso_urb_idx(USBHostDevice *s, int ep, int i)
|
||||
{
|
||||
s->endp_table[ep - 1].iso_urb_idx = i;
|
||||
}
|
||||
|
||||
static int get_iso_urb_idx(USBHostDevice *s, int ep)
|
||||
{
|
||||
return s->endp_table[ep - 1].iso_urb_idx;
|
||||
}
|
||||
|
||||
static void set_iso_buffer_used(USBHostDevice *s, int ep, int i)
|
||||
{
|
||||
s->endp_table[ep - 1].iso_buffer_used = i;
|
||||
}
|
||||
|
||||
static int get_iso_buffer_used(USBHostDevice *s, int ep)
|
||||
{
|
||||
return s->endp_table[ep - 1].iso_buffer_used;
|
||||
}
|
||||
|
||||
static int get_max_packet_size(USBHostDevice *s, int ep)
|
||||
{
|
||||
return s->endp_table[ep - 1].max_packet_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Async URB state.
|
||||
* We always allocate one isoc descriptor even for bulk transfers
|
||||
* We always allocate iso packet descriptors even for bulk transfers
|
||||
* to simplify allocation and casts.
|
||||
*/
|
||||
typedef struct AsyncURB
|
||||
struct AsyncURB
|
||||
{
|
||||
struct usbdevfs_urb urb;
|
||||
struct usbdevfs_iso_packet_desc isocpd;
|
||||
struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
|
||||
|
||||
/* For regular async urbs */
|
||||
USBPacket *packet;
|
||||
USBHostDevice *hdev;
|
||||
} AsyncURB;
|
||||
|
||||
/* For buffered iso handling */
|
||||
int iso_frame_idx; /* -1 means in flight */
|
||||
};
|
||||
|
||||
static AsyncURB *async_alloc(void)
|
||||
{
|
||||
|
@ -244,11 +314,21 @@ static void async_complete(void *opaque)
|
|||
return;
|
||||
}
|
||||
|
||||
p = aurb->packet;
|
||||
|
||||
DPRINTF("husb: async completed. aurb %p status %d alen %d\n",
|
||||
aurb, aurb->urb.status, aurb->urb.actual_length);
|
||||
|
||||
/* If this is a buffered iso urb mark it as complete and don't do
|
||||
anything else (it is handled further in usb_host_handle_iso_data) */
|
||||
if (aurb->iso_frame_idx == -1) {
|
||||
if (aurb->urb.status == -EPIPE) {
|
||||
set_halt(s, aurb->urb.endpoint & 0xf);
|
||||
}
|
||||
aurb->iso_frame_idx = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
p = aurb->packet;
|
||||
|
||||
if (p) {
|
||||
switch (aurb->urb.status) {
|
||||
case 0:
|
||||
|
@ -268,7 +348,7 @@ static void async_complete(void *opaque)
|
|||
break;
|
||||
}
|
||||
|
||||
usb_packet_complete(p);
|
||||
usb_packet_complete(&s->dev, p);
|
||||
}
|
||||
|
||||
async_free(aurb);
|
||||
|
@ -415,11 +495,215 @@ static void usb_host_handle_destroy(USBDevice *dev)
|
|||
|
||||
static int usb_linux_update_endp_table(USBHostDevice *s);
|
||||
|
||||
/* iso data is special, we need to keep enough urbs in flight to make sure
|
||||
that the controller never runs out of them, otherwise the device will
|
||||
likely suffer a buffer underrun / overrun. */
|
||||
static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in)
|
||||
{
|
||||
AsyncURB *aurb;
|
||||
int i, j, len = get_max_packet_size(s, ep);
|
||||
|
||||
aurb = qemu_mallocz(ISO_URB_COUNT * sizeof(*aurb));
|
||||
for (i = 0; i < ISO_URB_COUNT; i++) {
|
||||
aurb[i].urb.endpoint = ep;
|
||||
aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len;
|
||||
aurb[i].urb.buffer = qemu_malloc(aurb[i].urb.buffer_length);
|
||||
aurb[i].urb.type = USBDEVFS_URB_TYPE_ISO;
|
||||
aurb[i].urb.flags = USBDEVFS_URB_ISO_ASAP;
|
||||
aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB;
|
||||
for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++)
|
||||
aurb[i].urb.iso_frame_desc[j].length = len;
|
||||
if (in) {
|
||||
aurb[i].urb.endpoint |= 0x80;
|
||||
/* Mark as fully consumed (idle) */
|
||||
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB;
|
||||
}
|
||||
}
|
||||
set_iso_urb(s, ep, aurb);
|
||||
|
||||
return aurb;
|
||||
}
|
||||
|
||||
static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep)
|
||||
{
|
||||
AsyncURB *aurb;
|
||||
int i, ret, killed = 0, free = 1;
|
||||
|
||||
aurb = get_iso_urb(s, ep);
|
||||
if (!aurb) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ISO_URB_COUNT; i++) {
|
||||
/* in flight? */
|
||||
if (aurb[i].iso_frame_idx == -1) {
|
||||
ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]);
|
||||
if (ret < 0) {
|
||||
printf("husb: discard isoc in urb failed errno %d\n", errno);
|
||||
free = 0;
|
||||
continue;
|
||||
}
|
||||
killed++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure any urbs we've killed are reaped before we free them */
|
||||
if (killed) {
|
||||
async_complete(s);
|
||||
}
|
||||
|
||||
for (i = 0; i < ISO_URB_COUNT; i++) {
|
||||
qemu_free(aurb[i].urb.buffer);
|
||||
}
|
||||
|
||||
if (free)
|
||||
qemu_free(aurb);
|
||||
else
|
||||
printf("husb: leaking iso urbs because of discard failure\n");
|
||||
set_iso_urb(s, ep, NULL);
|
||||
set_iso_urb_idx(s, ep, 0);
|
||||
clear_iso_started(s, ep);
|
||||
}
|
||||
|
||||
static int urb_status_to_usb_ret(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case -EPIPE:
|
||||
return USB_RET_STALL;
|
||||
default:
|
||||
return USB_RET_NAK;
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
|
||||
{
|
||||
AsyncURB *aurb;
|
||||
int i, j, ret, max_packet_size, offset, len = 0;
|
||||
|
||||
max_packet_size = get_max_packet_size(s, p->devep);
|
||||
if (max_packet_size == 0)
|
||||
return USB_RET_NAK;
|
||||
|
||||
aurb = get_iso_urb(s, p->devep);
|
||||
if (!aurb) {
|
||||
aurb = usb_host_alloc_iso(s, p->devep, in);
|
||||
}
|
||||
|
||||
i = get_iso_urb_idx(s, p->devep);
|
||||
j = aurb[i].iso_frame_idx;
|
||||
if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
|
||||
if (in) {
|
||||
/* Check urb status */
|
||||
if (aurb[i].urb.status) {
|
||||
len = urb_status_to_usb_ret(aurb[i].urb.status);
|
||||
/* Move to the next urb */
|
||||
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
|
||||
/* Check frame status */
|
||||
} else if (aurb[i].urb.iso_frame_desc[j].status) {
|
||||
len = urb_status_to_usb_ret(
|
||||
aurb[i].urb.iso_frame_desc[j].status);
|
||||
/* Check the frame fits */
|
||||
} else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) {
|
||||
printf("husb: received iso data is larger then packet\n");
|
||||
len = USB_RET_NAK;
|
||||
/* All good copy data over */
|
||||
} else {
|
||||
len = aurb[i].urb.iso_frame_desc[j].actual_length;
|
||||
memcpy(p->data,
|
||||
aurb[i].urb.buffer +
|
||||
j * aurb[i].urb.iso_frame_desc[0].length,
|
||||
len);
|
||||
}
|
||||
} else {
|
||||
len = p->len;
|
||||
offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep);
|
||||
|
||||
/* Check the frame fits */
|
||||
if (len > max_packet_size) {
|
||||
printf("husb: send iso data is larger then max packet size\n");
|
||||
return USB_RET_NAK;
|
||||
}
|
||||
|
||||
/* All good copy data over */
|
||||
memcpy(aurb[i].urb.buffer + offset, p->data, len);
|
||||
aurb[i].urb.iso_frame_desc[j].length = len;
|
||||
offset += len;
|
||||
set_iso_buffer_used(s, p->devep, offset);
|
||||
|
||||
/* Start the stream once we have buffered enough data */
|
||||
if (!is_iso_started(s, p->devep) && i == 1 && j == 8) {
|
||||
set_iso_started(s, p->devep);
|
||||
}
|
||||
}
|
||||
aurb[i].iso_frame_idx++;
|
||||
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
||||
i = (i + 1) % ISO_URB_COUNT;
|
||||
set_iso_urb_idx(s, p->devep, i);
|
||||
}
|
||||
} else {
|
||||
if (in) {
|
||||
set_iso_started(s, p->devep);
|
||||
} else {
|
||||
DPRINTF("hubs: iso out error no free buffer, dropping packet\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (is_iso_started(s, p->devep)) {
|
||||
/* (Re)-submit all fully consumed / filled urbs */
|
||||
for (i = 0; i < ISO_URB_COUNT; i++) {
|
||||
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
||||
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
|
||||
if (ret < 0) {
|
||||
printf("husb error submitting iso urb %d: %d\n", i, errno);
|
||||
if (!in || len == 0) {
|
||||
switch(errno) {
|
||||
case ETIMEDOUT:
|
||||
len = USB_RET_NAK;
|
||||
case EPIPE:
|
||||
default:
|
||||
len = USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
aurb[i].iso_frame_idx = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
|
||||
{
|
||||
struct usbdevfs_urb *urb;
|
||||
AsyncURB *aurb;
|
||||
int ret;
|
||||
uint8_t ep;
|
||||
|
||||
if (!is_valid(s, p->devep)) {
|
||||
return USB_RET_NAK;
|
||||
}
|
||||
|
||||
if (p->pid == USB_TOKEN_IN) {
|
||||
ep = p->devep | 0x80;
|
||||
} else {
|
||||
ep = p->devep;
|
||||
}
|
||||
|
||||
if (is_halted(s, p->devep)) {
|
||||
ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &ep);
|
||||
if (ret < 0) {
|
||||
DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n",
|
||||
ep, errno);
|
||||
return USB_RET_NAK;
|
||||
}
|
||||
clear_halt(s, p->devep);
|
||||
}
|
||||
|
||||
if (is_isoc(s, p->devep)) {
|
||||
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
|
||||
}
|
||||
|
||||
aurb = async_alloc();
|
||||
aurb->hdev = s;
|
||||
|
@ -427,37 +711,11 @@ static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
|
|||
|
||||
urb = &aurb->urb;
|
||||
|
||||
if (p->pid == USB_TOKEN_IN) {
|
||||
urb->endpoint = p->devep | 0x80;
|
||||
} else {
|
||||
urb->endpoint = p->devep;
|
||||
}
|
||||
|
||||
if (is_halted(s, p->devep)) {
|
||||
ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint);
|
||||
if (ret < 0) {
|
||||
DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n",
|
||||
urb->endpoint, errno);
|
||||
return USB_RET_NAK;
|
||||
}
|
||||
clear_halt(s, p->devep);
|
||||
}
|
||||
|
||||
urb->endpoint = ep;
|
||||
urb->buffer = p->data;
|
||||
urb->buffer_length = p->len;
|
||||
|
||||
if (is_isoc(s, p->devep)) {
|
||||
/* Setup ISOC transfer */
|
||||
urb->type = USBDEVFS_URB_TYPE_ISO;
|
||||
urb->flags = USBDEVFS_URB_ISO_ASAP;
|
||||
urb->number_of_packets = 1;
|
||||
urb->iso_frame_desc[0].length = p->len;
|
||||
} else {
|
||||
/* Setup bulk transfer */
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
}
|
||||
|
||||
urb->usercontext = s;
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
urb->usercontext = s;
|
||||
|
||||
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
|
||||
|
||||
|
@ -515,7 +773,13 @@ static int usb_host_set_config(USBHostDevice *s, int config)
|
|||
static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
|
||||
{
|
||||
struct usbdevfs_setinterface si;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
for (i = 1; i <= MAX_ENDPOINTS; i++) {
|
||||
if (is_isoc(s, i)) {
|
||||
usb_host_stop_n_free_iso(s, i);
|
||||
}
|
||||
}
|
||||
|
||||
si.interface = iface;
|
||||
si.altsetting = alt;
|
||||
|
@ -823,13 +1087,56 @@ usbdevfs:
|
|||
return configuration;
|
||||
}
|
||||
|
||||
static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
|
||||
uint8_t configuration, uint8_t interface)
|
||||
{
|
||||
uint8_t alt_setting;
|
||||
struct usb_ctrltransfer ct;
|
||||
int ret;
|
||||
|
||||
if (usb_fs_type == USB_FS_SYS) {
|
||||
char device_name[64], line[1024];
|
||||
int alt_setting;
|
||||
|
||||
sprintf(device_name, "%d-%d:%d.%d", s->bus_num, s->devpath,
|
||||
(int)configuration, (int)interface);
|
||||
|
||||
if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
|
||||
device_name)) {
|
||||
goto usbdevfs;
|
||||
}
|
||||
if (sscanf(line, "%d", &alt_setting) != 1) {
|
||||
goto usbdevfs;
|
||||
}
|
||||
return alt_setting;
|
||||
}
|
||||
|
||||
usbdevfs:
|
||||
ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE;
|
||||
ct.bRequest = USB_REQ_GET_INTERFACE;
|
||||
ct.wValue = 0;
|
||||
ct.wIndex = interface;
|
||||
ct.wLength = 1;
|
||||
ct.data = &alt_setting;
|
||||
ct.timeout = 50;
|
||||
ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
|
||||
if (ret < 0) {
|
||||
/* Assume alt 0 on error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return alt_setting;
|
||||
}
|
||||
|
||||
/* returns 1 on problem encountered or 0 for success */
|
||||
static int usb_linux_update_endp_table(USBHostDevice *s)
|
||||
{
|
||||
uint8_t *descriptors;
|
||||
uint8_t devep, type, configuration, alt_interface;
|
||||
struct usb_ctrltransfer ct;
|
||||
int interface, ret, length, i;
|
||||
int interface, length, i;
|
||||
|
||||
for (i = 0; i < MAX_ENDPOINTS; i++)
|
||||
s->endp_table[i].type = INVALID_EP_TYPE;
|
||||
|
||||
i = usb_linux_get_configuration(s);
|
||||
if (i < 0)
|
||||
|
@ -858,19 +1165,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
|
|||
}
|
||||
|
||||
interface = descriptors[i + 2];
|
||||
|
||||
ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE;
|
||||
ct.bRequest = USB_REQ_GET_INTERFACE;
|
||||
ct.wValue = 0;
|
||||
ct.wIndex = interface;
|
||||
ct.wLength = 1;
|
||||
ct.data = &alt_interface;
|
||||
ct.timeout = 50;
|
||||
|
||||
ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
|
||||
if (ret < 0) {
|
||||
alt_interface = interface;
|
||||
}
|
||||
alt_interface = usb_linux_get_alt_setting(s, configuration, interface);
|
||||
|
||||
/* the current interface descriptor is the active interface
|
||||
* and has endpoints */
|
||||
|
@ -899,6 +1194,8 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
|
|||
break;
|
||||
case 0x01:
|
||||
type = USBDEVFS_URB_TYPE_ISO;
|
||||
s->endp_table[(devep & 0xf) - 1].max_packet_size =
|
||||
descriptors[i + 4] + (descriptors[i + 5] << 8);
|
||||
break;
|
||||
case 0x02:
|
||||
type = USBDEVFS_URB_TYPE_BULK;
|
||||
|
@ -1021,12 +1318,19 @@ fail:
|
|||
|
||||
static int usb_host_close(USBHostDevice *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dev->fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
|
||||
dev->closing = 1;
|
||||
for (i = 1; i <= MAX_ENDPOINTS; i++) {
|
||||
if (is_isoc(dev, i)) {
|
||||
usb_host_stop_n_free_iso(dev, i);
|
||||
}
|
||||
}
|
||||
async_complete(dev);
|
||||
dev->closing = 0;
|
||||
usb_device_detach(&dev->dev);
|
||||
|
|
Loading…
Reference in New Issue