xref: /openbmc/qemu/hw/usb/dev-serial.c (revision c445593d)
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 licensed under the LGPL.
9  */
10 
11 #include "qemu/osdep.h"
12 #include "qapi/error.h"
13 #include "qemu/cutils.h"
14 #include "qemu/error-report.h"
15 #include "qemu/module.h"
16 #include "hw/qdev-properties.h"
17 #include "hw/usb.h"
18 #include "migration/vmstate.h"
19 #include "desc.h"
20 #include "chardev/char-serial.h"
21 #include "chardev/char-fe.h"
22 #include "qom/object.h"
23 #include "trace.h"
24 
25 
26 #define RECV_BUF (512 - (2 * 8))
27 
28 /* Commands */
29 #define FTDI_RESET             0
30 #define FTDI_SET_MDM_CTRL      1
31 #define FTDI_SET_FLOW_CTRL     2
32 #define FTDI_SET_BAUD          3
33 #define FTDI_SET_DATA          4
34 #define FTDI_GET_MDM_ST        5
35 #define FTDI_SET_EVENT_CHR     6
36 #define FTDI_SET_ERROR_CHR     7
37 #define FTDI_SET_LATENCY       9
38 #define FTDI_GET_LATENCY       10
39 
40 /* RESET */
41 
42 #define FTDI_RESET_SIO 0
43 #define FTDI_RESET_RX  1
44 #define FTDI_RESET_TX  2
45 
46 /* SET_MDM_CTRL */
47 
48 #define FTDI_DTR       1
49 #define FTDI_SET_DTR   (FTDI_DTR << 8)
50 #define FTDI_RTS       2
51 #define FTDI_SET_RTS   (FTDI_RTS << 8)
52 
53 /* SET_FLOW_CTRL */
54 
55 #define FTDI_NO_HS         0
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 struct USBSerialState {
92     USBDevice dev;
93 
94     USBEndpoint *intr;
95     uint8_t recv_buf[RECV_BUF];
96     uint16_t recv_ptr;
97     uint16_t recv_used;
98     uint8_t event_chr;
99     uint8_t error_chr;
100     uint8_t event_trigger;
101     bool always_plugged;
102     uint8_t flow_control;
103     uint8_t xon;
104     uint8_t xoff;
105     QEMUSerialSetParams params;
106     int latency;        /* ms */
107     CharBackend cs;
108 };
109 
110 #define TYPE_USB_SERIAL "usb-serial-dev"
111 OBJECT_DECLARE_SIMPLE_TYPE(USBSerialState, USB_SERIAL)
112 
113 enum {
114     STR_MANUFACTURER = 1,
115     STR_PRODUCT_SERIAL,
116     STR_PRODUCT_BRAILLE,
117     STR_SERIALNUMBER,
118 };
119 
120 static const USBDescStrings desc_strings = {
121     [STR_MANUFACTURER]    = "QEMU",
122     [STR_PRODUCT_SERIAL]  = "QEMU USB SERIAL",
123     [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE",
124     [STR_SERIALNUMBER]    = "1",
125 };
126 
127 static const USBDescIface desc_iface0 = {
128     .bInterfaceNumber              = 0,
129     .bNumEndpoints                 = 2,
130     .bInterfaceClass               = 0xff,
131     .bInterfaceSubClass            = 0xff,
132     .bInterfaceProtocol            = 0xff,
133     .eps = (USBDescEndpoint[]) {
134         {
135             .bEndpointAddress      = USB_DIR_IN | 0x01,
136             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
137             .wMaxPacketSize        = 64,
138         },{
139             .bEndpointAddress      = USB_DIR_OUT | 0x02,
140             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
141             .wMaxPacketSize        = 64,
142         },
143     }
144 };
145 
146 static const USBDescDevice desc_device = {
147     .bcdUSB                        = 0x0200,
148     .bMaxPacketSize0               = 8,
149     .bNumConfigurations            = 1,
150     .confs = (USBDescConfig[]) {
151         {
152             .bNumInterfaces        = 1,
153             .bConfigurationValue   = 1,
154             .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP,
155             .bMaxPower             = 50,
156             .nif = 1,
157             .ifs = &desc_iface0,
158         },
159     },
160 };
161 
162 static const USBDesc desc_serial = {
163     .id = {
164         .idVendor          = 0x0403,
165         .idProduct         = 0x6001,
166         .bcdDevice         = 0x0400,
167         .iManufacturer     = STR_MANUFACTURER,
168         .iProduct          = STR_PRODUCT_SERIAL,
169         .iSerialNumber     = STR_SERIALNUMBER,
170     },
171     .full = &desc_device,
172     .str  = desc_strings,
173 };
174 
175 static const USBDesc desc_braille = {
176     .id = {
177         .idVendor          = 0x0403,
178         .idProduct         = 0xfe72,
179         .bcdDevice         = 0x0400,
180         .iManufacturer     = STR_MANUFACTURER,
181         .iProduct          = STR_PRODUCT_BRAILLE,
182         .iSerialNumber     = STR_SERIALNUMBER,
183     },
184     .full = &desc_device,
185     .str  = desc_strings,
186 };
187 
188 static void usb_serial_set_flow_control(USBSerialState *s,
189                                         uint8_t flow_control)
190 {
191     USBDevice *dev = USB_DEVICE(s);
192     USBBus *bus = usb_bus_from_device(dev);
193 
194     /* TODO: ioctl */
195     s->flow_control = flow_control;
196     trace_usb_serial_set_flow_control(bus->busnr, dev->addr, flow_control);
197 }
198 
199 static void usb_serial_set_xonxoff(USBSerialState *s, int xonxoff)
200 {
201     USBDevice *dev = USB_DEVICE(s);
202     USBBus *bus = usb_bus_from_device(dev);
203 
204     s->xon = xonxoff & 0xff;
205     s->xoff = (xonxoff >> 8) & 0xff;
206 
207     trace_usb_serial_set_xonxoff(bus->busnr, dev->addr, s->xon, s->xoff);
208 }
209 
210 static void usb_serial_reset(USBSerialState *s)
211 {
212     s->event_chr = 0x0d;
213     s->event_trigger = 0;
214     s->recv_ptr = 0;
215     s->recv_used = 0;
216     /* TODO: purge in char driver */
217     usb_serial_set_flow_control(s, FTDI_NO_HS);
218 }
219 
220 static void usb_serial_handle_reset(USBDevice *dev)
221 {
222     USBSerialState *s = USB_SERIAL(dev);
223     USBBus *bus = usb_bus_from_device(dev);
224 
225     trace_usb_serial_reset(bus->busnr, dev->addr);
226 
227     usb_serial_reset(s);
228     /* TODO: Reset char device, send BREAK? */
229 }
230 
231 static uint8_t usb_get_modem_lines(USBSerialState *s)
232 {
233     int flags;
234     uint8_t ret;
235 
236     if (qemu_chr_fe_ioctl(&s->cs,
237                           CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
238         return FTDI_CTS | FTDI_DSR | FTDI_RLSD;
239     }
240 
241     ret = 0;
242     if (flags & CHR_TIOCM_CTS) {
243         ret |= FTDI_CTS;
244     }
245     if (flags & CHR_TIOCM_DSR) {
246         ret |= FTDI_DSR;
247     }
248     if (flags & CHR_TIOCM_RI) {
249         ret |= FTDI_RI;
250     }
251     if (flags & CHR_TIOCM_CAR) {
252         ret |= FTDI_RLSD;
253     }
254 
255     return ret;
256 }
257 
258 static void usb_serial_handle_control(USBDevice *dev, USBPacket *p,
259                                       int request, int value, int index,
260                                       int length, uint8_t *data)
261 {
262     USBSerialState *s = USB_SERIAL(dev);
263     USBBus *bus = usb_bus_from_device(dev);
264     int ret;
265 
266     trace_usb_serial_handle_control(bus->busnr, dev->addr, request, value);
267 
268     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
269     if (ret >= 0) {
270         return;
271     }
272 
273     switch (request) {
274     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
275         break;
276 
277     /* Class specific requests.  */
278     case VendorDeviceOutRequest | FTDI_RESET:
279         switch (value) {
280         case FTDI_RESET_SIO:
281             usb_serial_reset(s);
282             break;
283         case FTDI_RESET_RX:
284             s->recv_ptr = 0;
285             s->recv_used = 0;
286             /* TODO: purge from char device */
287             break;
288         case FTDI_RESET_TX:
289             /* TODO: purge from char device */
290             break;
291         }
292         break;
293     case VendorDeviceOutRequest | FTDI_SET_MDM_CTRL:
294     {
295         static int flags;
296         qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
297         if (value & FTDI_SET_RTS) {
298             if (value & FTDI_RTS) {
299                 flags |= CHR_TIOCM_RTS;
300             } else {
301                 flags &= ~CHR_TIOCM_RTS;
302             }
303         }
304         if (value & FTDI_SET_DTR) {
305             if (value & FTDI_DTR) {
306                 flags |= CHR_TIOCM_DTR;
307             } else {
308                 flags &= ~CHR_TIOCM_DTR;
309             }
310         }
311         qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
312         break;
313     }
314     case VendorDeviceOutRequest | FTDI_SET_FLOW_CTRL: {
315         uint8_t flow_control = index >> 8;
316 
317         usb_serial_set_flow_control(s, flow_control);
318         if (flow_control & FTDI_XON_XOFF_HS) {
319             usb_serial_set_xonxoff(s, value);
320         }
321         break;
322     }
323     case VendorDeviceOutRequest | FTDI_SET_BAUD: {
324         static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
325         int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
326                                      | ((index & 1) << 2)];
327         int divisor = value & 0x3fff;
328 
329         /* chip special cases */
330         if (divisor == 1 && subdivisor8 == 0) {
331             subdivisor8 = 4;
332         }
333         if (divisor == 0 && subdivisor8 == 0) {
334             divisor = 1;
335         }
336 
337         s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
338         trace_usb_serial_set_baud(bus->busnr, dev->addr, s->params.speed);
339         qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
340         break;
341     }
342     case VendorDeviceOutRequest | FTDI_SET_DATA:
343         switch (value & 0xff) {
344         case 7:
345             s->params.data_bits = 7;
346             break;
347         case 8:
348             s->params.data_bits = 8;
349             break;
350         default:
351             /*
352              * According to a comment in Linux's ftdi_sio.c original FTDI
353              * chips fall back to 8 data bits for unsupported data_bits
354              */
355             trace_usb_serial_unsupported_data_bits(bus->busnr, dev->addr,
356                                                    value & 0xff);
357             s->params.data_bits = 8;
358         }
359 
360         switch (value & FTDI_PARITY) {
361         case 0:
362             s->params.parity = 'N';
363             break;
364         case FTDI_ODD:
365             s->params.parity = 'O';
366             break;
367         case FTDI_EVEN:
368             s->params.parity = 'E';
369             break;
370         default:
371             trace_usb_serial_unsupported_parity(bus->busnr, dev->addr,
372                                                 value & FTDI_PARITY);
373             goto fail;
374         }
375 
376         switch (value & FTDI_STOP) {
377         case FTDI_STOP1:
378             s->params.stop_bits = 1;
379             break;
380         case FTDI_STOP2:
381             s->params.stop_bits = 2;
382             break;
383         default:
384             trace_usb_serial_unsupported_stopbits(bus->busnr, dev->addr,
385                                                   value & FTDI_STOP);
386             goto fail;
387         }
388 
389         trace_usb_serial_set_data(bus->busnr, dev->addr, s->params.parity,
390                                   s->params.data_bits, s->params.stop_bits);
391         qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
392         /* TODO: TX ON/OFF */
393         break;
394     case VendorDeviceRequest | FTDI_GET_MDM_ST:
395         data[0] = usb_get_modem_lines(s) | 1;
396         data[1] = FTDI_THRE | FTDI_TEMT;
397         p->actual_length = 2;
398         break;
399     case VendorDeviceOutRequest | FTDI_SET_EVENT_CHR:
400         /* TODO: handle it */
401         s->event_chr = value;
402         break;
403     case VendorDeviceOutRequest | FTDI_SET_ERROR_CHR:
404         /* TODO: handle it */
405         s->error_chr = value;
406         break;
407     case VendorDeviceOutRequest | FTDI_SET_LATENCY:
408         s->latency = value;
409         break;
410     case VendorDeviceRequest | FTDI_GET_LATENCY:
411         data[0] = s->latency;
412         p->actual_length = 1;
413         break;
414     default:
415     fail:
416         trace_usb_serial_unsupported_control(bus->busnr, dev->addr, request,
417                                              value);
418         p->status = USB_RET_STALL;
419         break;
420     }
421 }
422 
423 static void usb_serial_token_in(USBSerialState *s, USBPacket *p)
424 {
425     const int max_packet_size = desc_iface0.eps[0].wMaxPacketSize;
426     int packet_len;
427     uint8_t header[2];
428 
429     packet_len = p->iov.size;
430     if (packet_len <= 2) {
431         p->status = USB_RET_NAK;
432         return;
433     }
434 
435     header[0] = usb_get_modem_lines(s) | 1;
436     /* We do not have the uart details */
437     /* handle serial break */
438     if (s->event_trigger && s->event_trigger & FTDI_BI) {
439         s->event_trigger &= ~FTDI_BI;
440         header[1] = FTDI_BI;
441         usb_packet_copy(p, header, 2);
442         return;
443     } else {
444         header[1] = 0;
445     }
446 
447     if (!s->recv_used) {
448         p->status = USB_RET_NAK;
449         return;
450     }
451 
452     while (s->recv_used && packet_len > 2) {
453         int first_len, len;
454 
455         len = MIN(packet_len, max_packet_size);
456         len -= 2;
457         if (len > s->recv_used) {
458             len = s->recv_used;
459         }
460 
461         first_len = RECV_BUF - s->recv_ptr;
462         if (first_len > len) {
463             first_len = len;
464         }
465         usb_packet_copy(p, header, 2);
466         usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len);
467         if (len > first_len) {
468             usb_packet_copy(p, s->recv_buf, len - first_len);
469         }
470         s->recv_used -= len;
471         s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
472         packet_len -= len + 2;
473     }
474 
475     return;
476 }
477 
478 static void usb_serial_handle_data(USBDevice *dev, USBPacket *p)
479 {
480     USBSerialState *s = USB_SERIAL(dev);
481     USBBus *bus = usb_bus_from_device(dev);
482     uint8_t devep = p->ep->nr;
483     struct iovec *iov;
484     int i;
485 
486     switch (p->pid) {
487     case USB_TOKEN_OUT:
488         if (devep != 2) {
489             goto fail;
490         }
491         for (i = 0; i < p->iov.niov; i++) {
492             iov = p->iov.iov + i;
493             /*
494              * XXX this blocks entire thread. Rewrite to use
495              * qemu_chr_fe_write and background I/O callbacks
496              */
497             qemu_chr_fe_write_all(&s->cs, iov->iov_base, iov->iov_len);
498         }
499         p->actual_length = p->iov.size;
500         break;
501 
502     case USB_TOKEN_IN:
503         if (devep != 1) {
504             goto fail;
505         }
506         usb_serial_token_in(s, p);
507         break;
508 
509     default:
510         trace_usb_serial_bad_token(bus->busnr, dev->addr);
511     fail:
512         p->status = USB_RET_STALL;
513         break;
514     }
515 }
516 
517 static int usb_serial_can_read(void *opaque)
518 {
519     USBSerialState *s = opaque;
520 
521     if (!s->dev.attached) {
522         return 0;
523     }
524     return RECV_BUF - s->recv_used;
525 }
526 
527 static void usb_serial_read(void *opaque, const uint8_t *buf, int size)
528 {
529     USBSerialState *s = opaque;
530     int first_size, start;
531 
532     /* room in the buffer? */
533     if (size > (RECV_BUF - s->recv_used)) {
534         size = RECV_BUF - s->recv_used;
535     }
536 
537     start = s->recv_ptr + s->recv_used;
538     if (start < RECV_BUF) {
539         /* copy data to end of buffer */
540         first_size = RECV_BUF - start;
541         if (first_size > size) {
542             first_size = size;
543         }
544 
545         memcpy(s->recv_buf + start, buf, first_size);
546 
547         /* wrap around to front if needed */
548         if (size > first_size) {
549             memcpy(s->recv_buf, buf + first_size, size - first_size);
550         }
551     } else {
552         start -= RECV_BUF;
553         memcpy(s->recv_buf + start, buf, size);
554     }
555     s->recv_used += size;
556 
557     usb_wakeup(s->intr, 0);
558 }
559 
560 static void usb_serial_event(void *opaque, QEMUChrEvent event)
561 {
562     USBSerialState *s = opaque;
563 
564     switch (event) {
565     case CHR_EVENT_BREAK:
566         s->event_trigger |= FTDI_BI;
567         break;
568     case CHR_EVENT_OPENED:
569         if (!s->always_plugged && !s->dev.attached) {
570             usb_device_attach(&s->dev, &error_abort);
571         }
572         break;
573     case CHR_EVENT_CLOSED:
574         if (!s->always_plugged && s->dev.attached) {
575             usb_device_detach(&s->dev);
576         }
577         break;
578     case CHR_EVENT_MUX_IN:
579     case CHR_EVENT_MUX_OUT:
580         /* Ignore */
581         break;
582     }
583 }
584 
585 static void usb_serial_realize(USBDevice *dev, Error **errp)
586 {
587     USBSerialState *s = USB_SERIAL(dev);
588     Error *local_err = NULL;
589 
590     usb_desc_create_serial(dev);
591     usb_desc_init(dev);
592     dev->auto_attach = 0;
593 
594     if (!qemu_chr_fe_backend_connected(&s->cs)) {
595         error_setg(errp, "Property chardev is required");
596         return;
597     }
598 
599     usb_check_attach(dev, &local_err);
600     if (local_err) {
601         error_propagate(errp, local_err);
602         return;
603     }
604 
605     qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read,
606                              usb_serial_event, NULL, s, NULL, true);
607     usb_serial_handle_reset(dev);
608 
609     if ((s->always_plugged || qemu_chr_fe_backend_open(&s->cs)) &&
610         !dev->attached) {
611         usb_device_attach(dev, &error_abort);
612     }
613     s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
614 }
615 
616 static USBDevice *usb_braille_init(const char *unused)
617 {
618     USBDevice *dev;
619     Chardev *cdrv;
620 
621     cdrv = qemu_chr_new("braille", "braille", NULL);
622     if (!cdrv) {
623         return NULL;
624     }
625 
626     dev = usb_new("usb-braille");
627     qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
628     return dev;
629 }
630 
631 static const VMStateDescription vmstate_usb_serial = {
632     .name = "usb-serial",
633     .unmigratable = 1,
634 };
635 
636 static Property serial_properties[] = {
637     DEFINE_PROP_CHR("chardev", USBSerialState, cs),
638     DEFINE_PROP_BOOL("always-plugged", USBSerialState, always_plugged, false),
639     DEFINE_PROP_END_OF_LIST(),
640 };
641 
642 static void usb_serial_dev_class_init(ObjectClass *klass, void *data)
643 {
644     DeviceClass *dc = DEVICE_CLASS(klass);
645     USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
646 
647     uc->realize        = usb_serial_realize;
648     uc->handle_reset   = usb_serial_handle_reset;
649     uc->handle_control = usb_serial_handle_control;
650     uc->handle_data    = usb_serial_handle_data;
651     dc->vmsd = &vmstate_usb_serial;
652     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
653 }
654 
655 static const TypeInfo usb_serial_dev_type_info = {
656     .name = TYPE_USB_SERIAL,
657     .parent = TYPE_USB_DEVICE,
658     .instance_size = sizeof(USBSerialState),
659     .abstract = true,
660     .class_init = usb_serial_dev_class_init,
661 };
662 
663 static void usb_serial_class_initfn(ObjectClass *klass, void *data)
664 {
665     DeviceClass *dc = DEVICE_CLASS(klass);
666     USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
667 
668     uc->product_desc   = "QEMU USB Serial";
669     uc->usb_desc       = &desc_serial;
670     device_class_set_props(dc, serial_properties);
671 }
672 
673 static const TypeInfo serial_info = {
674     .name          = "usb-serial",
675     .parent        = TYPE_USB_SERIAL,
676     .class_init    = usb_serial_class_initfn,
677 };
678 
679 static Property braille_properties[] = {
680     DEFINE_PROP_CHR("chardev", USBSerialState, cs),
681     DEFINE_PROP_END_OF_LIST(),
682 };
683 
684 static void usb_braille_class_initfn(ObjectClass *klass, void *data)
685 {
686     DeviceClass *dc = DEVICE_CLASS(klass);
687     USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
688 
689     uc->product_desc   = "QEMU USB Braille";
690     uc->usb_desc       = &desc_braille;
691     device_class_set_props(dc, braille_properties);
692 }
693 
694 static const TypeInfo braille_info = {
695     .name          = "usb-braille",
696     .parent        = TYPE_USB_SERIAL,
697     .class_init    = usb_braille_class_initfn,
698 };
699 
700 static void usb_serial_register_types(void)
701 {
702     type_register_static(&usb_serial_dev_type_info);
703     type_register_static(&serial_info);
704     type_register_static(&braille_info);
705     usb_legacy_register("usb-braille", "braille", usb_braille_init);
706 }
707 
708 type_init(usb_serial_register_types)
709