[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [HVM][DM] Backport the USB support from QEMU 0.8.1 to the current Xen
# HG changeset patch # User kaf24@xxxxxxxxxxxxxxxxxxxx # Node ID e2a2b2da92f471b9cd8674f776ddd3b6f28042cf # Parent 5b1bd9eab3b9a715543937f3d9a083faa23f0f53 [HVM][DM] Backport the USB support from QEMU 0.8.1 to the current Xen device model. To support USB there are two new optional config lines that can be added to the guest config file: usb=1 This will enable USB without defining a specific USB device. This option is assumed and not needed if the `usbdevice' option is given. usbdevice='device' This will enable USB and also enable support for the given device. Currently, the only two devices are `mouse' (a PS/2 mouse) and `tablet' (an absolute pointing device). The advantage of `tablet' is that Windows guests will automatically recognize and support this device so specifying the config line: usbdevice='tablet' will create a mouse that works transparently with Windows guests under VNC. (Linux doesn't recognize the USB tablet yet so Linux guests under VNC will still need the Summagraphics emulation.) Signed-off-by: Don Dugger <donald.d.dugger@xxxxxxxxx> --- tools/ioemu/hw/pc.c | 6 tools/ioemu/hw/pckbd.c | 183 ++++----- tools/ioemu/hw/usb-hid.c | 537 ++++++++++++++++++++++++++++ tools/ioemu/hw/usb-hub.c | 549 +++++++++++++++++++++++++++++ tools/ioemu/hw/usb-uhci.c | 680 ++++++++++++++++++++++++++++++++++++ tools/ioemu/hw/usb.c | 193 ++++++++++ tools/ioemu/hw/usb.h | 166 ++++++++ tools/ioemu/monitor.c | 8 tools/ioemu/sdl.c | 46 ++ tools/ioemu/target-i386-dm/Makefile | 3 tools/ioemu/usb-linux.c | 487 +++++++++++++++++++++++++ tools/ioemu/vl.c | 167 ++++++++ tools/ioemu/vl.h | 21 - tools/ioemu/vnc.c | 58 ++- tools/python/xen/xend/image.py | 5 tools/python/xen/xm/create.py | 16 16 files changed, 2995 insertions(+), 130 deletions(-) diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/hw/pc.c --- a/tools/ioemu/hw/pc.c Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/ioemu/hw/pc.c Mon Jun 12 09:06:55 2006 +0100 @@ -40,6 +40,7 @@ int dummy_refresh_clock; int dummy_refresh_clock; static fdctrl_t *floppy_controller; static RTCState *rtc_state; +static USBPort *usb_root_ports[2]; static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) { @@ -584,6 +585,11 @@ void pc_init(uint64_t ram_size, int vga_ cmos_init(ram_size, boot_device, bs_table, timeoffset); acpi_init(0x8000); + if (pci_enabled && usb_enabled) { + usb_uhci_init(pci_bus, usb_root_ports); + usb_attach(usb_root_ports[0], vm_usb_hub); + } + /* must be done after all PCI devices are instanciated */ /* XXX: should be done in the Bochs BIOS */ if (pci_enabled) { diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/hw/pckbd.c --- a/tools/ioemu/hw/pckbd.c Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/ioemu/hw/pckbd.c Mon Jun 12 09:06:55 2006 +0100 @@ -118,6 +118,9 @@ #define SUMMA_MAXX (16000 - 1) #define SUMMA_MAXY (16000 - 1) +#define MAX_ABSX 0x7fff +#define MAX_ABSY 0x7fff + typedef struct { uint8_t aux[KBD_QUEUE_SIZE]; uint8_t data[KBD_QUEUE_SIZE]; @@ -149,8 +152,6 @@ typedef struct KBDState { uint8_t mouse_wrap; uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */ uint8_t mouse_detect_state; - int mouse_x; /* absolute coordinates (for mousepad) */ - int mouse_y; int mouse_dx; /* current values, needed for 'poll' mode */ int mouse_dy; int mouse_dz; @@ -422,7 +423,7 @@ static void kbd_write_keyboard(KBDState int mouse_maxx, mouse_maxy; -static int kbd_mouse_send_packet(KBDState *s) +static void kbd_mouse_send_packet(KBDState *s) { unsigned int b; int dx1, dy1, dz1; @@ -430,100 +431,63 @@ static int kbd_mouse_send_packet(KBDStat dx1 = s->mouse_dx; dy1 = s->mouse_dy; dz1 = s->mouse_dz; + /* XXX: increase range to 8 bits ? */ + if (dx1 > 127) + dx1 = 127; + else if (dx1 < -127) + dx1 = -127; + if (dy1 > 127) + dy1 = 127; + else if (dy1 < -127) + dy1 = -127; + b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); + kbd_queue(s, b, 1); + kbd_queue(s, dx1 & 0xff, 1); + kbd_queue(s, dy1 & 0xff, 1); + /* extra byte for IMPS/2 or IMEX */ switch(s->mouse_type) { - - case TABLET: /* Summagraphics pen tablet */ - if (SummaState.report_mode == MODE_STREAM) { - dx1 = s->mouse_x; - dy1 = s->mouse_y; - if (SummaState.origin == ORIGIN_LOWER_LEFT) - dy1 = mouse_maxy - dy1; - dx1 = ((dx1 * SUMMA_MAXX) / mouse_maxx) + SUMMA_BORDER; - dy1 = ((dy1 * SUMMA_MAXY) / mouse_maxy) + SUMMA_BORDER; - ser_queue(s->serial, 0x80 | (s->mouse_buttons & 7)); - ser_queue(s->serial, dx1 & 0x7f); - ser_queue(s->serial, dx1 >> 7); - ser_queue(s->serial, dy1 & 0x7f); - ser_queue(s->serial, dy1 >> 7); - } - s->mouse_dx = 0; - s->mouse_dy = 0; - s->mouse_dz = 0; - return 0; - - default: /* PS/2 style mice */ - /* XXX: increase range to 8 bits ? */ - if (dx1 > 127) - dx1 = 127; - else if (dx1 < -127) - dx1 = -127; - if (dy1 > 127) - dy1 = 127; - else if (dy1 < -127) - dy1 = -127; - b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); - kbd_queue(s, b, 1); - kbd_queue(s, dx1 & 0xff, 1); - kbd_queue(s, dy1 & 0xff, 1); - /* extra byte for IMPS/2 or IMEX */ - switch(s->mouse_type) { - - default: - break; - - case IMPS2: - if (dz1 > 127) - dz1 = 127; - else if (dz1 < -127) - dz1 = -127; - kbd_queue(s, dz1 & 0xff, 1); - break; - - case IMEX: - if (dz1 > 7) - dz1 = 7; - else if (dz1 < -7) - dz1 = -7; - b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); - kbd_queue(s, b, 1); - break; - } - - /* update deltas */ - s->mouse_dx -= dx1; - s->mouse_dy -= dy1; - s->mouse_dz -= dz1; - return s->mouse_dx || s->mouse_dy || s->mouse_dz; - - } -} - -static void pc_kbd_mouse_event(void *opaque, - int dx, int dy, int dz, int buttons_state, - int x, int y) + default: + break; + case IMPS2: + if (dz1 > 127) + dz1 = 127; + else if (dz1 < -127) + dz1 = -127; + kbd_queue(s, dz1 & 0xff, 1); + break; + case IMEX: + if (dz1 > 7) + dz1 = 7; + else if (dz1 < -7) + dz1 = -7; + b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); + kbd_queue(s, b, 1); + break; + } + + /* update deltas */ + s->mouse_dx -= dx1; + s->mouse_dy -= dy1; + s->mouse_dz -= dz1; +} + +static void summa_mouse_event(void *opaque, int x, int y, int z, int buttons_state) { KBDState *s = opaque; - /* check if deltas are recorded when disabled */ - if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) - return; - - s->mouse_x = x; - s->mouse_y = y; - s->mouse_dx += dx; - s->mouse_dy -= dy; - s->mouse_dz += dz; - /* XXX: SDL sometimes generates nul events: we delete them */ - if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 && - s->mouse_buttons == buttons_state) - return; - s->mouse_buttons = buttons_state; - - if (!(s->mouse_status & MOUSE_STATUS_REMOTE) && - (s->queue.count < (KBD_QUEUE_SIZE - 16))) { - while (kbd_mouse_send_packet(s)) - ; - } + if (SummaState.report_mode == MODE_STREAM) { + if (SummaState.origin == ORIGIN_LOWER_LEFT) + y = mouse_maxy - y; + x = ((x * SUMMA_MAXX) / MAX_ABSX) + SUMMA_BORDER; + y = ((y * SUMMA_MAXY) / MAX_ABSY) + SUMMA_BORDER; +fprintf(stderr, "summa_mouse_event: x, y - %d, %d\n", x, y); + ser_queue(s->serial, 0x80 | (buttons_state & 7)); + ser_queue(s->serial, x & 0x7f); + ser_queue(s->serial, x >> 7); + ser_queue(s->serial, y & 0x7f); + ser_queue(s->serial, y >> 7); + } + return; } static void summa(KBDState *s, uint8_t val) @@ -564,6 +528,7 @@ static void summa(KBDState *s, uint8_t v s->mouse_status |= MOUSE_STATUS_ENABLED; SummaState.origin = ORIGIN_LOWER_LEFT; SummaState.report_mode = (val == 'B') ? MODE_POINT : MODE_STREAM_SWITCH; + qemu_add_mouse_event_handler(summa_mouse_event, s, 1); break; case 'z': /* start of 2 byte command */ @@ -645,6 +610,36 @@ void summa_init(SerialState *serial, Cha chr->chr_write = summa_write; chr->opaque = (void *)&kbd_state; return; +} + +static void pc_kbd_mouse_event(void *opaque, + int dx, int dy, int dz, int buttons_state) +{ + KBDState *s = opaque; + + /* check if deltas are recorded when disabled */ + if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) + return; + + s->mouse_dx += dx; + s->mouse_dy -= dy; + s->mouse_dz += dz; + /* XXX: SDL sometimes generates nul events: we delete them */ + if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 && + s->mouse_buttons == buttons_state) + return; + s->mouse_buttons = buttons_state; + + if (!(s->mouse_status & MOUSE_STATUS_REMOTE) && + (s->queue.count < (KBD_QUEUE_SIZE - 16))) { + for(;;) { + /* if not remote, send event. Multiple events are sent if + too big deltas */ + kbd_mouse_send_packet(s); + if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0) + break; + } + } } static void kbd_write_mouse(KBDState *s, int val) @@ -890,6 +885,6 @@ void kbd_init(void) register_ioport_write(0x64, 1, 1, kbd_write_command, s); qemu_add_kbd_event_handler(pc_kbd_put_keycode, s); - qemu_add_mouse_event_handler(pc_kbd_mouse_event, s); + qemu_add_mouse_event_handler(pc_kbd_mouse_event, s, 0); qemu_register_reset(kbd_reset, s); } diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/monitor.c --- a/tools/ioemu/monitor.c Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/ioemu/monitor.c Mon Jun 12 09:06:55 2006 +0100 @@ -492,6 +492,10 @@ static term_cmd_t term_cmds[] = { "", "quit the emulator" }, { "sendkey", "s", do_send_key, "keys", "send keys to the VM (e.g. 'sendkey ctrl-alt-f1')" }, + { "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, }, }; @@ -510,6 +514,10 @@ static term_cmd_t info_cmds[] = { "", "show i8259 (PIC) state", }, { "pci", "", pci_info, "", "show PCI info", }, + { "usb", "", usb_info, + "", "show guest USB devices", }, + { "usbhost", "", usb_host_info, + "", "show host USB devices", }, { "hvmiopage", "", sp_info, "", "show HVM device model shared page info", }, { NULL, NULL, }, diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/sdl.c --- a/tools/ioemu/sdl.c Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/ioemu/sdl.c Mon Jun 12 09:06:55 2006 +0100 @@ -49,8 +49,12 @@ static int gui_fullscreen_initial_grab; static int gui_fullscreen_initial_grab; static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; static uint8_t modifiers_state[256]; - -SDL_PixelFormat* sdl_get_format() { +static int width, height; +static SDL_Cursor *sdl_cursor_normal; +static SDL_Cursor *sdl_cursor_hidden; +static int absolute_enabled = 0; + +SDL_PixelFormat* sdl_get_format(void) { return screen->format; } @@ -69,6 +73,8 @@ static void sdl_resize(DisplayState *ds, flags |= SDL_RESIZABLE; if (gui_fullscreen) flags |= SDL_FULLSCREEN; + width = w; + height = h; screen = SDL_SetVideoMode(w, h, 0, flags); if (!screen) { fprintf(stderr, "Could not open SDL display\n"); @@ -368,9 +374,21 @@ static void sdl_update_caption(void) SDL_WM_SetCaption(buf, domain_name); } +static void sdl_hide_cursor(void) +{ + SDL_SetCursor(sdl_cursor_hidden); +} + +static void sdl_show_cursor(void) +{ + if (!kbd_mouse_is_absolute()) { + SDL_SetCursor(sdl_cursor_normal); + } +} + static void sdl_grab_start(void) { - SDL_ShowCursor(0); + sdl_hide_cursor(); SDL_WM_GrabInput(SDL_GRAB_ON); /* dummy read to avoid moving the mouse */ SDL_GetRelativeMouseState(NULL, NULL); @@ -381,6 +399,7 @@ static void sdl_grab_end(void) static void sdl_grab_end(void) { SDL_WM_GrabInput(SDL_GRAB_OFF); + sdl_show_cursor(); SDL_ShowCursor(1); gui_grab = 0; sdl_update_caption(); @@ -397,6 +416,21 @@ static void sdl_send_mouse_event(void) buttons |= MOUSE_EVENT_RBUTTON; if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) buttons |= MOUSE_EVENT_MBUTTON; + + if (kbd_mouse_is_absolute()) { + if (!absolute_enabled) { + sdl_hide_cursor(); + if (gui_grab) { + sdl_grab_end(); + } + absolute_enabled = 1; + } + + SDL_GetMouseState(&dx, &dy); + dx = dx * 0x7FFF / width; + dy = dy * 0x7FFF / height; + } + /* XXX: test wheel */ dz = 0; #ifdef SDL_BUTTON_WHEELUP @@ -405,7 +439,7 @@ static void sdl_send_mouse_event(void) if (state & SDL_BUTTON(SDL_BUTTON_WHEELDOWN)) dz++; #endif - kbd_mouse_event(dx, dy, dz, buttons, 0, 0); + kbd_mouse_event(dx, dy, dz, buttons); } static void toggle_full_screen(DisplayState *ds) @@ -571,6 +605,7 @@ void sdl_display_init(DisplayState *ds, void sdl_display_init(DisplayState *ds, int full_screen) { int flags; + uint8_t data = 0; if(keyboard_layout) kbd_layout=init_keyboard_layout(keyboard_layout); @@ -597,6 +632,9 @@ void sdl_display_init(DisplayState *ds, SDL_EnableUNICODE(1); gui_grab = 0; + sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); + sdl_cursor_normal = SDL_GetCursor(); + atexit(sdl_cleanup); if (full_screen) { gui_fullscreen = 1; diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/target-i386-dm/Makefile --- a/tools/ioemu/target-i386-dm/Makefile Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/ioemu/target-i386-dm/Makefile Mon Jun 12 09:06:55 2006 +0100 @@ -274,6 +274,9 @@ audio.o fmodaudio.o: DEFINES := -I$(CONF audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES) LIBS += $(CONFIG_FMOD_LIB) endif + +# USB layer +VL_OBJS+= usb.o usb-hub.o usb-uhci.o usb-linux.o usb-hid.o # Hardware support VL_OBJS+= ide.o ne2000.o pckbd.o vga.o dma.o diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/vl.c --- a/tools/ioemu/vl.c Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/ioemu/vl.c Mon Jun 12 09:06:55 2006 +0100 @@ -144,6 +144,9 @@ int graphic_depth = 15; int graphic_depth = 15; int full_screen = 0; int repeat_key = 1; +int usb_enabled = 0; +USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; +USBDevice *vm_usb_hub; TextConsole *vga_console; CharDriverState *serial_hds[MAX_SERIAL_PORTS]; int serial_summa_port = -1; @@ -438,6 +441,7 @@ static void *qemu_put_kbd_event_opaque; static void *qemu_put_kbd_event_opaque; static QEMUPutMouseEvent *qemu_put_mouse_event; static void *qemu_put_mouse_event_opaque; +static int qemu_put_mouse_event_absolute; void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) { @@ -445,10 +449,11 @@ void qemu_add_kbd_event_handler(QEMUPutK qemu_put_kbd_event = func; } -void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque) +void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute) { qemu_put_mouse_event_opaque = opaque; qemu_put_mouse_event = func; + qemu_put_mouse_event_absolute = absolute; } void kbd_put_keycode(int keycode) @@ -458,12 +463,17 @@ void kbd_put_keycode(int keycode) } } -void kbd_mouse_event(int dx, int dy, int dz, int buttons_state, int x, int y) +void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) { if (qemu_put_mouse_event) { qemu_put_mouse_event(qemu_put_mouse_event_opaque, - dx, dy, dz, buttons_state, x, y); - } + dx, dy, dz, buttons_state); + } +} + +int kbd_mouse_is_absolute(void) +{ + return qemu_put_mouse_event_absolute; } /***********************************************************/ @@ -1644,6 +1654,121 @@ static int net_fd_init(NetDriverState *n } #endif /* !_WIN32 */ + +/***********************************************************/ +/* USB devices */ + +static int usb_device_add(const char *devname) +{ + const char *p; + USBDevice *dev; + int i; + + if (!vm_usb_hub) + return -1; + for(i = 0;i < MAX_VM_USB_PORTS; i++) { + if (!vm_usb_ports[i]->dev) + break; + } + if (i == MAX_VM_USB_PORTS) + return -1; + + if (strstart(devname, "host:", &p)) { + dev = usb_host_device_open(p); + if (!dev) + return -1; + } else if (!strcmp(devname, "mouse")) { + dev = usb_mouse_init(); + if (!dev) + return -1; + } else if (!strcmp(devname, "tablet")) { + dev = usb_tablet_init(); + if (!dev) + return -1; + } else { + return -1; + } + usb_attach(vm_usb_ports[i], dev); + return 0; +} + +static int usb_device_del(const char *devname) +{ + USBDevice *dev; + int bus_num, addr, i; + const char *p; + + if (!vm_usb_hub) + return -1; + + p = strchr(devname, '.'); + if (!p) + return -1; + bus_num = strtoul(devname, NULL, 0); + addr = strtoul(p + 1, NULL, 0); + if (bus_num != 0) + return -1; + for(i = 0;i < MAX_VM_USB_PORTS; i++) { + dev = vm_usb_ports[i]->dev; + if (dev && dev->addr == addr) + break; + } + if (i == MAX_VM_USB_PORTS) + return -1; + usb_attach(vm_usb_ports[i], NULL); + return 0; +} + +void do_usb_add(const char *devname) +{ + int ret; + ret = usb_device_add(devname); + if (ret < 0) + term_printf("Could not add USB device '%s'\n", devname); +} + +void do_usb_del(const char *devname) +{ + int ret; + ret = usb_device_del(devname); + if (ret < 0) + term_printf("Could not remove USB device '%s'\n", devname); +} + +void usb_info(void) +{ + USBDevice *dev; + int i; + const char *speed_str; + + if (!vm_usb_hub) { + term_printf("USB support not enabled\n"); + return; + } + + for(i = 0; i < MAX_VM_USB_PORTS; i++) { + dev = vm_usb_ports[i]->dev; + if (dev) { + term_printf("Hub port %d:\n", i); + switch(dev->speed) { + case USB_SPEED_LOW: + speed_str = "1.5"; + break; + case USB_SPEED_FULL: + speed_str = "12"; + break; + case USB_SPEED_HIGH: + speed_str = "480"; + break; + default: + speed_str = "?"; + break; + } + term_printf(" Device %d.%d, speed %s Mb/s\n", + 0, dev->addr, speed_str); + } + } +} /***********************************************************/ /* dumb display */ @@ -2214,6 +2339,8 @@ void help(void) "-enable-audio enable audio support\n" "-localtime set the real time clock to local time [default=utc]\n" "-full-screen start in full screen\n" + "-usb enable the USB driver (will be the default soon)\n" + "-usbdevice name add the host or guest USB device 'name'\n" #ifdef TARGET_PPC "-prep Simulate a PREP system (default is PowerMAC)\n" "-g WxH[xDEPTH] Set the initial VGA graphic mode\n" @@ -2355,6 +2482,8 @@ enum { QEMU_OPTION_full_screen, QEMU_OPTION_vgaacc, QEMU_OPTION_repeatkey, + QEMU_OPTION_usb, + QEMU_OPTION_usbdevice, }; typedef struct QEMUOption { @@ -2428,8 +2557,10 @@ const QEMUOption qemu_options[] = { { "serial", 1, QEMU_OPTION_serial }, { "loadvm", HAS_ARG, QEMU_OPTION_loadvm }, { "full-screen", 0, QEMU_OPTION_full_screen }, + { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice }, /* temporary options */ + { "usb", 0, QEMU_OPTION_usb }, { "pci", 0, QEMU_OPTION_pci }, { "nic-ne2000", 0, QEMU_OPTION_nic_ne2000 }, { "cirrusvga", 0, QEMU_OPTION_cirrusvga }, @@ -2555,6 +2686,8 @@ int main(int argc, char **argv) char monitor_device[128]; char serial_devices[MAX_SERIAL_PORTS][128]; int serial_device_index; + char usb_devices[MAX_VM_USB_PORTS][128]; + int usb_devices_index; char qemu_dm_logfilename[64]; const char *loadvm = NULL; unsigned long nr_pages; @@ -2595,6 +2728,7 @@ int main(int argc, char **argv) serial_devices[i][0] = '\0'; serial_device_index = 0; + usb_devices_index = 0; nb_tun_fds = 0; net_if_type = -1; nb_nics = 1; @@ -2939,6 +3073,20 @@ int main(int argc, char **argv) case QEMU_OPTION_full_screen: full_screen = 1; break; + case QEMU_OPTION_usb: + usb_enabled = 1; + break; + case QEMU_OPTION_usbdevice: + usb_enabled = 1; + if (usb_devices_index >= MAX_VM_USB_PORTS) { + fprintf(stderr, "Too many USB devices\n"); + exit(1); + } + pstrcpy(usb_devices[usb_devices_index], + sizeof(usb_devices[usb_devices_index]), + optarg); + usb_devices_index++; + break; case QEMU_OPTION_domainname: strncat(domain_name, optarg, sizeof(domain_name) - 20); break; @@ -3139,6 +3287,17 @@ int main(int argc, char **argv) } } + /* init USB devices */ + if (usb_enabled) { + vm_usb_hub = usb_hub_init(vm_usb_ports, MAX_VM_USB_PORTS); + for(i = 0; i < usb_devices_index; i++) { + if (usb_device_add(usb_devices[i]) < 0) { + fprintf(stderr, "Warning: could not add USB device %s\n", + usb_devices[i]); + } + } + } + /* init CPU state */ env = cpu_init(); global_env = env; diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/vl.h --- a/tools/ioemu/vl.h Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/ioemu/vl.h Mon Jun 12 09:06:55 2006 +0100 @@ -154,13 +154,14 @@ extern int graphic_depth; #define MOUSE_EVENT_MBUTTON 0x04 typedef void QEMUPutKBDEvent(void *opaque, int keycode); -typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state, int x, int y); +typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state); void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque); -void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque); +void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute); void kbd_put_keycode(int keycode); -void kbd_mouse_event(int dx, int dy, int dz, int buttons_state, int x, int y); +void kbd_mouse_event(int dx, int dy, int dz, int buttons_state); +int kbd_mouse_is_absolute(void); /* keysym is a unicode code except for special keys (see QEMU_KEY_xxx constants) */ @@ -633,6 +634,7 @@ void kbd_init(void); void kbd_init(void); extern const char* keyboard_layout; extern int repeat_key; +extern int usb_enabled; /* mc146818rtc.c */ @@ -792,6 +794,19 @@ void adb_mouse_init(ADBBusState *bus); /* cuda.c */ +#include "hw/usb.h" + +/* usb ports of the VM */ + +#define MAX_VM_USB_PORTS 8 + +extern USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; +extern USBDevice *vm_usb_hub; + +void do_usb_add(const char *devname); +void do_usb_del(const char *devname); +void usb_info(void); + extern ADBBusState adb_bus; int cuda_init(openpic_t *openpic, int irq); diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/vnc.c --- a/tools/ioemu/vnc.c Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/ioemu/vnc.c Mon Jun 12 09:06:55 2006 +0100 @@ -138,9 +138,16 @@ static void init_mouse(int max_x,int max } static void mouse_refresh() { + static int last_x = -1; + static int last_y = -1; + static int last_z = -1; + static int last_b = -1; int dx=0,dy=0,dz=new_mouse_z; static int counter=1; + if (new_mouse_x == last_x && new_mouse_y == last_y && + new_mouse_z == last_z && new_mouse_buttons == last_b) + return; /* * Simulate lifting the mouse by pressing left <ctl><alt> together * e.g. don't send mouse events. @@ -148,27 +155,40 @@ static void mouse_refresh() { if (ctl_keys == 3) { mouse_x = new_mouse_x; mouse_y = new_mouse_y; + last_x = new_mouse_x; + last_y = new_mouse_y; + last_z = new_mouse_z; + last_b = new_mouse_buttons; return; } counter++; - if(!mouse_magic->calibration && counter>=2) { counter=0; return; } - - dx=new_mouse_x-mouse_x; - dy=new_mouse_y-mouse_y; - - if(mouse_magic->sonic_wall_is_orthogonal) { - if(abs(dx)>=mouse_magic->sonic_wall_x) { dx/=2; mouse_x+=dx; } - if(abs(dy)>=mouse_magic->sonic_wall_y) { dy/=2; mouse_y+=dy; } + //fprintf(stderr,"sending mouse event %d,%d\n",dx,dy); + if (kbd_mouse_is_absolute()) { + kbd_mouse_event(new_mouse_x * 0x7FFF / screen->width, + new_mouse_y * 0x7FFF / screen->height, dz, new_mouse_buttons); } else { - if(abs(dx)>=mouse_magic->sonic_wall_x || abs(dy)>=mouse_magic->sonic_wall_y) { - dx/=2; mouse_x+=dx; - dy/=2; mouse_y+=dy; - } - } - //fprintf(stderr,"sending mouse event %d,%d\n",dx,dy); - kbd_mouse_event(dx,dy,dz,new_mouse_buttons,new_mouse_x,new_mouse_y); - mouse_x+=dx; - mouse_y+=dy; + if(!mouse_magic->calibration && counter>=2) { counter=0; return; } + + dx=new_mouse_x-last_x; + dy=new_mouse_y-last_y; + + if(mouse_magic->sonic_wall_is_orthogonal) { + if(abs(dx)>=mouse_magic->sonic_wall_x) { dx/=2; mouse_x+=dx; } + if(abs(dy)>=mouse_magic->sonic_wall_y) { dy/=2; mouse_y+=dy; } + } else { + if(abs(dx)>=mouse_magic->sonic_wall_x || abs(dy)>=mouse_magic->sonic_wall_y) { + dx/=2; mouse_x+=dx; + dy/=2; mouse_y+=dy; + } + } + if (last_x != -1) + kbd_mouse_event(dx,dy,dz,new_mouse_buttons); + + } + last_x = new_mouse_x; + last_y = new_mouse_y; + last_z = new_mouse_z; + last_b = new_mouse_buttons; updates_since_mouse=0; } @@ -250,7 +270,7 @@ static void mouse_calibration_refresh() if(calibration_step==0) { x=0; y=1; - kbd_mouse_event(0,-1,0,0,x,y); + kbd_mouse_event(0,-1,0,0); calibration_step++; } else if(calibration_step==1) { // find out the initial position of the cursor @@ -282,7 +302,7 @@ static void mouse_calibration_refresh() } else { y++; move_calibrate: - kbd_mouse_event(-x,-y,0,0,x,y); + kbd_mouse_event(-x,-y,0,0); before_update=last_update; } } else if(calibration_step==3) { diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/python/xen/xend/image.py --- a/tools/python/xen/xend/image.py Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/python/xen/xend/image.py Mon Jun 12 09:06:55 2006 +0100 @@ -249,7 +249,8 @@ class HVMImageHandler(ImageHandler): # xm config file def parseDeviceModelArgs(self, imageConfig, deviceConfig): dmargs = [ 'cdrom', 'boot', 'fda', 'fdb', 'ne2000', 'audio', - 'localtime', 'serial', 'stdvga', 'isa', 'vcpus'] + 'localtime', 'serial', 'stdvga', 'isa', 'vcpus', + 'usb', 'usbdevice'] ret = [] for a in dmargs: v = sxp.child_value(imageConfig, a) @@ -260,7 +261,7 @@ class HVMImageHandler(ImageHandler): if a == 'audio': a = 'enable-audio' # Handle booleans gracefully - if a in ['localtime', 'std-vga', 'isa', 'nic-ne2000', 'enable-audio']: + if a in ['localtime', 'std-vga', 'isa', 'nic-ne2000', 'enable-audio', 'usb']: if v != None: v = int(v) if v: ret.append("-%s" % a) else: diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py Mon Jun 12 08:53:38 2006 +0100 +++ b/tools/python/xen/xm/create.py Mon Jun 12 09:06:55 2006 +0100 @@ -264,7 +264,7 @@ gopts.var('irq', val='IRQ', For example 'irq=7'. This option may be repeated to add more than one IRQ.""") -gopts.var('usb', val='PATH', +gopts.var('usbport', val='PATH', fn=append_value, default=[], use="""Add a physical USB port to a domain, as specified by the path to that port. This option may be repeated to add more than one port.""") @@ -371,6 +371,14 @@ gopts.var('localtime', val='no|yes', gopts.var('localtime', val='no|yes', fn=set_bool, default=0, use="Is RTC set to localtime?") + +gopts.var('usb', val='no|yes', + fn=set_bool, default=0, + use="Emulate USB devices?") + +gopts.var('usbdevice', val='NAME', + fn=set_value, default='', + use="Name of USB device to add?") gopts.var('stdvga', val='no|yes', fn=set_bool, default=0, @@ -508,8 +516,8 @@ def configure_irq(config_devs, vals): config_devs.append(['device', config_irq]) def configure_usb(config_devs, vals): - for path in vals.usb: - config_usb = ['usb', ['path', path]] + for path in vals.usbport: + config_usb = ['usbport', ['path', path]] config_devs.append(['device', config_usb]) @@ -614,7 +622,7 @@ def configure_hvm(config_image, vals): args = [ 'device_model', 'pae', 'vcpus', 'cdrom', 'boot', 'fda', 'fdb', 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'audio', 'vnc', 'vncviewer', 'sdl', 'display', 'ne2000', 'acpi', 'apic', - 'xauthority' ] + 'xauthority', 'usb', 'usbdevice' ] for a in args: if (vals.__dict__[a]): config_image.append([a, vals.__dict__[a]]) diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/hw/usb-hid.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/hw/usb-hid.c Mon Jun 12 09:06:55 2006 +0100 @@ -0,0 +1,537 @@ +/* + * QEMU USB HID devices + * + * 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" + +/* HID interface requests */ +#define GET_REPORT 0xa101 +#define GET_IDLE 0xa102 +#define GET_PROTOCOL 0xa103 +#define SET_IDLE 0x210a +#define SET_PROTOCOL 0x210b + +#define USB_MOUSE 1 +#define USB_TABLET 2 + +typedef struct USBMouseState { + USBDevice dev; + int dx, dy, dz, buttons_state; + int x, y; + int kind; + int mouse_grabbed; +} USBMouseState; + +/* mostly the same values as the Bochs USB Mouse device */ +static const uint8_t qemu_mouse_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ + 0x10, 0x00, /* u16 bcdUSB; v1.0 */ + + 0x00, /* u8 bDeviceClass; */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ + + 0x27, 0x06, /* u16 idVendor; */ + 0x01, 0x00, /* u16 idProduct; */ + 0x00, 0x00, /* u16 bcdDevice */ + + 0x03, /* u8 iManufacturer; */ + 0x02, /* u8 iProduct; */ + 0x01, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ +}; + +static const uint8_t qemu_mouse_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ + + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 50, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x03, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_tablet_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ + + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 74, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_mouse_hid_report_descriptor[] = { + 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, + 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, + 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, + 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06, + 0xC0, 0xC0, +}; + +static const uint8_t qemu_tablet_hid_report_descriptor[] = { + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x01, /* Usage Mouse */ + 0xA1, 0x01, /* Collection Application */ + 0x09, 0x01, /* Usage Pointer */ + 0xA1, 0x00, /* Collection Physical */ + 0x05, 0x09, /* Usage Page Button */ + 0x19, 0x01, /* Usage Minimum Button 1 */ + 0x29, 0x03, /* Usage Maximum Button 3 */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x25, 0x01, /* Logical Maximum 1 */ + 0x95, 0x03, /* Report Count 3 */ + 0x75, 0x01, /* Report Size 1 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x01, /* Report Count 1 */ + 0x75, 0x05, /* Report Size 5 */ + 0x81, 0x01, /* Input (Cnst, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x30, /* Usage X */ + 0x09, 0x31, /* Usage Y */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */ + 0x35, 0x00, /* Physical Minimum 0 */ + 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */ + 0x75, 0x10, /* Report Size 16 */ + 0x95, 0x02, /* Report Count 2 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x38, /* Usage Wheel */ + 0x15, 0x81, /* Logical Minimum -127 */ + 0x25, 0x7F, /* Logical Maximum 127 */ + 0x35, 0x00, /* Physical Minimum 0 (same as logical) */ + 0x45, 0x00, /* Physical Maximum 0 (same as logical) */ + 0x75, 0x08, /* Report Size 8 */ + 0x95, 0x01, /* Report Count 1 */ + 0x81, 0x02, /* Input (Data, Var, Rel) */ + 0xC0, /* End Collection */ + 0xC0, /* End Collection */ +}; + +static void usb_mouse_event(void *opaque, + int dx1, int dy1, int dz1, int buttons_state) +{ + USBMouseState *s = opaque; + + s->dx += dx1; + s->dy += dy1; + s->dz += dz1; + s->buttons_state = buttons_state; +} + +static void usb_tablet_event(void *opaque, + int x, int y, int dz, int buttons_state) +{ + USBMouseState *s = opaque; + + s->x = x; + s->y = y; + s->dz += dz; + s->buttons_state = buttons_state; +} + +static inline int int_clamp(int val, int vmin, int vmax) +{ + if (val < vmin) + return vmin; + else if (val > vmax) + return vmax; + else + return val; +} + +static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dx, dy, dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_mouse_event, s, 0); + s->mouse_grabbed = 1; + } + + dx = int_clamp(s->dx, -128, 127); + dy = int_clamp(s->dy, -128, 127); + dz = int_clamp(s->dz, -128, 127); + + s->dx -= dx; + s->dy -= dy; + s->dz -= dz; + + b = 0; + if (s->buttons_state & MOUSE_EVENT_LBUTTON) + b |= 0x01; + if (s->buttons_state & MOUSE_EVENT_RBUTTON) + b |= 0x02; + if (s->buttons_state & MOUSE_EVENT_MBUTTON) + b |= 0x04; + + buf[0] = b; + buf[1] = dx; + buf[2] = dy; + l = 3; + if (len >= 4) { + buf[3] = dz; + l = 4; + } + return l; +} + +static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_tablet_event, s, 1); + s->mouse_grabbed = 1; + } + + dz = int_clamp(s->dz, -128, 127); + s->dz -= dz; + + /* Appears we have to invert the wheel direction */ + dz = 0 - dz; + b = 0; + if (s->buttons_state & MOUSE_EVENT_LBUTTON) + b |= 0x01; + if (s->buttons_state & MOUSE_EVENT_RBUTTON) + b |= 0x02; + if (s->buttons_state & MOUSE_EVENT_MBUTTON) + b |= 0x04; + + buf[0] = b; + buf[1] = s->x & 0xff; + buf[2] = s->x >> 8; + buf[3] = s->y & 0xff; + buf[4] = s->y >> 8; + buf[5] = dz; + l = 6; + + return l; +} + +static void usb_mouse_handle_reset(USBDevice *dev) +{ + USBMouseState *s = (USBMouseState *)dev; + + s->dx = 0; + s->dy = 0; + s->dz = 0; + s->x = 0; + s->y = 0; + s->buttons_state = 0; +} + +static int usb_mouse_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + USBMouseState *s = (USBMouseState *)dev; + int ret = 0; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, qemu_mouse_dev_descriptor, + sizeof(qemu_mouse_dev_descriptor)); + ret = sizeof(qemu_mouse_dev_descriptor); + break; + case USB_DT_CONFIG: + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_config_descriptor, + sizeof(qemu_mouse_config_descriptor)); + ret = sizeof(qemu_mouse_config_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_config_descriptor, + sizeof(qemu_tablet_config_descriptor)); + ret = sizeof(qemu_tablet_config_descriptor); + } + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "1"); + break; + case 2: + /* product description */ + if (s->kind == USB_MOUSE) + ret = set_usb_string(data, "QEMU USB Mouse"); + else if (s->kind == USB_TABLET) + ret = set_usb_string(data, "QEMU USB Tablet"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "QEMU " QEMU_VERSION); + break; + case 4: + ret = set_usb_string(data, "HID Mouse"); + break; + case 5: + ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* hid specific requests */ + case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case 0x22: + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_hid_report_descriptor, + sizeof(qemu_mouse_hid_report_descriptor)); + ret = sizeof(qemu_mouse_hid_report_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_hid_report_descriptor, + sizeof(qemu_tablet_hid_report_descriptor)); + ret = sizeof(qemu_tablet_hid_report_descriptor); + } + break; + default: + goto fail; + } + break; + case GET_REPORT: + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, length); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, length); + break; + case SET_IDLE: + ret = 0; + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_mouse_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + USBMouseState *s = (USBMouseState *)dev; + int ret = 0; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, len); + } else { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +USBDevice *usb_tablet_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_mouse_handle_reset; + s->dev.handle_control = usb_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->kind = USB_TABLET; + + return (USBDevice *)s; +} + +USBDevice *usb_mouse_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_mouse_handle_reset; + s->dev.handle_control = usb_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->kind = USB_MOUSE; + + return (USBDevice *)s; +} diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/hw/usb-hub.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/hw/usb-hub.c Mon Jun 12 09:06:55 2006 +0100 @@ -0,0 +1,549 @@ +/* + * QEMU USB HUB emulation + * + * 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" + +//#define DEBUG + +#define MAX_PORTS 8 + +typedef struct USBHubPort { + USBPort port; + uint16_t wPortStatus; + uint16_t wPortChange; +} USBHubPort; + +typedef struct USBHubState { + USBDevice dev; + int nb_ports; + USBHubPort ports[MAX_PORTS]; +} USBHubState; + +#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) +#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) +#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) +#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) +#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) +#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) +#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) + +#define PORT_STAT_CONNECTION 0x0001 +#define PORT_STAT_ENABLE 0x0002 +#define PORT_STAT_SUSPEND 0x0004 +#define PORT_STAT_OVERCURRENT 0x0008 +#define PORT_STAT_RESET 0x0010 +#define PORT_STAT_POWER 0x0100 +#define PORT_STAT_LOW_SPEED 0x0200 +#define PORT_STAT_HIGH_SPEED 0x0400 +#define PORT_STAT_TEST 0x0800 +#define PORT_STAT_INDICATOR 0x1000 + +#define PORT_STAT_C_CONNECTION 0x0001 +#define PORT_STAT_C_ENABLE 0x0002 +#define PORT_STAT_C_SUSPEND 0x0004 +#define PORT_STAT_C_OVERCURRENT 0x0008 +#define PORT_STAT_C_RESET 0x0010 + +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVERCURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOWSPEED 9 +#define PORT_HIGHSPEED 10 +#define PORT_C_CONNECTION 16 +#define PORT_C_ENABLE 17 +#define PORT_C_SUSPEND 18 +#define PORT_C_OVERCURRENT 19 +#define PORT_C_RESET 20 +#define PORT_TEST 21 +#define PORT_INDICATOR 22 + +/* same as Linux kernel root hubs */ + +static const uint8_t qemu_hub_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ + 0x10, 0x01, /* u16 bcdUSB; v1.1 */ + + 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ + + 0x00, 0x00, /* u16 idVendor; */ + 0x00, 0x00, /* u16 idProduct; */ + 0x01, 0x01, /* u16 bcdDevice */ + + 0x03, /* u8 iManufacturer; */ + 0x02, /* u8 iProduct; */ + 0x01, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ +}; + +/* XXX: patch interrupt size */ +static const uint8_t qemu_hub_config_descriptor[] = { + + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x19, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x00, /* u8 iConfiguration; */ + 0xc0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* u8 if_bInterfaceSubClass; */ + 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x00, /* u8 if_iInterface; */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_hub_hub_descriptor[] = +{ + 0x09, /* u8 bLength; */ + 0x29, /* u8 bDescriptorType; Hub-descriptor */ + 0x00, /* u8 bNbrPorts; (patched later) */ + 0x0a, /* u16 wHubCharacteristics; */ + 0x00, /* (per-port OC, no power switching) */ + 0x01, /* u8 bPwrOn2pwrGood; 2ms */ + 0x00 /* u8 bHubContrCurrent; 0 mA */ + + /* DeviceRemovable and PortPwrCtrlMask patched in later */ +}; + +static void usb_hub_attach(USBPort *port1, USBDevice *dev) +{ + USBHubState *s = port1->opaque; + USBHubPort *port = &s->ports[port1->index]; + + if (dev) { + if (port->port.dev) + usb_attach(port1, NULL); + + port->wPortStatus |= PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (dev->speed == USB_SPEED_LOW) + port->wPortStatus |= PORT_STAT_LOW_SPEED; + else + port->wPortStatus &= ~PORT_STAT_LOW_SPEED; + port->port.dev = dev; + } else { + dev = port->port.dev; + if (dev) { + port->wPortStatus &= ~PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (port->wPortStatus & PORT_STAT_ENABLE) { + port->wPortStatus &= ~PORT_STAT_ENABLE; + port->wPortChange |= PORT_STAT_C_ENABLE; + } + port->port.dev = NULL; + } + } +} + +static void usb_hub_handle_reset(USBDevice *dev) +{ + /* XXX: do it */ +} + +static int usb_hub_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + USBHubState *s = (USBHubState *)dev; + int ret; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == 0 && index != 0x81) { /* clear ep halt */ + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, qemu_hub_dev_descriptor, + sizeof(qemu_hub_dev_descriptor)); + ret = sizeof(qemu_hub_dev_descriptor); + break; + case USB_DT_CONFIG: + memcpy(data, qemu_hub_config_descriptor, + sizeof(qemu_hub_config_descriptor)); + + /* status change endpoint size based on number + * of ports */ + data[22] = (s->nb_ports + 1 + 7) / 8; + + ret = sizeof(qemu_hub_config_descriptor); + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "314159"); + break; + case 2: + /* product description */ + ret = set_usb_string(data, "QEMU USB Hub"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "QEMU " QEMU_VERSION); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* usb specific requests */ + case GetHubStatus: + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 0; + ret = 4; + break; + case GetPortStatus: + { + unsigned int n = index - 1; + USBHubPort *port; + if (n >= s->nb_ports) + goto fail; + port = &s->ports[n]; + data[0] = port->wPortStatus; + data[1] = port->wPortStatus >> 8; + data[2] = port->wPortChange; + data[3] = port->wPortChange >> 8; + ret = 4; + } + break; + case SetHubFeature: + case ClearHubFeature: + if (value == 0 || value == 1) { + } else { + goto fail; + } + ret = 0; + break; + case SetPortFeature: + { + unsigned int n = index - 1; + USBHubPort *port; + USBDevice *dev; + if (n >= s->nb_ports) + goto fail; + port = &s->ports[n]; + dev = port->port.dev; + switch(value) { + case PORT_SUSPEND: + port->wPortStatus |= PORT_STAT_SUSPEND; + break; + case PORT_RESET: + if (dev) { + dev->handle_packet(dev, + USB_MSG_RESET, 0, 0, NULL, 0); + port->wPortChange |= PORT_STAT_C_RESET; + /* set enable bit */ + port->wPortStatus |= PORT_STAT_ENABLE; + } + break; + case PORT_POWER: + break; + default: + goto fail; + } + ret = 0; + } + break; + case ClearPortFeature: + { + unsigned int n = index - 1; + USBHubPort *port; + USBDevice *dev; + if (n >= s->nb_ports) + goto fail; + port = &s->ports[n]; + dev = port->port.dev; + switch(value) { + case PORT_ENABLE: + port->wPortStatus &= ~PORT_STAT_ENABLE; + break; + case PORT_C_ENABLE: + port->wPortChange &= ~PORT_STAT_C_ENABLE; + break; + case PORT_SUSPEND: + port->wPortStatus &= ~PORT_STAT_SUSPEND; + break; + case PORT_C_SUSPEND: + port->wPortChange &= ~PORT_STAT_C_SUSPEND; + break; + case PORT_C_CONNECTION: + port->wPortChange &= ~PORT_STAT_C_CONNECTION; + break; + case PORT_C_OVERCURRENT: + port->wPortChange &= ~PORT_STAT_C_OVERCURRENT; + break; + case PORT_C_RESET: + port->wPortChange &= ~PORT_STAT_C_RESET; + break; + default: + goto fail; + } + ret = 0; + } + break; + case GetHubDescriptor: + { + unsigned int n, limit, var_hub_size = 0; + memcpy(data, qemu_hub_hub_descriptor, + sizeof(qemu_hub_hub_descriptor)); + data[2] = s->nb_ports; + + /* fill DeviceRemovable bits */ + limit = ((s->nb_ports + 1 + 7) / 8) + 7; + for (n = 7; n < limit; n++) { + data[n] = 0x00; + var_hub_size++; + } + + /* fill PortPwrCtrlMask bits */ + limit = limit + ((s->nb_ports + 7) / 8); + for (;n < limit; n++) { + data[n] = 0xff; + var_hub_size++; + } + + ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size; + break; + } + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_hub_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + USBHubState *s = (USBHubState *)dev; + int ret; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + USBHubPort *port; + unsigned int status; + int i, n; + n = (s->nb_ports + 1 + 7) / 8; + if (len == 1) { /* FreeBSD workaround */ + n = 1; + } else if (n > len) { + return USB_RET_BABBLE; + } + status = 0; + for(i = 0; i < s->nb_ports; i++) { + port = &s->ports[i]; + if (port->wPortChange) + status |= (1 << (i + 1)); + } + if (status != 0) { + for(i = 0; i < n; i++) { + data[i] = status >> (8 * i); + } + ret = n; + } else { + ret = USB_RET_NAK; /* usb11 11.13.1 */ + } + } else { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_hub_broadcast_packet(USBHubState *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + USBHubPort *port; + USBDevice *dev; + int i, ret; + + for(i = 0; i < s->nb_ports; i++) { + port = &s->ports[i]; + dev = port->port.dev; + if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { + ret = dev->handle_packet(dev, pid, + devaddr, devep, + data, len); + if (ret != USB_RET_NODEV) { + return ret; + } + } + } + return USB_RET_NODEV; +} + +static int usb_hub_handle_packet(USBDevice *dev, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + USBHubState *s = (USBHubState *)dev; + +#if defined(DEBUG) && 0 + printf("usb_hub: pid=0x%x\n", pid); +#endif + if (dev->state == USB_STATE_DEFAULT && + dev->addr != 0 && + devaddr != dev->addr && + (pid == USB_TOKEN_SETUP || + pid == USB_TOKEN_OUT || + pid == USB_TOKEN_IN)) { + /* broadcast the packet to the devices */ + return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len); + } + return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len); +} + +USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports) +{ + USBHubState *s; + USBHubPort *port; + int i; + + if (nb_ports > MAX_PORTS) + return NULL; + s = qemu_mallocz(sizeof(USBHubState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_hub_handle_packet; + + /* generic USB device init */ + s->dev.handle_reset = usb_hub_handle_reset; + s->dev.handle_control = usb_hub_handle_control; + s->dev.handle_data = usb_hub_handle_data; + + s->nb_ports = nb_ports; + for(i = 0; i < s->nb_ports; i++) { + port = &s->ports[i]; + port->wPortStatus = PORT_STAT_POWER; + port->wPortChange = 0; + port->port.attach = usb_hub_attach; + port->port.opaque = s; + port->port.index = i; + usb_ports[i] = &port->port; + } + return (USBDevice *)s; +} diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/hw/usb-uhci.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/hw/usb-uhci.c Mon Jun 12 09:06:55 2006 +0100 @@ -0,0 +1,680 @@ +/* + * USB UHCI controller emulation + * + * 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" + +//#define DEBUG +//#define DEBUG_PACKET + +#define UHCI_CMD_GRESET (1 << 2) +#define UHCI_CMD_HCRESET (1 << 1) +#define UHCI_CMD_RS (1 << 0) + +#define UHCI_STS_HCHALTED (1 << 5) +#define UHCI_STS_HCPERR (1 << 4) +#define UHCI_STS_HSERR (1 << 3) +#define UHCI_STS_RD (1 << 2) +#define UHCI_STS_USBERR (1 << 1) +#define UHCI_STS_USBINT (1 << 0) + +#define TD_CTRL_SPD (1 << 29) +#define TD_CTRL_ERROR_SHIFT 27 +#define TD_CTRL_IOS (1 << 25) +#define TD_CTRL_IOC (1 << 24) +#define TD_CTRL_ACTIVE (1 << 23) +#define TD_CTRL_STALL (1 << 22) +#define TD_CTRL_BABBLE (1 << 20) +#define TD_CTRL_NAK (1 << 19) +#define TD_CTRL_TIMEOUT (1 << 18) + +#define UHCI_PORT_RESET (1 << 9) +#define UHCI_PORT_LSDA (1 << 8) +#define UHCI_PORT_ENC (1 << 3) +#define UHCI_PORT_EN (1 << 2) +#define UHCI_PORT_CSC (1 << 1) +#define UHCI_PORT_CCS (1 << 0) + +#define FRAME_TIMER_FREQ 1000 + +#define FRAME_MAX_LOOPS 100 + +#define NB_PORTS 2 + +typedef struct UHCIPort { + USBPort port; + uint16_t ctrl; +} UHCIPort; + +typedef struct UHCIState { + PCIDevice dev; + uint16_t cmd; /* cmd register */ + uint16_t status; + uint16_t intr; /* interrupt enable register */ + uint16_t frnum; /* frame number */ + uint32_t fl_base_addr; /* frame list base address */ + uint8_t sof_timing; + uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ + QEMUTimer *frame_timer; + UHCIPort ports[NB_PORTS]; +} UHCIState; + +typedef struct UHCI_TD { + uint32_t link; + uint32_t ctrl; /* see TD_CTRL_xxx */ + uint32_t token; + uint32_t buffer; +} UHCI_TD; + +typedef struct UHCI_QH { + uint32_t link; + uint32_t el_link; +} UHCI_QH; + +static void uhci_attach(USBPort *port1, USBDevice *dev); + +static void uhci_update_irq(UHCIState *s) +{ + int level; + if (((s->status2 & 1) && (s->intr & (1 << 2))) || + ((s->status2 & 2) && (s->intr & (1 << 3))) || + ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) || + ((s->status & UHCI_STS_RD) && (s->intr & (1 << 1))) || + (s->status & UHCI_STS_HSERR) || + (s->status & UHCI_STS_HCPERR)) { + level = 1; + } else { + level = 0; + } + pci_set_irq(&s->dev, 3, level); +} + +static void uhci_reset(UHCIState *s) +{ + uint8_t *pci_conf; + int i; + UHCIPort *port; + + pci_conf = s->dev.config; + + pci_conf[0x6a] = 0x01; /* usb clock */ + pci_conf[0x6b] = 0x00; + s->cmd = 0; + s->status = 0; + s->status2 = 0; + s->intr = 0; + s->fl_base_addr = 0; + s->sof_timing = 64; + for(i = 0; i < NB_PORTS; i++) { + port = &s->ports[i]; + port->ctrl = 0x0080; + if (port->port.dev) + uhci_attach(&port->port, port->port.dev); + } +} + +static void uhci_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + UHCIState *s = opaque; + + addr &= 0x1f; + switch(addr) { + case 0x0c: + s->sof_timing = val; + break; + } +} + +static uint32_t uhci_ioport_readb(void *opaque, uint32_t addr) +{ + UHCIState *s = opaque; + uint32_t val; + + addr &= 0x1f; + switch(addr) { + case 0x0c: + val = s->sof_timing; + break; + default: + val = 0xff; + break; + } + return val; +} + +static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) +{ + UHCIState *s = opaque; + + addr &= 0x1f; +#ifdef DEBUG + printf("uhci writew port=0x%04x val=0x%04x\n", addr, val); +#endif + switch(addr) { + case 0x00: + if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { + /* start frame processing */ + qemu_mod_timer(s->frame_timer, qemu_get_clock(vm_clock)); + s->status &= ~UHCI_STS_HCHALTED; + } else if (!(val & UHCI_CMD_RS)) { + s->status |= UHCI_STS_HCHALTED; + } + if (val & UHCI_CMD_GRESET) { + UHCIPort *port; + USBDevice *dev; + int i; + + /* send reset on the USB bus */ + for(i = 0; i < NB_PORTS; i++) { + port = &s->ports[i]; + dev = port->port.dev; + if (dev) { + dev->handle_packet(dev, + USB_MSG_RESET, 0, 0, NULL, 0); + } + } + uhci_reset(s); + return; + } + if (val & UHCI_CMD_HCRESET) { + uhci_reset(s); + return; + } + s->cmd = val; + break; + case 0x02: + s->status &= ~val; + /* XXX: the chip spec is not coherent, so we add a hidden + register to distinguish between IOC and SPD */ + if (val & UHCI_STS_USBINT) + s->status2 = 0; + uhci_update_irq(s); + break; + case 0x04: + s->intr = val; + uhci_update_irq(s); + break; + case 0x06: + if (s->status & UHCI_STS_HCHALTED) + s->frnum = val & 0x7ff; + break; + case 0x10 ... 0x1f: + { + UHCIPort *port; + USBDevice *dev; + int n; + + n = (addr >> 1) & 7; + if (n >= NB_PORTS) + return; + port = &s->ports[n]; + dev = port->port.dev; + if (dev) { + /* port reset */ + if ( (val & UHCI_PORT_RESET) && + !(port->ctrl & UHCI_PORT_RESET) ) { + dev->handle_packet(dev, + USB_MSG_RESET, 0, 0, NULL, 0); + } + } + port->ctrl = (port->ctrl & 0x01fb) | (val & ~0x01fb); + /* some bits are reset when a '1' is written to them */ + port->ctrl &= ~(val & 0x000a); + } + break; + } +} + +static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr) +{ + UHCIState *s = opaque; + uint32_t val; + + addr &= 0x1f; + switch(addr) { + case 0x00: + val = s->cmd; + break; + case 0x02: + val = s->status; + break; + case 0x04: + val = s->intr; + break; + case 0x06: + val = s->frnum; + break; + case 0x10 ... 0x1f: + { + UHCIPort *port; + int n; + n = (addr >> 1) & 7; + if (n >= NB_PORTS) + goto read_default; + port = &s->ports[n]; + val = port->ctrl; + } + break; + default: + read_default: + val = 0xff7f; /* disabled port */ + break; + } +#ifdef DEBUG + printf("uhci readw port=0x%04x val=0x%04x\n", addr, val); +#endif + return val; +} + +static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +{ + UHCIState *s = opaque; + + addr &= 0x1f; +#ifdef DEBUG + printf("uhci writel port=0x%04x val=0x%08x\n", addr, val); +#endif + switch(addr) { + case 0x08: + s->fl_base_addr = val & ~0xfff; + break; + } +} + +static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr) +{ + UHCIState *s = opaque; + uint32_t val; + + addr &= 0x1f; + switch(addr) { + case 0x08: + val = s->fl_base_addr; + break; + default: + val = 0xffffffff; + break; + } + return val; +} + +static void uhci_attach(USBPort *port1, USBDevice *dev) +{ + UHCIState *s = port1->opaque; + UHCIPort *port = &s->ports[port1->index]; + + if (dev) { + if (port->port.dev) { + usb_attach(port1, NULL); + } + /* set connect status */ + if (!(port->ctrl & UHCI_PORT_CCS)) { + port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; + } + /* update speed */ + if (dev->speed == USB_SPEED_LOW) + port->ctrl |= UHCI_PORT_LSDA; + else + port->ctrl &= ~UHCI_PORT_LSDA; + port->port.dev = dev; + /* send the attach message */ + dev->handle_packet(dev, + USB_MSG_ATTACH, 0, 0, NULL, 0); + } else { + /* set connect status */ + if (!(port->ctrl & UHCI_PORT_CCS)) { + port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; + } + /* disable port */ + if (port->ctrl & UHCI_PORT_EN) { + port->ctrl &= ~UHCI_PORT_EN; + port->ctrl |= UHCI_PORT_ENC; + } + dev = port->port.dev; + if (dev) { + /* send the detach message */ + dev->handle_packet(dev, + USB_MSG_DETACH, 0, 0, NULL, 0); + } + port->port.dev = NULL; + } +} + +static int uhci_broadcast_packet(UHCIState *s, uint8_t pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + UHCIPort *port; + USBDevice *dev; + int i, ret; + +#ifdef DEBUG_PACKET + { + const char *pidstr; + switch(pid) { + case USB_TOKEN_SETUP: pidstr = "SETUP"; break; + case USB_TOKEN_IN: pidstr = "IN"; break; + case USB_TOKEN_OUT: pidstr = "OUT"; break; + default: pidstr = "?"; break; + } + printf("frame %d: pid=%s addr=0x%02x ep=%d len=%d\n", + s->frnum, pidstr, devaddr, devep, len); + if (pid != USB_TOKEN_IN) { + printf(" data_out="); + for(i = 0; i < len; i++) { + printf(" %02x", data[i]); + } + printf("\n"); + } + } +#endif + for(i = 0; i < NB_PORTS; i++) { + port = &s->ports[i]; + dev = port->port.dev; + if (dev && (port->ctrl & UHCI_PORT_EN)) { + ret = dev->handle_packet(dev, pid, + devaddr, devep, + data, len); + if (ret != USB_RET_NODEV) { +#ifdef DEBUG_PACKET + { + printf(" ret=%d ", ret); + if (pid == USB_TOKEN_IN && ret > 0) { + printf("data_in="); + for(i = 0; i < ret; i++) { + printf(" %02x", data[i]); + } + } + printf("\n"); + } +#endif + return ret; + } + } + } + return USB_RET_NODEV; +} + +/* return -1 if fatal error (frame must be stopped) + 0 if TD successful + 1 if TD unsuccessful or inactive +*/ +static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) +{ + uint8_t pid; + uint8_t buf[1280]; + int len, max_len, err, ret; + + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } + + if (!(td->ctrl & TD_CTRL_ACTIVE)) + return 1; + + /* TD is active */ + max_len = ((td->token >> 21) + 1) & 0x7ff; + pid = td->token & 0xff; + switch(pid) { + case USB_TOKEN_OUT: + case USB_TOKEN_SETUP: + cpu_physical_memory_read(td->buffer, buf, max_len); + ret = uhci_broadcast_packet(s, pid, + (td->token >> 8) & 0x7f, + (td->token >> 15) & 0xf, + buf, max_len); + len = max_len; + break; + case USB_TOKEN_IN: + ret = uhci_broadcast_packet(s, pid, + (td->token >> 8) & 0x7f, + (td->token >> 15) & 0xf, + buf, max_len); + if (ret >= 0) { + len = ret; + if (len > max_len) { + len = max_len; + ret = USB_RET_BABBLE; + } + if (len > 0) { + /* write the data back */ + cpu_physical_memory_write(td->buffer, buf, len); + } + } else { + len = 0; + } + break; + default: + /* invalid pid : frame interrupted */ + s->status |= UHCI_STS_HCPERR; + uhci_update_irq(s); + return -1; + } + if (td->ctrl & TD_CTRL_IOS) + td->ctrl &= ~TD_CTRL_ACTIVE; + if (ret >= 0) { + td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); + td->ctrl &= ~TD_CTRL_ACTIVE; + if (pid == USB_TOKEN_IN && + (td->ctrl & TD_CTRL_SPD) && + len < max_len) { + *int_mask |= 0x02; + /* short packet: do not update QH */ + return 1; + } else { + /* success */ + return 0; + } + } else { + switch(ret) { + default: + case USB_RET_NODEV: + do_timeout: + td->ctrl |= TD_CTRL_TIMEOUT; + err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3; + if (err != 0) { + err--; + if (err == 0) { + td->ctrl &= ~TD_CTRL_ACTIVE; + s->status |= UHCI_STS_USBERR; + uhci_update_irq(s); + } + } + td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | + (err << TD_CTRL_ERROR_SHIFT); + return 1; + case USB_RET_NAK: + td->ctrl |= TD_CTRL_NAK; + if (pid == USB_TOKEN_SETUP) + goto do_timeout; + return 1; + case USB_RET_STALL: + td->ctrl |= TD_CTRL_STALL; + td->ctrl &= ~TD_CTRL_ACTIVE; + return 1; + case USB_RET_BABBLE: + td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; + td->ctrl &= ~TD_CTRL_ACTIVE; + /* frame interrupted */ + return -1; + } + } +} + +static void uhci_frame_timer(void *opaque) +{ + UHCIState *s = opaque; + int64_t expire_time; + uint32_t frame_addr, link, old_td_ctrl, val; + int int_mask, cnt, ret; + UHCI_TD td; + UHCI_QH qh; + + if (!(s->cmd & UHCI_CMD_RS)) { + qemu_del_timer(s->frame_timer); + /* set hchalted bit in status - UHCI11D 2.1.2 */ + s->status |= UHCI_STS_HCHALTED; + return; + } + frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); + cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4); + le32_to_cpus(&link); + int_mask = 0; + cnt = FRAME_MAX_LOOPS; + while ((link & 1) == 0) { + if (--cnt == 0) + break; + /* valid frame */ + if (link & 2) { + /* QH */ + cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh)); + le32_to_cpus(&qh.link); + le32_to_cpus(&qh.el_link); + depth_first: + if (qh.el_link & 1) { + /* no element : go to next entry */ + link = qh.link; + } else if (qh.el_link & 2) { + /* QH */ + link = qh.el_link; + } else { + /* TD */ + if (--cnt == 0) + break; + cpu_physical_memory_read(qh.el_link & ~0xf, + (uint8_t *)&td, sizeof(td)); + le32_to_cpus(&td.link); + le32_to_cpus(&td.ctrl); + le32_to_cpus(&td.token); + le32_to_cpus(&td.buffer); + old_td_ctrl = td.ctrl; + ret = uhci_handle_td(s, &td, &int_mask); + /* update the status bits of the TD */ + if (old_td_ctrl != td.ctrl) { + val = cpu_to_le32(td.ctrl); + cpu_physical_memory_write((qh.el_link & ~0xf) + 4, + (const uint8_t *)&val, + sizeof(val)); + } + if (ret < 0) + break; /* interrupted frame */ + if (ret == 0) { + /* update qh element link */ + qh.el_link = td.link; + val = cpu_to_le32(qh.el_link); + cpu_physical_memory_write((link & ~0xf) + 4, + (const uint8_t *)&val, + sizeof(val)); + if (qh.el_link & 4) { + /* depth first */ + goto depth_first; + } + } + /* go to next entry */ + link = qh.link; + } + } else { + /* TD */ + cpu_physical_memory_read(link & ~0xf, (uint8_t *)&td, sizeof(td)); + le32_to_cpus(&td.link); + le32_to_cpus(&td.ctrl); + le32_to_cpus(&td.token); + le32_to_cpus(&td.buffer); + old_td_ctrl = td.ctrl; + ret = uhci_handle_td(s, &td, &int_mask); + /* update the status bits of the TD */ + if (old_td_ctrl != td.ctrl) { + val = cpu_to_le32(td.ctrl); + cpu_physical_memory_write((link & ~0xf) + 4, + (const uint8_t *)&val, + sizeof(val)); + } + if (ret < 0) + break; /* interrupted frame */ + link = td.link; + } + } + s->frnum = (s->frnum + 1) & 0x7ff; + if (int_mask) { + s->status2 |= int_mask; + s->status |= UHCI_STS_USBINT; + uhci_update_irq(s); + } + /* prepare the timer for the next frame */ + expire_time = qemu_get_clock(vm_clock) + + (ticks_per_sec / FRAME_TIMER_FREQ); + qemu_mod_timer(s->frame_timer, expire_time); +} + +static void uhci_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + UHCIState *s = (UHCIState *)pci_dev; + + register_ioport_write(addr, 32, 2, uhci_ioport_writew, s); + register_ioport_read(addr, 32, 2, uhci_ioport_readw, s); + register_ioport_write(addr, 32, 4, uhci_ioport_writel, s); + register_ioport_read(addr, 32, 4, uhci_ioport_readl, s); + register_ioport_write(addr, 32, 1, uhci_ioport_writeb, s); + register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); +} + +void usb_uhci_init(PCIBus *bus, USBPort **usb_ports) +{ + UHCIState *s; + uint8_t *pci_conf; + UHCIPort *port; + int i; + + s = (UHCIState *)pci_register_device(bus, + "USB-UHCI", sizeof(UHCIState), + ((PCIDevice *)piix3_state)->devfn + 2, + NULL, NULL); + pci_conf = s->dev.config; + pci_conf[0x00] = 0x86; + pci_conf[0x01] = 0x80; + pci_conf[0x02] = 0x20; + pci_conf[0x03] = 0x70; + pci_conf[0x08] = 0x01; // revision number + pci_conf[0x09] = 0x00; + pci_conf[0x0a] = 0x03; + pci_conf[0x0b] = 0x0c; + pci_conf[0x0e] = 0x00; // header_type + pci_conf[0x3d] = 4; // interrupt pin 3 + pci_conf[0x60] = 0x10; // release number + + for(i = 0; i < NB_PORTS; i++) { + port = &s->ports[i]; + port->port.opaque = s; + port->port.index = i; + port->port.attach = uhci_attach; + usb_ports[i] = &port->port; + } + s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); + + uhci_reset(s); + + /* Use region 4 for consistency with real hardware. BSD guests seem + to rely on this. */ + pci_register_io_region(&s->dev, 4, 0x20, + PCI_ADDRESS_SPACE_IO, uhci_map); +} diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/hw/usb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/hw/usb.c Mon Jun 12 09:06:55 2006 +0100 @@ -0,0 +1,193 @@ +/* + * QEMU USB emulation + * + * 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" + +void usb_attach(USBPort *port, USBDevice *dev) +{ + port->attach(port, dev); +} + +/**********************/ +/* generic USB device helpers (you are not forced to use them when + writing your USB device driver, but they help handling the + protocol) +*/ + +#define SETUP_STATE_IDLE 0 +#define SETUP_STATE_DATA 1 +#define SETUP_STATE_ACK 2 + +int usb_generic_handle_packet(USBDevice *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + int l, ret = 0; + + switch(pid) { + case USB_MSG_ATTACH: + s->state = USB_STATE_ATTACHED; + break; + case USB_MSG_DETACH: + s->state = USB_STATE_NOTATTACHED; + break; + case USB_MSG_RESET: + s->remote_wakeup = 0; + s->addr = 0; + s->state = USB_STATE_DEFAULT; + s->handle_reset(s); + break; + case USB_TOKEN_SETUP: + if (s->state < USB_STATE_DEFAULT || devaddr != s->addr) + return USB_RET_NODEV; + if (len != 8) + goto fail; + memcpy(s->setup_buf, data, 8); + s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; + s->setup_index = 0; + if (s->setup_buf[0] & USB_DIR_IN) { + ret = s->handle_control(s, + (s->setup_buf[0] << 8) | s->setup_buf[1], + (s->setup_buf[3] << 8) | s->setup_buf[2], + (s->setup_buf[5] << 8) | s->setup_buf[4], + s->setup_len, + s->data_buf); + if (ret < 0) + return ret; + if (ret < s->setup_len) + s->setup_len = ret; + s->setup_state = SETUP_STATE_DATA; + } else { + if (s->setup_len == 0) + s->setup_state = SETUP_STATE_ACK; + else + s->setup_state = SETUP_STATE_DATA; + } + break; + case USB_TOKEN_IN: + if (s->state < USB_STATE_DEFAULT || devaddr != s->addr) + return USB_RET_NODEV; + switch(devep) { + case 0: + switch(s->setup_state) { + case SETUP_STATE_ACK: + if (!(s->setup_buf[0] & USB_DIR_IN)) { + s->setup_state = SETUP_STATE_IDLE; + ret = s->handle_control(s, + (s->setup_buf[0] << 8) | s->setup_buf[1], + (s->setup_buf[3] << 8) | s->setup_buf[2], + (s->setup_buf[5] << 8) | s->setup_buf[4], + s->setup_len, + s->data_buf); + if (ret > 0) + ret = 0; + } else { + /* return 0 byte */ + } + break; + case SETUP_STATE_DATA: + if (s->setup_buf[0] & USB_DIR_IN) { + l = s->setup_len - s->setup_index; + if (l > len) + l = len; + memcpy(data, s->data_buf + s->setup_index, l); + s->setup_index += l; + if (s->setup_index >= s->setup_len) + s->setup_state = SETUP_STATE_ACK; + ret = l; + } else { + s->setup_state = SETUP_STATE_IDLE; + goto fail; + } + break; + default: + goto fail; + } + break; + default: + ret = s->handle_data(s, pid, devep, data, len); + break; + } + break; + case USB_TOKEN_OUT: + if (s->state < USB_STATE_DEFAULT || devaddr != s->addr) + return USB_RET_NODEV; + switch(devep) { + case 0: + switch(s->setup_state) { + case SETUP_STATE_ACK: + if (s->setup_buf[0] & USB_DIR_IN) { + s->setup_state = SETUP_STATE_IDLE; + /* transfer OK */ + } else { + /* ignore additionnal output */ + } + break; + case SETUP_STATE_DATA: + if (!(s->setup_buf[0] & USB_DIR_IN)) { + l = s->setup_len - s->setup_index; + if (l > len) + l = len; + memcpy(s->data_buf + s->setup_index, data, l); + s->setup_index += l; + if (s->setup_index >= s->setup_len) + s->setup_state = SETUP_STATE_ACK; + ret = l; + } else { + s->setup_state = SETUP_STATE_IDLE; + goto fail; + } + break; + default: + goto fail; + } + break; + default: + ret = s->handle_data(s, pid, devep, data, len); + break; + } + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +/* XXX: fix overflow */ +int set_usb_string(uint8_t *buf, const char *str) +{ + int len, i; + uint8_t *q; + + q = buf; + len = strlen(str); + *q++ = 2 * len + 2; + *q++ = 3; + for(i = 0; i < len; i++) { + *q++ = str[i]; + *q++ = 0; + } + return q - buf; +} diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/hw/usb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/hw/usb.h Mon Jun 12 09:06:55 2006 +0100 @@ -0,0 +1,166 @@ +/* + * 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_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 + +#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 InterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define InterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) +#define EndpointOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<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 { + USBDevice *dev; + 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_device_open(const char *devname); +void usb_host_info(void); + +/* usb-hid.c */ +USBDevice *usb_mouse_init(void); +USBDevice *usb_tablet_init(void); diff -r 5b1bd9eab3b9 -r e2a2b2da92f4 tools/ioemu/usb-linux.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ioemu/usb-linux.c Mon Jun 12 09:06:55 2006 +0100 @@ -0,0 +1,487 @@ +/* + * 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; +}; + +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 DEBUG + +#define USBDEVFS_PATH "/proc/bus/usb" + +typedef struct USBHostDevice { + USBDevice dev; + int fd; +} USBHostDevice; + +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; + } +} + +/* XXX: exclude high speed devices or implement EHCI */ +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; + + 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(buf); + return NULL; + } + + /* 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 */ + fprintf(stderr, "usb_host: only one interface supported\n"); + goto fail; + } + +#ifdef USBDEVFS_DISCONNECT + /* earlier Linux 2.4 do not support that */ + { + struct usbdevfs_ioctl ctrl; + ctrl.ioctl_code = USBDEVFS_DISCONNECT; + ctrl.ifno = 0; + ret = ioctl(fd, USBDEVFS_IOCTL, &ctrl); + 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) { + fprintf(stderr, "usb_host: device already grabbed\n"); + } else { + perror("USBDEVFS_CLAIMINTERFACE"); + } + fail: + close(fd); + return NULL; + } + + ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); + if (ret < 0) { + perror("USBDEVFS_CONNECTINFO"); + goto fail; + } + +#ifdef DEBUG + printf("host USB device %d.%d grabbed\n", bus_num, addr); +#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_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; +} + +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 int usb_host_scan(void *opaque, USBScanFunc *func) +{ + FILE *f; + char line[1024]; + char buf[1024]; + 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(;;) { + if (fgets(line, sizeof(line), f) == NULL) + break; + if (strlen(line) > 0) + line[strlen(line) - 1] = '\0'; + if (line[0] == 'T' && line[1] == ':') { + if (device_count && (vendor_id || product_id)) { + /* New device. Add the previously discovered device. */ + 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 && (vendor_id || product_id)) { + /* Add the last device. */ + ret = func(opaque, bus_num, addr, class_id, vendor_id, + product_id, product_name, speed); + } + the_end: + fclose(f); + return ret; +} + +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) +{ + 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; + } +} + +/* 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; + } + } + return -1; +} + +/**********************/ +/* 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) +{ + const struct usb_class_info *p; + for(p = usb_class_info; p->class != -1; p++) { + if (p->class == class) + break; + } + return p->class_name; +} + +void usb_info_device(int bus_num, int addr, int class_id, + int vendor_id, int product_id, + const char *product_name, + int speed) +{ + const char *class_str, *speed_str; + + switch(speed) { + case USB_SPEED_LOW: + speed_str = "1.5"; + break; + case USB_SPEED_FULL: + speed_str = "12"; + break; + case USB_SPEED_HIGH: + speed_str = "480"; + break; + default: + speed_str = "?"; + break; + } + + term_printf(" Device %d.%d, speed %s Mb/s\n", + bus_num, addr, speed_str); + class_str = usb_class_str(class_id); + if (class_str) + term_printf(" %s:", class_str); + else + term_printf(" Class %02x:", class_id); + term_printf(" USB device %04x:%04x", vendor_id, product_id); + if (product_name[0] != '\0') + term_printf(", %s", product_name); + term_printf("\n"); +} + +static int usb_host_info_device(void *opaque, int bus_num, int addr, + int class_id, + int vendor_id, int product_id, + const char *product_name, + int speed) +{ + usb_info_device(bus_num, addr, class_id, vendor_id, product_id, + product_name, speed); + return 0; +} + +void usb_host_info(void) +{ + usb_host_scan(NULL, usb_host_info_device); +} + +#else + +void usb_host_info(void) +{ + term_printf("USB host devices not supported\n"); +} + +/* XXX: modify configure to compile the right host driver */ +USBDevice *usb_host_device_open(const char *devname) +{ + return NULL; +} + +#endif _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |