Commit a795421883395256c09e48829898bb3875866f30

Authored by balrog
1 parent fd56059f

USB-to-serial device (Samuel Thibault).


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3914 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile
... ... @@ -57,7 +57,7 @@ OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o
57 57 OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o
58 58 OBJS+=scsi-disk.o cdrom.o
59 59 OBJS+=scsi-generic.o
60   -OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
  60 +OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o
61 61 OBJS+=sd.o ssi-sd.o
62 62  
63 63 ifdef CONFIG_WIN32
... ...
hw/usb-serial.c 0 → 100644
  1 +/*
  2 + * FTDI FT232BM Device emulation
  3 + *
  4 + * Copyright (c) 2006 CodeSourcery.
  5 + * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
  6 + * Written by Paul Brook, reused for FTDI by Samuel Thibault
  7 + *
  8 + * This code is licenced under the LGPL.
  9 + */
  10 +
  11 +#include "qemu-common.h"
  12 +#include "usb.h"
  13 +#include "qemu-char.h"
  14 +
  15 +//#define DEBUG_Serial
  16 +
  17 +#ifdef DEBUG_Serial
  18 +#define DPRINTF(fmt, args...) \
  19 +do { printf("usb-serial: " fmt , ##args); } while (0)
  20 +#else
  21 +#define DPRINTF(fmt, args...) do {} while(0)
  22 +#endif
  23 +
  24 +#define RECV_BUF 384
  25 +#define SEND_BUF 128 // Not used for now
  26 +
  27 +/* Commands */
  28 +#define FTDI_RESET 0
  29 +#define FTDI_SET_MDM_CTRL 1
  30 +#define FTDI_SET_FLOW_CTRL 2
  31 +#define FTDI_SET_BAUD 3
  32 +#define FTDI_SET_DATA 4
  33 +#define FTDI_GET_MDM_ST 5
  34 +#define FTDI_SET_EVENT_CHR 6
  35 +#define FTDI_SET_ERROR_CHR 7
  36 +#define FTDI_SET_LATENCY 9
  37 +#define FTDI_GET_LATENCY 10
  38 +
  39 +#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
  40 +#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
  41 +
  42 +/* RESET */
  43 +
  44 +#define FTDI_RESET_SIO 0
  45 +#define FTDI_RESET_RX 1
  46 +#define FTDI_RESET_TX 2
  47 +
  48 +/* SET_MDM_CTRL */
  49 +
  50 +#define FTDI_MDM_CTRL 3
  51 +#define FTDI_DTR 1
  52 +#define FTDI_RTS 2
  53 +
  54 +/* SET_FLOW_CTRL */
  55 +
  56 +#define FTDI_RTS_CTS_HS 1
  57 +#define FTDI_DTR_DSR_HS 2
  58 +#define FTDI_XON_XOFF_HS 4
  59 +
  60 +/* SET_DATA */
  61 +
  62 +#define FTDI_PARITY (0x7 << 8)
  63 +#define FTDI_ODD (0x1 << 8)
  64 +#define FTDI_EVEN (0x2 << 8)
  65 +#define FTDI_MARK (0x3 << 8)
  66 +#define FTDI_SPACE (0x4 << 8)
  67 +
  68 +#define FTDI_STOP (0x3 << 11)
  69 +#define FTDI_STOP1 (0x0 << 11)
  70 +#define FTDI_STOP15 (0x1 << 11)
  71 +#define FTDI_STOP2 (0x2 << 11)
  72 +
  73 +/* GET_MDM_ST */
  74 +/* TODO: should be sent every 40ms */
  75 +#define FTDI_CTS (1<<4) // CTS line status
  76 +#define FTDI_DSR (1<<5) // DSR line status
  77 +#define FTDI_RI (1<<6) // RI line status
  78 +#define FTDI_RLSD (1<<7) // Receive Line Signal Detect
  79 +
  80 +/* Status */
  81 +
  82 +#define FTDI_DR (1<<0) // Data Ready
  83 +#define FTDI_OE (1<<1) // Overrun Err
  84 +#define FTDI_PE (1<<2) // Parity Err
  85 +#define FTDI_FE (1<<3) // Framing Err
  86 +#define FTDI_BI (1<<4) // Break Interrupt
  87 +#define FTDI_THRE (1<<5) // Transmitter Holding Register
  88 +#define FTDI_TEMT (1<<6) // Transmitter Empty
  89 +#define FTDI_FIFO (1<<7) // Error in FIFO
  90 +
  91 +typedef struct {
  92 + USBDevice dev;
  93 + uint16_t vendorid;
  94 + uint16_t productid;
  95 + uint8_t recv_buf[RECV_BUF];
  96 + uint8_t recv_ptr;
  97 + uint8_t recv_used;
  98 + uint8_t send_buf[SEND_BUF];
  99 + uint8_t event_chr;
  100 + uint8_t error_chr;
  101 + uint8_t event_trigger;
  102 + uint8_t lines;
  103 + QEMUSerialSetParams params;
  104 + int latency; /* ms */
  105 + CharDriverState *cs;
  106 +} USBSerialState;
  107 +
  108 +static const uint8_t qemu_serial_dev_descriptor[] = {
  109 + 0x12, /* u8 bLength; */
  110 + 0x01, /* u8 bDescriptorType; Device */
  111 + 0x00, 0x02, /* u16 bcdUSB; v2.0 */
  112 +
  113 + 0x00, /* u8 bDeviceClass; */
  114 + 0x00, /* u8 bDeviceSubClass; */
  115 + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
  116 + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
  117 +
  118 + /* Vendor and product id are arbitrary. */
  119 + 0x03, 0x04, /* u16 idVendor; */
  120 + 0x00, 0xFF, /* u16 idProduct; */
  121 + 0x00, 0x04, /* u16 bcdDevice */
  122 +
  123 + 0x01, /* u8 iManufacturer; */
  124 + 0x02, /* u8 iProduct; */
  125 + 0x03, /* u8 iSerialNumber; */
  126 + 0x01 /* u8 bNumConfigurations; */
  127 +};
  128 +
  129 +static const uint8_t qemu_serial_config_descriptor[] = {
  130 +
  131 + /* one configuration */
  132 + 0x09, /* u8 bLength; */
  133 + 0x02, /* u8 bDescriptorType; Configuration */
  134 + 0x20, 0x00, /* u16 wTotalLength; */
  135 + 0x01, /* u8 bNumInterfaces; (1) */
  136 + 0x01, /* u8 bConfigurationValue; */
  137 + 0x00, /* u8 iConfiguration; */
  138 + 0x80, /* u8 bmAttributes;
  139 + Bit 7: must be set,
  140 + 6: Self-powered,
  141 + 5: Remote wakeup,
  142 + 4..0: resvd */
  143 + 100/2, /* u8 MaxPower; */
  144 +
  145 + /* one interface */
  146 + 0x09, /* u8 if_bLength; */
  147 + 0x04, /* u8 if_bDescriptorType; Interface */
  148 + 0x00, /* u8 if_bInterfaceNumber; */
  149 + 0x00, /* u8 if_bAlternateSetting; */
  150 + 0x02, /* u8 if_bNumEndpoints; */
  151 + 0xff, /* u8 if_bInterfaceClass; Vendor Specific */
  152 + 0xff, /* u8 if_bInterfaceSubClass; Vendor Specific */
  153 + 0xff, /* u8 if_bInterfaceProtocol; Vendor Specific */
  154 + 0x02, /* u8 if_iInterface; */
  155 +
  156 + /* Bulk-In endpoint */
  157 + 0x07, /* u8 ep_bLength; */
  158 + 0x05, /* u8 ep_bDescriptorType; Endpoint */
  159 + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
  160 + 0x02, /* u8 ep_bmAttributes; Bulk */
  161 + 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
  162 + 0x00, /* u8 ep_bInterval; */
  163 +
  164 + /* Bulk-Out endpoint */
  165 + 0x07, /* u8 ep_bLength; */
  166 + 0x05, /* u8 ep_bDescriptorType; Endpoint */
  167 + 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
  168 + 0x02, /* u8 ep_bmAttributes; Bulk */
  169 + 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
  170 + 0x00 /* u8 ep_bInterval; */
  171 +};
  172 +
  173 +static void usb_serial_reset(USBSerialState *s)
  174 +{
  175 + /* TODO: Set flow control to none */
  176 + s->event_chr = 0x0d;
  177 + s->event_trigger = 0;
  178 + s->recv_ptr = 0;
  179 + s->recv_used = 0;
  180 + /* TODO: purge in char driver */
  181 + s->lines &= ~(FTDI_DTR|FTDI_RTS);
  182 +}
  183 +
  184 +static void usb_serial_handle_reset(USBDevice *dev)
  185 +{
  186 + USBSerialState *s = (USBSerialState *)dev;
  187 +
  188 + DPRINTF("Reset\n");
  189 +
  190 + usb_serial_reset(s);
  191 + /* TODO: Reset char device, send BREAK? */
  192 +}
  193 +
  194 +static int usb_serial_handle_control(USBDevice *dev, int request, int value,
  195 + int index, int length, uint8_t *data)
  196 +{
  197 + USBSerialState *s = (USBSerialState *)dev;
  198 + int ret = 0;
  199 +
  200 + //DPRINTF("got control %x, value %x\n",request, value);
  201 + switch (request) {
  202 + case DeviceRequest | USB_REQ_GET_STATUS:
  203 + data[0] = (0 << USB_DEVICE_SELF_POWERED) |
  204 + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
  205 + data[1] = 0x00;
  206 + ret = 2;
  207 + break;
  208 + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
  209 + if (value == USB_DEVICE_REMOTE_WAKEUP) {
  210 + dev->remote_wakeup = 0;
  211 + } else {
  212 + goto fail;
  213 + }
  214 + ret = 0;
  215 + break;
  216 + case DeviceOutRequest | USB_REQ_SET_FEATURE:
  217 + if (value == USB_DEVICE_REMOTE_WAKEUP) {
  218 + dev->remote_wakeup = 1;
  219 + } else {
  220 + goto fail;
  221 + }
  222 + ret = 0;
  223 + break;
  224 + case DeviceOutRequest | USB_REQ_SET_ADDRESS:
  225 + dev->addr = value;
  226 + ret = 0;
  227 + break;
  228 + case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
  229 + switch(value >> 8) {
  230 + case USB_DT_DEVICE:
  231 + memcpy(data, qemu_serial_dev_descriptor,
  232 + sizeof(qemu_serial_dev_descriptor));
  233 + data[8] = s->vendorid & 0xff;
  234 + data[9] = ((s->vendorid) >> 8) & 0xff;
  235 + data[10] = s->productid & 0xff;
  236 + data[11] = ((s->productid) >> 8) & 0xff;
  237 + ret = sizeof(qemu_serial_dev_descriptor);
  238 + break;
  239 + case USB_DT_CONFIG:
  240 + memcpy(data, qemu_serial_config_descriptor,
  241 + sizeof(qemu_serial_config_descriptor));
  242 + ret = sizeof(qemu_serial_config_descriptor);
  243 + break;
  244 + case USB_DT_STRING:
  245 + switch(value & 0xff) {
  246 + case 0:
  247 + /* language ids */
  248 + data[0] = 4;
  249 + data[1] = 3;
  250 + data[2] = 0x09;
  251 + data[3] = 0x04;
  252 + ret = 4;
  253 + break;
  254 + case 1:
  255 + /* vendor description */
  256 + ret = set_usb_string(data, "QEMU " QEMU_VERSION);
  257 + break;
  258 + case 2:
  259 + /* product description */
  260 + ret = set_usb_string(data, "QEMU USB SERIAL");
  261 + break;
  262 + case 3:
  263 + /* serial number */
  264 + ret = set_usb_string(data, "1");
  265 + break;
  266 + default:
  267 + goto fail;
  268 + }
  269 + break;
  270 + default:
  271 + goto fail;
  272 + }
  273 + break;
  274 + case DeviceRequest | USB_REQ_GET_CONFIGURATION:
  275 + data[0] = 1;
  276 + ret = 1;
  277 + break;
  278 + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
  279 + ret = 0;
  280 + break;
  281 + case DeviceRequest | USB_REQ_GET_INTERFACE:
  282 + data[0] = 0;
  283 + ret = 1;
  284 + break;
  285 + case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
  286 + ret = 0;
  287 + break;
  288 + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
  289 + ret = 0;
  290 + break;
  291 +
  292 + /* Class specific requests. */
  293 + case DeviceOutVendor | FTDI_RESET:
  294 + switch (value) {
  295 + case FTDI_RESET_SIO:
  296 + usb_serial_reset(s);
  297 + break;
  298 + case FTDI_RESET_RX:
  299 + s->recv_ptr = 0;
  300 + s->recv_used = 0;
  301 + /* TODO: purge from char device */
  302 + break;
  303 + case FTDI_RESET_TX:
  304 + /* TODO: purge from char device */
  305 + break;
  306 + }
  307 + break;
  308 + case DeviceOutVendor | FTDI_SET_MDM_CTRL:
  309 + s->lines = value & FTDI_MDM_CTRL;
  310 + break;
  311 + case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
  312 + /* TODO: ioctl */
  313 + break;
  314 + case DeviceOutVendor | FTDI_SET_BAUD: {
  315 + static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
  316 + int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
  317 + | ((index & 1) << 2)];
  318 + int divisor = value & 0x3fff;
  319 +
  320 + /* chip special cases */
  321 + if (divisor == 1 && subdivisor8 == 0)
  322 + subdivisor8 = 4;
  323 + if (divisor == 0 && subdivisor8 == 0)
  324 + divisor = 1;
  325 +
  326 + s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
  327 + qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
  328 + break;
  329 + }
  330 + case DeviceOutVendor | FTDI_SET_DATA:
  331 + switch (value & FTDI_PARITY) {
  332 + case 0:
  333 + s->params.parity = 'N';
  334 + break;
  335 + case FTDI_ODD:
  336 + s->params.parity = 'O';
  337 + break;
  338 + case FTDI_EVEN:
  339 + s->params.parity = 'E';
  340 + break;
  341 + default:
  342 + DPRINTF("unsupported parity %d\n", value & FTDI_PARITY);
  343 + goto fail;
  344 + }
  345 + switch (value & FTDI_STOP) {
  346 + case FTDI_STOP1:
  347 + s->params.stop_bits = 1;
  348 + break;
  349 + case FTDI_STOP2:
  350 + s->params.stop_bits = 2;
  351 + break;
  352 + default:
  353 + DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
  354 + goto fail;
  355 + }
  356 + qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
  357 + /* TODO: TX ON/OFF */
  358 + break;
  359 + case DeviceInVendor | FTDI_GET_MDM_ST:
  360 + /* TODO: return modem status */
  361 + data[0] = 0;
  362 + ret = 1;
  363 + break;
  364 + case DeviceOutVendor | FTDI_SET_EVENT_CHR:
  365 + /* TODO: handle it */
  366 + s->event_chr = value;
  367 + break;
  368 + case DeviceOutVendor | FTDI_SET_ERROR_CHR:
  369 + /* TODO: handle it */
  370 + s->error_chr = value;
  371 + break;
  372 + case DeviceOutVendor | FTDI_SET_LATENCY:
  373 + s->latency = value;
  374 + break;
  375 + case DeviceInVendor | FTDI_GET_LATENCY:
  376 + data[0] = s->latency;
  377 + ret = 1;
  378 + break;
  379 + default:
  380 + fail:
  381 + DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
  382 + ret = USB_RET_STALL;
  383 + break;
  384 + }
  385 + return ret;
  386 +}
  387 +
  388 +static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
  389 +{
  390 + USBSerialState *s = (USBSerialState *)dev;
  391 + int ret = 0;
  392 + uint8_t devep = p->devep;
  393 + uint8_t *data = p->data;
  394 + int len = p->len;
  395 + int first_len;
  396 +
  397 + switch (p->pid) {
  398 + case USB_TOKEN_OUT:
  399 + if (devep != 2)
  400 + goto fail;
  401 + qemu_chr_write(s->cs, data, len);
  402 + break;
  403 +
  404 + case USB_TOKEN_IN:
  405 + if (devep != 1)
  406 + goto fail;
  407 + first_len = RECV_BUF - s->recv_ptr;
  408 + if (len <= 2) {
  409 + ret = USB_RET_NAK;
  410 + break;
  411 + }
  412 + /* TODO: Report serial line status */
  413 + *data++ = 0;
  414 + *data++ = 0;
  415 + len -= 2;
  416 + if (len > s->recv_used)
  417 + len = s->recv_used;
  418 + if (!len) {
  419 + ret = USB_RET_NAK;
  420 + break;
  421 + }
  422 + if (first_len > len)
  423 + first_len = len;
  424 + memcpy(data, s->recv_buf + s->recv_ptr, first_len);
  425 + if (len > first_len)
  426 + memcpy(data + first_len, s->recv_buf, len - first_len);
  427 + s->recv_used -= len;
  428 + s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
  429 + ret = len + 2;
  430 + break;
  431 +
  432 + default:
  433 + DPRINTF("Bad token\n");
  434 + fail:
  435 + ret = USB_RET_STALL;
  436 + break;
  437 + }
  438 +
  439 + return ret;
  440 +}
  441 +
  442 +static void usb_serial_handle_destroy(USBDevice *dev)
  443 +{
  444 + USBSerialState *s = (USBSerialState *)dev;
  445 +
  446 + qemu_chr_close(s->cs);
  447 + qemu_free(s);
  448 +}
  449 +
  450 +int usb_serial_can_read(void *opaque)
  451 +{
  452 + USBSerialState *s = opaque;
  453 + return RECV_BUF - s->recv_used;
  454 +}
  455 +
  456 +void usb_serial_read(void *opaque, const uint8_t *buf, int size)
  457 +{
  458 + USBSerialState *s = opaque;
  459 + int first_size = RECV_BUF - s->recv_ptr;
  460 + if (first_size > size)
  461 + first_size = size;
  462 + memcpy(s->recv_buf + s->recv_ptr + s->recv_used, buf, first_size);
  463 + if (size > first_size)
  464 + memcpy(s->recv_buf, buf + first_size, size - first_size);
  465 + s->recv_used += size;
  466 +}
  467 +
  468 +void usb_serial_event(void *opaque, int event)
  469 +{
  470 + USBSerialState *s = opaque;
  471 +
  472 + switch (event) {
  473 + case CHR_EVENT_BREAK:
  474 + /* TODO: Send Break to USB */
  475 + break;
  476 + case CHR_EVENT_FOCUS:
  477 + break;
  478 + case CHR_EVENT_RESET:
  479 + usb_serial_reset(s);
  480 + /* TODO: Reset USB port */
  481 + break;
  482 + }
  483 +}
  484 +
  485 +USBDevice *usb_serial_init(const char *filename)
  486 +{
  487 + USBSerialState *s;
  488 + CharDriverState *cdrv;
  489 + unsigned short vendorid = 0x0403, productid = 0xFF00;
  490 +
  491 + while (*filename && *filename != ':') {
  492 + const char *p;
  493 + char *e;
  494 + if (strstart(filename, "vendorid=", &p)) {
  495 + vendorid = strtol(p, &e, 16);
  496 + if (e == p || (*e && *e != ',' && *e != ':')) {
  497 + printf("bogus vendor ID %s\n", p);
  498 + return NULL;
  499 + }
  500 + filename = e;
  501 + } else if (strstart(filename, "productid=", &p)) {
  502 + productid = strtol(p, &e, 16);
  503 + if (e == p || (*e && *e != ',' && *e != ':')) {
  504 + printf("bogus product ID %s\n", p);
  505 + return NULL;
  506 + }
  507 + filename = e;
  508 + } else {
  509 + printf("unrecognized serial USB option %s\n", filename);
  510 + return NULL;
  511 + }
  512 + while(*filename == ',')
  513 + filename++;
  514 + }
  515 + if (!*filename) {
  516 + printf("character device specification needed\n");
  517 + return NULL;
  518 + }
  519 + filename++;
  520 + s = qemu_mallocz(sizeof(USBSerialState));
  521 + if (!s)
  522 + return NULL;
  523 +
  524 + cdrv = qemu_chr_open(filename);
  525 + if (!cdrv)
  526 + goto fail;
  527 + s->cs = cdrv;
  528 + qemu_chr_add_handlers(cdrv, usb_serial_can_read, usb_serial_read, usb_serial_event, s);
  529 +
  530 + s->dev.speed = USB_SPEED_FULL;
  531 + s->dev.handle_packet = usb_generic_handle_packet;
  532 +
  533 + s->dev.handle_reset = usb_serial_handle_reset;
  534 + s->dev.handle_control = usb_serial_handle_control;
  535 + s->dev.handle_data = usb_serial_handle_data;
  536 + s->dev.handle_destroy = usb_serial_handle_destroy;
  537 +
  538 + s->vendorid = vendorid;
  539 + s->productid = productid;
  540 +
  541 + snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Serial(%.16s)",
  542 + filename);
  543 +
  544 + usb_serial_handle_reset((USBDevice *)s);
  545 + return (USBDevice *)s;
  546 + fail:
  547 + qemu_free(s);
  548 + return NULL;
  549 +}
