Merge remote-tracking branch 'kraxel/usb.7.pull' into staging

master
Anthony Liguori 2011-05-05 13:04:57 -05:00
commit a69fb35079
8 changed files with 453 additions and 157 deletions

View File

@ -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)

View File

@ -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. */

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);