]> git.proxmox.com Git - mirror_qemu.git/blame - hw/usb/dev-serial.c
Move QOM typedefs and add missing includes
[mirror_qemu.git] / hw / usb / dev-serial.c
CommitLineData
a7954218
AZ
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 *
8e31bf38 8 * This code is licensed under the LGPL.
a7954218
AZ
9 */
10
e532b2e0 11#include "qemu/osdep.h"
da34e65c 12#include "qapi/error.h"
f348b6d1 13#include "qemu/cutils.h"
d49b6836 14#include "qemu/error-report.h"
0b8fa32f 15#include "qemu/module.h"
a27bd6c7 16#include "hw/qdev-properties.h"
f1ae32a1 17#include "hw/usb.h"
d6454270 18#include "migration/vmstate.h"
463581a8 19#include "desc.h"
7566c6ef 20#include "chardev/char-serial.h"
4d43a603 21#include "chardev/char-fe.h"
db1015e9 22#include "qom/object.h"
a7954218
AZ
23
24//#define DEBUG_Serial
25
26#ifdef DEBUG_Serial
001faf32
BS
27#define DPRINTF(fmt, ...) \
28do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0)
a7954218 29#else
001faf32 30#define DPRINTF(fmt, ...) do {} while(0)
a7954218
AZ
31#endif
32
30ad5fdd 33#define RECV_BUF (512 - (2 * 8))
a7954218
AZ
34
35/* Commands */
36#define FTDI_RESET 0
37#define FTDI_SET_MDM_CTRL 1
38#define FTDI_SET_FLOW_CTRL 2
39#define FTDI_SET_BAUD 3
40#define FTDI_SET_DATA 4
41#define FTDI_GET_MDM_ST 5
42#define FTDI_SET_EVENT_CHR 6
43#define FTDI_SET_ERROR_CHR 7
44#define FTDI_SET_LATENCY 9
45#define FTDI_GET_LATENCY 10
46
47#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
48#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
49
50/* RESET */
51
52#define FTDI_RESET_SIO 0
53#define FTDI_RESET_RX 1
54#define FTDI_RESET_TX 2
55
56/* SET_MDM_CTRL */
57
a7954218 58#define FTDI_DTR 1
abb8a139 59#define FTDI_SET_DTR (FTDI_DTR << 8)
a7954218 60#define FTDI_RTS 2
abb8a139 61#define FTDI_SET_RTS (FTDI_RTS << 8)
a7954218
AZ
62
63/* SET_FLOW_CTRL */
64
65#define FTDI_RTS_CTS_HS 1
66#define FTDI_DTR_DSR_HS 2
67#define FTDI_XON_XOFF_HS 4
68
69/* SET_DATA */
70
71#define FTDI_PARITY (0x7 << 8)
72#define FTDI_ODD (0x1 << 8)
73#define FTDI_EVEN (0x2 << 8)
74#define FTDI_MARK (0x3 << 8)
75#define FTDI_SPACE (0x4 << 8)
76
77#define FTDI_STOP (0x3 << 11)
78#define FTDI_STOP1 (0x0 << 11)
79#define FTDI_STOP15 (0x1 << 11)
80#define FTDI_STOP2 (0x2 << 11)
81
82/* GET_MDM_ST */
83/* TODO: should be sent every 40ms */
84#define FTDI_CTS (1<<4) // CTS line status
85#define FTDI_DSR (1<<5) // DSR line status
86#define FTDI_RI (1<<6) // RI line status
87#define FTDI_RLSD (1<<7) // Receive Line Signal Detect
88
89/* Status */
90
91#define FTDI_DR (1<<0) // Data Ready
92#define FTDI_OE (1<<1) // Overrun Err
93#define FTDI_PE (1<<2) // Parity Err
94#define FTDI_FE (1<<3) // Framing Err
95#define FTDI_BI (1<<4) // Break Interrupt
96#define FTDI_THRE (1<<5) // Transmitter Holding Register
97#define FTDI_TEMT (1<<6) // Transmitter Empty
98#define FTDI_FIFO (1<<7) // Error in FIFO
99
db1015e9 100struct USBSerialState {
a7954218 101 USBDevice dev;
5843b6b3 102 USBEndpoint *intr;
a7954218 103 uint8_t recv_buf[RECV_BUF];
8109b9b6
AJ
104 uint16_t recv_ptr;
105 uint16_t recv_used;
a7954218
AZ
106 uint8_t event_chr;
107 uint8_t error_chr;
108 uint8_t event_trigger;
a7954218
AZ
109 QEMUSerialSetParams params;
110 int latency; /* ms */
becdfa00 111 CharBackend cs;
db1015e9
EH
112};
113typedef struct USBSerialState USBSerialState;
a7954218 114
cdf0d769
GA
115#define TYPE_USB_SERIAL "usb-serial-dev"
116#define USB_SERIAL_DEV(obj) OBJECT_CHECK(USBSerialState, (obj), TYPE_USB_SERIAL)
117
f29783f7
GH
118enum {
119 STR_MANUFACTURER = 1,
120 STR_PRODUCT_SERIAL,
121 STR_PRODUCT_BRAILLE,
122 STR_SERIALNUMBER,
a7954218
AZ
123};
124
f29783f7 125static const USBDescStrings desc_strings = {
93bfef4c 126 [STR_MANUFACTURER] = "QEMU",
f29783f7 127 [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL",
2964cd9b 128 [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE",
f29783f7
GH
129 [STR_SERIALNUMBER] = "1",
130};
131
132static const USBDescIface desc_iface0 = {
133 .bInterfaceNumber = 0,
134 .bNumEndpoints = 2,
135 .bInterfaceClass = 0xff,
136 .bInterfaceSubClass = 0xff,
137 .bInterfaceProtocol = 0xff,
138 .eps = (USBDescEndpoint[]) {
139 {
140 .bEndpointAddress = USB_DIR_IN | 0x01,
141 .bmAttributes = USB_ENDPOINT_XFER_BULK,
142 .wMaxPacketSize = 64,
143 },{
144 .bEndpointAddress = USB_DIR_OUT | 0x02,
145 .bmAttributes = USB_ENDPOINT_XFER_BULK,
146 .wMaxPacketSize = 64,
147 },
148 }
149};
150
151static const USBDescDevice desc_device = {
152 .bcdUSB = 0x0200,
153 .bMaxPacketSize0 = 8,
154 .bNumConfigurations = 1,
155 .confs = (USBDescConfig[]) {
156 {
157 .bNumInterfaces = 1,
158 .bConfigurationValue = 1,
5843b6b3 159 .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP,
f29783f7 160 .bMaxPower = 50,
add75088 161 .nif = 1,
f29783f7
GH
162 .ifs = &desc_iface0,
163 },
164 },
165};
166
167static const USBDesc desc_serial = {
168 .id = {
169 .idVendor = 0x0403,
170 .idProduct = 0x6001,
171 .bcdDevice = 0x0400,
172 .iManufacturer = STR_MANUFACTURER,
173 .iProduct = STR_PRODUCT_SERIAL,
174 .iSerialNumber = STR_SERIALNUMBER,
175 },
176 .full = &desc_device,
177 .str = desc_strings,
178};
179
180static const USBDesc desc_braille = {
181 .id = {
182 .idVendor = 0x0403,
183 .idProduct = 0xfe72,
184 .bcdDevice = 0x0400,
185 .iManufacturer = STR_MANUFACTURER,
186 .iProduct = STR_PRODUCT_BRAILLE,
187 .iSerialNumber = STR_SERIALNUMBER,
188 },
189 .full = &desc_device,
190 .str = desc_strings,
a7954218
AZ
191};
192
193static void usb_serial_reset(USBSerialState *s)
194{
195 /* TODO: Set flow control to none */
196 s->event_chr = 0x0d;
197 s->event_trigger = 0;
198 s->recv_ptr = 0;
199 s->recv_used = 0;
200 /* TODO: purge in char driver */
a7954218
AZ
201}
202
203static void usb_serial_handle_reset(USBDevice *dev)
204{
205 USBSerialState *s = (USBSerialState *)dev;
206
207 DPRINTF("Reset\n");
208
209 usb_serial_reset(s);
210 /* TODO: Reset char device, send BREAK? */
211}
212
abb8a139
AJ
213static uint8_t usb_get_modem_lines(USBSerialState *s)
214{
215 int flags;
216 uint8_t ret;
217
5345fdb4 218 if (qemu_chr_fe_ioctl(&s->cs,
becdfa00 219 CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
abb8a139 220 return FTDI_CTS|FTDI_DSR|FTDI_RLSD;
becdfa00 221 }
abb8a139
AJ
222
223 ret = 0;
224 if (flags & CHR_TIOCM_CTS)
225 ret |= FTDI_CTS;
226 if (flags & CHR_TIOCM_DSR)
227 ret |= FTDI_DSR;
228 if (flags & CHR_TIOCM_RI)
229 ret |= FTDI_RI;
230 if (flags & CHR_TIOCM_CAR)
231 ret |= FTDI_RLSD;
232
233 return ret;
234}
235
9a77a0f5 236static void usb_serial_handle_control(USBDevice *dev, USBPacket *p,
007fd62f 237 int request, int value, int index, int length, uint8_t *data)
a7954218
AZ
238{
239 USBSerialState *s = (USBSerialState *)dev;
f29783f7
GH
240 int ret;
241
242 DPRINTF("got control %x, value %x\n",request, value);
007fd62f 243 ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
f29783f7 244 if (ret >= 0) {
9a77a0f5 245 return;
f29783f7 246 }
a7954218 247
a7954218 248 switch (request) {
a7954218 249 case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
a7954218
AZ
250 break;
251
252 /* Class specific requests. */
253 case DeviceOutVendor | FTDI_RESET:
254 switch (value) {
255 case FTDI_RESET_SIO:
256 usb_serial_reset(s);
257 break;
258 case FTDI_RESET_RX:
259 s->recv_ptr = 0;
260 s->recv_used = 0;
261 /* TODO: purge from char device */
262 break;
263 case FTDI_RESET_TX:
264 /* TODO: purge from char device */
265 break;
266 }
267 break;
268 case DeviceOutVendor | FTDI_SET_MDM_CTRL:
abb8a139
AJ
269 {
270 static int flags;
5345fdb4 271 qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
abb8a139
AJ
272 if (value & FTDI_SET_RTS) {
273 if (value & FTDI_RTS)
274 flags |= CHR_TIOCM_RTS;
275 else
276 flags &= ~CHR_TIOCM_RTS;
277 }
278 if (value & FTDI_SET_DTR) {
279 if (value & FTDI_DTR)
280 flags |= CHR_TIOCM_DTR;
281 else
282 flags &= ~CHR_TIOCM_DTR;
283 }
5345fdb4 284 qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
a7954218 285 break;
abb8a139 286 }
a7954218
AZ
287 case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
288 /* TODO: ioctl */
289 break;
290 case DeviceOutVendor | FTDI_SET_BAUD: {
291 static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
292 int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
293 | ((index & 1) << 2)];
294 int divisor = value & 0x3fff;
295
296 /* chip special cases */
297 if (divisor == 1 && subdivisor8 == 0)
298 subdivisor8 = 4;
299 if (divisor == 0 && subdivisor8 == 0)
300 divisor = 1;
301
302 s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
5345fdb4 303 qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
a7954218
AZ
304 break;
305 }
306 case DeviceOutVendor | FTDI_SET_DATA:
307 switch (value & FTDI_PARITY) {
308 case 0:
309 s->params.parity = 'N';
310 break;
311 case FTDI_ODD:
312 s->params.parity = 'O';
313 break;
314 case FTDI_EVEN:
315 s->params.parity = 'E';
316 break;
317 default:
318 DPRINTF("unsupported parity %d\n", value & FTDI_PARITY);
319 goto fail;
320 }
321 switch (value & FTDI_STOP) {
322 case FTDI_STOP1:
323 s->params.stop_bits = 1;
324 break;
325 case FTDI_STOP2:
326 s->params.stop_bits = 2;
327 break;
328 default:
329 DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
330 goto fail;
331 }
5345fdb4 332 qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
a7954218
AZ
333 /* TODO: TX ON/OFF */
334 break;
335 case DeviceInVendor | FTDI_GET_MDM_ST:
abb8a139 336 data[0] = usb_get_modem_lines(s) | 1;
647ee987 337 data[1] = FTDI_THRE | FTDI_TEMT;
9a77a0f5 338 p->actual_length = 2;
a7954218
AZ
339 break;
340 case DeviceOutVendor | FTDI_SET_EVENT_CHR:
341 /* TODO: handle it */
342 s->event_chr = value;
343 break;
344 case DeviceOutVendor | FTDI_SET_ERROR_CHR:
345 /* TODO: handle it */
346 s->error_chr = value;
347 break;
348 case DeviceOutVendor | FTDI_SET_LATENCY:
349 s->latency = value;
350 break;
351 case DeviceInVendor | FTDI_GET_LATENCY:
352 data[0] = s->latency;
9a77a0f5 353 p->actual_length = 1;
a7954218
AZ
354 break;
355 default:
356 fail:
357 DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
9a77a0f5 358 p->status = USB_RET_STALL;
a7954218
AZ
359 break;
360 }
a7954218
AZ
361}
362
2bcf4e9f
JA
363static void usb_serial_token_in(USBSerialState *s, USBPacket *p)
364{
87db78f7
JA
365 const int max_packet_size = desc_iface0.eps[0].wMaxPacketSize;
366 int packet_len;
2bcf4e9f
JA
367 uint8_t header[2];
368
87db78f7
JA
369 packet_len = p->iov.size;
370 if (packet_len <= 2) {
2bcf4e9f
JA
371 p->status = USB_RET_NAK;
372 return;
373 }
87db78f7 374
2bcf4e9f
JA
375 header[0] = usb_get_modem_lines(s) | 1;
376 /* We do not have the uart details */
377 /* handle serial break */
378 if (s->event_trigger && s->event_trigger & FTDI_BI) {
379 s->event_trigger &= ~FTDI_BI;
380 header[1] = FTDI_BI;
381 usb_packet_copy(p, header, 2);
382 return;
383 } else {
384 header[1] = 0;
385 }
87db78f7
JA
386
387 if (!s->recv_used) {
2bcf4e9f
JA
388 p->status = USB_RET_NAK;
389 return;
390 }
87db78f7
JA
391
392 while (s->recv_used && packet_len > 2) {
393 int first_len, len;
394
395 len = MIN(packet_len, max_packet_size);
396 len -= 2;
397 if (len > s->recv_used) {
398 len = s->recv_used;
399 }
400
401 first_len = RECV_BUF - s->recv_ptr;
402 if (first_len > len) {
403 first_len = len;
404 }
405 usb_packet_copy(p, header, 2);
406 usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len);
407 if (len > first_len) {
408 usb_packet_copy(p, s->recv_buf, len - first_len);
409 }
410 s->recv_used -= len;
411 s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
412 packet_len -= len + 2;
2bcf4e9f 413 }
2bcf4e9f
JA
414
415 return;
416}
417
9a77a0f5 418static void usb_serial_handle_data(USBDevice *dev, USBPacket *p)
a7954218
AZ
419{
420 USBSerialState *s = (USBSerialState *)dev;
079d0b7f 421 uint8_t devep = p->ep->nr;
9440b7e5 422 struct iovec *iov;
2bcf4e9f 423 int i;
a7954218
AZ
424
425 switch (p->pid) {
426 case USB_TOKEN_OUT:
427 if (devep != 2)
428 goto fail;
9440b7e5
GH
429 for (i = 0; i < p->iov.niov; i++) {
430 iov = p->iov.iov + i;
6ab3fc32
DB
431 /* XXX this blocks entire thread. Rewrite to use
432 * qemu_chr_fe_write and background I/O callbacks */
5345fdb4 433 qemu_chr_fe_write_all(&s->cs, iov->iov_base, iov->iov_len);
9440b7e5 434 }
9a77a0f5 435 p->actual_length = p->iov.size;
a7954218
AZ
436 break;
437
438 case USB_TOKEN_IN:
439 if (devep != 1)
440 goto fail;
2bcf4e9f 441 usb_serial_token_in(s, p);
a7954218
AZ
442 break;
443
444 default:
445 DPRINTF("Bad token\n");
446 fail:
9a77a0f5 447 p->status = USB_RET_STALL;
a7954218
AZ
448 break;
449 }
a7954218
AZ
450}
451
8fcd3692 452static int usb_serial_can_read(void *opaque)
a7954218
AZ
453{
454 USBSerialState *s = opaque;
da124e62
GH
455
456 if (!s->dev.attached) {
457 return 0;
458 }
a7954218
AZ
459 return RECV_BUF - s->recv_used;
460}
461
8fcd3692 462static void usb_serial_read(void *opaque, const uint8_t *buf, int size)
a7954218
AZ
463{
464 USBSerialState *s = opaque;
4ab4183d
DA
465 int first_size, start;
466
467 /* room in the buffer? */
468 if (size > (RECV_BUF - s->recv_used))
469 size = RECV_BUF - s->recv_used;
470
471 start = s->recv_ptr + s->recv_used;
472 if (start < RECV_BUF) {
473 /* copy data to end of buffer */
474 first_size = RECV_BUF - start;
475 if (first_size > size)
476 first_size = size;
477
478 memcpy(s->recv_buf + start, buf, first_size);
479
480 /* wrap around to front if needed */
481 if (size > first_size)
482 memcpy(s->recv_buf, buf + first_size, size - first_size);
483 } else {
484 start -= RECV_BUF;
485 memcpy(s->recv_buf + start, buf, size);
486 }
a7954218 487 s->recv_used += size;
5843b6b3
JA
488
489 usb_wakeup(s->intr, 0);
a7954218
AZ
490}
491
083b266f 492static void usb_serial_event(void *opaque, QEMUChrEvent event)
a7954218
AZ
493{
494 USBSerialState *s = opaque;
495
496 switch (event) {
497 case CHR_EVENT_BREAK:
7e57f049 498 s->event_trigger |= FTDI_BI;
a7954218 499 break;
b6b8df56 500 case CHR_EVENT_OPENED:
da124e62 501 if (!s->dev.attached) {
7334d650 502 usb_device_attach(&s->dev, &error_abort);
da124e62
GH
503 }
504 break;
505 case CHR_EVENT_CLOSED:
506 if (s->dev.attached) {
507 usb_device_detach(&s->dev);
508 }
a7954218 509 break;
c263158e
PMD
510 case CHR_EVENT_MUX_IN:
511 case CHR_EVENT_MUX_OUT:
512 /* Ignore */
513 break;
a7954218
AZ
514 }
515}
516
38fff2c9 517static void usb_serial_realize(USBDevice *dev, Error **errp)
806b6024 518{
cdf0d769 519 USBSerialState *s = USB_SERIAL_DEV(dev);
7334d650 520 Error *local_err = NULL;
a980a065 521
9d55d1ad 522 usb_desc_create_serial(dev);
a980a065 523 usb_desc_init(dev);
da124e62 524 dev->auto_attach = 0;
2b0efdc3 525
30650701 526 if (!qemu_chr_fe_backend_connected(&s->cs)) {
38fff2c9
GA
527 error_setg(errp, "Property chardev is required");
528 return;
81bf96d3
MA
529 }
530
7334d650
GA
531 usb_check_attach(dev, &local_err);
532 if (local_err) {
533 error_propagate(errp, local_err);
534 return;
535 }
536
5345fdb4 537 qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read,
81517ba3 538 usb_serial_event, NULL, s, NULL, true);
2b0efdc3 539 usb_serial_handle_reset(dev);
da124e62 540
30650701 541 if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) {
7334d650 542 usb_device_attach(dev, &error_abort);
da124e62 543 }
5843b6b3 544 s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
806b6024
GH
545}
546
590ce74a 547static USBDevice *usb_braille_init(const char *unused)
2b0efdc3
GH
548{
549 USBDevice *dev;
0ec7b3e7 550 Chardev *cdrv;
2b0efdc3 551
4ad6f6cb 552 cdrv = qemu_chr_new("braille", "braille", NULL);
2b0efdc3
GH
553 if (!cdrv)
554 return NULL;
806b6024 555
590ce74a 556 dev = usb_new("usb-braille");
2b0efdc3 557 qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
2b0efdc3 558 return dev;
a7954218 559}
806b6024 560
98e51ec9
GH
561static const VMStateDescription vmstate_usb_serial = {
562 .name = "usb-serial",
563 .unmigratable = 1,
564};
565
39bffca2
AL
566static Property serial_properties[] = {
567 DEFINE_PROP_CHR("chardev", USBSerialState, cs),
568 DEFINE_PROP_END_OF_LIST(),
569};
570
cdf0d769 571static void usb_serial_dev_class_init(ObjectClass *klass, void *data)
62aed765 572{
39bffca2 573 DeviceClass *dc = DEVICE_CLASS(klass);
62aed765
AL
574 USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
575
cdf0d769 576 uc->realize = usb_serial_realize;
62aed765
AL
577 uc->handle_reset = usb_serial_handle_reset;
578 uc->handle_control = usb_serial_handle_control;
579 uc->handle_data = usb_serial_handle_data;
39bffca2 580 dc->vmsd = &vmstate_usb_serial;
125ee0ed 581 set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
62aed765
AL
582}
583
cdf0d769
GA
584static const TypeInfo usb_serial_dev_type_info = {
585 .name = TYPE_USB_SERIAL,
586 .parent = TYPE_USB_DEVICE,
587 .instance_size = sizeof(USBSerialState),
588 .abstract = true,
589 .class_init = usb_serial_dev_class_init,
590};
591
592static void usb_serial_class_initfn(ObjectClass *klass, void *data)
593{
594 DeviceClass *dc = DEVICE_CLASS(klass);
595 USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
596
597 uc->product_desc = "QEMU USB Serial";
598 uc->usb_desc = &desc_serial;
4f67d30b 599 device_class_set_props(dc, serial_properties);
cdf0d769
GA
600}
601
8c43a6f0 602static const TypeInfo serial_info = {
39bffca2 603 .name = "usb-serial",
cdf0d769 604 .parent = TYPE_USB_SERIAL,
39bffca2
AL
605 .class_init = usb_serial_class_initfn,
606};
607
608static Property braille_properties[] = {
609 DEFINE_PROP_CHR("chardev", USBSerialState, cs),
610 DEFINE_PROP_END_OF_LIST(),
2b0efdc3
GH
611};
612
62aed765
AL
613static void usb_braille_class_initfn(ObjectClass *klass, void *data)
614{
39bffca2 615 DeviceClass *dc = DEVICE_CLASS(klass);
62aed765
AL
616 USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
617
62aed765
AL
618 uc->product_desc = "QEMU USB Braille";
619 uc->usb_desc = &desc_braille;
4f67d30b 620 device_class_set_props(dc, braille_properties);
62aed765
AL
621}
622
8c43a6f0 623static const TypeInfo braille_info = {
39bffca2 624 .name = "usb-braille",
cdf0d769 625 .parent = TYPE_USB_SERIAL,
39bffca2 626 .class_init = usb_braille_class_initfn,
806b6024
GH
627};
628
83f7d43a 629static void usb_serial_register_types(void)
806b6024 630{
cdf0d769 631 type_register_static(&usb_serial_dev_type_info);
39bffca2 632 type_register_static(&serial_info);
39bffca2 633 type_register_static(&braille_info);
ba02430f 634 usb_legacy_register("usb-braille", "braille", usb_braille_init);
806b6024 635}
83f7d43a
AF
636
637type_init(usb_serial_register_types)