mirror of https://github.com/xemu-project/xemu.git
husb: Make control transactions asynchronous (Max Krasnyansky)
USB is 99.8% async now :). 0.2% is the three control requests that we need to execute synchronously. We could off-load that to a thread or something but it's not worth the pain since those requests are performed only during device initialization (ie when device is connected to the VM). The change is a bit bigger than I wanted due to the fact that generic handle_packet()/handle_control() interface was not designed for async transactions. So I ended up adding custom handle_packet() code to usb-linux. We can make that generic if/when some other component needs it. Signed-off-by: Max Krasnyansky <maxk@kernel.org> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5204 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
9d0efc88e4
commit
446ab1284e
464
usb-linux.c
464
usb-linux.c
|
@ -25,28 +25,20 @@
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu-timer.h"
|
#include "qemu-timer.h"
|
||||||
#include "hw/usb.h"
|
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <linux/usbdevice_fs.h>
|
|
||||||
#include <linux/version.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
/* We redefine it to avoid version problems */
|
#include <linux/usb/ch9.h>
|
||||||
struct usb_ctrltransfer {
|
#include <linux/usbdevice_fs.h>
|
||||||
uint8_t bRequestType;
|
#include <linux/version.h>
|
||||||
uint8_t bRequest;
|
#include "hw/usb.h"
|
||||||
uint16_t wValue;
|
|
||||||
uint16_t wIndex;
|
|
||||||
uint16_t wLength;
|
|
||||||
uint32_t timeout;
|
|
||||||
void *data;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
|
typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
|
||||||
int vendor_id, int product_id,
|
int vendor_id, int product_id,
|
||||||
|
@ -54,7 +46,6 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
|
||||||
static int usb_host_find_device(int *pbus_num, int *paddr,
|
static int usb_host_find_device(int *pbus_num, int *paddr,
|
||||||
char *product_name, int product_name_size,
|
char *product_name, int product_name_size,
|
||||||
const char *devname);
|
const char *devname);
|
||||||
|
|
||||||
//#define DEBUG
|
//#define DEBUG
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -67,14 +58,32 @@ static int usb_host_find_device(int *pbus_num, int *paddr,
|
||||||
#define PRODUCT_NAME_SZ 32
|
#define PRODUCT_NAME_SZ 32
|
||||||
#define MAX_ENDPOINTS 16
|
#define MAX_ENDPOINTS 16
|
||||||
|
|
||||||
struct sigaction sigact;
|
|
||||||
|
|
||||||
/* endpoint association data */
|
/* endpoint association data */
|
||||||
struct endp_data {
|
struct endp_data {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t halted;
|
uint8_t halted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CTRL_STATE_IDLE = 0,
|
||||||
|
CTRL_STATE_SETUP,
|
||||||
|
CTRL_STATE_DATA,
|
||||||
|
CTRL_STATE_ACK
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Control transfer state.
|
||||||
|
* Note that 'buffer' _must_ follow 'req' field because
|
||||||
|
* we need contigious buffer when we submit control URB.
|
||||||
|
*/
|
||||||
|
struct ctrl_struct {
|
||||||
|
uint16_t len;
|
||||||
|
uint16_t offset;
|
||||||
|
uint8_t state;
|
||||||
|
struct usb_ctrlrequest req;
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct USBHostDevice {
|
typedef struct USBHostDevice {
|
||||||
USBDevice dev;
|
USBDevice dev;
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -82,8 +91,10 @@ typedef struct USBHostDevice {
|
||||||
uint8_t descr[1024];
|
uint8_t descr[1024];
|
||||||
int descr_len;
|
int descr_len;
|
||||||
int configuration;
|
int configuration;
|
||||||
|
int ninterfaces;
|
||||||
int closing;
|
int closing;
|
||||||
|
|
||||||
|
struct ctrl_struct ctrl;
|
||||||
struct endp_data endp_table[MAX_ENDPOINTS];
|
struct endp_data endp_table[MAX_ENDPOINTS];
|
||||||
|
|
||||||
/* Host side address */
|
/* Host side address */
|
||||||
|
@ -172,6 +183,26 @@ static void async_free(AsyncURB *aurb)
|
||||||
qemu_free(aurb);
|
qemu_free(aurb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void async_complete_ctrl(USBHostDevice *s, USBPacket *p)
|
||||||
|
{
|
||||||
|
switch(s->ctrl.state) {
|
||||||
|
case CTRL_STATE_SETUP:
|
||||||
|
if (p->len < s->ctrl.len)
|
||||||
|
s->ctrl.len = p->len;
|
||||||
|
s->ctrl.state = CTRL_STATE_DATA;
|
||||||
|
p->len = 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CTRL_STATE_ACK:
|
||||||
|
s->ctrl.state = CTRL_STATE_IDLE;
|
||||||
|
p->len = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void async_complete(void *opaque)
|
static void async_complete(void *opaque)
|
||||||
{
|
{
|
||||||
USBHostDevice *s = opaque;
|
USBHostDevice *s = opaque;
|
||||||
|
@ -204,6 +235,8 @@ static void async_complete(void *opaque)
|
||||||
switch (aurb->urb.status) {
|
switch (aurb->urb.status) {
|
||||||
case 0:
|
case 0:
|
||||||
p->len = aurb->urb.actual_length;
|
p->len = aurb->urb.actual_length;
|
||||||
|
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL)
|
||||||
|
async_complete_ctrl(s, p);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case -EPIPE:
|
case -EPIPE:
|
||||||
|
@ -237,7 +270,7 @@ static void async_cancel(USBPacket *unused, void *opaque)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int usb_host_update_interfaces(USBHostDevice *dev, int configuration)
|
static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||||
{
|
{
|
||||||
int dev_descr_len, config_descr_len;
|
int dev_descr_len, config_descr_len;
|
||||||
int interface, nb_interfaces, nb_configurations;
|
int interface, nb_interfaces, nb_configurations;
|
||||||
|
@ -246,6 +279,8 @@ static int usb_host_update_interfaces(USBHostDevice *dev, int configuration)
|
||||||
if (configuration == 0) /* address state - ignore */
|
if (configuration == 0) /* address state - ignore */
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
dprintf("husb: claiming interfaces. config %d\n", configuration);
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
dev_descr_len = dev->descr[0];
|
dev_descr_len = dev->descr[0];
|
||||||
if (dev_descr_len > dev->descr_len)
|
if (dev_descr_len > dev->descr_len)
|
||||||
|
@ -265,8 +300,10 @@ static int usb_host_update_interfaces(USBHostDevice *dev, int configuration)
|
||||||
|
|
||||||
printf("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
|
printf("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
|
||||||
|
|
||||||
if (configuration < 0 || configuration == dev->descr[i + 5])
|
if (configuration < 0 || configuration == dev->descr[i + 5]) {
|
||||||
|
configuration = dev->descr[i + 5];
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
i += config_descr_len;
|
i += config_descr_len;
|
||||||
}
|
}
|
||||||
|
@ -310,17 +347,37 @@ static int usb_host_update_interfaces(USBHostDevice *dev, int configuration)
|
||||||
printf("husb: %d interfaces claimed for configuration %d\n",
|
printf("husb: %d interfaces claimed for configuration %d\n",
|
||||||
nb_interfaces, configuration);
|
nb_interfaces, configuration);
|
||||||
|
|
||||||
|
dev->ninterfaces = nb_interfaces;
|
||||||
|
dev->configuration = configuration;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_host_release_interfaces(USBHostDevice *s)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
dprintf("husb: releasing interfaces\n");
|
||||||
|
|
||||||
|
for (i = 0; i < s->ninterfaces; i++) {
|
||||||
|
ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("husb: failed to release interface");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_host_handle_reset(USBDevice *dev)
|
static void usb_host_handle_reset(USBDevice *dev)
|
||||||
{
|
{
|
||||||
USBHostDevice *s = (USBHostDevice *)dev;
|
USBHostDevice *s = (USBHostDevice *) dev;
|
||||||
|
|
||||||
dprintf("husb: reset device %u.%u\n", s->bus_num, s->addr);
|
dprintf("husb: reset device %u.%u\n", s->bus_num, s->addr);
|
||||||
|
|
||||||
ioctl(s->fd, USBDEVFS_RESET);
|
ioctl(s->fd, USBDEVFS_RESET);
|
||||||
usb_host_update_interfaces(s, s->configuration);
|
|
||||||
|
usb_host_claim_interfaces(s, s->configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_host_handle_destroy(USBDevice *dev)
|
static void usb_host_handle_destroy(USBDevice *dev)
|
||||||
|
@ -343,73 +400,10 @@ static void usb_host_handle_destroy(USBDevice *dev)
|
||||||
|
|
||||||
static int usb_linux_update_endp_table(USBHostDevice *s);
|
static int usb_linux_update_endp_table(USBHostDevice *s);
|
||||||
|
|
||||||
static int usb_host_handle_control(USBDevice *dev,
|
static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
|
||||||
int request,
|
|
||||||
int value,
|
|
||||||
int index,
|
|
||||||
int length,
|
|
||||||
uint8_t *data)
|
|
||||||
{
|
{
|
||||||
USBHostDevice *s = (USBHostDevice *)dev;
|
|
||||||
struct usb_ctrltransfer ct;
|
|
||||||
struct usbdevfs_setinterface si;
|
|
||||||
int intf_update_required = 0;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (request == (DeviceOutRequest | USB_REQ_SET_ADDRESS)) {
|
|
||||||
/* specific SET_ADDRESS support */
|
|
||||||
dev->addr = value;
|
|
||||||
return 0;
|
|
||||||
} else if (request == ((USB_RECIP_INTERFACE << 8) |
|
|
||||||
USB_REQ_SET_INTERFACE)) {
|
|
||||||
/* set alternate setting for the interface */
|
|
||||||
si.interface = index;
|
|
||||||
si.altsetting = value;
|
|
||||||
ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
|
|
||||||
usb_linux_update_endp_table(s);
|
|
||||||
} else if (request == (DeviceOutRequest | USB_REQ_SET_CONFIGURATION)) {
|
|
||||||
dprintf("husb: ctrl set config %d\n", value & 0xff);
|
|
||||||
if (s->configuration != (value & 0xff)) {
|
|
||||||
s->configuration = (value & 0xff);
|
|
||||||
intf_update_required = 1;
|
|
||||||
}
|
|
||||||
goto do_request;
|
|
||||||
} else {
|
|
||||||
do_request:
|
|
||||||
ct.bRequestType = request >> 8;
|
|
||||||
ct.bRequest = request;
|
|
||||||
ct.wValue = value;
|
|
||||||
ct.wIndex = index;
|
|
||||||
ct.wLength = length;
|
|
||||||
ct.timeout = 50;
|
|
||||||
ct.data = data;
|
|
||||||
ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
|
|
||||||
|
|
||||||
dprintf("husb: ctrl req 0x%x val 0x%x index %u len %u ret %d\n",
|
|
||||||
ct.bRequest, ct.wValue, ct.wIndex, ct.wLength, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
switch(errno) {
|
|
||||||
case ETIMEDOUT:
|
|
||||||
return USB_RET_NAK;
|
|
||||||
default:
|
|
||||||
return USB_RET_STALL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (intf_update_required) {
|
|
||||||
dprintf("husb: updating interfaces\n");
|
|
||||||
usb_host_update_interfaces(s, value & 0xff);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
|
||||||
{
|
|
||||||
USBHostDevice *s = (USBHostDevice *) dev;
|
|
||||||
AsyncURB *aurb;
|
|
||||||
struct usbdevfs_urb *urb;
|
struct usbdevfs_urb *urb;
|
||||||
|
AsyncURB *aurb;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
aurb = async_alloc();
|
aurb = async_alloc();
|
||||||
|
@ -474,12 +468,292 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
||||||
return USB_RET_ASYNC;
|
return USB_RET_ASYNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ctrl_error(void)
|
||||||
|
{
|
||||||
|
if (errno == ETIMEDOUT)
|
||||||
|
return USB_RET_NAK;
|
||||||
|
else
|
||||||
|
return USB_RET_STALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_host_set_address(USBHostDevice *s, int addr)
|
||||||
|
{
|
||||||
|
dprintf("husb: ctrl set addr %u\n", addr);
|
||||||
|
s->dev.addr = addr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_host_set_config(USBHostDevice *s, int config)
|
||||||
|
{
|
||||||
|
usb_host_release_interfaces(s);
|
||||||
|
|
||||||
|
int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
|
||||||
|
|
||||||
|
dprintf("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ctrl_error();
|
||||||
|
|
||||||
|
usb_host_claim_interfaces(s, config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
|
||||||
|
{
|
||||||
|
struct usbdevfs_setinterface si;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
si.interface = iface;
|
||||||
|
si.altsetting = alt;
|
||||||
|
ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
|
||||||
|
|
||||||
|
dprintf("husb: ctrl set iface %d altset %d ret %d errno %d\n",
|
||||||
|
iface, alt, ret, errno);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ctrl_error();
|
||||||
|
|
||||||
|
usb_linux_update_endp_table(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
|
||||||
|
{
|
||||||
|
struct usbdevfs_urb *urb;
|
||||||
|
AsyncURB *aurb;
|
||||||
|
int ret, value, index;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process certain standard device requests.
|
||||||
|
* These are infrequent and are processed synchronously.
|
||||||
|
*/
|
||||||
|
value = le16_to_cpu(s->ctrl.req.wValue);
|
||||||
|
index = le16_to_cpu(s->ctrl.req.wIndex);
|
||||||
|
|
||||||
|
dprintf("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n",
|
||||||
|
s->ctrl.req.bRequestType, s->ctrl.req.bRequest, value, index,
|
||||||
|
s->ctrl.len);
|
||||||
|
|
||||||
|
if (s->ctrl.req.bRequestType == 0) {
|
||||||
|
switch (s->ctrl.req.bRequest) {
|
||||||
|
case USB_REQ_SET_ADDRESS:
|
||||||
|
return usb_host_set_address(s, value);
|
||||||
|
|
||||||
|
case USB_REQ_SET_CONFIGURATION:
|
||||||
|
return usb_host_set_config(s, value & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->ctrl.req.bRequestType == 1 &&
|
||||||
|
s->ctrl.req.bRequest == USB_REQ_SET_INTERFACE)
|
||||||
|
return usb_host_set_interface(s, index, value);
|
||||||
|
|
||||||
|
/* The rest are asynchronous */
|
||||||
|
|
||||||
|
aurb = async_alloc();
|
||||||
|
if (!aurb) {
|
||||||
|
dprintf("husb: async malloc failed\n");
|
||||||
|
return USB_RET_NAK;
|
||||||
|
}
|
||||||
|
aurb->hdev = s;
|
||||||
|
aurb->packet = p;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup ctrl transfer.
|
||||||
|
*
|
||||||
|
* s->ctrl is layed out such that data buffer immediately follows
|
||||||
|
* 'req' struct which is exactly what usbdevfs expects.
|
||||||
|
*/
|
||||||
|
urb = &aurb->urb;
|
||||||
|
|
||||||
|
urb->type = USBDEVFS_URB_TYPE_CONTROL;
|
||||||
|
urb->endpoint = p->devep;
|
||||||
|
|
||||||
|
urb->buffer = &s->ctrl.req;
|
||||||
|
urb->buffer_length = 8 + s->ctrl.len;
|
||||||
|
|
||||||
|
urb->usercontext = s;
|
||||||
|
|
||||||
|
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
|
||||||
|
|
||||||
|
dprintf("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dprintf("husb: submit failed. errno %d\n", errno);
|
||||||
|
async_free(aurb);
|
||||||
|
|
||||||
|
switch(errno) {
|
||||||
|
case ETIMEDOUT:
|
||||||
|
return USB_RET_NAK;
|
||||||
|
case EPIPE:
|
||||||
|
default:
|
||||||
|
return USB_RET_STALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_defer_packet(p, async_cancel, aurb);
|
||||||
|
return USB_RET_ASYNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_token_setup(USBDevice *dev, USBPacket *p)
|
||||||
|
{
|
||||||
|
USBHostDevice *s = (USBHostDevice *) dev;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (p->len != 8)
|
||||||
|
return USB_RET_STALL;
|
||||||
|
|
||||||
|
memcpy(&s->ctrl.req, p->data, 8);
|
||||||
|
s->ctrl.len = le16_to_cpu(s->ctrl.req.wLength);
|
||||||
|
s->ctrl.offset = 0;
|
||||||
|
s->ctrl.state = CTRL_STATE_SETUP;
|
||||||
|
|
||||||
|
if (s->ctrl.req.bRequestType & USB_DIR_IN) {
|
||||||
|
ret = usb_host_handle_control(s, p);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (ret < s->ctrl.len)
|
||||||
|
s->ctrl.len = ret;
|
||||||
|
s->ctrl.state = CTRL_STATE_DATA;
|
||||||
|
} else {
|
||||||
|
if (s->ctrl.len == 0)
|
||||||
|
s->ctrl.state = CTRL_STATE_ACK;
|
||||||
|
else
|
||||||
|
s->ctrl.state = CTRL_STATE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_token_in(USBDevice *dev, USBPacket *p)
|
||||||
|
{
|
||||||
|
USBHostDevice *s = (USBHostDevice *) dev;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (p->devep != 0)
|
||||||
|
return usb_host_handle_data(s, p);
|
||||||
|
|
||||||
|
switch(s->ctrl.state) {
|
||||||
|
case CTRL_STATE_ACK:
|
||||||
|
if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
|
||||||
|
ret = usb_host_handle_control(s, p);
|
||||||
|
if (ret == USB_RET_ASYNC)
|
||||||
|
return USB_RET_ASYNC;
|
||||||
|
|
||||||
|
s->ctrl.state = CTRL_STATE_IDLE;
|
||||||
|
return ret > 0 ? 0 : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case CTRL_STATE_DATA:
|
||||||
|
if (s->ctrl.req.bRequestType & USB_DIR_IN) {
|
||||||
|
int len = s->ctrl.len - s->ctrl.offset;
|
||||||
|
if (len > p->len)
|
||||||
|
len = p->len;
|
||||||
|
memcpy(p->data, s->ctrl.buffer + s->ctrl.offset, len);
|
||||||
|
s->ctrl.offset += len;
|
||||||
|
if (s->ctrl.offset >= s->ctrl.len)
|
||||||
|
s->ctrl.state = CTRL_STATE_ACK;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->ctrl.state = CTRL_STATE_IDLE;
|
||||||
|
return USB_RET_STALL;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return USB_RET_STALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_token_out(USBDevice *dev, USBPacket *p)
|
||||||
|
{
|
||||||
|
USBHostDevice *s = (USBHostDevice *) dev;
|
||||||
|
|
||||||
|
if (p->devep != 0)
|
||||||
|
return usb_host_handle_data(s, p);
|
||||||
|
|
||||||
|
switch(s->ctrl.state) {
|
||||||
|
case CTRL_STATE_ACK:
|
||||||
|
if (s->ctrl.req.bRequestType & USB_DIR_IN) {
|
||||||
|
s->ctrl.state = CTRL_STATE_IDLE;
|
||||||
|
/* transfer OK */
|
||||||
|
} else {
|
||||||
|
/* ignore additional output */
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case CTRL_STATE_DATA:
|
||||||
|
if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
|
||||||
|
int len = s->ctrl.len - s->ctrl.offset;
|
||||||
|
if (len > p->len)
|
||||||
|
len = p->len;
|
||||||
|
memcpy(s->ctrl.buffer + s->ctrl.offset, p->data, len);
|
||||||
|
s->ctrl.offset += len;
|
||||||
|
if (s->ctrl.offset >= s->ctrl.len)
|
||||||
|
s->ctrl.state = CTRL_STATE_ACK;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->ctrl.state = CTRL_STATE_IDLE;
|
||||||
|
return USB_RET_STALL;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return USB_RET_STALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Packet handler.
|
||||||
|
* Called by the HC (host controller).
|
||||||
|
*
|
||||||
|
* Returns length of the transaction or one of the USB_RET_XXX codes.
|
||||||
|
*/
|
||||||
|
int usb_host_handle_packet(USBDevice *s, USBPacket *p)
|
||||||
|
{
|
||||||
|
switch(p->pid) {
|
||||||
|
case USB_MSG_ATTACH:
|
||||||
|
s->state = USB_STATE_ATTACHED;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case USB_MSG_DETACH:
|
||||||
|
s->state = USB_STATE_NOTATTACHED;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case USB_MSG_RESET:
|
||||||
|
s->remote_wakeup = 0;
|
||||||
|
s->addr = 0;
|
||||||
|
s->state = USB_STATE_DEFAULT;
|
||||||
|
s->handle_reset(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rest of the PIDs must match our address */
|
||||||
|
if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr)
|
||||||
|
return USB_RET_NODEV;
|
||||||
|
|
||||||
|
switch (p->pid) {
|
||||||
|
case USB_TOKEN_SETUP:
|
||||||
|
return do_token_setup(s, p);
|
||||||
|
|
||||||
|
case USB_TOKEN_IN:
|
||||||
|
return do_token_in(s, p);
|
||||||
|
|
||||||
|
case USB_TOKEN_OUT:
|
||||||
|
return do_token_out(s, p);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return USB_RET_STALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* returns 1 on problem encountered or 0 for success */
|
/* returns 1 on problem encountered or 0 for success */
|
||||||
static int usb_linux_update_endp_table(USBHostDevice *s)
|
static int usb_linux_update_endp_table(USBHostDevice *s)
|
||||||
{
|
{
|
||||||
uint8_t *descriptors;
|
uint8_t *descriptors;
|
||||||
uint8_t devep, type, configuration, alt_interface;
|
uint8_t devep, type, configuration, alt_interface;
|
||||||
struct usb_ctrltransfer ct;
|
struct usbdevfs_ctrltransfer ct;
|
||||||
int interface, ret, length, i;
|
int interface, ret, length, i;
|
||||||
|
|
||||||
ct.bRequestType = USB_DIR_IN;
|
ct.bRequestType = USB_DIR_IN;
|
||||||
|
@ -624,10 +898,14 @@ static USBDevice *usb_host_device_open_addr(int bus_num, int addr, const char *p
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dev->fd = fd;
|
dev->fd = fd;
|
||||||
dev->configuration = 1;
|
|
||||||
|
|
||||||
/* XXX - do something about initial configuration */
|
/*
|
||||||
if (!usb_host_update_interfaces(dev, -1))
|
* Initial configuration is -1 which makes us claim first
|
||||||
|
* available config. We used to start with 1, which does not
|
||||||
|
* always work. I've seen devices where first config starts
|
||||||
|
* with 2.
|
||||||
|
*/
|
||||||
|
if (!usb_host_claim_interfaces(dev, -1))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
|
ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
|
||||||
|
@ -646,11 +924,9 @@ static USBDevice *usb_host_device_open_addr(int bus_num, int addr, const char *p
|
||||||
dev->dev.speed = USB_SPEED_LOW;
|
dev->dev.speed = USB_SPEED_LOW;
|
||||||
else
|
else
|
||||||
dev->dev.speed = USB_SPEED_HIGH;
|
dev->dev.speed = USB_SPEED_HIGH;
|
||||||
dev->dev.handle_packet = usb_generic_handle_packet;
|
|
||||||
|
|
||||||
dev->dev.handle_reset = usb_host_handle_reset;
|
dev->dev.handle_packet = usb_host_handle_packet;
|
||||||
dev->dev.handle_control = usb_host_handle_control;
|
dev->dev.handle_reset = usb_host_handle_reset;
|
||||||
dev->dev.handle_data = usb_host_handle_data;
|
|
||||||
dev->dev.handle_destroy = usb_host_handle_destroy;
|
dev->dev.handle_destroy = usb_host_handle_destroy;
|
||||||
|
|
||||||
if (!prod_name || prod_name[0] == '\0')
|
if (!prod_name || prod_name[0] == '\0')
|
||||||
|
@ -1055,6 +1331,8 @@ void usb_host_info(void)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
#include "hw/usb.h"
|
||||||
|
|
||||||
void usb_host_info(void)
|
void usb_host_info(void)
|
||||||
{
|
{
|
||||||
term_printf("USB host devices not supported\n");
|
term_printf("USB host devices not supported\n");
|
||||||
|
|
Loading…
Reference in New Issue