mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
initial USB support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1597 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
@@ -6,6 +6,7 @@ version 0.7.3:
|
||||
- ALSA audio driver (malc)
|
||||
- new audio options: '-soundhw' and '-audio-help' (malc)
|
||||
- ES1370 PCI audio device (malc)
|
||||
- Initial USB support
|
||||
|
||||
version 0.7.2:
|
||||
|
||||
|
||||
+1
-1
@@ -294,7 +294,7 @@ 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+= cirrus_vga.o mixeng.o apic.o parallel.o usb.o usb-uhci.o usb-linux.o
|
||||
DEFINES += -DHAS_AUDIO
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), ppc)
|
||||
|
||||
@@ -620,6 +620,28 @@ 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_hub;
|
||||
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_hub = usb_hub_init(usb_hub1_ports, 4);
|
||||
usb_attach(usb_root_ports[0], usb_hub);
|
||||
|
||||
/* test: we simulate a USB hub */
|
||||
usb_hub = usb_hub_init(usb_hub2_ports, 2);
|
||||
usb_attach(usb_hub1_ports[0], usb_hub);
|
||||
}
|
||||
#endif
|
||||
/* simulated hub with the host USB devices connected to it */
|
||||
usb_hub = usb_host_hub_init();
|
||||
usb_attach(usb_root_ports[0], usb_hub);
|
||||
}
|
||||
|
||||
/* must be done after all PCI devices are instanciated */
|
||||
/* XXX: should be done in the Bochs BIOS */
|
||||
if (pci_enabled) {
|
||||
|
||||
+672
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* QEMU USB API
|
||||
*
|
||||
* Copyright (c) 2005 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#define USB_TOKEN_SETUP 0x2d
|
||||
#define USB_TOKEN_IN 0x69 /* device -> host */
|
||||
#define USB_TOKEN_OUT 0xe1 /* host -> device */
|
||||
|
||||
/* specific usb messages, also sent in the 'pid' parameter */
|
||||
#define USB_MSG_ATTACH 0x100
|
||||
#define USB_MSG_DETACH 0x101
|
||||
#define USB_MSG_RESET 0x102
|
||||
|
||||
#define USB_RET_NODEV (-1)
|
||||
#define USB_RET_NAK (-2)
|
||||
#define USB_RET_STALL (-3)
|
||||
#define USB_RET_BABBLE (-4)
|
||||
|
||||
#define USB_SPEED_LOW 0
|
||||
#define USB_SPEED_FULL 1
|
||||
#define USB_SPEED_HIGH 2
|
||||
|
||||
#define USB_STATE_NOTATTACHED 0
|
||||
#define USB_STATE_ATTACHED 1
|
||||
//#define USB_STATE_POWERED 2
|
||||
#define USB_STATE_DEFAULT 3
|
||||
//#define USB_STATE_ADDRESS 4
|
||||
//#define USB_STATE_CONFIGURED 5
|
||||
#define USB_STATE_SUSPENDED 6
|
||||
|
||||
#define USB_DIR_OUT 0
|
||||
#define USB_DIR_IN 0x80
|
||||
|
||||
#define USB_TYPE_MASK (0x03 << 5)
|
||||
#define USB_TYPE_STANDARD (0x00 << 5)
|
||||
#define USB_TYPE_CLASS (0x01 << 5)
|
||||
#define USB_TYPE_VENDOR (0x02 << 5)
|
||||
#define USB_TYPE_RESERVED (0x03 << 5)
|
||||
|
||||
#define USB_RECIP_MASK 0x1f
|
||||
#define USB_RECIP_DEVICE 0x00
|
||||
#define USB_RECIP_INTERFACE 0x01
|
||||
#define USB_RECIP_ENDPOINT 0x02
|
||||
#define USB_RECIP_OTHER 0x03
|
||||
|
||||
#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
|
||||
#define USB_REQ_GET_STATUS 0x00
|
||||
#define USB_REQ_CLEAR_FEATURE 0x01
|
||||
#define USB_REQ_SET_FEATURE 0x03
|
||||
#define USB_REQ_SET_ADDRESS 0x05
|
||||
#define USB_REQ_GET_DESCRIPTOR 0x06
|
||||
#define USB_REQ_SET_DESCRIPTOR 0x07
|
||||
#define USB_REQ_GET_CONFIGURATION 0x08
|
||||
#define USB_REQ_SET_CONFIGURATION 0x09
|
||||
#define USB_REQ_GET_INTERFACE 0x0A
|
||||
#define USB_REQ_SET_INTERFACE 0x0B
|
||||
#define USB_REQ_SYNCH_FRAME 0x0C
|
||||
|
||||
#define USB_DEVICE_SELF_POWERED 0
|
||||
#define USB_DEVICE_REMOTE_WAKEUP 1
|
||||
|
||||
#define USB_DT_DEVICE 0x01
|
||||
#define USB_DT_CONFIG 0x02
|
||||
#define USB_DT_STRING 0x03
|
||||
#define USB_DT_INTERFACE 0x04
|
||||
#define USB_DT_ENDPOINT 0x05
|
||||
|
||||
typedef struct USBPort USBPort;
|
||||
typedef struct USBDevice USBDevice;
|
||||
|
||||
/* definition of a USB device */
|
||||
struct USBDevice {
|
||||
void *opaque;
|
||||
int (*handle_packet)(USBDevice *dev, int pid,
|
||||
uint8_t devaddr, uint8_t devep,
|
||||
uint8_t *data, int len);
|
||||
int speed;
|
||||
|
||||
/* The following fields are used by the generic USB device
|
||||
layer. They are here just to avoid creating a new structure for
|
||||
them. */
|
||||
void (*handle_reset)(USBDevice *dev);
|
||||
int (*handle_control)(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data);
|
||||
int (*handle_data)(USBDevice *dev, int pid, uint8_t devep,
|
||||
uint8_t *data, int len);
|
||||
uint8_t addr;
|
||||
|
||||
int state;
|
||||
uint8_t setup_buf[8];
|
||||
uint8_t data_buf[1024];
|
||||
int remote_wakeup;
|
||||
int setup_state;
|
||||
int setup_len;
|
||||
int setup_index;
|
||||
};
|
||||
|
||||
/* USB port on which a device can be connected */
|
||||
struct USBPort {
|
||||
void (*attach)(USBPort *port, USBDevice *dev);
|
||||
void *opaque;
|
||||
int index; /* internal port index, may be used with the opaque */
|
||||
};
|
||||
|
||||
void usb_attach(USBPort *port, USBDevice *dev);
|
||||
int usb_generic_handle_packet(USBDevice *s, int pid,
|
||||
uint8_t devaddr, uint8_t devep,
|
||||
uint8_t *data, int len);
|
||||
int set_usb_string(uint8_t *buf, const char *str);
|
||||
|
||||
/* usb hub */
|
||||
USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports);
|
||||
|
||||
/* usb-uhci.c */
|
||||
void usb_uhci_init(PCIBus *bus, USBPort **usb_ports);
|
||||
|
||||
/* usb-linux.c */
|
||||
USBDevice *usb_host_hub_init(void);
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Linux host USB redirector
|
||||
*
|
||||
* Copyright (c) 2005 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <dirent.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
/* We redefine it to avoid version problems */
|
||||
struct usb_ctrltransfer {
|
||||
uint8_t bRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
uint32_t timeout;
|
||||
void *data;
|
||||
};
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define MAX_DEVICES 8
|
||||
|
||||
#define USBDEVFS_PATH "/proc/bus/usb"
|
||||
|
||||
typedef struct USBHostDevice {
|
||||
USBDevice dev;
|
||||
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
|
||||
USBHostDevice *s = (USBHostDevice *)dev;
|
||||
/* USBDEVFS_RESET, but not the first time as it has already be
|
||||
done by the host OS */
|
||||
ioctl(s->fd, USBDEVFS_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int usb_host_handle_control(USBDevice *dev,
|
||||
int request,
|
||||
int value,
|
||||
int index,
|
||||
int length,
|
||||
uint8_t *data)
|
||||
{
|
||||
USBHostDevice *s = (USBHostDevice *)dev;
|
||||
struct usb_ctrltransfer ct;
|
||||
int ret;
|
||||
|
||||
if (request == (DeviceOutRequest | USB_REQ_SET_ADDRESS)) {
|
||||
/* specific SET_ADDRESS support */
|
||||
dev->addr = value;
|
||||
return 0;
|
||||
} else {
|
||||
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);
|
||||
if (ret < 0) {
|
||||
switch(errno) {
|
||||
case ETIMEDOUT:
|
||||
return USB_RET_NAK;
|
||||
default:
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_host_handle_data(USBDevice *dev, int pid,
|
||||
uint8_t devep,
|
||||
uint8_t *data, int len)
|
||||
{
|
||||
USBHostDevice *s = (USBHostDevice *)dev;
|
||||
struct usbdevfs_bulktransfer bt;
|
||||
int ret;
|
||||
|
||||
/* XXX: optimize and handle all data types by looking at the
|
||||
config descriptor */
|
||||
if (pid == USB_TOKEN_IN)
|
||||
devep |= 0x80;
|
||||
bt.ep = devep;
|
||||
bt.len = len;
|
||||
bt.timeout = 50;
|
||||
bt.data = data;
|
||||
ret = ioctl(s->fd, USBDEVFS_BULK, &bt);
|
||||
if (ret < 0) {
|
||||
switch(errno) {
|
||||
case ETIMEDOUT:
|
||||
return USB_RET_NAK;
|
||||
case EPIPE:
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("handle_data: errno=%d\n", errno);
|
||||
#endif
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int fd, interface, ret, i;
|
||||
USBHostDevice *dev;
|
||||
struct usbdevfs_connectinfo ci;
|
||||
uint8_t descr[1024];
|
||||
int descr_len, dev_descr_len, config_descr_len, nb_interfaces;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("scanning %s\n", filename);
|
||||
#endif
|
||||
fd = open(filename, O_RDWR);
|
||||
if (fd < 0) {
|
||||
perror(filename);
|
||||
return;
|
||||
}
|
||||
|
||||
/* read the config description */
|
||||
descr_len = read(fd, descr, sizeof(descr));
|
||||
if (descr_len <= 0) {
|
||||
perror("read descr");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
dev_descr_len = descr[0];
|
||||
if (dev_descr_len > descr_len)
|
||||
goto fail;
|
||||
i += dev_descr_len;
|
||||
config_descr_len = descr[i];
|
||||
if (i + config_descr_len > descr_len)
|
||||
goto fail;
|
||||
nb_interfaces = descr[i + 4];
|
||||
if (nb_interfaces != 1) {
|
||||
/* NOTE: currently we grab only one interface */
|
||||
goto fail;
|
||||
}
|
||||
/* 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
|
||||
} else {
|
||||
perror("USBDEVFS_CLAIMINTERFACE");
|
||||
}
|
||||
fail:
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
|
||||
if (ret < 0) {
|
||||
perror("USBDEVFS_CONNECTINFO");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("%s grabbed\n", filename);
|
||||
#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;
|
||||
dev->fd = fd;
|
||||
if (ci.slow)
|
||||
dev->dev.speed = USB_SPEED_LOW;
|
||||
else
|
||||
dev->dev.speed = USB_SPEED_HIGH;
|
||||
dev->dev.handle_packet = usb_host_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;
|
||||
|
||||
s->hub_devices[i] = (USBDevice *)dev;
|
||||
|
||||
/* activate device on hub */
|
||||
usb_attach(s->hub_ports[i], s->hub_devices[i]);
|
||||
}
|
||||
|
||||
static void scan_host_devices(USBHostHubState *s, const char *bus_path)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
char buf[1024];
|
||||
|
||||
d = opendir(bus_path);
|
||||
if (!d)
|
||||
return;
|
||||
for(;;) {
|
||||
de = readdir(d);
|
||||
if (!de)
|
||||
break;
|
||||
if (de->d_name[0] != '.') {
|
||||
snprintf(buf, sizeof(buf), "%s/%s", bus_path, de->d_name);
|
||||
scan_host_device(s, buf);
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
static void scan_host_buses(USBHostHubState *s)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
char buf[1024];
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
/* virtual hub containing the USB devices of the host */
|
||||
USBDevice *usb_host_hub_init(void)
|
||||
{
|
||||
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;
|
||||
}
|
||||
scan_host_buses(s);
|
||||
return s->hub_dev;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* XXX: modify configure to compile the right host driver */
|
||||
USBDevice *usb_host_hub_init(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -153,6 +153,7 @@ CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
|
||||
#ifdef TARGET_I386
|
||||
int win2k_install_hack = 0;
|
||||
#endif
|
||||
int usb_enabled = 0;
|
||||
|
||||
/***********************************************************/
|
||||
/* x86 ISA bus support */
|
||||
@@ -2986,6 +2987,7 @@ enum {
|
||||
QEMU_OPTION_pidfile,
|
||||
QEMU_OPTION_no_kqemu,
|
||||
QEMU_OPTION_win2k_hack,
|
||||
QEMU_OPTION_usb,
|
||||
};
|
||||
|
||||
typedef struct QEMUOption {
|
||||
@@ -3060,6 +3062,7 @@ const QEMUOption qemu_options[] = {
|
||||
{ "full-screen", 0, QEMU_OPTION_full_screen },
|
||||
{ "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
|
||||
{ "win2k-hack", 0, QEMU_OPTION_win2k_hack },
|
||||
{ "usb", 0, QEMU_OPTION_usb },
|
||||
|
||||
/* temporary options */
|
||||
{ "pci", 0, QEMU_OPTION_pci },
|
||||
@@ -3646,6 +3649,9 @@ int main(int argc, char **argv)
|
||||
kqemu_allowed = 0;
|
||||
break;
|
||||
#endif
|
||||
case QEMU_OPTION_usb:
|
||||
usb_enabled = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ extern int graphic_depth;
|
||||
extern const char *keyboard_layout;
|
||||
extern int kqemu_allowed;
|
||||
extern int win2k_install_hack;
|
||||
extern int usb_enabled;
|
||||
|
||||
/* XXX: make it dynamic */
|
||||
#if defined (TARGET_PPC)
|
||||
@@ -859,6 +860,8 @@ void adb_mouse_init(ADBBusState *bus);
|
||||
extern ADBBusState adb_bus;
|
||||
int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq);
|
||||
|
||||
#include "hw/usb.h"
|
||||
|
||||
#endif /* defined(QEMU_TOOL) */
|
||||
|
||||
/* monitor.c */
|
||||
|
||||
Reference in New Issue
Block a user