xref: /qemu/hw/usb/dev-serial.c (revision 405cf80c)
1f1ae32a1SGerd Hoffmann /*
2f1ae32a1SGerd Hoffmann  * FTDI FT232BM Device emulation
3f1ae32a1SGerd Hoffmann  *
4f1ae32a1SGerd Hoffmann  * Copyright (c) 2006 CodeSourcery.
5f1ae32a1SGerd Hoffmann  * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
6f1ae32a1SGerd Hoffmann  * Written by Paul Brook, reused for FTDI by Samuel Thibault
7f1ae32a1SGerd Hoffmann  *
8f1ae32a1SGerd Hoffmann  * This code is licensed under the LGPL.
9f1ae32a1SGerd Hoffmann  */
10f1ae32a1SGerd Hoffmann 
11e532b2e0SPeter Maydell #include "qemu/osdep.h"
12da34e65cSMarkus Armbruster #include "qapi/error.h"
13f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
14d49b6836SMarkus Armbruster #include "qemu/error-report.h"
150b8fa32fSMarkus Armbruster #include "qemu/module.h"
16a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
17ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
18f1ae32a1SGerd Hoffmann #include "hw/usb.h"
19d6454270SMarkus Armbruster #include "migration/vmstate.h"
20463581a8SMichael S. Tsirkin #include "desc.h"
217566c6efSMarc-André Lureau #include "chardev/char-serial.h"
224d43a603SMarc-André Lureau #include "chardev/char-fe.h"
23db1015e9SEduardo Habkost #include "qom/object.h"
24ebb11320SMark Cave-Ayland #include "trace.h"
25f1ae32a1SGerd Hoffmann 
26f1ae32a1SGerd Hoffmann 
2730ad5fddSJason Andryuk #define RECV_BUF (512 - (2 * 8))
28f1ae32a1SGerd Hoffmann 
29f1ae32a1SGerd Hoffmann /* Commands */
30f1ae32a1SGerd Hoffmann #define FTDI_RESET             0
31f1ae32a1SGerd Hoffmann #define FTDI_SET_MDM_CTRL      1
32f1ae32a1SGerd Hoffmann #define FTDI_SET_FLOW_CTRL     2
33f1ae32a1SGerd Hoffmann #define FTDI_SET_BAUD          3
34f1ae32a1SGerd Hoffmann #define FTDI_SET_DATA          4
35f1ae32a1SGerd Hoffmann #define FTDI_GET_MDM_ST        5
36f1ae32a1SGerd Hoffmann #define FTDI_SET_EVENT_CHR     6
37f1ae32a1SGerd Hoffmann #define FTDI_SET_ERROR_CHR     7
38f1ae32a1SGerd Hoffmann #define FTDI_SET_LATENCY       9
39f1ae32a1SGerd Hoffmann #define FTDI_GET_LATENCY       10
40f1ae32a1SGerd Hoffmann 
41f1ae32a1SGerd Hoffmann /* RESET */
42f1ae32a1SGerd Hoffmann 
43f1ae32a1SGerd Hoffmann #define FTDI_RESET_SIO 0
44f1ae32a1SGerd Hoffmann #define FTDI_RESET_RX  1
45f1ae32a1SGerd Hoffmann #define FTDI_RESET_TX  2
46f1ae32a1SGerd Hoffmann 
47f1ae32a1SGerd Hoffmann /* SET_MDM_CTRL */
48f1ae32a1SGerd Hoffmann 
49f1ae32a1SGerd Hoffmann #define FTDI_DTR       1
50f1ae32a1SGerd Hoffmann #define FTDI_SET_DTR   (FTDI_DTR << 8)
51f1ae32a1SGerd Hoffmann #define FTDI_RTS       2
52f1ae32a1SGerd Hoffmann #define FTDI_SET_RTS   (FTDI_RTS << 8)
53f1ae32a1SGerd Hoffmann 
54f1ae32a1SGerd Hoffmann /* SET_FLOW_CTRL */
55f1ae32a1SGerd Hoffmann 
56963a7bedSMark Cave-Ayland #define FTDI_NO_HS         0
57f1ae32a1SGerd Hoffmann #define FTDI_RTS_CTS_HS    1
58f1ae32a1SGerd Hoffmann #define FTDI_DTR_DSR_HS    2
59f1ae32a1SGerd Hoffmann #define FTDI_XON_XOFF_HS   4
60f1ae32a1SGerd Hoffmann 
61f1ae32a1SGerd Hoffmann /* SET_DATA */
62f1ae32a1SGerd Hoffmann 
63f1ae32a1SGerd Hoffmann #define FTDI_PARITY    (0x7 << 8)
64f1ae32a1SGerd Hoffmann #define FTDI_ODD       (0x1 << 8)
65f1ae32a1SGerd Hoffmann #define FTDI_EVEN      (0x2 << 8)
66f1ae32a1SGerd Hoffmann #define FTDI_MARK      (0x3 << 8)
67f1ae32a1SGerd Hoffmann #define FTDI_SPACE     (0x4 << 8)
68f1ae32a1SGerd Hoffmann 
69f1ae32a1SGerd Hoffmann #define FTDI_STOP      (0x3 << 11)
70f1ae32a1SGerd Hoffmann #define FTDI_STOP1     (0x0 << 11)
71f1ae32a1SGerd Hoffmann #define FTDI_STOP15    (0x1 << 11)
72f1ae32a1SGerd Hoffmann #define FTDI_STOP2     (0x2 << 11)
73f1ae32a1SGerd Hoffmann 
74f1ae32a1SGerd Hoffmann /* GET_MDM_ST */
75f1ae32a1SGerd Hoffmann /* TODO: should be sent every 40ms */
76adab8d48SMark Cave-Ayland #define FTDI_CTS   (1 << 4)    /* CTS line status */
77adab8d48SMark Cave-Ayland #define FTDI_DSR   (1 << 5)    /* DSR line status */
78adab8d48SMark Cave-Ayland #define FTDI_RI    (1 << 6)    /* RI line status */
79adab8d48SMark Cave-Ayland #define FTDI_RLSD  (1 << 7)    /* Receive Line Signal Detect */
80f1ae32a1SGerd Hoffmann 
81f1ae32a1SGerd Hoffmann /* Status */
82f1ae32a1SGerd Hoffmann 
83adab8d48SMark Cave-Ayland #define FTDI_DR    (1 << 0)    /* Data Ready */
84adab8d48SMark Cave-Ayland #define FTDI_OE    (1 << 1)    /* Overrun Err */
85adab8d48SMark Cave-Ayland #define FTDI_PE    (1 << 2)    /* Parity Err */
86adab8d48SMark Cave-Ayland #define FTDI_FE    (1 << 3)    /* Framing Err */
87adab8d48SMark Cave-Ayland #define FTDI_BI    (1 << 4)    /* Break Interrupt */
88adab8d48SMark Cave-Ayland #define FTDI_THRE  (1 << 5)    /* Transmitter Holding Register */
89adab8d48SMark Cave-Ayland #define FTDI_TEMT  (1 << 6)    /* Transmitter Empty */
90adab8d48SMark Cave-Ayland #define FTDI_FIFO  (1 << 7)    /* Error in FIFO */
91f1ae32a1SGerd Hoffmann 
92db1015e9SEduardo Habkost struct USBSerialState {
93f1ae32a1SGerd Hoffmann     USBDevice dev;
94adab8d48SMark Cave-Ayland 
955843b6b3SJason Andryuk     USBEndpoint *intr;
96f1ae32a1SGerd Hoffmann     uint8_t recv_buf[RECV_BUF];
97f1ae32a1SGerd Hoffmann     uint16_t recv_ptr;
98f1ae32a1SGerd Hoffmann     uint16_t recv_used;
99f1ae32a1SGerd Hoffmann     uint8_t event_chr;
100f1ae32a1SGerd Hoffmann     uint8_t error_chr;
101f1ae32a1SGerd Hoffmann     uint8_t event_trigger;
10266007a95SMark Cave-Ayland     bool always_plugged;
103963a7bedSMark Cave-Ayland     uint8_t flow_control;
104963a7bedSMark Cave-Ayland     uint8_t xon;
105963a7bedSMark Cave-Ayland     uint8_t xoff;
106f1ae32a1SGerd Hoffmann     QEMUSerialSetParams params;
107f1ae32a1SGerd Hoffmann     int latency;        /* ms */
108becdfa00SMarc-André Lureau     CharBackend cs;
109db1015e9SEduardo Habkost };
110f1ae32a1SGerd Hoffmann 
111cdf0d769SGonglei #define TYPE_USB_SERIAL "usb-serial-dev"
1128063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(USBSerialState, USB_SERIAL)
113cdf0d769SGonglei 
114f1ae32a1SGerd Hoffmann enum {
115f1ae32a1SGerd Hoffmann     STR_MANUFACTURER = 1,
116f1ae32a1SGerd Hoffmann     STR_PRODUCT_SERIAL,
117f1ae32a1SGerd Hoffmann     STR_PRODUCT_BRAILLE,
118f1ae32a1SGerd Hoffmann     STR_SERIALNUMBER,
119f1ae32a1SGerd Hoffmann };
120f1ae32a1SGerd Hoffmann 
121f1ae32a1SGerd Hoffmann static const USBDescStrings desc_strings = {
12293bfef4cSCrístian Viana     [STR_MANUFACTURER]    = "QEMU",
123f1ae32a1SGerd Hoffmann     [STR_PRODUCT_SERIAL]  = "QEMU USB SERIAL",
1242964cd9bSSamuel Thibault     [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE",
125f1ae32a1SGerd Hoffmann     [STR_SERIALNUMBER]    = "1",
126f1ae32a1SGerd Hoffmann };
127f1ae32a1SGerd Hoffmann 
128f1ae32a1SGerd Hoffmann static const USBDescIface desc_iface0 = {
129f1ae32a1SGerd Hoffmann     .bInterfaceNumber              = 0,
130f1ae32a1SGerd Hoffmann     .bNumEndpoints                 = 2,
131f1ae32a1SGerd Hoffmann     .bInterfaceClass               = 0xff,
132f1ae32a1SGerd Hoffmann     .bInterfaceSubClass            = 0xff,
133f1ae32a1SGerd Hoffmann     .bInterfaceProtocol            = 0xff,
134f1ae32a1SGerd Hoffmann     .eps = (USBDescEndpoint[]) {
135f1ae32a1SGerd Hoffmann         {
136f1ae32a1SGerd Hoffmann             .bEndpointAddress      = USB_DIR_IN | 0x01,
137f1ae32a1SGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
138f1ae32a1SGerd Hoffmann             .wMaxPacketSize        = 64,
139f1ae32a1SGerd Hoffmann         },{
140f1ae32a1SGerd Hoffmann             .bEndpointAddress      = USB_DIR_OUT | 0x02,
141f1ae32a1SGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_BULK,
142f1ae32a1SGerd Hoffmann             .wMaxPacketSize        = 64,
143f1ae32a1SGerd Hoffmann         },
144f1ae32a1SGerd Hoffmann     }
145f1ae32a1SGerd Hoffmann };
146f1ae32a1SGerd Hoffmann 
147f1ae32a1SGerd Hoffmann static const USBDescDevice desc_device = {
148f1ae32a1SGerd Hoffmann     .bcdUSB                        = 0x0200,
149f1ae32a1SGerd Hoffmann     .bMaxPacketSize0               = 8,
150f1ae32a1SGerd Hoffmann     .bNumConfigurations            = 1,
151f1ae32a1SGerd Hoffmann     .confs = (USBDescConfig[]) {
152f1ae32a1SGerd Hoffmann         {
153f1ae32a1SGerd Hoffmann             .bNumInterfaces        = 1,
154f1ae32a1SGerd Hoffmann             .bConfigurationValue   = 1,
1555843b6b3SJason Andryuk             .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP,
156f1ae32a1SGerd Hoffmann             .bMaxPower             = 50,
157f1ae32a1SGerd Hoffmann             .nif = 1,
158f1ae32a1SGerd Hoffmann             .ifs = &desc_iface0,
159f1ae32a1SGerd Hoffmann         },
160f1ae32a1SGerd Hoffmann     },
161f1ae32a1SGerd Hoffmann };
162f1ae32a1SGerd Hoffmann 
163f1ae32a1SGerd Hoffmann static const USBDesc desc_serial = {
164f1ae32a1SGerd Hoffmann     .id = {
165f1ae32a1SGerd Hoffmann         .idVendor          = 0x0403,
166f1ae32a1SGerd Hoffmann         .idProduct         = 0x6001,
167f1ae32a1SGerd Hoffmann         .bcdDevice         = 0x0400,
168f1ae32a1SGerd Hoffmann         .iManufacturer     = STR_MANUFACTURER,
169f1ae32a1SGerd Hoffmann         .iProduct          = STR_PRODUCT_SERIAL,
170f1ae32a1SGerd Hoffmann         .iSerialNumber     = STR_SERIALNUMBER,
171f1ae32a1SGerd Hoffmann     },
172f1ae32a1SGerd Hoffmann     .full = &desc_device,
173f1ae32a1SGerd Hoffmann     .str  = desc_strings,
174f1ae32a1SGerd Hoffmann };
175f1ae32a1SGerd Hoffmann 
176f1ae32a1SGerd Hoffmann static const USBDesc desc_braille = {
177f1ae32a1SGerd Hoffmann     .id = {
178f1ae32a1SGerd Hoffmann         .idVendor          = 0x0403,
179f1ae32a1SGerd Hoffmann         .idProduct         = 0xfe72,
180f1ae32a1SGerd Hoffmann         .bcdDevice         = 0x0400,
181f1ae32a1SGerd Hoffmann         .iManufacturer     = STR_MANUFACTURER,
182f1ae32a1SGerd Hoffmann         .iProduct          = STR_PRODUCT_BRAILLE,
183f1ae32a1SGerd Hoffmann         .iSerialNumber     = STR_SERIALNUMBER,
184f1ae32a1SGerd Hoffmann     },
185f1ae32a1SGerd Hoffmann     .full = &desc_device,
186f1ae32a1SGerd Hoffmann     .str  = desc_strings,
187f1ae32a1SGerd Hoffmann };
188f1ae32a1SGerd Hoffmann 
usb_serial_set_flow_control(USBSerialState * s,uint8_t flow_control)189963a7bedSMark Cave-Ayland static void usb_serial_set_flow_control(USBSerialState *s,
190963a7bedSMark Cave-Ayland                                         uint8_t flow_control)
191963a7bedSMark Cave-Ayland {
192963a7bedSMark Cave-Ayland     USBDevice *dev = USB_DEVICE(s);
193963a7bedSMark Cave-Ayland     USBBus *bus = usb_bus_from_device(dev);
194963a7bedSMark Cave-Ayland 
195963a7bedSMark Cave-Ayland     /* TODO: ioctl */
196963a7bedSMark Cave-Ayland     s->flow_control = flow_control;
197963a7bedSMark Cave-Ayland     trace_usb_serial_set_flow_control(bus->busnr, dev->addr, flow_control);
198963a7bedSMark Cave-Ayland }
199963a7bedSMark Cave-Ayland 
usb_serial_set_xonxoff(USBSerialState * s,int xonxoff)200963a7bedSMark Cave-Ayland static void usb_serial_set_xonxoff(USBSerialState *s, int xonxoff)
201963a7bedSMark Cave-Ayland {
202963a7bedSMark Cave-Ayland     USBDevice *dev = USB_DEVICE(s);
203963a7bedSMark Cave-Ayland     USBBus *bus = usb_bus_from_device(dev);
204963a7bedSMark Cave-Ayland 
205963a7bedSMark Cave-Ayland     s->xon = xonxoff & 0xff;
206963a7bedSMark Cave-Ayland     s->xoff = (xonxoff >> 8) & 0xff;
207963a7bedSMark Cave-Ayland 
208963a7bedSMark Cave-Ayland     trace_usb_serial_set_xonxoff(bus->busnr, dev->addr, s->xon, s->xoff);
209963a7bedSMark Cave-Ayland }
210963a7bedSMark Cave-Ayland 
usb_serial_reset(USBSerialState * s)211f1ae32a1SGerd Hoffmann static void usb_serial_reset(USBSerialState *s)
212f1ae32a1SGerd Hoffmann {
213f1ae32a1SGerd Hoffmann     s->event_chr = 0x0d;
214f1ae32a1SGerd Hoffmann     s->event_trigger = 0;
215f1ae32a1SGerd Hoffmann     s->recv_ptr = 0;
216f1ae32a1SGerd Hoffmann     s->recv_used = 0;
217f1ae32a1SGerd Hoffmann     /* TODO: purge in char driver */
218963a7bedSMark Cave-Ayland     usb_serial_set_flow_control(s, FTDI_NO_HS);
219f1ae32a1SGerd Hoffmann }
220f1ae32a1SGerd Hoffmann 
usb_serial_handle_reset(USBDevice * dev)221f1ae32a1SGerd Hoffmann static void usb_serial_handle_reset(USBDevice *dev)
222f1ae32a1SGerd Hoffmann {
2238a0e4ee7SMark Cave-Ayland     USBSerialState *s = USB_SERIAL(dev);
224ebb11320SMark Cave-Ayland     USBBus *bus = usb_bus_from_device(dev);
225f1ae32a1SGerd Hoffmann 
226ebb11320SMark Cave-Ayland     trace_usb_serial_reset(bus->busnr, dev->addr);
227f1ae32a1SGerd Hoffmann 
228f1ae32a1SGerd Hoffmann     usb_serial_reset(s);
229f1ae32a1SGerd Hoffmann     /* TODO: Reset char device, send BREAK? */
230f1ae32a1SGerd Hoffmann }
231f1ae32a1SGerd Hoffmann 
usb_get_modem_lines(USBSerialState * s)232f1ae32a1SGerd Hoffmann static uint8_t usb_get_modem_lines(USBSerialState *s)
233f1ae32a1SGerd Hoffmann {
234f1ae32a1SGerd Hoffmann     int flags;
235f1ae32a1SGerd Hoffmann     uint8_t ret;
236f1ae32a1SGerd Hoffmann 
2375345fdb4SMarc-André Lureau     if (qemu_chr_fe_ioctl(&s->cs,
238becdfa00SMarc-André Lureau                           CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
239f1ae32a1SGerd Hoffmann         return FTDI_CTS | FTDI_DSR | FTDI_RLSD;
240becdfa00SMarc-André Lureau     }
241f1ae32a1SGerd Hoffmann 
242f1ae32a1SGerd Hoffmann     ret = 0;
243adab8d48SMark Cave-Ayland     if (flags & CHR_TIOCM_CTS) {
244f1ae32a1SGerd Hoffmann         ret |= FTDI_CTS;
245adab8d48SMark Cave-Ayland     }
246adab8d48SMark Cave-Ayland     if (flags & CHR_TIOCM_DSR) {
247f1ae32a1SGerd Hoffmann         ret |= FTDI_DSR;
248adab8d48SMark Cave-Ayland     }
249adab8d48SMark Cave-Ayland     if (flags & CHR_TIOCM_RI) {
250f1ae32a1SGerd Hoffmann         ret |= FTDI_RI;
251adab8d48SMark Cave-Ayland     }
252adab8d48SMark Cave-Ayland     if (flags & CHR_TIOCM_CAR) {
253f1ae32a1SGerd Hoffmann         ret |= FTDI_RLSD;
254adab8d48SMark Cave-Ayland     }
255f1ae32a1SGerd Hoffmann 
256f1ae32a1SGerd Hoffmann     return ret;
257f1ae32a1SGerd Hoffmann }
258f1ae32a1SGerd Hoffmann 
usb_serial_handle_control(USBDevice * dev,USBPacket * p,int request,int value,int index,int length,uint8_t * data)2599a77a0f5SHans de Goede static void usb_serial_handle_control(USBDevice *dev, USBPacket *p,
260adab8d48SMark Cave-Ayland                                       int request, int value, int index,
261adab8d48SMark Cave-Ayland                                       int length, uint8_t *data)
262f1ae32a1SGerd Hoffmann {
2638a0e4ee7SMark Cave-Ayland     USBSerialState *s = USB_SERIAL(dev);
264ebb11320SMark Cave-Ayland     USBBus *bus = usb_bus_from_device(dev);
265f1ae32a1SGerd Hoffmann     int ret;
266f1ae32a1SGerd Hoffmann 
267ebb11320SMark Cave-Ayland     trace_usb_serial_handle_control(bus->busnr, dev->addr, request, value);
268ebb11320SMark Cave-Ayland 
269f1ae32a1SGerd Hoffmann     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
270f1ae32a1SGerd Hoffmann     if (ret >= 0) {
2719a77a0f5SHans de Goede         return;
272f1ae32a1SGerd Hoffmann     }
273f1ae32a1SGerd Hoffmann 
274f1ae32a1SGerd Hoffmann     switch (request) {
275f1ae32a1SGerd Hoffmann     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
276f1ae32a1SGerd Hoffmann         break;
277f1ae32a1SGerd Hoffmann 
278f1ae32a1SGerd Hoffmann     /* Class specific requests.  */
279687dfe63SMark Cave-Ayland     case VendorDeviceOutRequest | FTDI_RESET:
280f1ae32a1SGerd Hoffmann         switch (value) {
281f1ae32a1SGerd Hoffmann         case FTDI_RESET_SIO:
282f1ae32a1SGerd Hoffmann             usb_serial_reset(s);
283f1ae32a1SGerd Hoffmann             break;
284f1ae32a1SGerd Hoffmann         case FTDI_RESET_RX:
285f1ae32a1SGerd Hoffmann             s->recv_ptr = 0;
286f1ae32a1SGerd Hoffmann             s->recv_used = 0;
287f1ae32a1SGerd Hoffmann             /* TODO: purge from char device */
288f1ae32a1SGerd Hoffmann             break;
289f1ae32a1SGerd Hoffmann         case FTDI_RESET_TX:
290f1ae32a1SGerd Hoffmann             /* TODO: purge from char device */
291f1ae32a1SGerd Hoffmann             break;
292f1ae32a1SGerd Hoffmann         }
293f1ae32a1SGerd Hoffmann         break;
294687dfe63SMark Cave-Ayland     case VendorDeviceOutRequest | FTDI_SET_MDM_CTRL:
295f1ae32a1SGerd Hoffmann     {
296f1ae32a1SGerd Hoffmann         static int flags;
2975345fdb4SMarc-André Lureau         qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
298f1ae32a1SGerd Hoffmann         if (value & FTDI_SET_RTS) {
299adab8d48SMark Cave-Ayland             if (value & FTDI_RTS) {
300f1ae32a1SGerd Hoffmann                 flags |= CHR_TIOCM_RTS;
301adab8d48SMark Cave-Ayland             } else {
302f1ae32a1SGerd Hoffmann                 flags &= ~CHR_TIOCM_RTS;
303f1ae32a1SGerd Hoffmann             }
304adab8d48SMark Cave-Ayland         }
305f1ae32a1SGerd Hoffmann         if (value & FTDI_SET_DTR) {
306adab8d48SMark Cave-Ayland             if (value & FTDI_DTR) {
307f1ae32a1SGerd Hoffmann                 flags |= CHR_TIOCM_DTR;
308adab8d48SMark Cave-Ayland             } else {
309f1ae32a1SGerd Hoffmann                 flags &= ~CHR_TIOCM_DTR;
310f1ae32a1SGerd Hoffmann             }
311adab8d48SMark Cave-Ayland         }
3125345fdb4SMarc-André Lureau         qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
313f1ae32a1SGerd Hoffmann         break;
314f1ae32a1SGerd Hoffmann     }
315963a7bedSMark Cave-Ayland     case VendorDeviceOutRequest | FTDI_SET_FLOW_CTRL: {
316963a7bedSMark Cave-Ayland         uint8_t flow_control = index >> 8;
317963a7bedSMark Cave-Ayland 
318963a7bedSMark Cave-Ayland         usb_serial_set_flow_control(s, flow_control);
319963a7bedSMark Cave-Ayland         if (flow_control & FTDI_XON_XOFF_HS) {
320963a7bedSMark Cave-Ayland             usb_serial_set_xonxoff(s, value);
321963a7bedSMark Cave-Ayland         }
322f1ae32a1SGerd Hoffmann         break;
323963a7bedSMark Cave-Ayland     }
324687dfe63SMark Cave-Ayland     case VendorDeviceOutRequest | FTDI_SET_BAUD: {
325f1ae32a1SGerd Hoffmann         static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
326f1ae32a1SGerd Hoffmann         int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
327f1ae32a1SGerd Hoffmann                                      | ((index & 1) << 2)];
328f1ae32a1SGerd Hoffmann         int divisor = value & 0x3fff;
329f1ae32a1SGerd Hoffmann 
330f1ae32a1SGerd Hoffmann         /* chip special cases */
331adab8d48SMark Cave-Ayland         if (divisor == 1 && subdivisor8 == 0) {
332f1ae32a1SGerd Hoffmann             subdivisor8 = 4;
333adab8d48SMark Cave-Ayland         }
334adab8d48SMark Cave-Ayland         if (divisor == 0 && subdivisor8 == 0) {
335f1ae32a1SGerd Hoffmann             divisor = 1;
336adab8d48SMark Cave-Ayland         }
337f1ae32a1SGerd Hoffmann 
338f1ae32a1SGerd Hoffmann         s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
339655ec806SMark Cave-Ayland         trace_usb_serial_set_baud(bus->busnr, dev->addr, s->params.speed);
3405345fdb4SMarc-André Lureau         qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
341f1ae32a1SGerd Hoffmann         break;
342f1ae32a1SGerd Hoffmann     }
343687dfe63SMark Cave-Ayland     case VendorDeviceOutRequest | FTDI_SET_DATA:
344a79f86cdSMark Cave-Ayland         switch (value & 0xff) {
345a79f86cdSMark Cave-Ayland         case 7:
346a79f86cdSMark Cave-Ayland             s->params.data_bits = 7;
347a79f86cdSMark Cave-Ayland             break;
348a79f86cdSMark Cave-Ayland         case 8:
349a79f86cdSMark Cave-Ayland             s->params.data_bits = 8;
350a79f86cdSMark Cave-Ayland             break;
351a79f86cdSMark Cave-Ayland         default:
352a79f86cdSMark Cave-Ayland             /*
353a79f86cdSMark Cave-Ayland              * According to a comment in Linux's ftdi_sio.c original FTDI
354a79f86cdSMark Cave-Ayland              * chips fall back to 8 data bits for unsupported data_bits
355a79f86cdSMark Cave-Ayland              */
356a79f86cdSMark Cave-Ayland             trace_usb_serial_unsupported_data_bits(bus->busnr, dev->addr,
357a79f86cdSMark Cave-Ayland                                                    value & 0xff);
358a79f86cdSMark Cave-Ayland             s->params.data_bits = 8;
359a79f86cdSMark Cave-Ayland         }
360a79f86cdSMark Cave-Ayland 
361f1ae32a1SGerd Hoffmann         switch (value & FTDI_PARITY) {
362f1ae32a1SGerd Hoffmann         case 0:
363f1ae32a1SGerd Hoffmann             s->params.parity = 'N';
364f1ae32a1SGerd Hoffmann             break;
365f1ae32a1SGerd Hoffmann         case FTDI_ODD:
366f1ae32a1SGerd Hoffmann             s->params.parity = 'O';
367f1ae32a1SGerd Hoffmann             break;
368f1ae32a1SGerd Hoffmann         case FTDI_EVEN:
369f1ae32a1SGerd Hoffmann             s->params.parity = 'E';
370f1ae32a1SGerd Hoffmann             break;
371f1ae32a1SGerd Hoffmann         default:
372ebb11320SMark Cave-Ayland             trace_usb_serial_unsupported_parity(bus->busnr, dev->addr,
373ebb11320SMark Cave-Ayland                                                 value & FTDI_PARITY);
374f1ae32a1SGerd Hoffmann             goto fail;
375f1ae32a1SGerd Hoffmann         }
376adab8d48SMark Cave-Ayland 
377f1ae32a1SGerd Hoffmann         switch (value & FTDI_STOP) {
378f1ae32a1SGerd Hoffmann         case FTDI_STOP1:
379f1ae32a1SGerd Hoffmann             s->params.stop_bits = 1;
380f1ae32a1SGerd Hoffmann             break;
381f1ae32a1SGerd Hoffmann         case FTDI_STOP2:
382f1ae32a1SGerd Hoffmann             s->params.stop_bits = 2;
383f1ae32a1SGerd Hoffmann             break;
384f1ae32a1SGerd Hoffmann         default:
385ebb11320SMark Cave-Ayland             trace_usb_serial_unsupported_stopbits(bus->busnr, dev->addr,
386ebb11320SMark Cave-Ayland                                                   value & FTDI_STOP);
387f1ae32a1SGerd Hoffmann             goto fail;
388f1ae32a1SGerd Hoffmann         }
389adab8d48SMark Cave-Ayland 
390655ec806SMark Cave-Ayland         trace_usb_serial_set_data(bus->busnr, dev->addr, s->params.parity,
391655ec806SMark Cave-Ayland                                   s->params.data_bits, s->params.stop_bits);
3925345fdb4SMarc-André Lureau         qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
393f1ae32a1SGerd Hoffmann         /* TODO: TX ON/OFF */
394f1ae32a1SGerd Hoffmann         break;
395687dfe63SMark Cave-Ayland     case VendorDeviceRequest | FTDI_GET_MDM_ST:
396f1ae32a1SGerd Hoffmann         data[0] = usb_get_modem_lines(s) | 1;
397647ee987SJason Andryuk         data[1] = FTDI_THRE | FTDI_TEMT;
3989a77a0f5SHans de Goede         p->actual_length = 2;
399f1ae32a1SGerd Hoffmann         break;
400687dfe63SMark Cave-Ayland     case VendorDeviceOutRequest | FTDI_SET_EVENT_CHR:
401f1ae32a1SGerd Hoffmann         /* TODO: handle it */
402f1ae32a1SGerd Hoffmann         s->event_chr = value;
403f1ae32a1SGerd Hoffmann         break;
404687dfe63SMark Cave-Ayland     case VendorDeviceOutRequest | FTDI_SET_ERROR_CHR:
405f1ae32a1SGerd Hoffmann         /* TODO: handle it */
406f1ae32a1SGerd Hoffmann         s->error_chr = value;
407f1ae32a1SGerd Hoffmann         break;
408687dfe63SMark Cave-Ayland     case VendorDeviceOutRequest | FTDI_SET_LATENCY:
409f1ae32a1SGerd Hoffmann         s->latency = value;
410f1ae32a1SGerd Hoffmann         break;
411687dfe63SMark Cave-Ayland     case VendorDeviceRequest | FTDI_GET_LATENCY:
412f1ae32a1SGerd Hoffmann         data[0] = s->latency;
4139a77a0f5SHans de Goede         p->actual_length = 1;
414f1ae32a1SGerd Hoffmann         break;
415f1ae32a1SGerd Hoffmann     default:
416f1ae32a1SGerd Hoffmann     fail:
417ebb11320SMark Cave-Ayland         trace_usb_serial_unsupported_control(bus->busnr, dev->addr, request,
418ebb11320SMark Cave-Ayland                                              value);
4199a77a0f5SHans de Goede         p->status = USB_RET_STALL;
420f1ae32a1SGerd Hoffmann         break;
421f1ae32a1SGerd Hoffmann     }
422f1ae32a1SGerd Hoffmann }
423f1ae32a1SGerd Hoffmann 
usb_serial_token_in(USBSerialState * s,USBPacket * p)4242bcf4e9fSJason Andryuk static void usb_serial_token_in(USBSerialState *s, USBPacket *p)
4252bcf4e9fSJason Andryuk {
42687db78f7SJason Andryuk     const int max_packet_size = desc_iface0.eps[0].wMaxPacketSize;
42787db78f7SJason Andryuk     int packet_len;
4282bcf4e9fSJason Andryuk     uint8_t header[2];
4292bcf4e9fSJason Andryuk 
43087db78f7SJason Andryuk     packet_len = p->iov.size;
43187db78f7SJason Andryuk     if (packet_len <= 2) {
4322bcf4e9fSJason Andryuk         p->status = USB_RET_NAK;
4332bcf4e9fSJason Andryuk         return;
4342bcf4e9fSJason Andryuk     }
43587db78f7SJason Andryuk 
4362bcf4e9fSJason Andryuk     header[0] = usb_get_modem_lines(s) | 1;
4372bcf4e9fSJason Andryuk     /* We do not have the uart details */
4382bcf4e9fSJason Andryuk     /* handle serial break */
4392bcf4e9fSJason Andryuk     if (s->event_trigger && s->event_trigger & FTDI_BI) {
4402bcf4e9fSJason Andryuk         s->event_trigger &= ~FTDI_BI;
4412bcf4e9fSJason Andryuk         header[1] = FTDI_BI;
4422bcf4e9fSJason Andryuk         usb_packet_copy(p, header, 2);
4432bcf4e9fSJason Andryuk         return;
4442bcf4e9fSJason Andryuk     } else {
4452bcf4e9fSJason Andryuk         header[1] = 0;
4462bcf4e9fSJason Andryuk     }
44787db78f7SJason Andryuk 
44887db78f7SJason Andryuk     if (!s->recv_used) {
44987db78f7SJason Andryuk         p->status = USB_RET_NAK;
45087db78f7SJason Andryuk         return;
45187db78f7SJason Andryuk     }
45287db78f7SJason Andryuk 
45387db78f7SJason Andryuk     while (s->recv_used && packet_len > 2) {
45487db78f7SJason Andryuk         int first_len, len;
45587db78f7SJason Andryuk 
45687db78f7SJason Andryuk         len = MIN(packet_len, max_packet_size);
4572bcf4e9fSJason Andryuk         len -= 2;
4582bcf4e9fSJason Andryuk         if (len > s->recv_used) {
4592bcf4e9fSJason Andryuk             len = s->recv_used;
4602bcf4e9fSJason Andryuk         }
46187db78f7SJason Andryuk 
46287db78f7SJason Andryuk         first_len = RECV_BUF - s->recv_ptr;
4632bcf4e9fSJason Andryuk         if (first_len > len) {
4642bcf4e9fSJason Andryuk             first_len = len;
4652bcf4e9fSJason Andryuk         }
4662bcf4e9fSJason Andryuk         usb_packet_copy(p, header, 2);
4672bcf4e9fSJason Andryuk         usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len);
4682bcf4e9fSJason Andryuk         if (len > first_len) {
4692bcf4e9fSJason Andryuk             usb_packet_copy(p, s->recv_buf, len - first_len);
4702bcf4e9fSJason Andryuk         }
4712bcf4e9fSJason Andryuk         s->recv_used -= len;
4722bcf4e9fSJason Andryuk         s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
47387db78f7SJason Andryuk         packet_len -= len + 2;
47487db78f7SJason Andryuk     }
4752bcf4e9fSJason Andryuk 
4762bcf4e9fSJason Andryuk     return;
4772bcf4e9fSJason Andryuk }
4782bcf4e9fSJason Andryuk 
usb_serial_handle_data(USBDevice * dev,USBPacket * p)4799a77a0f5SHans de Goede static void usb_serial_handle_data(USBDevice *dev, USBPacket *p)
480f1ae32a1SGerd Hoffmann {
4818a0e4ee7SMark Cave-Ayland     USBSerialState *s = USB_SERIAL(dev);
482ebb11320SMark Cave-Ayland     USBBus *bus = usb_bus_from_device(dev);
483f1ae32a1SGerd Hoffmann     uint8_t devep = p->ep->nr;
484f1ae32a1SGerd Hoffmann     struct iovec *iov;
4852bcf4e9fSJason Andryuk     int i;
486f1ae32a1SGerd Hoffmann 
487f1ae32a1SGerd Hoffmann     switch (p->pid) {
488f1ae32a1SGerd Hoffmann     case USB_TOKEN_OUT:
489adab8d48SMark Cave-Ayland         if (devep != 2) {
490f1ae32a1SGerd Hoffmann             goto fail;
491adab8d48SMark Cave-Ayland         }
492f1ae32a1SGerd Hoffmann         for (i = 0; i < p->iov.niov; i++) {
493f1ae32a1SGerd Hoffmann             iov = p->iov.iov + i;
494adab8d48SMark Cave-Ayland             /*
495adab8d48SMark Cave-Ayland              * XXX this blocks entire thread. Rewrite to use
496adab8d48SMark Cave-Ayland              * qemu_chr_fe_write and background I/O callbacks
497adab8d48SMark Cave-Ayland              */
4985345fdb4SMarc-André Lureau             qemu_chr_fe_write_all(&s->cs, iov->iov_base, iov->iov_len);
499f1ae32a1SGerd Hoffmann         }
5009a77a0f5SHans de Goede         p->actual_length = p->iov.size;
501f1ae32a1SGerd Hoffmann         break;
502f1ae32a1SGerd Hoffmann 
503f1ae32a1SGerd Hoffmann     case USB_TOKEN_IN:
504adab8d48SMark Cave-Ayland         if (devep != 1) {
505f1ae32a1SGerd Hoffmann             goto fail;
506adab8d48SMark Cave-Ayland         }
5072bcf4e9fSJason Andryuk         usb_serial_token_in(s, p);
508f1ae32a1SGerd Hoffmann         break;
509f1ae32a1SGerd Hoffmann 
510f1ae32a1SGerd Hoffmann     default:
511ebb11320SMark Cave-Ayland         trace_usb_serial_bad_token(bus->busnr, dev->addr);
512f1ae32a1SGerd Hoffmann     fail:
5139a77a0f5SHans de Goede         p->status = USB_RET_STALL;
514f1ae32a1SGerd Hoffmann         break;
515f1ae32a1SGerd Hoffmann     }
516f1ae32a1SGerd Hoffmann }
517f1ae32a1SGerd Hoffmann 
usb_serial_can_read(void * opaque)518f1ae32a1SGerd Hoffmann static int usb_serial_can_read(void *opaque)
519f1ae32a1SGerd Hoffmann {
520f1ae32a1SGerd Hoffmann     USBSerialState *s = opaque;
521da124e62SGerd Hoffmann 
522da124e62SGerd Hoffmann     if (!s->dev.attached) {
523da124e62SGerd Hoffmann         return 0;
524da124e62SGerd Hoffmann     }
525f1ae32a1SGerd Hoffmann     return RECV_BUF - s->recv_used;
526f1ae32a1SGerd Hoffmann }
527f1ae32a1SGerd Hoffmann 
usb_serial_read(void * opaque,const uint8_t * buf,int size)528f1ae32a1SGerd Hoffmann static void usb_serial_read(void *opaque, const uint8_t *buf, int size)
529f1ae32a1SGerd Hoffmann {
530f1ae32a1SGerd Hoffmann     USBSerialState *s = opaque;
531f1ae32a1SGerd Hoffmann     int first_size, start;
532f1ae32a1SGerd Hoffmann 
533f1ae32a1SGerd Hoffmann     /* room in the buffer? */
534adab8d48SMark Cave-Ayland     if (size > (RECV_BUF - s->recv_used)) {
535f1ae32a1SGerd Hoffmann         size = RECV_BUF - s->recv_used;
536adab8d48SMark Cave-Ayland     }
537f1ae32a1SGerd Hoffmann 
538f1ae32a1SGerd Hoffmann     start = s->recv_ptr + s->recv_used;
539f1ae32a1SGerd Hoffmann     if (start < RECV_BUF) {
540f1ae32a1SGerd Hoffmann         /* copy data to end of buffer */
541f1ae32a1SGerd Hoffmann         first_size = RECV_BUF - start;
542adab8d48SMark Cave-Ayland         if (first_size > size) {
543f1ae32a1SGerd Hoffmann             first_size = size;
544adab8d48SMark Cave-Ayland         }
545f1ae32a1SGerd Hoffmann 
546f1ae32a1SGerd Hoffmann         memcpy(s->recv_buf + start, buf, first_size);
547f1ae32a1SGerd Hoffmann 
548f1ae32a1SGerd Hoffmann         /* wrap around to front if needed */
549adab8d48SMark Cave-Ayland         if (size > first_size) {
550f1ae32a1SGerd Hoffmann             memcpy(s->recv_buf, buf + first_size, size - first_size);
551adab8d48SMark Cave-Ayland         }
552f1ae32a1SGerd Hoffmann     } else {
553f1ae32a1SGerd Hoffmann         start -= RECV_BUF;
554f1ae32a1SGerd Hoffmann         memcpy(s->recv_buf + start, buf, size);
555f1ae32a1SGerd Hoffmann     }
556f1ae32a1SGerd Hoffmann     s->recv_used += size;
5575843b6b3SJason Andryuk 
5585843b6b3SJason Andryuk     usb_wakeup(s->intr, 0);
559f1ae32a1SGerd Hoffmann }
560f1ae32a1SGerd Hoffmann 
usb_serial_event(void * opaque,QEMUChrEvent event)561083b266fSPhilippe Mathieu-Daudé static void usb_serial_event(void *opaque, QEMUChrEvent event)
562f1ae32a1SGerd Hoffmann {
563f1ae32a1SGerd Hoffmann     USBSerialState *s = opaque;
564f1ae32a1SGerd Hoffmann 
565f1ae32a1SGerd Hoffmann     switch (event) {
566f1ae32a1SGerd Hoffmann     case CHR_EVENT_BREAK:
567f1ae32a1SGerd Hoffmann         s->event_trigger |= FTDI_BI;
568f1ae32a1SGerd Hoffmann         break;
569f1ae32a1SGerd Hoffmann     case CHR_EVENT_OPENED:
57066007a95SMark Cave-Ayland         if (!s->always_plugged && !s->dev.attached) {
5717334d650SGonglei             usb_device_attach(&s->dev, &error_abort);
572da124e62SGerd Hoffmann         }
573da124e62SGerd Hoffmann         break;
574da124e62SGerd Hoffmann     case CHR_EVENT_CLOSED:
57566007a95SMark Cave-Ayland         if (!s->always_plugged && s->dev.attached) {
576da124e62SGerd Hoffmann             usb_device_detach(&s->dev);
577da124e62SGerd Hoffmann         }
578f1ae32a1SGerd Hoffmann         break;
579c263158eSPhilippe Mathieu-Daudé     case CHR_EVENT_MUX_IN:
580c263158eSPhilippe Mathieu-Daudé     case CHR_EVENT_MUX_OUT:
581c263158eSPhilippe Mathieu-Daudé         /* Ignore */
582c263158eSPhilippe Mathieu-Daudé         break;
583f1ae32a1SGerd Hoffmann     }
584f1ae32a1SGerd Hoffmann }
585f1ae32a1SGerd Hoffmann 
usb_serial_realize(USBDevice * dev,Error ** errp)58638fff2c9SGonglei static void usb_serial_realize(USBDevice *dev, Error **errp)
587f1ae32a1SGerd Hoffmann {
588bdd5f27eSEduardo Habkost     USBSerialState *s = USB_SERIAL(dev);
5897334d650SGonglei     Error *local_err = NULL;
590f1ae32a1SGerd Hoffmann 
5919d55d1adSGerd Hoffmann     usb_desc_create_serial(dev);
592f1ae32a1SGerd Hoffmann     usb_desc_init(dev);
593da124e62SGerd Hoffmann     dev->auto_attach = 0;
594f1ae32a1SGerd Hoffmann 
59530650701SAnton Nefedov     if (!qemu_chr_fe_backend_connected(&s->cs)) {
59638fff2c9SGonglei         error_setg(errp, "Property chardev is required");
59738fff2c9SGonglei         return;
598f1ae32a1SGerd Hoffmann     }
599f1ae32a1SGerd Hoffmann 
6007334d650SGonglei     usb_check_attach(dev, &local_err);
6017334d650SGonglei     if (local_err) {
6027334d650SGonglei         error_propagate(errp, local_err);
6037334d650SGonglei         return;
6047334d650SGonglei     }
6057334d650SGonglei 
6065345fdb4SMarc-André Lureau     qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read,
60781517ba3SAnton Nefedov                              usb_serial_event, NULL, s, NULL, true);
608f1ae32a1SGerd Hoffmann     usb_serial_handle_reset(dev);
609da124e62SGerd Hoffmann 
61066007a95SMark Cave-Ayland     if ((s->always_plugged || qemu_chr_fe_backend_open(&s->cs)) &&
61166007a95SMark Cave-Ayland         !dev->attached) {
6127334d650SGonglei         usb_device_attach(dev, &error_abort);
6137d553f27SGonglei     }
6145843b6b3SJason Andryuk     s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
615da124e62SGerd Hoffmann }
616f1ae32a1SGerd Hoffmann 
usb_braille_init(void)617*405cf80cSPaolo Bonzini static USBDevice *usb_braille_init(void)
618f1ae32a1SGerd Hoffmann {
619f1ae32a1SGerd Hoffmann     USBDevice *dev;
6200ec7b3e7SMarc-André Lureau     Chardev *cdrv;
621f1ae32a1SGerd Hoffmann 
6224ad6f6cbSPaolo Bonzini     cdrv = qemu_chr_new("braille", "braille", NULL);
623adab8d48SMark Cave-Ayland     if (!cdrv) {
624f1ae32a1SGerd Hoffmann         return NULL;
625adab8d48SMark Cave-Ayland     }
626f1ae32a1SGerd Hoffmann 
627590ce74aSMarkus Armbruster     dev = usb_new("usb-braille");
628f1ae32a1SGerd Hoffmann     qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
629f1ae32a1SGerd Hoffmann     return dev;
630f1ae32a1SGerd Hoffmann }
631f1ae32a1SGerd Hoffmann 
632f1ae32a1SGerd Hoffmann static const VMStateDescription vmstate_usb_serial = {
633f1ae32a1SGerd Hoffmann     .name = "usb-serial",
634f1ae32a1SGerd Hoffmann     .unmigratable = 1,
635f1ae32a1SGerd Hoffmann };
636f1ae32a1SGerd Hoffmann 
637f1ae32a1SGerd Hoffmann static Property serial_properties[] = {
638f1ae32a1SGerd Hoffmann     DEFINE_PROP_CHR("chardev", USBSerialState, cs),
63966007a95SMark Cave-Ayland     DEFINE_PROP_BOOL("always-plugged", USBSerialState, always_plugged, false),
640f1ae32a1SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
641f1ae32a1SGerd Hoffmann };
642f1ae32a1SGerd Hoffmann 
usb_serial_dev_class_init(ObjectClass * klass,void * data)643cdf0d769SGonglei static void usb_serial_dev_class_init(ObjectClass *klass, void *data)
644f1ae32a1SGerd Hoffmann {
645f1ae32a1SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
646f1ae32a1SGerd Hoffmann     USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
647f1ae32a1SGerd Hoffmann 
64838fff2c9SGonglei     uc->realize        = usb_serial_realize;
649f1ae32a1SGerd Hoffmann     uc->handle_reset   = usb_serial_handle_reset;
650f1ae32a1SGerd Hoffmann     uc->handle_control = usb_serial_handle_control;
651f1ae32a1SGerd Hoffmann     uc->handle_data    = usb_serial_handle_data;
652f1ae32a1SGerd Hoffmann     dc->vmsd = &vmstate_usb_serial;
653125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
654f1ae32a1SGerd Hoffmann }
655f1ae32a1SGerd Hoffmann 
656cdf0d769SGonglei static const TypeInfo usb_serial_dev_type_info = {
657cdf0d769SGonglei     .name = TYPE_USB_SERIAL,
658cdf0d769SGonglei     .parent = TYPE_USB_DEVICE,
659cdf0d769SGonglei     .instance_size = sizeof(USBSerialState),
660cdf0d769SGonglei     .abstract = true,
661cdf0d769SGonglei     .class_init = usb_serial_dev_class_init,
662cdf0d769SGonglei };
663cdf0d769SGonglei 
usb_serial_class_initfn(ObjectClass * klass,void * data)664cdf0d769SGonglei static void usb_serial_class_initfn(ObjectClass *klass, void *data)
665cdf0d769SGonglei {
666cdf0d769SGonglei     DeviceClass *dc = DEVICE_CLASS(klass);
667cdf0d769SGonglei     USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
668cdf0d769SGonglei 
669cdf0d769SGonglei     uc->product_desc   = "QEMU USB Serial";
670cdf0d769SGonglei     uc->usb_desc       = &desc_serial;
6714f67d30bSMarc-André Lureau     device_class_set_props(dc, serial_properties);
672cdf0d769SGonglei }
673cdf0d769SGonglei 
6748c43a6f0SAndreas Färber static const TypeInfo serial_info = {
675f1ae32a1SGerd Hoffmann     .name          = "usb-serial",
676cdf0d769SGonglei     .parent        = TYPE_USB_SERIAL,
677f1ae32a1SGerd Hoffmann     .class_init    = usb_serial_class_initfn,
678f1ae32a1SGerd Hoffmann };
679f1ae32a1SGerd Hoffmann 
680f1ae32a1SGerd Hoffmann static Property braille_properties[] = {
681f1ae32a1SGerd Hoffmann     DEFINE_PROP_CHR("chardev", USBSerialState, cs),
682f1ae32a1SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
683f1ae32a1SGerd Hoffmann };
684f1ae32a1SGerd Hoffmann 
usb_braille_class_initfn(ObjectClass * klass,void * data)685f1ae32a1SGerd Hoffmann static void usb_braille_class_initfn(ObjectClass *klass, void *data)
686f1ae32a1SGerd Hoffmann {
687f1ae32a1SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
688f1ae32a1SGerd Hoffmann     USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
689f1ae32a1SGerd Hoffmann 
690f1ae32a1SGerd Hoffmann     uc->product_desc   = "QEMU USB Braille";
691f1ae32a1SGerd Hoffmann     uc->usb_desc       = &desc_braille;
6924f67d30bSMarc-André Lureau     device_class_set_props(dc, braille_properties);
693f1ae32a1SGerd Hoffmann }
694f1ae32a1SGerd Hoffmann 
6958c43a6f0SAndreas Färber static const TypeInfo braille_info = {
696f1ae32a1SGerd Hoffmann     .name          = "usb-braille",
697cdf0d769SGonglei     .parent        = TYPE_USB_SERIAL,
698f1ae32a1SGerd Hoffmann     .class_init    = usb_braille_class_initfn,
699f1ae32a1SGerd Hoffmann };
700f1ae32a1SGerd Hoffmann 
usb_serial_register_types(void)701f1ae32a1SGerd Hoffmann static void usb_serial_register_types(void)
702f1ae32a1SGerd Hoffmann {
703cdf0d769SGonglei     type_register_static(&usb_serial_dev_type_info);
704f1ae32a1SGerd Hoffmann     type_register_static(&serial_info);
705f1ae32a1SGerd Hoffmann     type_register_static(&braille_info);
706f1ae32a1SGerd Hoffmann     usb_legacy_register("usb-braille", "braille", usb_braille_init);
707f1ae32a1SGerd Hoffmann }
708f1ae32a1SGerd Hoffmann 
709f1ae32a1SGerd Hoffmann type_init(usb_serial_register_types)
710