|
|
1.1 ! root 1: /* ! 2: * FTDI FT232BM Device emulation ! 3: * ! 4: * Copyright (c) 2006 CodeSourcery. ! 5: * Copyright (c) 2008 Samuel Thibault <[email protected]> ! 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-common.h" ! 12: #include "qemu-error.h" ! 13: #include "hw/usb.h" ! 14: #include "hw/usb/desc.h" ! 15: #include "qemu-char.h" ! 16: ! 17: //#define DEBUG_Serial ! 18: ! 19: #ifdef DEBUG_Serial ! 20: #define DPRINTF(fmt, ...) \ ! 21: do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) ! 22: #else ! 23: #define DPRINTF(fmt, ...) do {} while(0) ! 24: #endif ! 25: ! 26: #define RECV_BUF 384 ! 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: #define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) ! 41: #define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) ! 42: ! 43: /* RESET */ ! 44: ! 45: #define FTDI_RESET_SIO 0 ! 46: #define FTDI_RESET_RX 1 ! 47: #define FTDI_RESET_TX 2 ! 48: ! 49: /* SET_MDM_CTRL */ ! 50: ! 51: #define FTDI_DTR 1 ! 52: #define FTDI_SET_DTR (FTDI_DTR << 8) ! 53: #define FTDI_RTS 2 ! 54: #define FTDI_SET_RTS (FTDI_RTS << 8) ! 55: ! 56: /* SET_FLOW_CTRL */ ! 57: ! 58: #define FTDI_RTS_CTS_HS 1 ! 59: #define FTDI_DTR_DSR_HS 2 ! 60: #define FTDI_XON_XOFF_HS 4 ! 61: ! 62: /* SET_DATA */ ! 63: ! 64: #define FTDI_PARITY (0x7 << 8) ! 65: #define FTDI_ODD (0x1 << 8) ! 66: #define FTDI_EVEN (0x2 << 8) ! 67: #define FTDI_MARK (0x3 << 8) ! 68: #define FTDI_SPACE (0x4 << 8) ! 69: ! 70: #define FTDI_STOP (0x3 << 11) ! 71: #define FTDI_STOP1 (0x0 << 11) ! 72: #define FTDI_STOP15 (0x1 << 11) ! 73: #define FTDI_STOP2 (0x2 << 11) ! 74: ! 75: /* GET_MDM_ST */ ! 76: /* TODO: should be sent every 40ms */ ! 77: #define FTDI_CTS (1<<4) // CTS line status ! 78: #define FTDI_DSR (1<<5) // DSR line status ! 79: #define FTDI_RI (1<<6) // RI line status ! 80: #define FTDI_RLSD (1<<7) // Receive Line Signal Detect ! 81: ! 82: /* Status */ ! 83: ! 84: #define FTDI_DR (1<<0) // Data Ready ! 85: #define FTDI_OE (1<<1) // Overrun Err ! 86: #define FTDI_PE (1<<2) // Parity Err ! 87: #define FTDI_FE (1<<3) // Framing Err ! 88: #define FTDI_BI (1<<4) // Break Interrupt ! 89: #define FTDI_THRE (1<<5) // Transmitter Holding Register ! 90: #define FTDI_TEMT (1<<6) // Transmitter Empty ! 91: #define FTDI_FIFO (1<<7) // Error in FIFO ! 92: ! 93: typedef struct { ! 94: USBDevice dev; ! 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: QEMUSerialSetParams params; ! 102: int latency; /* ms */ ! 103: CharDriverState *cs; ! 104: } USBSerialState; ! 105: ! 106: enum { ! 107: STR_MANUFACTURER = 1, ! 108: STR_PRODUCT_SERIAL, ! 109: STR_PRODUCT_BRAILLE, ! 110: STR_SERIALNUMBER, ! 111: }; ! 112: ! 113: static const USBDescStrings desc_strings = { ! 114: [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, ! 115: [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL", ! 116: [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE", ! 117: [STR_SERIALNUMBER] = "1", ! 118: }; ! 119: ! 120: static const USBDescIface desc_iface0 = { ! 121: .bInterfaceNumber = 0, ! 122: .bNumEndpoints = 2, ! 123: .bInterfaceClass = 0xff, ! 124: .bInterfaceSubClass = 0xff, ! 125: .bInterfaceProtocol = 0xff, ! 126: .eps = (USBDescEndpoint[]) { ! 127: { ! 128: .bEndpointAddress = USB_DIR_IN | 0x01, ! 129: .bmAttributes = USB_ENDPOINT_XFER_BULK, ! 130: .wMaxPacketSize = 64, ! 131: },{ ! 132: .bEndpointAddress = USB_DIR_OUT | 0x02, ! 133: .bmAttributes = USB_ENDPOINT_XFER_BULK, ! 134: .wMaxPacketSize = 64, ! 135: }, ! 136: } ! 137: }; ! 138: ! 139: static const USBDescDevice desc_device = { ! 140: .bcdUSB = 0x0200, ! 141: .bMaxPacketSize0 = 8, ! 142: .bNumConfigurations = 1, ! 143: .confs = (USBDescConfig[]) { ! 144: { ! 145: .bNumInterfaces = 1, ! 146: .bConfigurationValue = 1, ! 147: .bmAttributes = 0x80, ! 148: .bMaxPower = 50, ! 149: .nif = 1, ! 150: .ifs = &desc_iface0, ! 151: }, ! 152: }, ! 153: }; ! 154: ! 155: static const USBDesc desc_serial = { ! 156: .id = { ! 157: .idVendor = 0x0403, ! 158: .idProduct = 0x6001, ! 159: .bcdDevice = 0x0400, ! 160: .iManufacturer = STR_MANUFACTURER, ! 161: .iProduct = STR_PRODUCT_SERIAL, ! 162: .iSerialNumber = STR_SERIALNUMBER, ! 163: }, ! 164: .full = &desc_device, ! 165: .str = desc_strings, ! 166: }; ! 167: ! 168: static const USBDesc desc_braille = { ! 169: .id = { ! 170: .idVendor = 0x0403, ! 171: .idProduct = 0xfe72, ! 172: .bcdDevice = 0x0400, ! 173: .iManufacturer = STR_MANUFACTURER, ! 174: .iProduct = STR_PRODUCT_BRAILLE, ! 175: .iSerialNumber = STR_SERIALNUMBER, ! 176: }, ! 177: .full = &desc_device, ! 178: .str = desc_strings, ! 179: }; ! 180: ! 181: static void usb_serial_reset(USBSerialState *s) ! 182: { ! 183: /* TODO: Set flow control to none */ ! 184: s->event_chr = 0x0d; ! 185: s->event_trigger = 0; ! 186: s->recv_ptr = 0; ! 187: s->recv_used = 0; ! 188: /* TODO: purge in char driver */ ! 189: } ! 190: ! 191: static void usb_serial_handle_reset(USBDevice *dev) ! 192: { ! 193: USBSerialState *s = (USBSerialState *)dev; ! 194: ! 195: DPRINTF("Reset\n"); ! 196: ! 197: usb_serial_reset(s); ! 198: /* TODO: Reset char device, send BREAK? */ ! 199: } ! 200: ! 201: static uint8_t usb_get_modem_lines(USBSerialState *s) ! 202: { ! 203: int flags; ! 204: uint8_t ret; ! 205: ! 206: if (qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) ! 207: return FTDI_CTS|FTDI_DSR|FTDI_RLSD; ! 208: ! 209: ret = 0; ! 210: if (flags & CHR_TIOCM_CTS) ! 211: ret |= FTDI_CTS; ! 212: if (flags & CHR_TIOCM_DSR) ! 213: ret |= FTDI_DSR; ! 214: if (flags & CHR_TIOCM_RI) ! 215: ret |= FTDI_RI; ! 216: if (flags & CHR_TIOCM_CAR) ! 217: ret |= FTDI_RLSD; ! 218: ! 219: return ret; ! 220: } ! 221: ! 222: static int usb_serial_handle_control(USBDevice *dev, USBPacket *p, ! 223: int request, int value, int index, int length, uint8_t *data) ! 224: { ! 225: USBSerialState *s = (USBSerialState *)dev; ! 226: int ret; ! 227: ! 228: DPRINTF("got control %x, value %x\n",request, value); ! 229: ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ! 230: if (ret >= 0) { ! 231: return ret; ! 232: } ! 233: ! 234: ret = 0; ! 235: switch (request) { ! 236: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: ! 237: ret = 0; ! 238: break; ! 239: ! 240: /* Class specific requests. */ ! 241: case DeviceOutVendor | FTDI_RESET: ! 242: switch (value) { ! 243: case FTDI_RESET_SIO: ! 244: usb_serial_reset(s); ! 245: break; ! 246: case FTDI_RESET_RX: ! 247: s->recv_ptr = 0; ! 248: s->recv_used = 0; ! 249: /* TODO: purge from char device */ ! 250: break; ! 251: case FTDI_RESET_TX: ! 252: /* TODO: purge from char device */ ! 253: break; ! 254: } ! 255: break; ! 256: case DeviceOutVendor | FTDI_SET_MDM_CTRL: ! 257: { ! 258: static int flags; ! 259: qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); ! 260: if (value & FTDI_SET_RTS) { ! 261: if (value & FTDI_RTS) ! 262: flags |= CHR_TIOCM_RTS; ! 263: else ! 264: flags &= ~CHR_TIOCM_RTS; ! 265: } ! 266: if (value & FTDI_SET_DTR) { ! 267: if (value & FTDI_DTR) ! 268: flags |= CHR_TIOCM_DTR; ! 269: else ! 270: flags &= ~CHR_TIOCM_DTR; ! 271: } ! 272: qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); ! 273: break; ! 274: } ! 275: case DeviceOutVendor | FTDI_SET_FLOW_CTRL: ! 276: /* TODO: ioctl */ ! 277: break; ! 278: case DeviceOutVendor | FTDI_SET_BAUD: { ! 279: static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 }; ! 280: int subdivisor8 = subdivisors8[((value & 0xc000) >> 14) ! 281: | ((index & 1) << 2)]; ! 282: int divisor = value & 0x3fff; ! 283: ! 284: /* chip special cases */ ! 285: if (divisor == 1 && subdivisor8 == 0) ! 286: subdivisor8 = 4; ! 287: if (divisor == 0 && subdivisor8 == 0) ! 288: divisor = 1; ! 289: ! 290: s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); ! 291: qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); ! 292: break; ! 293: } ! 294: case DeviceOutVendor | FTDI_SET_DATA: ! 295: switch (value & FTDI_PARITY) { ! 296: case 0: ! 297: s->params.parity = 'N'; ! 298: break; ! 299: case FTDI_ODD: ! 300: s->params.parity = 'O'; ! 301: break; ! 302: case FTDI_EVEN: ! 303: s->params.parity = 'E'; ! 304: break; ! 305: default: ! 306: DPRINTF("unsupported parity %d\n", value & FTDI_PARITY); ! 307: goto fail; ! 308: } ! 309: switch (value & FTDI_STOP) { ! 310: case FTDI_STOP1: ! 311: s->params.stop_bits = 1; ! 312: break; ! 313: case FTDI_STOP2: ! 314: s->params.stop_bits = 2; ! 315: break; ! 316: default: ! 317: DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP); ! 318: goto fail; ! 319: } ! 320: qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); ! 321: /* TODO: TX ON/OFF */ ! 322: break; ! 323: case DeviceInVendor | FTDI_GET_MDM_ST: ! 324: data[0] = usb_get_modem_lines(s) | 1; ! 325: data[1] = 0; ! 326: ret = 2; ! 327: break; ! 328: case DeviceOutVendor | FTDI_SET_EVENT_CHR: ! 329: /* TODO: handle it */ ! 330: s->event_chr = value; ! 331: break; ! 332: case DeviceOutVendor | FTDI_SET_ERROR_CHR: ! 333: /* TODO: handle it */ ! 334: s->error_chr = value; ! 335: break; ! 336: case DeviceOutVendor | FTDI_SET_LATENCY: ! 337: s->latency = value; ! 338: break; ! 339: case DeviceInVendor | FTDI_GET_LATENCY: ! 340: data[0] = s->latency; ! 341: ret = 1; ! 342: break; ! 343: default: ! 344: fail: ! 345: DPRINTF("got unsupported/bogus control %x, value %x\n", request, value); ! 346: ret = USB_RET_STALL; ! 347: break; ! 348: } ! 349: return ret; ! 350: } ! 351: ! 352: static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) ! 353: { ! 354: USBSerialState *s = (USBSerialState *)dev; ! 355: int i, ret = 0; ! 356: uint8_t devep = p->ep->nr; ! 357: struct iovec *iov; ! 358: uint8_t header[2]; ! 359: int first_len, len; ! 360: ! 361: switch (p->pid) { ! 362: case USB_TOKEN_OUT: ! 363: if (devep != 2) ! 364: goto fail; ! 365: for (i = 0; i < p->iov.niov; i++) { ! 366: iov = p->iov.iov + i; ! 367: qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len); ! 368: } ! 369: break; ! 370: ! 371: case USB_TOKEN_IN: ! 372: if (devep != 1) ! 373: goto fail; ! 374: first_len = RECV_BUF - s->recv_ptr; ! 375: len = p->iov.size; ! 376: if (len <= 2) { ! 377: ret = USB_RET_NAK; ! 378: break; ! 379: } ! 380: header[0] = usb_get_modem_lines(s) | 1; ! 381: /* We do not have the uart details */ ! 382: /* handle serial break */ ! 383: if (s->event_trigger && s->event_trigger & FTDI_BI) { ! 384: s->event_trigger &= ~FTDI_BI; ! 385: header[1] = FTDI_BI; ! 386: usb_packet_copy(p, header, 2); ! 387: ret = 2; ! 388: break; ! 389: } else { ! 390: header[1] = 0; ! 391: } ! 392: len -= 2; ! 393: if (len > s->recv_used) ! 394: len = s->recv_used; ! 395: if (!len) { ! 396: ret = USB_RET_NAK; ! 397: break; ! 398: } ! 399: if (first_len > len) ! 400: first_len = len; ! 401: usb_packet_copy(p, header, 2); ! 402: usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); ! 403: if (len > first_len) ! 404: usb_packet_copy(p, s->recv_buf, len - first_len); ! 405: s->recv_used -= len; ! 406: s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; ! 407: ret = len + 2; ! 408: break; ! 409: ! 410: default: ! 411: DPRINTF("Bad token\n"); ! 412: fail: ! 413: ret = USB_RET_STALL; ! 414: break; ! 415: } ! 416: ! 417: return ret; ! 418: } ! 419: ! 420: static void usb_serial_handle_destroy(USBDevice *dev) ! 421: { ! 422: USBSerialState *s = (USBSerialState *)dev; ! 423: ! 424: qemu_chr_delete(s->cs); ! 425: } ! 426: ! 427: static int usb_serial_can_read(void *opaque) ! 428: { ! 429: USBSerialState *s = opaque; ! 430: return RECV_BUF - s->recv_used; ! 431: } ! 432: ! 433: static void usb_serial_read(void *opaque, const uint8_t *buf, int size) ! 434: { ! 435: USBSerialState *s = opaque; ! 436: int first_size, start; ! 437: ! 438: /* room in the buffer? */ ! 439: if (size > (RECV_BUF - s->recv_used)) ! 440: size = RECV_BUF - s->recv_used; ! 441: ! 442: start = s->recv_ptr + s->recv_used; ! 443: if (start < RECV_BUF) { ! 444: /* copy data to end of buffer */ ! 445: first_size = RECV_BUF - start; ! 446: if (first_size > size) ! 447: first_size = size; ! 448: ! 449: memcpy(s->recv_buf + start, buf, first_size); ! 450: ! 451: /* wrap around to front if needed */ ! 452: if (size > first_size) ! 453: memcpy(s->recv_buf, buf + first_size, size - first_size); ! 454: } else { ! 455: start -= RECV_BUF; ! 456: memcpy(s->recv_buf + start, buf, size); ! 457: } ! 458: s->recv_used += size; ! 459: } ! 460: ! 461: static void usb_serial_event(void *opaque, int event) ! 462: { ! 463: USBSerialState *s = opaque; ! 464: ! 465: switch (event) { ! 466: case CHR_EVENT_BREAK: ! 467: s->event_trigger |= FTDI_BI; ! 468: break; ! 469: case CHR_EVENT_FOCUS: ! 470: break; ! 471: case CHR_EVENT_OPENED: ! 472: usb_serial_reset(s); ! 473: /* TODO: Reset USB port */ ! 474: break; ! 475: } ! 476: } ! 477: ! 478: static int usb_serial_initfn(USBDevice *dev) ! 479: { ! 480: USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev); ! 481: ! 482: usb_desc_create_serial(dev); ! 483: usb_desc_init(dev); ! 484: ! 485: if (!s->cs) { ! 486: error_report("Property chardev is required"); ! 487: return -1; ! 488: } ! 489: ! 490: qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read, ! 491: usb_serial_event, s); ! 492: usb_serial_handle_reset(dev); ! 493: return 0; ! 494: } ! 495: ! 496: static USBDevice *usb_serial_init(USBBus *bus, const char *filename) ! 497: { ! 498: USBDevice *dev; ! 499: CharDriverState *cdrv; ! 500: uint32_t vendorid = 0, productid = 0; ! 501: char label[32]; ! 502: static int index; ! 503: ! 504: while (*filename && *filename != ':') { ! 505: const char *p; ! 506: char *e; ! 507: if (strstart(filename, "vendorid=", &p)) { ! 508: vendorid = strtol(p, &e, 16); ! 509: if (e == p || (*e && *e != ',' && *e != ':')) { ! 510: error_report("bogus vendor ID %s", p); ! 511: return NULL; ! 512: } ! 513: filename = e; ! 514: } else if (strstart(filename, "productid=", &p)) { ! 515: productid = strtol(p, &e, 16); ! 516: if (e == p || (*e && *e != ',' && *e != ':')) { ! 517: error_report("bogus product ID %s", p); ! 518: return NULL; ! 519: } ! 520: filename = e; ! 521: } else { ! 522: error_report("unrecognized serial USB option %s", filename); ! 523: return NULL; ! 524: } ! 525: while(*filename == ',') ! 526: filename++; ! 527: } ! 528: if (!*filename) { ! 529: error_report("character device specification needed"); ! 530: return NULL; ! 531: } ! 532: filename++; ! 533: ! 534: snprintf(label, sizeof(label), "usbserial%d", index++); ! 535: cdrv = qemu_chr_new(label, filename, NULL); ! 536: if (!cdrv) ! 537: return NULL; ! 538: ! 539: dev = usb_create(bus, "usb-serial"); ! 540: if (!dev) { ! 541: return NULL; ! 542: } ! 543: qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); ! 544: if (vendorid) ! 545: qdev_prop_set_uint16(&dev->qdev, "vendorid", vendorid); ! 546: if (productid) ! 547: qdev_prop_set_uint16(&dev->qdev, "productid", productid); ! 548: qdev_init_nofail(&dev->qdev); ! 549: ! 550: return dev; ! 551: } ! 552: ! 553: static USBDevice *usb_braille_init(USBBus *bus, const char *unused) ! 554: { ! 555: USBDevice *dev; ! 556: CharDriverState *cdrv; ! 557: ! 558: cdrv = qemu_chr_new("braille", "braille", NULL); ! 559: if (!cdrv) ! 560: return NULL; ! 561: ! 562: dev = usb_create(bus, "usb-braille"); ! 563: qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); ! 564: qdev_init_nofail(&dev->qdev); ! 565: ! 566: return dev; ! 567: } ! 568: ! 569: static const VMStateDescription vmstate_usb_serial = { ! 570: .name = "usb-serial", ! 571: .unmigratable = 1, ! 572: }; ! 573: ! 574: static Property serial_properties[] = { ! 575: DEFINE_PROP_CHR("chardev", USBSerialState, cs), ! 576: DEFINE_PROP_END_OF_LIST(), ! 577: }; ! 578: ! 579: static void usb_serial_class_initfn(ObjectClass *klass, void *data) ! 580: { ! 581: DeviceClass *dc = DEVICE_CLASS(klass); ! 582: USBDeviceClass *uc = USB_DEVICE_CLASS(klass); ! 583: ! 584: uc->init = usb_serial_initfn; ! 585: uc->product_desc = "QEMU USB Serial"; ! 586: uc->usb_desc = &desc_serial; ! 587: uc->handle_reset = usb_serial_handle_reset; ! 588: uc->handle_control = usb_serial_handle_control; ! 589: uc->handle_data = usb_serial_handle_data; ! 590: uc->handle_destroy = usb_serial_handle_destroy; ! 591: dc->vmsd = &vmstate_usb_serial; ! 592: dc->props = serial_properties; ! 593: } ! 594: ! 595: static TypeInfo serial_info = { ! 596: .name = "usb-serial", ! 597: .parent = TYPE_USB_DEVICE, ! 598: .instance_size = sizeof(USBSerialState), ! 599: .class_init = usb_serial_class_initfn, ! 600: }; ! 601: ! 602: static Property braille_properties[] = { ! 603: DEFINE_PROP_CHR("chardev", USBSerialState, cs), ! 604: DEFINE_PROP_END_OF_LIST(), ! 605: }; ! 606: ! 607: static void usb_braille_class_initfn(ObjectClass *klass, void *data) ! 608: { ! 609: DeviceClass *dc = DEVICE_CLASS(klass); ! 610: USBDeviceClass *uc = USB_DEVICE_CLASS(klass); ! 611: ! 612: uc->init = usb_serial_initfn; ! 613: uc->product_desc = "QEMU USB Braille"; ! 614: uc->usb_desc = &desc_braille; ! 615: uc->handle_reset = usb_serial_handle_reset; ! 616: uc->handle_control = usb_serial_handle_control; ! 617: uc->handle_data = usb_serial_handle_data; ! 618: uc->handle_destroy = usb_serial_handle_destroy; ! 619: dc->vmsd = &vmstate_usb_serial; ! 620: dc->props = braille_properties; ! 621: } ! 622: ! 623: static TypeInfo braille_info = { ! 624: .name = "usb-braille", ! 625: .parent = TYPE_USB_DEVICE, ! 626: .instance_size = sizeof(USBSerialState), ! 627: .class_init = usb_braille_class_initfn, ! 628: }; ! 629: ! 630: static void usb_serial_register_types(void) ! 631: { ! 632: type_register_static(&serial_info); ! 633: usb_legacy_register("usb-serial", "serial", usb_serial_init); ! 634: type_register_static(&braille_info); ! 635: usb_legacy_register("usb-braille", "braille", usb_braille_init); ! 636: } ! 637: ! 638: type_init(usb_serial_register_types)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.