... ...
hw/usb.h
... ... @@ -217,6 +217,9 @@ USBDevice *usb_msd_init(const char *filename);
217 217 /* usb-wacom.c */
218 218 USBDevice *usb_wacom_init(void);
219 219  
  220 +/* usb-serial.c */
  221 +USBDevice *usb_serial_init(const char *filename);
  222 +
220 223 /* usb ports of the VM */
221 224  
222 225 void qemu_register_usb_port(USBPort *port, void *opaque, int index,
... ...
... ... @@ -2240,45 +2240,33 @@ static void tty_serial_init(int fd, int speed,
2240 2240 #endif
2241 2241 tcgetattr (fd, &tty);
2242 2242  
2243   - switch(speed) {
2244   - case 50:
  2243 +#define MARGIN 1.1
  2244 + if (speed <= 50 * MARGIN)
2245 2245 spd = B50;
2246   - break;
2247   - case 75:
  2246 + else if (speed <= 75 * MARGIN)
2248 2247 spd = B75;
2249   - break;
2250   - case 300:
  2248 + else if (speed <= 300 * MARGIN)
2251 2249 spd = B300;
2252   - break;
2253   - case 600:
  2250 + else if (speed <= 600 * MARGIN)
2254 2251 spd = B600;
2255   - break;
2256   - case 1200:
  2252 + else if (speed <= 1200 * MARGIN)
2257 2253 spd = B1200;
2258   - break;
2259   - case 2400:
  2254 + else if (speed <= 2400 * MARGIN)
2260 2255 spd = B2400;
2261   - break;
2262   - case 4800:
  2256 + else if (speed <= 4800 * MARGIN)
2263 2257 spd = B4800;
2264   - break;
2265   - case 9600:
  2258 + else if (speed <= 9600 * MARGIN)
2266 2259 spd = B9600;
2267   - break;
2268   - case 19200:
  2260 + else if (speed <= 19200 * MARGIN)
2269 2261 spd = B19200;
2270   - break;
2271   - case 38400:
  2262 + else if (speed <= 38400 * MARGIN)
2272 2263 spd = B38400;
2273   - break;
2274   - case 57600:
  2264 + else if (speed <= 57600 * MARGIN)
2275 2265 spd = B57600;
2276   - break;
2277   - default:
2278   - case 115200:
  2266 + else if (speed <= 115200 * MARGIN)
  2267 + spd = B115200;
  2268 + else
2279 2269 spd = B115200;
2280   - break;
2281   - }
2282 2270  
2283 2271 cfsetispeed(&tty, spd);
2284 2272 cfsetospeed(&tty, spd);
... ... @@ -5223,6 +5211,8 @@ static int usb_device_add(const char *devname)
5223 5211 dev = usb_msd_init(p);
5224 5212 } else if (!strcmp(devname, "wacom-tablet")) {
5225 5213 dev = usb_wacom_init();
  5214 + } else if (strstart(devname, "serial:", &p)) {
  5215 + dev = usb_serial_init(p);
5226 5216 } else {
5227 5217 return -1;
5228 5218 }
... ...