* [Qemu-devel] USB serial device
@ 2008-01-11 0:23 Samuel Thibault
2008-01-11 11:09 ` [Qemu-devel] [PATCH] " Samuel Thibault
0 siblings, 1 reply; 10+ messages in thread
From: Samuel Thibault @ 2008-01-11 0:23 UTC (permalink / raw)
To: qemu-devel
Hello,
I would like to implement support for braille devices, and for this I'd
need to first implement a USB serial device (FTDI chip). Has anybody
worked on that already?
Samuel
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH] USB serial device
2008-01-11 0:23 [Qemu-devel] USB serial device Samuel Thibault
@ 2008-01-11 11:09 ` Samuel Thibault
2008-01-13 1:55 ` [Qemu-devel] " Samuel Thibault
0 siblings, 1 reply; 10+ messages in thread
From: Samuel Thibault @ 2008-01-11 11:09 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1018 bytes --]
Hello,
Samuel Thibault, le Fri 11 Jan 2008 00:23:12 +0000, a écrit :
> I would like to implement support for braille devices, and for this I'd
> need to first implement a USB serial device (FTDI chip). Has anybody
> worked on that already?
Ok, was easier than expected, Here is a patch. The serial support is
incomplete however because qemu still lacks support for flow control and
modem lines.
You will notice in tty_serial_init that I made the baud values more
relaxed. This is because with divisor/baud conversions, things never get
exact, so we need to be laxist with the value. For instance here with
FTDI, the base divisor is 48000000/2, so for 57600 bps the guest needs
to choose between divisors 416 and 417, which bring to either 57692bps
or 57553bps but not exactly 57600bps. It happens that Linux chooses
divisor 416, hence 57692bps. Of course, the higher the speed, the worse
things get. The 1.1 factor is the smallest factor I could find between
usual bps values, notably B110, B134 and B150.
Samuel
[-- Attachment #2: patch-qemu-usb-serial --]
[-- Type: text/plain, Size: 18913 bytes --]
Index: Makefile
===================================================================
RCS file: /sources/qemu/qemu/Makefile,v
retrieving revision 1.140
diff -u -p -r1.140 Makefile
--- Makefile 6 Jan 2008 18:27:12 -0000 1.140
+++ Makefile 11 Jan 2008 10:59:50 -0000
@@ -57,7 +57,7 @@ OBJS+=i2c.o smbus.o smbus_eeprom.o max73
OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o
OBJS+=scsi-disk.o cdrom.o
OBJS+=scsi-generic.o
-OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
+OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o
OBJS+=sd.o ssi-sd.o
ifdef CONFIG_WIN32
Index: vl.c
===================================================================
RCS file: /sources/qemu/qemu/vl.c,v
retrieving revision 1.395
diff -u -p -r1.395 vl.c
--- vl.c 8 Jan 2008 19:32:16 -0000 1.395
+++ vl.c 11 Jan 2008 10:59:52 -0000
@@ -2237,45 +2237,33 @@ static void tty_serial_init(int fd, int
#endif
tcgetattr (fd, &tty);
- switch(speed) {
- case 50:
+#define MARGIN 1.1
+ if (speed <= 50 * MARGIN)
spd = B50;
- break;
- case 75:
+ else if (speed <= 75 * MARGIN)
spd = B75;
- break;
- case 300:
+ else if (speed <= 300 * MARGIN)
spd = B300;
- break;
- case 600:
+ else if (speed <= 600 * MARGIN)
spd = B600;
- break;
- case 1200:
+ else if (speed <= 1200 * MARGIN)
spd = B1200;
- break;
- case 2400:
+ else if (speed <= 2400 * MARGIN)
spd = B2400;
- break;
- case 4800:
+ else if (speed <= 4800 * MARGIN)
spd = B4800;
- break;
- case 9600:
+ else if (speed <= 9600 * MARGIN)
spd = B9600;
- break;
- case 19200:
+ else if (speed <= 19200 * MARGIN)
spd = B19200;
- break;
- case 38400:
+ else if (speed <= 38400 * MARGIN)
spd = B38400;
- break;
- case 57600:
+ else if (speed <= 57600 * MARGIN)
spd = B57600;
- break;
- default:
- case 115200:
+ else if (speed <= 115200 * MARGIN)
+ spd = B115200;
+ else
spd = B115200;
- break;
- }
cfsetispeed(&tty, spd);
cfsetospeed(&tty, spd);
@@ -5196,6 +5184,8 @@ static int usb_device_add(const char *de
dev = usb_msd_init(p);
} else if (!strcmp(devname, "wacom-tablet")) {
dev = usb_wacom_init();
+ } else if (strstart(devname, "serial:", &p)) {
+ dev = usb_serial_init(p);
} else {
return -1;
}
Index: hw/usb-serial.c
===================================================================
RCS file: hw/usb-serial.c
diff -N hw/usb-serial.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ hw/usb-serial.c 11 Jan 2008 10:59:52 -0000
@@ -0,0 +1,514 @@
+/*
+ * FTDI FT232BM Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "qemu-char.h"
+
+//#define DEBUG_Serial
+
+#ifdef DEBUG_Serial
+#define DPRINTF(fmt, args...) \
+do { printf("usb-serial: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#define RECV_BUF 384
+#define SEND_BUF 128 // Not used for now
+
+/* Commands */
+#define FTDI_RESET 0
+#define FTDI_SET_MDM_CTRL 1
+#define FTDI_SET_FLOW_CTRL 2
+#define FTDI_SET_BAUD 3
+#define FTDI_SET_DATA 4
+#define FTDI_GET_MDM_ST 5
+#define FTDI_SET_EVENT_CHR 6
+#define FTDI_SET_ERROR_CHR 7
+#define FTDI_SET_LATENCY 9
+#define FTDI_GET_LATENCY 10
+
+#define DeviceVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+
+/* RESET */
+
+#define FTDI_RESET_SIO 0
+#define FTDI_RESET_RX 1
+#define FTDI_RESET_TX 2
+
+/* SET_MDM_CTRL */
+
+#define FTDI_MDM_CTRL 3
+#define FTDI_DTR 1
+#define FTDI_RTS 2
+
+/* SET_FLOW_CTRL */
+
+#define FTDI_RTS_CTS_HS 1
+#define FTDI_DTR_DSR_HS 2
+#define FTDI_XON_XOFF_HS 4
+
+/* SET_DATA */
+
+#define FTDI_PARITY (0x7 << 8)
+#define FTDI_ODD (0x1 << 8)
+#define FTDI_EVEN (0x2 << 8)
+#define FTDI_MARK (0x3 << 8)
+#define FTDI_SPACE (0x4 << 8)
+
+#define FTDI_STOP (0x3 << 11)
+#define FTDI_STOP1 (0x0 << 11)
+#define FTDI_STOP15 (0x1 << 11)
+#define FTDI_STOP2 (0x2 << 11)
+
+/* GET_MDM_ST */
+/* TODO: should be sent every 40ms */
+#define FTDI_CTS (1<<4) // CTS line status
+#define FTDI_DSR (1<<5) // DSR line status
+#define FTDI_RI (1<<6) // RI line status
+#define FTDI_RLSD (1<<7) // Receive Line Signal Detect
+
+/* Status */
+
+#define FTDI_DR (1<<0) // Data Ready
+#define FTDI_OE (1<<1) // Overrun Err
+#define FTDI_PE (1<<2) // Parity Err
+#define FTDI_FE (1<<3) // Framing Err
+#define FTDI_BI (1<<4) // Break Interrupt
+#define FTDI_THRE (1<<5) // Transmitter Holding Register
+#define FTDI_TEMT (1<<6) // Transmitter Empty
+#define FTDI_FIFO (1<<7) // Error in FIFO
+
+typedef struct {
+ USBDevice dev;
+ uint8_t recv_buf[RECV_BUF];
+ uint8_t recv_ptr;
+ uint8_t recv_used;
+ uint8_t send_buf[SEND_BUF];
+ uint8_t event_chr;
+ uint8_t error_chr;
+ uint8_t event_trigger;
+ uint8_t lines;
+ QEMUSerialSetParams params;
+ int latency; /* ms */
+ CharDriverState *cs;
+} USBSerialState;
+
+static const uint8_t qemu_serial_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x00, 0x02, /* u16 bcdUSB; v2.0 */
+
+ 0x00, /* u8 bDeviceClass; */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ /* Vendor and product id are arbitrary. */
+ 0x03, 0x04, /* u16 idVendor; */
+ 0x71, 0xFE, /* u16 idProduct; */
+ //0x00, 0xFF, /* u16 idProduct; */
+ 0x00, 0x04, /* u16 bcdDevice */
+
+ 0x01, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x03, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+static const uint8_t qemu_serial_config_descriptor[] = {
+
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x20, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x00, /* u8 iConfiguration; */
+ 0x80, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 100/2, /* u8 MaxPower; */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x02, /* u8 if_bNumEndpoints; */
+ 0xff, /* u8 if_bInterfaceClass; Vendor Specific */
+ 0xff, /* u8 if_bInterfaceSubClass; Vendor Specific */
+ 0xff, /* u8 if_bInterfaceProtocol; Vendor Specific */
+ 0x02, /* u8 if_iInterface; */
+
+ /* Bulk-In endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Bulk-Out endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00 /* u8 ep_bInterval; */
+};
+
+static void usb_serial_reset(USBSerialState *s)
+{
+ /* TODO: Set flow control to none */
+ s->event_chr = 0x0d;
+ s->event_trigger = 0;
+ s->recv_ptr = 0;
+ s->recv_used = 0;
+ /* TODO: purge in char driver */
+ s->lines &= ~(FTDI_DTR|FTDI_RTS);
+}
+
+static void usb_serial_handle_reset(USBDevice *dev)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+
+ DPRINTF("Reset\n");
+
+ usb_serial_reset(s);
+ /* TODO: Reset virtual device, send BREAK? */
+}
+
+static int usb_serial_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+ int ret = 0;
+
+ DPRINTF("got control %x, value %x\n",request, value);
+ switch (request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (0 << 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_serial_dev_descriptor,
+ sizeof(qemu_serial_dev_descriptor));
+ ret = sizeof(qemu_serial_dev_descriptor);
+ break;
+ case USB_DT_CONFIG:
+ memcpy(data, qemu_serial_config_descriptor,
+ sizeof(qemu_serial_config_descriptor));
+ ret = sizeof(qemu_serial_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:
+ /* vendor description */
+ ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+ break;
+ case 2:
+ /* product description */
+ ret = set_usb_string(data, "QEMU USB SERIAL");
+ break;
+ case 3:
+ /* serial number */
+ ret = set_usb_string(data, "1");
+ 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;
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == 0 && index != 0x81) { /* clear ep halt */
+ goto fail;
+ }
+ ret = 0;
+ break;
+
+ /* Class specific requests. */
+ case DeviceVendor | FTDI_RESET:
+ switch (value) {
+ case FTDI_RESET_SIO:
+ usb_serial_reset(s);
+ break;
+ case FTDI_RESET_RX:
+ s->recv_ptr = 0;
+ s->recv_used = 0;
+ /* TODO: purge from char device */
+ break;
+ case FTDI_RESET_TX:
+ /* TODO: purge from char device */
+ break;
+ }
+ break;
+ case DeviceVendor | FTDI_SET_MDM_CTRL:
+ s->lines = value & FTDI_MDM_CTRL;
+ break;
+ case DeviceVendor | FTDI_SET_FLOW_CTRL:
+ /* TODO: ioctl */
+ break;
+ case DeviceVendor | FTDI_SET_BAUD: {
+ static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
+ int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
+ | ((index & 1) << 2)];
+ int divisor = value & 0x3fff;
+
+ /* chip special cases */
+ if (divisor == 1 && subdivisor8 == 0)
+ subdivisor8 = 4;
+ if (divisor == 0 && subdivisor8 == 0)
+ divisor = 1;
+
+ s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
+ qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+ break;
+ }
+ case DeviceVendor | FTDI_SET_DATA:
+ switch (value & FTDI_PARITY) {
+ case 0:
+ s->params.parity = 'N';
+ break;
+ case FTDI_ODD:
+ s->params.parity = 'O';
+ break;
+ case FTDI_EVEN:
+ s->params.parity = 'E';
+ break;
+ default:
+ printf("unsupported parity %d\n", value & FTDI_PARITY);
+ break;
+ }
+ switch (value & FTDI_STOP) {
+ case FTDI_STOP1:
+ s->params.stop_bits = 1;
+ break;
+ case FTDI_STOP2:
+ s->params.stop_bits = 2;
+ break;
+ default:
+ printf("unsupported stop bits %d\n", value & FTDI_STOP);
+ break;
+ }
+ qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+ /* TODO: TX ON/OFF */
+ break;
+ case DeviceVendor | FTDI_GET_MDM_ST:
+ /* TODO: return modem status */
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceVendor | FTDI_SET_EVENT_CHR:
+ /* TODO: handle it */
+ s->event_chr = value;
+ break;
+ case DeviceVendor | FTDI_SET_ERROR_CHR:
+ /* TODO: handle it */
+ s->error_chr = value;
+ break;
+ case DeviceVendor | FTDI_SET_LATENCY:
+ s->latency = value;
+ break;
+ case DeviceVendor | FTDI_GET_LATENCY:
+ data[0] = s->latency;
+ ret = 1;
+ break;
+ default:
+ fail:
+ printf("got unsupported control %x, value %x\n",request, value);
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+ int ret = 0;
+ uint8_t devep = p->devep;
+ uint8_t *data = p->data;
+ int len = p->len;
+ int first_len;
+
+ switch (p->pid) {
+ case USB_TOKEN_OUT:
+ if (devep != 2)
+ goto fail;
+ qemu_chr_write(s->cs, data, len);
+ break;
+
+ case USB_TOKEN_IN:
+ if (devep != 1)
+ goto fail;
+ first_len = RECV_BUF - s->recv_ptr;
+ if (len <= 2) {
+ ret = USB_RET_NAK;
+ break;
+ }
+ /* TODO: Report serial line status */
+ *data++ = 0;
+ *data++ = 0;
+ len -= 2;
+ if (len > s->recv_used)
+ len = s->recv_used;
+ if (!len) {
+ ret = USB_RET_NAK;
+ break;
+ }
+ if (first_len > len)
+ first_len = len;
+ memcpy(data, s->recv_buf + s->recv_ptr, first_len);
+ if (len > first_len)
+ memcpy(data + first_len, s->recv_buf, len - first_len);
+ s->recv_used -= len;
+ s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
+ ret = len + 2;
+ break;
+
+ default:
+ DPRINTF("Bad token\n");
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usb_serial_handle_destroy(USBDevice *dev)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+
+ qemu_chr_close(s->cs);
+ qemu_free(s);
+}
+
+int usb_serial_can_read(void *opaque)
+{
+ USBSerialState *s = opaque;
+ return RECV_BUF - s->recv_used;
+}
+
+void usb_serial_read(void *opaque, const uint8_t *buf, int size)
+{
+ USBSerialState *s = opaque;
+ int first_size = RECV_BUF - s->recv_ptr;
+ if (first_size > size)
+ first_size = size;
+ memcpy(s->recv_buf + s->recv_ptr, buf, first_size);
+ if (size > first_size)
+ memcpy(s->recv_buf, buf + first_size, size - first_size);
+ s->recv_used += size;
+}
+
+void usb_serial_event(void *opaque, int event)
+{
+ USBSerialState *s = opaque;
+
+ switch (event) {
+ case CHR_EVENT_BREAK:
+ /* TODO: Send Break to USB */
+ break;
+ case CHR_EVENT_FOCUS:
+ break;
+ case CHR_EVENT_RESET:
+ usb_serial_reset(s);
+ /* TODO: Reset USB port */
+ break;
+ }
+}
+
+USBDevice *usb_serial_init(const char *filename)
+{
+ USBSerialState *s;
+ CharDriverState *cdrv;
+
+ s = qemu_mallocz(sizeof(USBSerialState));
+ if (!s)
+ return NULL;
+
+ cdrv = qemu_chr_open(filename);
+ if (!cdrv)
+ goto fail;
+ s->cs = cdrv;
+
+ qemu_chr_add_handlers(cdrv, usb_serial_can_read, usb_serial_read, usb_serial_event, s);
+
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_serial_handle_reset;
+ s->dev.handle_control = usb_serial_handle_control;
+ s->dev.handle_data = usb_serial_handle_data;
+ s->dev.handle_destroy = usb_serial_handle_destroy;
+
+ snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Serial(%.16s)",
+ filename);
+
+ usb_serial_handle_reset((USBDevice *)s);
+ return (USBDevice *)s;
+ fail:
+ qemu_free(s);
+ return NULL;
+}
Index: hw/usb.h
===================================================================
RCS file: /sources/qemu/qemu/hw/usb.h,v
retrieving revision 1.19
diff -u -p -r1.19 usb.h
--- hw/usb.h 17 Nov 2007 17:14:50 -0000 1.19
+++ hw/usb.h 11 Jan 2008 10:59:52 -0000
@@ -217,6 +217,9 @@ USBDevice *usb_msd_init(const char *file
/* usb-wacom.c */
USBDevice *usb_wacom_init(void);
+/* usb-serial.c */
+USBDevice *usb_serial_init(const char *filename);
+
/* usb ports of the VM */
void qemu_register_usb_port(USBPort *port, void *opaque, int index,
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Qemu-devel] Re: [PATCH] USB serial device
2008-01-11 11:09 ` [Qemu-devel] [PATCH] " Samuel Thibault
@ 2008-01-13 1:55 ` Samuel Thibault
2008-01-13 18:59 ` Samuel Thibault
0 siblings, 1 reply; 10+ messages in thread
From: Samuel Thibault @ 2008-01-13 1:55 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1220 bytes --]
Hello,
Samuel Thibault, le Fri 11 Jan 2008 11:09:23 +0000, a écrit :
> Samuel Thibault, le Fri 11 Jan 2008 00:23:12 +0000, a écrit :
> > I would like to implement support for braille devices, and for this I'd
> > need to first implement a USB serial device (FTDI chip). Has anybody
> > worked on that already?
>
> Ok, was easier than expected, Here is a patch. The serial support is
> incomplete however because qemu still lacks support for flow control and
> modem lines.
>
> You will notice in tty_serial_init that I made the baud values more
> relaxed. This is because with divisor/baud conversions, things never get
> exact, so we need to be laxist with the value. For instance here with
> FTDI, the base divisor is 48000000/2, so for 57600 bps the guest needs
> to choose between divisors 416 and 417, which bring to either 57692bps
> or 57553bps but not exactly 57600bps. It happens that Linux chooses
> divisor 416, hence 57692bps. Of course, the higher the speed, the worse
> things get. The 1.1 factor is the smallest factor I could find between
> usual bps values, notably B110, B134 and B150.
Here is an updated version, that takes parameters, so as to be able to
notably provide the product ID.
Samuel
[-- Attachment #2: patch-qemu-usb-serial --]
[-- Type: text/plain, Size: 20007 bytes --]
? .vl.c.swp
? stvJqKpr
Index: Makefile
===================================================================
RCS file: /sources/qemu/qemu/Makefile,v
retrieving revision 1.140
diff -u -p -r1.140 Makefile
--- Makefile 6 Jan 2008 18:27:12 -0000 1.140
+++ Makefile 13 Jan 2008 01:54:17 -0000
@@ -57,7 +57,7 @@ OBJS+=i2c.o smbus.o smbus_eeprom.o max73
OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o
OBJS+=scsi-disk.o cdrom.o
OBJS+=scsi-generic.o
-OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
+OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o
OBJS+=sd.o ssi-sd.o
ifdef CONFIG_WIN32
Index: vl.c
===================================================================
RCS file: /sources/qemu/qemu/vl.c,v
retrieving revision 1.395
diff -u -p -r1.395 vl.c
--- vl.c 8 Jan 2008 19:32:16 -0000 1.395
+++ vl.c 13 Jan 2008 01:54:21 -0000
@@ -2237,45 +2237,33 @@ static void tty_serial_init(int fd, int
#endif
tcgetattr (fd, &tty);
- switch(speed) {
- case 50:
+#define MARGIN 1.1
+ if (speed <= 50 * MARGIN)
spd = B50;
- break;
- case 75:
+ else if (speed <= 75 * MARGIN)
spd = B75;
- break;
- case 300:
+ else if (speed <= 300 * MARGIN)
spd = B300;
- break;
- case 600:
+ else if (speed <= 600 * MARGIN)
spd = B600;
- break;
- case 1200:
+ else if (speed <= 1200 * MARGIN)
spd = B1200;
- break;
- case 2400:
+ else if (speed <= 2400 * MARGIN)
spd = B2400;
- break;
- case 4800:
+ else if (speed <= 4800 * MARGIN)
spd = B4800;
- break;
- case 9600:
+ else if (speed <= 9600 * MARGIN)
spd = B9600;
- break;
- case 19200:
+ else if (speed <= 19200 * MARGIN)
spd = B19200;
- break;
- case 38400:
+ else if (speed <= 38400 * MARGIN)
spd = B38400;
- break;
- case 57600:
+ else if (speed <= 57600 * MARGIN)
spd = B57600;
- break;
- default:
- case 115200:
+ else if (speed <= 115200 * MARGIN)
+ spd = B115200;
+ else
spd = B115200;
- break;
- }
cfsetispeed(&tty, spd);
cfsetospeed(&tty, spd);
@@ -5196,6 +5184,8 @@ static int usb_device_add(const char *de
dev = usb_msd_init(p);
} else if (!strcmp(devname, "wacom-tablet")) {
dev = usb_wacom_init();
+ } else if (strstart(devname, "serial:", &p)) {
+ dev = usb_serial_init(p);
} else {
return -1;
}
Index: hw/usb-serial.c
===================================================================
RCS file: hw/usb-serial.c
diff -N hw/usb-serial.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ hw/usb-serial.c 13 Jan 2008 01:54:21 -0000
@@ -0,0 +1,549 @@
+/*
+ * FTDI FT232BM Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "qemu-char.h"
+
+//#define DEBUG_Serial
+
+#ifdef DEBUG_Serial
+#define DPRINTF(fmt, args...) \
+do { printf("usb-serial: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#define RECV_BUF 384
+#define SEND_BUF 128 // Not used for now
+
+/* Commands */
+#define FTDI_RESET 0
+#define FTDI_SET_MDM_CTRL 1
+#define FTDI_SET_FLOW_CTRL 2
+#define FTDI_SET_BAUD 3
+#define FTDI_SET_DATA 4
+#define FTDI_GET_MDM_ST 5
+#define FTDI_SET_EVENT_CHR 6
+#define FTDI_SET_ERROR_CHR 7
+#define FTDI_SET_LATENCY 9
+#define FTDI_GET_LATENCY 10
+
+#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<< 8)
+#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<< 8)
+
+/* RESET */
+
+#define FTDI_RESET_SIO 0
+#define FTDI_RESET_RX 1
+#define FTDI_RESET_TX 2
+
+/* SET_MDM_CTRL */
+
+#define FTDI_MDM_CTRL 3
+#define FTDI_DTR 1
+#define FTDI_RTS 2
+
+/* SET_FLOW_CTRL */
+
+#define FTDI_RTS_CTS_HS 1
+#define FTDI_DTR_DSR_HS 2
+#define FTDI_XON_XOFF_HS 4
+
+/* SET_DATA */
+
+#define FTDI_PARITY (0x7 << 8)
+#define FTDI_ODD (0x1 << 8)
+#define FTDI_EVEN (0x2 << 8)
+#define FTDI_MARK (0x3 << 8)
+#define FTDI_SPACE (0x4 << 8)
+
+#define FTDI_STOP (0x3 << 11)
+#define FTDI_STOP1 (0x0 << 11)
+#define FTDI_STOP15 (0x1 << 11)
+#define FTDI_STOP2 (0x2 << 11)
+
+/* GET_MDM_ST */
+/* TODO: should be sent every 40ms */
+#define FTDI_CTS (1<<4) // CTS line status
+#define FTDI_DSR (1<<5) // DSR line status
+#define FTDI_RI (1<<6) // RI line status
+#define FTDI_RLSD (1<<7) // Receive Line Signal Detect
+
+/* Status */
+
+#define FTDI_DR (1<<0) // Data Ready
+#define FTDI_OE (1<<1) // Overrun Err
+#define FTDI_PE (1<<2) // Parity Err
+#define FTDI_FE (1<<3) // Framing Err
+#define FTDI_BI (1<<4) // Break Interrupt
+#define FTDI_THRE (1<<5) // Transmitter Holding Register
+#define FTDI_TEMT (1<<6) // Transmitter Empty
+#define FTDI_FIFO (1<<7) // Error in FIFO
+
+typedef struct {
+ USBDevice dev;
+ uint16_t vendorid;
+ uint16_t productid;
+ uint8_t recv_buf[RECV_BUF];
+ uint8_t recv_ptr;
+ uint8_t recv_used;
+ uint8_t send_buf[SEND_BUF];
+ uint8_t event_chr;
+ uint8_t error_chr;
+ uint8_t event_trigger;
+ uint8_t lines;
+ QEMUSerialSetParams params;
+ int latency; /* ms */
+ CharDriverState *cs;
+} USBSerialState;
+
+static const uint8_t qemu_serial_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x00, 0x02, /* u16 bcdUSB; v2.0 */
+
+ 0x00, /* u8 bDeviceClass; */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ /* Vendor and product id are arbitrary. */
+ 0x03, 0x04, /* u16 idVendor; */
+ 0x00, 0xFF, /* u16 idProduct; */
+ 0x00, 0x04, /* u16 bcdDevice */
+
+ 0x01, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x03, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+static const uint8_t qemu_serial_config_descriptor[] = {
+
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x20, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x00, /* u8 iConfiguration; */
+ 0x80, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 100/2, /* u8 MaxPower; */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x02, /* u8 if_bNumEndpoints; */
+ 0xff, /* u8 if_bInterfaceClass; Vendor Specific */
+ 0xff, /* u8 if_bInterfaceSubClass; Vendor Specific */
+ 0xff, /* u8 if_bInterfaceProtocol; Vendor Specific */
+ 0x02, /* u8 if_iInterface; */
+
+ /* Bulk-In endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Bulk-Out endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00 /* u8 ep_bInterval; */
+};
+
+static void usb_serial_reset(USBSerialState *s)
+{
+ /* TODO: Set flow control to none */
+ s->event_chr = 0x0d;
+ s->event_trigger = 0;
+ s->recv_ptr = 0;
+ s->recv_used = 0;
+ /* TODO: purge in char driver */
+ s->lines &= ~(FTDI_DTR|FTDI_RTS);
+}
+
+static void usb_serial_handle_reset(USBDevice *dev)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+
+ DPRINTF("Reset\n");
+
+ usb_serial_reset(s);
+ /* TODO: Reset char device, send BREAK? */
+}
+
+static int usb_serial_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+ int ret = 0;
+
+ //DPRINTF("got control %x, value %x\n",request, value);
+ switch (request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (0 << 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_serial_dev_descriptor,
+ sizeof(qemu_serial_dev_descriptor));
+ data[8] = s->vendorid & 0xff;
+ data[9] = ((s->vendorid) >> 8) & 0xff;
+ data[10] = s->productid & 0xff;
+ data[11] = ((s->productid) >> 8) & 0xff;
+ ret = sizeof(qemu_serial_dev_descriptor);
+ break;
+ case USB_DT_CONFIG:
+ memcpy(data, qemu_serial_config_descriptor,
+ sizeof(qemu_serial_config_descriptor));
+ ret = sizeof(qemu_serial_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:
+ /* vendor description */
+ ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+ break;
+ case 2:
+ /* product description */
+ ret = set_usb_string(data, "QEMU USB SERIAL");
+ break;
+ case 3:
+ /* serial number */
+ ret = set_usb_string(data, "1");
+ 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 InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ ret = 0;
+ break;
+
+ /* Class specific requests. */
+ case DeviceOutVendor | FTDI_RESET:
+ switch (value) {
+ case FTDI_RESET_SIO:
+ usb_serial_reset(s);
+ break;
+ case FTDI_RESET_RX:
+ s->recv_ptr = 0;
+ s->recv_used = 0;
+ /* TODO: purge from char device */
+ break;
+ case FTDI_RESET_TX:
+ /* TODO: purge from char device */
+ break;
+ }
+ break;
+ case DeviceOutVendor | FTDI_SET_MDM_CTRL:
+ s->lines = value & FTDI_MDM_CTRL;
+ break;
+ case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
+ /* TODO: ioctl */
+ break;
+ case DeviceOutVendor | FTDI_SET_BAUD: {
+ static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
+ int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
+ | ((index & 1) << 2)];
+ int divisor = value & 0x3fff;
+
+ /* chip special cases */
+ if (divisor == 1 && subdivisor8 == 0)
+ subdivisor8 = 4;
+ if (divisor == 0 && subdivisor8 == 0)
+ divisor = 1;
+
+ s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
+ qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+ break;
+ }
+ case DeviceOutVendor | FTDI_SET_DATA:
+ switch (value & FTDI_PARITY) {
+ case 0:
+ s->params.parity = 'N';
+ break;
+ case FTDI_ODD:
+ s->params.parity = 'O';
+ break;
+ case FTDI_EVEN:
+ s->params.parity = 'E';
+ break;
+ default:
+ DPRINTF("unsupported parity %d\n", value & FTDI_PARITY);
+ goto fail;
+ }
+ switch (value & FTDI_STOP) {
+ case FTDI_STOP1:
+ s->params.stop_bits = 1;
+ break;
+ case FTDI_STOP2:
+ s->params.stop_bits = 2;
+ break;
+ default:
+ DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
+ goto fail;
+ }
+ qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+ /* TODO: TX ON/OFF */
+ break;
+ case DeviceInVendor | FTDI_GET_MDM_ST:
+ /* TODO: return modem status */
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutVendor | FTDI_SET_EVENT_CHR:
+ /* TODO: handle it */
+ s->event_chr = value;
+ break;
+ case DeviceOutVendor | FTDI_SET_ERROR_CHR:
+ /* TODO: handle it */
+ s->error_chr = value;
+ break;
+ case DeviceOutVendor | FTDI_SET_LATENCY:
+ s->latency = value;
+ break;
+ case DeviceInVendor | FTDI_GET_LATENCY:
+ data[0] = s->latency;
+ ret = 1;
+ break;
+ default:
+ fail:
+ DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+ int ret = 0;
+ uint8_t devep = p->devep;
+ uint8_t *data = p->data;
+ int len = p->len;
+ int first_len;
+
+ switch (p->pid) {
+ case USB_TOKEN_OUT:
+ if (devep != 2)
+ goto fail;
+ qemu_chr_write(s->cs, data, len);
+ break;
+
+ case USB_TOKEN_IN:
+ if (devep != 1)
+ goto fail;
+ first_len = RECV_BUF - s->recv_ptr;
+ if (len <= 2) {
+ ret = USB_RET_NAK;
+ break;
+ }
+ /* TODO: Report serial line status */
+ *data++ = 0;
+ *data++ = 0;
+ len -= 2;
+ if (len > s->recv_used)
+ len = s->recv_used;
+ if (!len) {
+ ret = USB_RET_NAK;
+ break;
+ }
+ if (first_len > len)
+ first_len = len;
+ memcpy(data, s->recv_buf + s->recv_ptr, first_len);
+ if (len > first_len)
+ memcpy(data + first_len, s->recv_buf, len - first_len);
+ s->recv_used -= len;
+ s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
+ ret = len + 2;
+ break;
+
+ default:
+ DPRINTF("Bad token\n");
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usb_serial_handle_destroy(USBDevice *dev)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+
+ qemu_chr_close(s->cs);
+ qemu_free(s);
+}
+
+int usb_serial_can_read(void *opaque)
+{
+ USBSerialState *s = opaque;
+ return RECV_BUF - s->recv_used;
+}
+
+void usb_serial_read(void *opaque, const uint8_t *buf, int size)
+{
+ USBSerialState *s = opaque;
+ int first_size = RECV_BUF - s->recv_ptr;
+ if (first_size > size)
+ first_size = size;
+ memcpy(s->recv_buf + s->recv_ptr, buf, first_size);
+ if (size > first_size)
+ memcpy(s->recv_buf, buf + first_size, size - first_size);
+ s->recv_used += size;
+}
+
+void usb_serial_event(void *opaque, int event)
+{
+ USBSerialState *s = opaque;
+
+ switch (event) {
+ case CHR_EVENT_BREAK:
+ /* TODO: Send Break to USB */
+ break;
+ case CHR_EVENT_FOCUS:
+ break;
+ case CHR_EVENT_RESET:
+ usb_serial_reset(s);
+ /* TODO: Reset USB port */
+ break;
+ }
+}
+
+USBDevice *usb_serial_init(const char *filename)
+{
+ USBSerialState *s;
+ CharDriverState *cdrv;
+ unsigned short vendorid = 0x0403, productid = 0xFF00;
+
+ while (*filename && *filename != ':') {
+ const char *p;
+ char *e;
+ if (strstart(filename, "vendorid=", &p)) {
+ vendorid = strtol(p, &e, 16);
+ if (e == p || (*e && *e != ',' && *e != ':')) {
+ printf("bogus vendor ID %s\n", p);
+ return NULL;
+ }
+ filename = e;
+ } else if (strstart(filename, "productid=", &p)) {
+ productid = strtol(p, &e, 16);
+ if (e == p || (*e && *e != ',' && *e != ':')) {
+ printf("bogus product ID %s\n", p);
+ return NULL;
+ }
+ filename = e;
+ } else {
+ printf("unrecognized serial USB option %s\n", filename);
+ return NULL;
+ }
+ while(*filename == ',')
+ filename++;
+ }
+ if (!*filename) {
+ printf("character device specification needed\n");
+ return NULL;
+ }
+ filename++;
+ s = qemu_mallocz(sizeof(USBSerialState));
+ if (!s)
+ return NULL;
+
+ cdrv = qemu_chr_open(filename);
+ if (!cdrv)
+ goto fail;
+ s->cs = cdrv;
+ qemu_chr_add_handlers(cdrv, usb_serial_can_read, usb_serial_read, usb_serial_event, s);
+
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_serial_handle_reset;
+ s->dev.handle_control = usb_serial_handle_control;
+ s->dev.handle_data = usb_serial_handle_data;
+ s->dev.handle_destroy = usb_serial_handle_destroy;
+
+ s->vendorid = vendorid;
+ s->productid = productid;
+
+ snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Serial(%.16s)",
+ filename);
+
+ usb_serial_handle_reset((USBDevice *)s);
+ return (USBDevice *)s;
+ fail:
+ qemu_free(s);
+ return NULL;
+}
Index: hw/usb.h
===================================================================
RCS file: /sources/qemu/qemu/hw/usb.h,v
retrieving revision 1.19
diff -u -p -r1.19 usb.h
--- hw/usb.h 17 Nov 2007 17:14:50 -0000 1.19
+++ hw/usb.h 13 Jan 2008 01:54:21 -0000
@@ -217,6 +217,9 @@ USBDevice *usb_msd_init(const char *file
/* usb-wacom.c */
USBDevice *usb_wacom_init(void);
+/* usb-serial.c */
+USBDevice *usb_serial_init(const char *filename);
+
/* usb ports of the VM */
void qemu_register_usb_port(USBPort *port, void *opaque, int index,
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Qemu-devel] Re: [PATCH] USB serial device
2008-01-13 1:55 ` [Qemu-devel] " Samuel Thibault
@ 2008-01-13 18:59 ` Samuel Thibault
2008-01-17 14:09 ` andrzej zaborowski
0 siblings, 1 reply; 10+ messages in thread
From: Samuel Thibault @ 2008-01-13 18:59 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1481 bytes --]
Hello,
Samuel Thibault, le Sun 13 Jan 2008 01:55:56 +0000, a écrit :
> Samuel Thibault, le Fri 11 Jan 2008 11:09:23 +0000, a écrit :
> > Samuel Thibault, le Fri 11 Jan 2008 00:23:12 +0000, a écrit :
> > > I would like to implement support for braille devices, and for this I'd
> > > need to first implement a USB serial device (FTDI chip). Has anybody
> > > worked on that already?
> >
> > Ok, was easier than expected, Here is a patch. The serial support is
> > incomplete however because qemu still lacks support for flow control and
> > modem lines.
> >
> > You will notice in tty_serial_init that I made the baud values more
> > relaxed. This is because with divisor/baud conversions, things never get
> > exact, so we need to be laxist with the value. For instance here with
> > FTDI, the base divisor is 48000000/2, so for 57600 bps the guest needs
> > to choose between divisors 416 and 417, which bring to either 57692bps
> > or 57553bps but not exactly 57600bps. It happens that Linux chooses
> > divisor 416, hence 57692bps. Of course, the higher the speed, the worse
> > things get. The 1.1 factor is the smallest factor I could find between
> > usual bps values, notably B110, B134 and B150.
>
> Here is an updated version, that takes parameters, so as to be able to
> notably provide the product ID.
There was a small bug, here is a fixed version.
I also have a braille device emulation patch which now works, but it
probably needs a bit more polishing.
Samuel
[-- Attachment #2: patch-qemu-usb-serial --]
[-- Type: text/plain, Size: 19999 bytes --]
Index: Makefile
===================================================================
RCS file: /sources/qemu/qemu/Makefile,v
retrieving revision 1.140
diff -u -p -r1.140 Makefile
--- Makefile 6 Jan 2008 18:27:12 -0000 1.140
+++ Makefile 13 Jan 2008 01:54:17 -0000
@@ -57,7 +57,7 @@ OBJS+=i2c.o smbus.o smbus_eeprom.o max73
OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o
OBJS+=scsi-disk.o cdrom.o
OBJS+=scsi-generic.o
-OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
+OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o
OBJS+=sd.o ssi-sd.o
ifdef CONFIG_WIN32
Index: vl.c
===================================================================
RCS file: /sources/qemu/qemu/vl.c,v
retrieving revision 1.395
diff -u -p -r1.395 vl.c
--- vl.c 8 Jan 2008 19:32:16 -0000 1.395
+++ vl.c 13 Jan 2008 01:54:21 -0000
@@ -2237,45 +2237,33 @@ static void tty_serial_init(int fd, int
#endif
tcgetattr (fd, &tty);
- switch(speed) {
- case 50:
+#define MARGIN 1.1
+ if (speed <= 50 * MARGIN)
spd = B50;
- break;
- case 75:
+ else if (speed <= 75 * MARGIN)
spd = B75;
- break;
- case 300:
+ else if (speed <= 300 * MARGIN)
spd = B300;
- break;
- case 600:
+ else if (speed <= 600 * MARGIN)
spd = B600;
- break;
- case 1200:
+ else if (speed <= 1200 * MARGIN)
spd = B1200;
- break;
- case 2400:
+ else if (speed <= 2400 * MARGIN)
spd = B2400;
- break;
- case 4800:
+ else if (speed <= 4800 * MARGIN)
spd = B4800;
- break;
- case 9600:
+ else if (speed <= 9600 * MARGIN)
spd = B9600;
- break;
- case 19200:
+ else if (speed <= 19200 * MARGIN)
spd = B19200;
- break;
- case 38400:
+ else if (speed <= 38400 * MARGIN)
spd = B38400;
- break;
- case 57600:
+ else if (speed <= 57600 * MARGIN)
spd = B57600;
- break;
- default:
- case 115200:
+ else if (speed <= 115200 * MARGIN)
+ spd = B115200;
+ else
spd = B115200;
- break;
- }
cfsetispeed(&tty, spd);
cfsetospeed(&tty, spd);
@@ -5196,6 +5184,8 @@ static int usb_device_add(const char *de
dev = usb_msd_init(p);
} else if (!strcmp(devname, "wacom-tablet")) {
dev = usb_wacom_init();
+ } else if (strstart(devname, "serial:", &p)) {
+ dev = usb_serial_init(p);
} else {
return -1;
}
Index: hw/usb-serial.c
===================================================================
RCS file: hw/usb-serial.c
diff -N hw/usb-serial.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ hw/usb-serial.c 13 Jan 2008 01:54:21 -0000
@@ -0,0 +1,549 @@
+/*
+ * FTDI FT232BM Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "qemu-char.h"
+
+//#define DEBUG_Serial
+
+#ifdef DEBUG_Serial
+#define DPRINTF(fmt, args...) \
+do { printf("usb-serial: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#define RECV_BUF 384
+#define SEND_BUF 128 // Not used for now
+
+/* Commands */
+#define FTDI_RESET 0
+#define FTDI_SET_MDM_CTRL 1
+#define FTDI_SET_FLOW_CTRL 2
+#define FTDI_SET_BAUD 3
+#define FTDI_SET_DATA 4
+#define FTDI_GET_MDM_ST 5
+#define FTDI_SET_EVENT_CHR 6
+#define FTDI_SET_ERROR_CHR 7
+#define FTDI_SET_LATENCY 9
+#define FTDI_GET_LATENCY 10
+
+#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<< 8)
+#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<< 8)
+
+/* RESET */
+
+#define FTDI_RESET_SIO 0
+#define FTDI_RESET_RX 1
+#define FTDI_RESET_TX 2
+
+/* SET_MDM_CTRL */
+
+#define FTDI_MDM_CTRL 3
+#define FTDI_DTR 1
+#define FTDI_RTS 2
+
+/* SET_FLOW_CTRL */
+
+#define FTDI_RTS_CTS_HS 1
+#define FTDI_DTR_DSR_HS 2
+#define FTDI_XON_XOFF_HS 4
+
+/* SET_DATA */
+
+#define FTDI_PARITY (0x7 << 8)
+#define FTDI_ODD (0x1 << 8)
+#define FTDI_EVEN (0x2 << 8)
+#define FTDI_MARK (0x3 << 8)
+#define FTDI_SPACE (0x4 << 8)
+
+#define FTDI_STOP (0x3 << 11)
+#define FTDI_STOP1 (0x0 << 11)
+#define FTDI_STOP15 (0x1 << 11)
+#define FTDI_STOP2 (0x2 << 11)
+
+/* GET_MDM_ST */
+/* TODO: should be sent every 40ms */
+#define FTDI_CTS (1<<4) // CTS line status
+#define FTDI_DSR (1<<5) // DSR line status
+#define FTDI_RI (1<<6) // RI line status
+#define FTDI_RLSD (1<<7) // Receive Line Signal Detect
+
+/* Status */
+
+#define FTDI_DR (1<<0) // Data Ready
+#define FTDI_OE (1<<1) // Overrun Err
+#define FTDI_PE (1<<2) // Parity Err
+#define FTDI_FE (1<<3) // Framing Err
+#define FTDI_BI (1<<4) // Break Interrupt
+#define FTDI_THRE (1<<5) // Transmitter Holding Register
+#define FTDI_TEMT (1<<6) // Transmitter Empty
+#define FTDI_FIFO (1<<7) // Error in FIFO
+
+typedef struct {
+ USBDevice dev;
+ uint16_t vendorid;
+ uint16_t productid;
+ uint8_t recv_buf[RECV_BUF];
+ uint8_t recv_ptr;
+ uint8_t recv_used;
+ uint8_t send_buf[SEND_BUF];
+ uint8_t event_chr;
+ uint8_t error_chr;
+ uint8_t event_trigger;
+ uint8_t lines;
+ QEMUSerialSetParams params;
+ int latency; /* ms */
+ CharDriverState *cs;
+} USBSerialState;
+
+static const uint8_t qemu_serial_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x00, 0x02, /* u16 bcdUSB; v2.0 */
+
+ 0x00, /* u8 bDeviceClass; */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ /* Vendor and product id are arbitrary. */
+ 0x03, 0x04, /* u16 idVendor; */
+ 0x00, 0xFF, /* u16 idProduct; */
+ 0x00, 0x04, /* u16 bcdDevice */
+
+ 0x01, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x03, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+static const uint8_t qemu_serial_config_descriptor[] = {
+
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x20, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x00, /* u8 iConfiguration; */
+ 0x80, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 100/2, /* u8 MaxPower; */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x02, /* u8 if_bNumEndpoints; */
+ 0xff, /* u8 if_bInterfaceClass; Vendor Specific */
+ 0xff, /* u8 if_bInterfaceSubClass; Vendor Specific */
+ 0xff, /* u8 if_bInterfaceProtocol; Vendor Specific */
+ 0x02, /* u8 if_iInterface; */
+
+ /* Bulk-In endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Bulk-Out endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00 /* u8 ep_bInterval; */
+};
+
+static void usb_serial_reset(USBSerialState *s)
+{
+ /* TODO: Set flow control to none */
+ s->event_chr = 0x0d;
+ s->event_trigger = 0;
+ s->recv_ptr = 0;
+ s->recv_used = 0;
+ /* TODO: purge in char driver */
+ s->lines &= ~(FTDI_DTR|FTDI_RTS);
+}
+
+static void usb_serial_handle_reset(USBDevice *dev)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+
+ DPRINTF("Reset\n");
+
+ usb_serial_reset(s);
+ /* TODO: Reset char device, send BREAK? */
+}
+
+static int usb_serial_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+ int ret = 0;
+
+ //DPRINTF("got control %x, value %x\n",request, value);
+ switch (request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (0 << 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_serial_dev_descriptor,
+ sizeof(qemu_serial_dev_descriptor));
+ data[8] = s->vendorid & 0xff;
+ data[9] = ((s->vendorid) >> 8) & 0xff;
+ data[10] = s->productid & 0xff;
+ data[11] = ((s->productid) >> 8) & 0xff;
+ ret = sizeof(qemu_serial_dev_descriptor);
+ break;
+ case USB_DT_CONFIG:
+ memcpy(data, qemu_serial_config_descriptor,
+ sizeof(qemu_serial_config_descriptor));
+ ret = sizeof(qemu_serial_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:
+ /* vendor description */
+ ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+ break;
+ case 2:
+ /* product description */
+ ret = set_usb_string(data, "QEMU USB SERIAL");
+ break;
+ case 3:
+ /* serial number */
+ ret = set_usb_string(data, "1");
+ 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 InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ ret = 0;
+ break;
+
+ /* Class specific requests. */
+ case DeviceOutVendor | FTDI_RESET:
+ switch (value) {
+ case FTDI_RESET_SIO:
+ usb_serial_reset(s);
+ break;
+ case FTDI_RESET_RX:
+ s->recv_ptr = 0;
+ s->recv_used = 0;
+ /* TODO: purge from char device */
+ break;
+ case FTDI_RESET_TX:
+ /* TODO: purge from char device */
+ break;
+ }
+ break;
+ case DeviceOutVendor | FTDI_SET_MDM_CTRL:
+ s->lines = value & FTDI_MDM_CTRL;
+ break;
+ case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
+ /* TODO: ioctl */
+ break;
+ case DeviceOutVendor | FTDI_SET_BAUD: {
+ static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
+ int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
+ | ((index & 1) << 2)];
+ int divisor = value & 0x3fff;
+
+ /* chip special cases */
+ if (divisor == 1 && subdivisor8 == 0)
+ subdivisor8 = 4;
+ if (divisor == 0 && subdivisor8 == 0)
+ divisor = 1;
+
+ s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
+ qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+ break;
+ }
+ case DeviceOutVendor | FTDI_SET_DATA:
+ switch (value & FTDI_PARITY) {
+ case 0:
+ s->params.parity = 'N';
+ break;
+ case FTDI_ODD:
+ s->params.parity = 'O';
+ break;
+ case FTDI_EVEN:
+ s->params.parity = 'E';
+ break;
+ default:
+ DPRINTF("unsupported parity %d\n", value & FTDI_PARITY);
+ goto fail;
+ }
+ switch (value & FTDI_STOP) {
+ case FTDI_STOP1:
+ s->params.stop_bits = 1;
+ break;
+ case FTDI_STOP2:
+ s->params.stop_bits = 2;
+ break;
+ default:
+ DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
+ goto fail;
+ }
+ qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+ /* TODO: TX ON/OFF */
+ break;
+ case DeviceInVendor | FTDI_GET_MDM_ST:
+ /* TODO: return modem status */
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutVendor | FTDI_SET_EVENT_CHR:
+ /* TODO: handle it */
+ s->event_chr = value;
+ break;
+ case DeviceOutVendor | FTDI_SET_ERROR_CHR:
+ /* TODO: handle it */
+ s->error_chr = value;
+ break;
+ case DeviceOutVendor | FTDI_SET_LATENCY:
+ s->latency = value;
+ break;
+ case DeviceInVendor | FTDI_GET_LATENCY:
+ data[0] = s->latency;
+ ret = 1;
+ break;
+ default:
+ fail:
+ DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+ int ret = 0;
+ uint8_t devep = p->devep;
+ uint8_t *data = p->data;
+ int len = p->len;
+ int first_len;
+
+ switch (p->pid) {
+ case USB_TOKEN_OUT:
+ if (devep != 2)
+ goto fail;
+ qemu_chr_write(s->cs, data, len);
+ break;
+
+ case USB_TOKEN_IN:
+ if (devep != 1)
+ goto fail;
+ first_len = RECV_BUF - s->recv_ptr;
+ if (len <= 2) {
+ ret = USB_RET_NAK;
+ break;
+ }
+ /* TODO: Report serial line status */
+ *data++ = 0;
+ *data++ = 0;
+ len -= 2;
+ if (len > s->recv_used)
+ len = s->recv_used;
+ if (!len) {
+ ret = USB_RET_NAK;
+ break;
+ }
+ if (first_len > len)
+ first_len = len;
+ memcpy(data, s->recv_buf + s->recv_ptr, first_len);
+ if (len > first_len)
+ memcpy(data + first_len, s->recv_buf, len - first_len);
+ s->recv_used -= len;
+ s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
+ ret = len + 2;
+ break;
+
+ default:
+ DPRINTF("Bad token\n");
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usb_serial_handle_destroy(USBDevice *dev)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+
+ qemu_chr_close(s->cs);
+ qemu_free(s);
+}
+
+int usb_serial_can_read(void *opaque)
+{
+ USBSerialState *s = opaque;
+ return RECV_BUF - s->recv_used;
+}
+
+void usb_serial_read(void *opaque, const uint8_t *buf, int size)
+{
+ USBSerialState *s = opaque;
+ int first_size = RECV_BUF - s->recv_ptr;
+ if (first_size > size)
+ first_size = size;
+ memcpy(s->recv_buf + s->recv_ptr + s->recv_used, buf, first_size);
+ if (size > first_size)
+ memcpy(s->recv_buf, buf + first_size, size - first_size);
+ s->recv_used += size;
+}
+
+void usb_serial_event(void *opaque, int event)
+{
+ USBSerialState *s = opaque;
+
+ switch (event) {
+ case CHR_EVENT_BREAK:
+ /* TODO: Send Break to USB */
+ break;
+ case CHR_EVENT_FOCUS:
+ break;
+ case CHR_EVENT_RESET:
+ usb_serial_reset(s);
+ /* TODO: Reset USB port */
+ break;
+ }
+}
+
+USBDevice *usb_serial_init(const char *filename)
+{
+ USBSerialState *s;
+ CharDriverState *cdrv;
+ unsigned short vendorid = 0x0403, productid = 0xFF00;
+
+ while (*filename && *filename != ':') {
+ const char *p;
+ char *e;
+ if (strstart(filename, "vendorid=", &p)) {
+ vendorid = strtol(p, &e, 16);
+ if (e == p || (*e && *e != ',' && *e != ':')) {
+ printf("bogus vendor ID %s\n", p);
+ return NULL;
+ }
+ filename = e;
+ } else if (strstart(filename, "productid=", &p)) {
+ productid = strtol(p, &e, 16);
+ if (e == p || (*e && *e != ',' && *e != ':')) {
+ printf("bogus product ID %s\n", p);
+ return NULL;
+ }
+ filename = e;
+ } else {
+ printf("unrecognized serial USB option %s\n", filename);
+ return NULL;
+ }
+ while(*filename == ',')
+ filename++;
+ }
+ if (!*filename) {
+ printf("character device specification needed\n");
+ return NULL;
+ }
+ filename++;
+ s = qemu_mallocz(sizeof(USBSerialState));
+ if (!s)
+ return NULL;
+
+ cdrv = qemu_chr_open(filename);
+ if (!cdrv)
+ goto fail;
+ s->cs = cdrv;
+ qemu_chr_add_handlers(cdrv, usb_serial_can_read, usb_serial_read, usb_serial_event, s);
+
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_serial_handle_reset;
+ s->dev.handle_control = usb_serial_handle_control;
+ s->dev.handle_data = usb_serial_handle_data;
+ s->dev.handle_destroy = usb_serial_handle_destroy;
+
+ s->vendorid = vendorid;
+ s->productid = productid;
+
+ snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Serial(%.16s)",
+ filename);
+
+ usb_serial_handle_reset((USBDevice *)s);
+ return (USBDevice *)s;
+ fail:
+ qemu_free(s);
+ return NULL;
+}
Index: hw/usb.h
===================================================================
RCS file: /sources/qemu/qemu/hw/usb.h,v
retrieving revision 1.19
diff -u -p -r1.19 usb.h
--- hw/usb.h 17 Nov 2007 17:14:50 -0000 1.19
+++ hw/usb.h 13 Jan 2008 01:54:21 -0000
@@ -217,6 +217,9 @@ USBDevice *usb_msd_init(const char *file
/* usb-wacom.c */
USBDevice *usb_wacom_init(void);
+/* usb-serial.c */
+USBDevice *usb_serial_init(const char *filename);
+
/* usb ports of the VM */
void qemu_register_usb_port(USBPort *port, void *opaque, int index,
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] USB serial device
2008-01-13 18:59 ` Samuel Thibault
@ 2008-01-17 14:09 ` andrzej zaborowski
2008-01-17 14:47 ` Samuel Thibault
0 siblings, 1 reply; 10+ messages in thread
From: andrzej zaborowski @ 2008-01-17 14:09 UTC (permalink / raw)
To: qemu-devel
On 13/01/2008, Samuel Thibault <samuel.thibault@eu.citrix.com> wrote:
> Samuel Thibault, le Sun 13 Jan 2008 01:55:56 +0000, a écrit :
> > Here is an updated version, that takes parameters, so as to be able to
> > notably provide the product ID.
Can you provide a patch to the .texi docs some time, to document these
parameters and an example usb_add line to get the dongle detected
under linux?
These parameters probably don't need to be specific to this device but
evidently nobody needed them yet with the other USB devices so it's
ok.
>
> There was a small bug, here is a fixed version.
> I also have a braille device emulation patch which now works, but it
> probably needs a bit more polishing.
Regards
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] USB serial device
2008-01-17 14:09 ` andrzej zaborowski
@ 2008-01-17 14:47 ` Samuel Thibault
2008-01-17 22:25 ` andrzej zaborowski
0 siblings, 1 reply; 10+ messages in thread
From: Samuel Thibault @ 2008-01-17 14:47 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 504 bytes --]
andrzej zaborowski, le Thu 17 Jan 2008 15:09:54 +0100, a écrit :
> On 13/01/2008, Samuel Thibault <samuel.thibault@eu.citrix.com> wrote:
> > Samuel Thibault, le Sun 13 Jan 2008 01:55:56 +0000, a écrit :
> > > Here is an updated version, that takes parameters, so as to be able to
> > > notably provide the product ID.
>
> Can you provide a patch to the .texi docs some time, to document these
> parameters and an example usb_add line to get the dongle detected
> under linux?
Sure, here it is.
Samuel
[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 1873 bytes --]
Index: qemu-doc.texi
===================================================================
RCS file: /sources/qemu/qemu/qemu-doc.texi,v
retrieving revision 1.179
diff -u -p -r1.179 qemu-doc.texi
--- qemu-doc.texi 14 Jan 2008 22:09:11 -0000 1.179
+++ qemu-doc.texi 17 Jan 2008 14:46:37 -0000
@@ -525,6 +525,10 @@ Pass through the host device identified
@item host:vendor_id:product_id
Pass through the host device identified by vendor_id:product_id (Linux only).
+@item serial:[vendorid=@var{vendor_id}][,productid=@var{product_id}]:@var{dev}
+Serial converter to host character device @var{dev}, see @code{-serial} for the
+available devices.
+
@end table
@end table
@@ -1562,7 +1566,7 @@ as necessary to connect multiple USB dev
USB devices can be connected with the @option{-usbdevice} commandline option
or the @code{usb_add} monitor command. Available devices are:
-@table @var
+@table @code
@item @code{mouse}
Virtual Mouse. This will override the PS/2 mouse emulation when activated.
@item @code{tablet}
@@ -1583,6 +1587,16 @@ above but it can be used with the tslib
coordinates it reports touch pressure.
@item @code{keyboard}
Standard USB keyboard. Will override the PS/2 keyboard (if present).
+@item @code{serial}:[vendorid=@var{vendor_id}][,product_id=@var{product_id}]:@var{dev}
+Serial converter. This emulates an FTDI FT232BM chip connected to host character
+device @var{dev}. The available character devices are the same as for the
+@code{-serial} option. The @code{vendorid} and @code{productid} options can be
+used to override the default 0403:FF00. For instance,
+@example
+usb_add serial:productid=FA00:tcp:192.168.0.2:4444
+@end example
+will connect to tcp port 4444 of ip 192.168.0.2, and plug that to the virtual
+serial converter, faking a Matrix Orbital LCD Display (USB ID 0403:FA00).
@end table
@node host_usb_devices
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] USB serial device
2008-01-17 14:47 ` Samuel Thibault
@ 2008-01-17 22:25 ` andrzej zaborowski
2008-01-17 22:44 ` Samuel Thibault
0 siblings, 1 reply; 10+ messages in thread
From: andrzej zaborowski @ 2008-01-17 22:25 UTC (permalink / raw)
To: qemu-devel
On 17/01/2008, Samuel Thibault <samuel.thibault@ens-lyon.org> wrote:
> andrzej zaborowski, le Thu 17 Jan 2008 15:09:54 +0100, a écrit :
> > On 13/01/2008, Samuel Thibault <samuel.thibault@eu.citrix.com> wrote:
> > > Samuel Thibault, le Sun 13 Jan 2008 01:55:56 +0000, a écrit :
> > > > Here is an updated version, that takes parameters, so as to be able to
> > > > notably provide the product ID.
> >
> > Can you provide a patch to the .texi docs some time, to document these
> > parameters and an example usb_add line to get the dongle detected
> > under linux?
>
> Sure, here it is.
Thanks, committed although I hoped for something that lets easily test
that the adapter works, e.g. so that after a usb_add serial:...:stdio
you can do cat /dev/ttyXXXN in the guest and see what's being typed on
qemu's stdin. I got the device detected without problems but Linux
didn't create any new serial ports.
Regards
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] USB serial device
2008-01-17 22:25 ` andrzej zaborowski
@ 2008-01-17 22:44 ` Samuel Thibault
2008-01-19 13:05 ` andrzej zaborowski
0 siblings, 1 reply; 10+ messages in thread
From: Samuel Thibault @ 2008-01-17 22:44 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 623 bytes --]
andrzej zaborowski, le Thu 17 Jan 2008 23:25:04 +0100, a écrit :
> Thanks, committed although I hoped for something that lets easily test
> that the adapter works, e.g. so that after a usb_add serial:...:stdio
> you can do cat /dev/ttyXXXN in the guest and see what's being typed on
> qemu's stdin. I got the device detected without problems but Linux
> didn't create any new serial ports.
Oh? That's odd, I've no problem here, /dev/ttyUSB0 appears like a charm
and cat & echo work fine. We should probably use another product ID by
default, which would be more widely automatically recognized. Here is a
patch.
Samuel
[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 1372 bytes --]
Index: hw/usb-serial.c
===================================================================
RCS file: /sources/qemu/qemu/hw/usb-serial.c,v
retrieving revision 1.1
diff -u -p -r1.1 usb-serial.c
--- hw/usb-serial.c 14 Jan 2008 03:41:02 -0000 1.1
+++ hw/usb-serial.c 17 Jan 2008 22:44:19 -0000
@@ -486,7 +486,7 @@ USBDevice *usb_serial_init(const char *f
{
USBSerialState *s;
CharDriverState *cdrv;
- unsigned short vendorid = 0x0403, productid = 0xFF00;
+ unsigned short vendorid = 0x0403, productid = 0x6001;
while (*filename && *filename != ':') {
const char *p;
Index: qemu-doc.texi
===================================================================
RCS file: /sources/qemu/qemu/qemu-doc.texi,v
retrieving revision 1.181
diff -u -p -r1.181 qemu-doc.texi
--- qemu-doc.texi 17 Jan 2008 22:22:45 -0000 1.181
+++ qemu-doc.texi 17 Jan 2008 22:44:20 -0000
@@ -1594,7 +1594,7 @@ Standard USB keyboard. Will override th
Serial converter. This emulates an FTDI FT232BM chip connected to host character
device @var{dev}. The available character devices are the same as for the
@code{-serial} option. The @code{vendorid} and @code{productid} options can be
-used to override the default 0403:FF00. For instance,
+used to override the default 0403:6001. For instance,
@example
usb_add serial:productid=FA00:tcp:192.168.0.2:4444
@end example
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] USB serial device
2008-01-17 22:44 ` Samuel Thibault
@ 2008-01-19 13:05 ` andrzej zaborowski
2008-01-19 21:26 ` Samuel Thibault
0 siblings, 1 reply; 10+ messages in thread
From: andrzej zaborowski @ 2008-01-19 13:05 UTC (permalink / raw)
To: qemu-devel
Hi,
On 17/01/2008, Samuel Thibault <samuel.thibault@ens-lyon.org> wrote:
> andrzej zaborowski, le Thu 17 Jan 2008 23:25:04 +0100, a écrit :
> > Thanks, committed although I hoped for something that lets easily test
> > that the adapter works, e.g. so that after a usb_add serial:...:stdio
> > you can do cat /dev/ttyXXXN in the guest and see what's being typed on
> > qemu's stdin. I got the device detected without problems but Linux
> > didn't create any new serial ports.
Sorry, that was a user error (mine), I had been using the generic
usb-serial.ko driver instead of the FTDI specific one. Now it indeed
works like a charm.
>
> Oh? That's odd, I've no problem here, /dev/ttyUSB0 appears like a charm
> and cat & echo work fine. We should probably use another product ID by
> default, which would be more widely automatically recognized. Here is a
> patch.
Thanks, I applied the patch even though the old values worked ok too.
If they were better for some reason please tell. I also added a
chr_close callback for -serial stdio so that I can do usb_add
serial::stdio, then usb_del and usb_add serial::stdio again without
getting an error on opening "stdio".
Cheers
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] USB serial device
2008-01-19 13:05 ` andrzej zaborowski
@ 2008-01-19 21:26 ` Samuel Thibault
0 siblings, 0 replies; 10+ messages in thread
From: Samuel Thibault @ 2008-01-19 21:26 UTC (permalink / raw)
To: qemu-devel
andrzej zaborowski, le Sat 19 Jan 2008 14:05:20 +0100, a écrit :
> I applied the patch even though the old values worked ok too.
The old values weren't in linux 2.6.12 for instance, and it looks like
the new ones are the default ones for the FTDI builderr, so it should be
fine now.
> I also added a chr_close callback for -serial stdio so that I can do
> usb_add serial::stdio, then usb_del and usb_add serial::stdio again
> without getting an error on opening "stdio".
Good, thanks!
Samuel
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2008-01-19 21:26 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-01-11 0:23 [Qemu-devel] USB serial device Samuel Thibault
2008-01-11 11:09 ` [Qemu-devel] [PATCH] " Samuel Thibault
2008-01-13 1:55 ` [Qemu-devel] " Samuel Thibault
2008-01-13 18:59 ` Samuel Thibault
2008-01-17 14:09 ` andrzej zaborowski
2008-01-17 14:47 ` Samuel Thibault
2008-01-17 22:25 ` andrzej zaborowski
2008-01-17 22:44 ` Samuel Thibault
2008-01-19 13:05 ` andrzej zaborowski
2008-01-19 21:26 ` Samuel Thibault
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).