Commit a594cfbf authored by bellard's avatar bellard

USB user interface


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1603 c046a42c-6fe2-441c-8c8c-71466251a162
parent 8738a8d0
......@@ -290,12 +290,14 @@ ifdef CONFIG_ADLIB
SOUND_HW += fmopl.o adlib.o
endif
# USB layer
VL_OBJS+= usb.o usb-uhci.o usb-linux.o usb-hid.o
ifeq ($(TARGET_BASE_ARCH), i386)
# Hardware support
VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o
VL_OBJS+= usb.o usb-uhci.o usb-linux.o usb-hid.o
DEFINES += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), ppc)
......
......@@ -42,6 +42,7 @@ static fdctrl_t *floppy_controller;
static RTCState *rtc_state;
static PITState *pit;
static IOAPICState *ioapic;
static USBPort *usb_root_ports[2];
static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
{
......@@ -625,32 +626,8 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device,
cmos_init(ram_size, boot_device, bs_table);
if (pci_enabled && usb_enabled) {
USBPort *usb_root_ports[2];
USBDevice *usb_dev;
usb_uhci_init(pci_bus, usb_root_ports);
#if 0
{
USBPort *usb_hub1_ports[4];
USBPort *usb_hub2_ports[2];
/* test: we simulate a USB hub */
usb_dev = usb_hub_init(usb_hub1_ports, 4);
usb_attach(usb_root_ports[0], usb_dev);
/* test: we simulate a USB hub */
usb_dev = usb_hub_init(usb_hub2_ports, 2);
usb_attach(usb_hub1_ports[0], usb_dev);
}
#endif
#if 0
/* USB mouse */
usb_dev = usb_mouse_init();
usb_attach(usb_root_ports[0], usb_dev);
#endif
#if 1
/* simulated hub with the host USB devices connected to it */
usb_dev = usb_host_hub_init();
usb_attach(usb_root_ports[0], usb_dev);
#endif
usb_attach(usb_root_ports[0], vm_usb_hub);
}
/* must be done after all PCI devices are instanciated */
......
......@@ -63,7 +63,6 @@
typedef struct UHCIPort {
USBPort port;
uint16_t ctrl;
USBDevice *dev; /* connected device */
} UHCIPort;
typedef struct UHCIState {
......@@ -128,8 +127,8 @@ static void uhci_reset(UHCIState *s)
for(i = 0; i < NB_PORTS; i++) {
port = &s->ports[i];
port->ctrl = 0x0080;
if (port->dev)
uhci_attach(&port->port, port->dev);
if (port->port.dev)
uhci_attach(&port->port, port->port.dev);
}
}
......@@ -183,7 +182,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
/* send reset on the USB bus */
for(i = 0; i < NB_PORTS; i++) {
port = &s->ports[i];
dev = port->dev;
dev = port->port.dev;
if (dev) {
dev->handle_packet(dev,
USB_MSG_RESET, 0, 0, NULL, 0);
......@@ -224,7 +223,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
if (n >= NB_PORTS)
return;
port = &s->ports[n];
dev = port->dev;
dev = port->port.dev;
if (dev) {
/* port reset */
if ( (val & UHCI_PORT_RESET) &&
......@@ -320,7 +319,7 @@ static void uhci_attach(USBPort *port1, USBDevice *dev)
UHCIPort *port = &s->ports[port1->index];
if (dev) {
if (port->dev) {
if (port->port.dev) {
usb_attach(port1, NULL);
}
/* set connect status */
......@@ -332,7 +331,7 @@ static void uhci_attach(USBPort *port1, USBDevice *dev)
port->ctrl |= UHCI_PORT_LSDA;
else
port->ctrl &= ~UHCI_PORT_LSDA;
port->dev = dev;
port->port.dev = dev;
/* send the attach message */
dev->handle_packet(dev,
USB_MSG_ATTACH, 0, 0, NULL, 0);
......@@ -346,13 +345,13 @@ static void uhci_attach(USBPort *port1, USBDevice *dev)
port->ctrl &= ~UHCI_PORT_EN;
port->ctrl |= UHCI_PORT_ENC;
}
dev = port->dev;
dev = port->port.dev;
if (dev) {
/* send the detach message */
dev->handle_packet(dev,
USB_MSG_DETACH, 0, 0, NULL, 0);
}
port->dev = NULL;
port->port.dev = NULL;
}
}
......@@ -386,7 +385,7 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid,
#endif
for(i = 0; i < NB_PORTS; i++) {
port = &s->ports[i];
dev = port->dev;
dev = port->port.dev;
if (dev && (port->ctrl & UHCI_PORT_EN)) {
ret = dev->handle_packet(dev, pid,
devaddr, devep,
......
......@@ -201,7 +201,6 @@ int set_usb_string(uint8_t *buf, const char *str)
typedef struct USBHubPort {
USBPort port;
USBDevice *dev;
uint16_t wPortStatus;
uint16_t wPortChange;
} USBHubPort;
......@@ -342,7 +341,7 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
USBHubPort *port = &s->ports[port1->index];
if (dev) {
if (port->dev)
if (port->port.dev)
usb_attach(port1, NULL);
port->wPortStatus |= PORT_STAT_CONNECTION;
......@@ -351,9 +350,9 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
port->wPortStatus |= PORT_STAT_LOW_SPEED;
else
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
port->dev = dev;
port->port.dev = dev;
} else {
dev = port->dev;
dev = port->port.dev;
if (dev) {
port->wPortStatus &= ~PORT_STAT_CONNECTION;
port->wPortChange |= PORT_STAT_C_CONNECTION;
......@@ -361,7 +360,7 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
port->wPortStatus &= ~PORT_STAT_ENABLE;
port->wPortChange |= PORT_STAT_C_ENABLE;
}
port->dev = NULL;
port->port.dev = NULL;
}
}
}
......@@ -498,7 +497,7 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
if (n >= s->nb_ports)
goto fail;
port = &s->ports[n];
dev = port->dev;
dev = port->port.dev;
switch(value) {
case PORT_SUSPEND:
port->wPortStatus |= PORT_STAT_SUSPEND;
......@@ -529,7 +528,7 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
if (n >= s->nb_ports)
goto fail;
port = &s->ports[n];
dev = port->dev;
dev = port->port.dev;
switch(value) {
case PORT_ENABLE:
port->wPortStatus &= ~PORT_STAT_ENABLE;
......@@ -624,7 +623,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, int pid,
for(i = 0; i < s->nb_ports; i++) {
port = &s->ports[i];
dev = port->dev;
dev = port->port.dev;
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
ret = dev->handle_packet(dev, pid,
devaddr, devep,
......
......@@ -47,6 +47,20 @@
//#define USB_STATE_CONFIGURED 5
#define USB_STATE_SUSPENDED 6
#define USB_CLASS_AUDIO 1
#define USB_CLASS_COMM 2
#define USB_CLASS_HID 3
#define USB_CLASS_PHYSICAL 5
#define USB_CLASS_STILL_IMAGE 6
#define USB_CLASS_PRINTER 7
#define USB_CLASS_MASS_STORAGE 8
#define USB_CLASS_HUB 9
#define USB_CLASS_CDC_DATA 0x0a
#define USB_CLASS_CSCID 0x0b
#define USB_CLASS_CONTENT_SEC 0x0d
#define USB_CLASS_APP_SPEC 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff
#define USB_DIR_OUT 0
#define USB_DIR_IN 0x80
......@@ -125,6 +139,7 @@ struct USBDevice {
/* USB port on which a device can be connected */
struct USBPort {
USBDevice *dev;
void (*attach)(USBPort *port, USBDevice *dev);
void *opaque;
int index; /* internal port index, may be used with the opaque */
......@@ -143,7 +158,8 @@ USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports);
void usb_uhci_init(PCIBus *bus, USBPort **usb_ports);
/* usb-linux.c */
USBDevice *usb_host_hub_init(void);
USBDevice *usb_host_device_open(const char *devname);
void usb_host_info(void);
/* usb-hid.c */
USBDevice *usb_mouse_init(void);
......@@ -901,7 +901,6 @@ static void do_info_kqemu(void)
#endif
}
static term_cmd_t term_cmds[] = {
{ "help|?", "s?", do_help,
"[cmd]", "show the help" },
......@@ -948,6 +947,10 @@ static term_cmd_t term_cmds[] = {
"", "send system power down event" },
{ "sum", "ii", do_sum,
"addr size", "compute the checksum of a memory region" },
{ "usb_add", "s", do_usb_add,
"device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" },
{ "usb_del", "s", do_usb_del,
"device", "remove USB device 'bus.addr'" },
{ NULL, NULL, },
};
......@@ -978,6 +981,10 @@ static term_cmd_t info_cmds[] = {
"", "show dynamic compiler info", },
{ "kqemu", "", do_info_kqemu,
"", "show kqemu information", },
{ "usb", "", usb_info,
"", "show guest USB devices", },
{ "usbhost", "", usb_host_info,
"", "show host USB devices", },
{ NULL, NULL, },
};
......
......@@ -40,9 +40,13 @@ struct usb_ctrltransfer {
void *data;
};
//#define DEBUG
typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
int vendor_id, int product_id,
const char *product_name, int speed);
static int usb_host_find_device(int *pbus_num, int *paddr,
const char *devname);
#define MAX_DEVICES 8
//#define DEBUG
#define USBDEVFS_PATH "/proc/bus/usb"
......@@ -51,12 +55,6 @@ typedef struct USBHostDevice {
int fd;
} USBHostDevice;
typedef struct USBHostHubState {
USBDevice *hub_dev;
USBPort *hub_ports[MAX_DEVICES];
USBDevice *hub_devices[MAX_DEVICES];
} USBHostHubState;
static void usb_host_handle_reset(USBDevice *dev)
{
#if 0
......@@ -137,29 +135,26 @@ static int usb_host_handle_data(USBDevice *dev, int pid,
}
}
static int usb_host_handle_packet(USBDevice *dev, int pid,
uint8_t devaddr, uint8_t devep,
uint8_t *data, int len)
{
return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len);
}
/* XXX: exclude high speed devices or implement EHCI */
static void scan_host_device(USBHostHubState *s, const char *filename)
USBDevice *usb_host_device_open(const char *devname)
{
int fd, interface, ret, i;
USBHostDevice *dev;
struct usbdevfs_connectinfo ci;
uint8_t descr[1024];
char buf[1024];
int descr_len, dev_descr_len, config_descr_len, nb_interfaces;
int bus_num, addr;
#ifdef DEBUG
printf("scanning %s\n", filename);
#endif
fd = open(filename, O_RDWR);
if (usb_host_find_device(&bus_num, &addr, devname) < 0)
return NULL;
snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d",
bus_num, addr);
fd = open(buf, O_RDWR);
if (fd < 0) {
perror(filename);
return;
perror(buf);
return NULL;
}
/* read the config description */
......@@ -180,22 +175,31 @@ static void scan_host_device(USBHostHubState *s, const char *filename)
nb_interfaces = descr[i + 4];
if (nb_interfaces != 1) {
/* NOTE: currently we grab only one interface */
fprintf(stderr, "usb_host: only one interface supported\n");
goto fail;
}
#ifdef USBDEVFS_DISCONNECT
/* earlier Linux 2.4 do not support that */
ret = ioctl(fd, USBDEVFS_DISCONNECT);
if (ret < 0 && errno != ENODATA) {
perror("USBDEVFS_DISCONNECT");
goto fail;
}
#endif
/* XXX: only grab if all interfaces are free */
interface = 0;
ret = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &interface);
if (ret < 0) {
if (errno == EBUSY) {
#ifdef DEBUG
printf("%s already grabbed\n", filename);
#endif
fprintf(stderr, "usb_host: device already grabbed\n");
} else {
perror("USBDEVFS_CLAIMINTERFACE");
}
fail:
close(fd);
return;
return NULL;
}
ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
......@@ -205,21 +209,9 @@ static void scan_host_device(USBHostHubState *s, const char *filename)
}
#ifdef DEBUG
printf("%s grabbed\n", filename);
printf("host USB device %d.%d grabbed\n", bus_num, addr);
#endif
/* find a free slot */
for(i = 0; i < MAX_DEVICES; i++) {
if (!s->hub_devices[i])
break;
}
if (i == MAX_DEVICES) {
#ifdef DEBUG
printf("too many host devices\n");
goto fail;
#endif
}
dev = qemu_mallocz(sizeof(USBHostDevice));
if (!dev)
goto fail;
......@@ -228,80 +220,259 @@ static void scan_host_device(USBHostHubState *s, const char *filename)
dev->dev.speed = USB_SPEED_LOW;
else
dev->dev.speed = USB_SPEED_HIGH;
dev->dev.handle_packet = usb_host_handle_packet;
dev->dev.handle_packet = usb_generic_handle_packet;
dev->dev.handle_reset = usb_host_handle_reset;
dev->dev.handle_control = usb_host_handle_control;
dev->dev.handle_data = usb_host_handle_data;
return (USBDevice *)dev;
}
s->hub_devices[i] = (USBDevice *)dev;
/* activate device on hub */
usb_attach(s->hub_ports[i], s->hub_devices[i]);
static int get_tag_value(char *buf, int buf_size,
const char *str, const char *tag,
const char *stopchars)
{
const char *p;
char *q;
p = strstr(str, tag);
if (!p)
return -1;
p += strlen(tag);
while (isspace(*p))
p++;
q = buf;
while (*p != '\0' && !strchr(stopchars, *p)) {
if ((q - buf) < (buf_size - 1))
*q++ = *p;
p++;
}
*q = '\0';
return q - buf;
}
static void scan_host_devices(USBHostHubState *s, const char *bus_path)
static int usb_host_scan(void *opaque, USBScanFunc *func)
{
DIR *d;
struct dirent *de;
FILE *f;
char line[1024];
char buf[1024];
d = opendir(bus_path);
if (!d)
return;
int bus_num, addr, speed, device_count, class_id, product_id, vendor_id;
int ret;
char product_name[512];
f = fopen(USBDEVFS_PATH "/devices", "r");
if (!f) {
term_printf("Could not open %s\n", USBDEVFS_PATH "/devices");
return 0;
}
device_count = 0;
bus_num = addr = speed = class_id = product_id = vendor_id = 0;
ret = 0;
for(;;) {
de = readdir(d);
if (!de)
if (fgets(line, sizeof(line), f) == NULL)
break;
if (de->d_name[0] != '.') {
snprintf(buf, sizeof(buf), "%s/%s", bus_path, de->d_name);
scan_host_device(s, buf);
if (strlen(line) > 0)
line[strlen(line) - 1] = '\0';
if (line[0] == 'T' && line[1] == ':') {
if (device_count) {
ret = func(opaque, bus_num, addr, class_id, vendor_id,
product_id, product_name, speed);
if (ret)
goto the_end;
}
if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0)
goto fail;
bus_num = atoi(buf);
if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0)
goto fail;
addr = atoi(buf);
if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0)
goto fail;
if (!strcmp(buf, "480"))
speed = USB_SPEED_HIGH;
else if (!strcmp(buf, "1.5"))
speed = USB_SPEED_LOW;
else
speed = USB_SPEED_FULL;
product_name[0] = '\0';
class_id = 0xff;
device_count++;
product_id = 0;
vendor_id = 0;
} else if (line[0] == 'P' && line[1] == ':') {
if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0)
goto fail;
vendor_id = strtoul(buf, NULL, 16);
if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0)
goto fail;
product_id = strtoul(buf, NULL, 16);
} else if (line[0] == 'S' && line[1] == ':') {
if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0)
goto fail;
pstrcpy(product_name, sizeof(product_name), buf);
} else if (line[0] == 'D' && line[1] == ':') {
if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0)
goto fail;
class_id = strtoul(buf, NULL, 16);
}
fail: ;
}
if (device_count) {
ret = func(opaque, bus_num, addr, class_id, vendor_id,
product_id, product_name, speed);
}
closedir(d);
the_end:
fclose(f);
return ret;
}
static void scan_host_buses(USBHostHubState *s)
typedef struct FindDeviceState {
int vendor_id;
int product_id;
int bus_num;
int addr;
} FindDeviceState;
static int usb_host_find_device_scan(void *opaque, int bus_num, int addr,
int class_id,
int vendor_id, int product_id,
const char *product_name, int speed)
{
DIR *d;
struct dirent *de;
char buf[1024];
FindDeviceState *s = opaque;
if (vendor_id == s->vendor_id &&
product_id == s->product_id) {
s->bus_num = bus_num;
s->addr = addr;
return 1;
} else {
return 0;
}
}
d = opendir(USBDEVFS_PATH);
if (!d)
return;
for(;;) {
de = readdir(d);
if (!de)
break;
if (isdigit(de->d_name[0])) {
snprintf(buf, sizeof(buf), "%s/%s", USBDEVFS_PATH, de->d_name);
scan_host_devices(s, buf);
/* the syntax is :
'bus.addr' (decimal numbers) or
'vendor_id:product_id' (hexa numbers) */
static int usb_host_find_device(int *pbus_num, int *paddr,
const char *devname)
{
const char *p;
int ret;
FindDeviceState fs;
p = strchr(devname, '.');
if (p) {
*pbus_num = strtoul(devname, NULL, 0);
*paddr = strtoul(p + 1, NULL, 0);
return 0;
}
p = strchr(devname, ':');
if (p) {
fs.vendor_id = strtoul(devname, NULL, 16);
fs.product_id = strtoul(p + 1, NULL, 16);
ret = usb_host_scan(&fs, usb_host_find_device_scan);
if (ret) {
*pbus_num = fs.bus_num;
*paddr = fs.addr;
return 0;
}
}
closedir(d);
return -1;
}
/* virtual hub containing the USB devices of the host */
USBDevice *usb_host_hub_init(void)
/**********************/
/* USB host device info */
struct usb_class_info {
int class;
const char *class_name;
};
static const struct usb_class_info usb_class_info[] = {
{ USB_CLASS_AUDIO, "Audio"},
{ USB_CLASS_COMM, "Communication"},
{ USB_CLASS_HID, "HID"},
{ USB_CLASS_HUB, "Hub" },
{ USB_CLASS_PHYSICAL, "Physical" },
{ USB_CLASS_PRINTER, "Printer" },
{ USB_CLASS_MASS_STORAGE, "Storage" },
{ USB_CLASS_CDC_DATA, "Data" },
{ USB_CLASS_APP_SPEC, "Application Specific" },
{ USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
{ USB_CLASS_STILL_IMAGE, "Still Image" },
{ USB_CLASS_CSCID, "Smart Card" },
{ USB_CLASS_CONTENT_SEC, "Content Security" },
{ -1, NULL }
};
static const char *usb_class_str(uint8_t class)
{
USBHostHubState *s;
s = qemu_mallocz(sizeof(USBHostHubState));
if (!s)
return NULL;
s->hub_dev = usb_hub_init(s->hub_ports, MAX_DEVICES);
if (!s->hub_dev) {
free(s);
return NULL;
const struct usb_class_info *p;
for(p = usb_class_info; p->class != -1; p++) {
if (p->class == class)
break;
}