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" 17f1ae32a1SGerd Hoffmann #include "hw/usb.h" 18d6454270SMarkus Armbruster #include "migration/vmstate.h" 19463581a8SMichael S. Tsirkin #include "desc.h" 207566c6efSMarc-André Lureau #include "chardev/char-serial.h" 214d43a603SMarc-André Lureau #include "chardev/char-fe.h" 22db1015e9SEduardo Habkost #include "qom/object.h" 23ebb11320SMark Cave-Ayland #include "trace.h" 24f1ae32a1SGerd Hoffmann 25f1ae32a1SGerd Hoffmann 2630ad5fddSJason Andryuk #define RECV_BUF (512 - (2 * 8)) 27f1ae32a1SGerd Hoffmann 28f1ae32a1SGerd Hoffmann /* Commands */ 29f1ae32a1SGerd Hoffmann #define FTDI_RESET 0 30f1ae32a1SGerd Hoffmann #define FTDI_SET_MDM_CTRL 1 31f1ae32a1SGerd Hoffmann #define FTDI_SET_FLOW_CTRL 2 32f1ae32a1SGerd Hoffmann #define FTDI_SET_BAUD 3 33f1ae32a1SGerd Hoffmann #define FTDI_SET_DATA 4 34f1ae32a1SGerd Hoffmann #define FTDI_GET_MDM_ST 5 35f1ae32a1SGerd Hoffmann #define FTDI_SET_EVENT_CHR 6 36f1ae32a1SGerd Hoffmann #define FTDI_SET_ERROR_CHR 7 37f1ae32a1SGerd Hoffmann #define FTDI_SET_LATENCY 9 38f1ae32a1SGerd Hoffmann #define FTDI_GET_LATENCY 10 39f1ae32a1SGerd Hoffmann 40adab8d48SMark Cave-Ayland #define DeviceOutVendor \ 41adab8d48SMark Cave-Ayland ((USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE) << 8) 42adab8d48SMark Cave-Ayland #define DeviceInVendor \ 43adab8d48SMark Cave-Ayland ((USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE) << 8) 44f1ae32a1SGerd Hoffmann 45f1ae32a1SGerd Hoffmann /* RESET */ 46f1ae32a1SGerd Hoffmann 47f1ae32a1SGerd Hoffmann #define FTDI_RESET_SIO 0 48f1ae32a1SGerd Hoffmann #define FTDI_RESET_RX 1 49f1ae32a1SGerd Hoffmann #define FTDI_RESET_TX 2 50f1ae32a1SGerd Hoffmann 51f1ae32a1SGerd Hoffmann /* SET_MDM_CTRL */ 52f1ae32a1SGerd Hoffmann 53f1ae32a1SGerd Hoffmann #define FTDI_DTR 1 54f1ae32a1SGerd Hoffmann #define FTDI_SET_DTR (FTDI_DTR << 8) 55f1ae32a1SGerd Hoffmann #define FTDI_RTS 2 56f1ae32a1SGerd Hoffmann #define FTDI_SET_RTS (FTDI_RTS << 8) 57f1ae32a1SGerd Hoffmann 58f1ae32a1SGerd Hoffmann /* SET_FLOW_CTRL */ 59f1ae32a1SGerd Hoffmann 60f1ae32a1SGerd Hoffmann #define FTDI_RTS_CTS_HS 1 61f1ae32a1SGerd Hoffmann #define FTDI_DTR_DSR_HS 2 62f1ae32a1SGerd Hoffmann #define FTDI_XON_XOFF_HS 4 63f1ae32a1SGerd Hoffmann 64f1ae32a1SGerd Hoffmann /* SET_DATA */ 65f1ae32a1SGerd Hoffmann 66f1ae32a1SGerd Hoffmann #define FTDI_PARITY (0x7 << 8) 67f1ae32a1SGerd Hoffmann #define FTDI_ODD (0x1 << 8) 68f1ae32a1SGerd Hoffmann #define FTDI_EVEN (0x2 << 8) 69f1ae32a1SGerd Hoffmann #define FTDI_MARK (0x3 << 8) 70f1ae32a1SGerd Hoffmann #define FTDI_SPACE (0x4 << 8) 71f1ae32a1SGerd Hoffmann 72f1ae32a1SGerd Hoffmann #define FTDI_STOP (0x3 << 11) 73f1ae32a1SGerd Hoffmann #define FTDI_STOP1 (0x0 << 11) 74f1ae32a1SGerd Hoffmann #define FTDI_STOP15 (0x1 << 11) 75f1ae32a1SGerd Hoffmann #define FTDI_STOP2 (0x2 << 11) 76f1ae32a1SGerd Hoffmann 77f1ae32a1SGerd Hoffmann /* GET_MDM_ST */ 78f1ae32a1SGerd Hoffmann /* TODO: should be sent every 40ms */ 79adab8d48SMark Cave-Ayland #define FTDI_CTS (1 << 4) /* CTS line status */ 80adab8d48SMark Cave-Ayland #define FTDI_DSR (1 << 5) /* DSR line status */ 81adab8d48SMark Cave-Ayland #define FTDI_RI (1 << 6) /* RI line status */ 82adab8d48SMark Cave-Ayland #define FTDI_RLSD (1 << 7) /* Receive Line Signal Detect */ 83f1ae32a1SGerd Hoffmann 84f1ae32a1SGerd Hoffmann /* Status */ 85f1ae32a1SGerd Hoffmann 86adab8d48SMark Cave-Ayland #define FTDI_DR (1 << 0) /* Data Ready */ 87adab8d48SMark Cave-Ayland #define FTDI_OE (1 << 1) /* Overrun Err */ 88adab8d48SMark Cave-Ayland #define FTDI_PE (1 << 2) /* Parity Err */ 89adab8d48SMark Cave-Ayland #define FTDI_FE (1 << 3) /* Framing Err */ 90adab8d48SMark Cave-Ayland #define FTDI_BI (1 << 4) /* Break Interrupt */ 91adab8d48SMark Cave-Ayland #define FTDI_THRE (1 << 5) /* Transmitter Holding Register */ 92adab8d48SMark Cave-Ayland #define FTDI_TEMT (1 << 6) /* Transmitter Empty */ 93adab8d48SMark Cave-Ayland #define FTDI_FIFO (1 << 7) /* Error in FIFO */ 94f1ae32a1SGerd Hoffmann 95db1015e9SEduardo Habkost struct USBSerialState { 96f1ae32a1SGerd Hoffmann USBDevice dev; 97adab8d48SMark Cave-Ayland 985843b6b3SJason Andryuk USBEndpoint *intr; 99f1ae32a1SGerd Hoffmann uint8_t recv_buf[RECV_BUF]; 100f1ae32a1SGerd Hoffmann uint16_t recv_ptr; 101f1ae32a1SGerd Hoffmann uint16_t recv_used; 102f1ae32a1SGerd Hoffmann uint8_t event_chr; 103f1ae32a1SGerd Hoffmann uint8_t error_chr; 104f1ae32a1SGerd Hoffmann uint8_t event_trigger; 105f1ae32a1SGerd Hoffmann QEMUSerialSetParams params; 106f1ae32a1SGerd Hoffmann int latency; /* ms */ 107becdfa00SMarc-André Lureau CharBackend cs; 108db1015e9SEduardo Habkost }; 109f1ae32a1SGerd Hoffmann 110cdf0d769SGonglei #define TYPE_USB_SERIAL "usb-serial-dev" 1118063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(USBSerialState, USB_SERIAL) 112cdf0d769SGonglei 113f1ae32a1SGerd Hoffmann enum { 114f1ae32a1SGerd Hoffmann STR_MANUFACTURER = 1, 115f1ae32a1SGerd Hoffmann STR_PRODUCT_SERIAL, 116f1ae32a1SGerd Hoffmann STR_PRODUCT_BRAILLE, 117f1ae32a1SGerd Hoffmann STR_SERIALNUMBER, 118f1ae32a1SGerd Hoffmann }; 119f1ae32a1SGerd Hoffmann 120f1ae32a1SGerd Hoffmann static const USBDescStrings desc_strings = { 12193bfef4cSCrístian Viana [STR_MANUFACTURER] = "QEMU", 122f1ae32a1SGerd Hoffmann [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL", 1232964cd9bSSamuel Thibault [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE", 124f1ae32a1SGerd Hoffmann [STR_SERIALNUMBER] = "1", 125f1ae32a1SGerd Hoffmann }; 126f1ae32a1SGerd Hoffmann 127f1ae32a1SGerd Hoffmann static const USBDescIface desc_iface0 = { 128f1ae32a1SGerd Hoffmann .bInterfaceNumber = 0, 129f1ae32a1SGerd Hoffmann .bNumEndpoints = 2, 130f1ae32a1SGerd Hoffmann .bInterfaceClass = 0xff, 131f1ae32a1SGerd Hoffmann .bInterfaceSubClass = 0xff, 132f1ae32a1SGerd Hoffmann .bInterfaceProtocol = 0xff, 133f1ae32a1SGerd Hoffmann .eps = (USBDescEndpoint[]) { 134f1ae32a1SGerd Hoffmann { 135f1ae32a1SGerd Hoffmann .bEndpointAddress = USB_DIR_IN | 0x01, 136f1ae32a1SGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 137f1ae32a1SGerd Hoffmann .wMaxPacketSize = 64, 138f1ae32a1SGerd Hoffmann },{ 139f1ae32a1SGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | 0x02, 140f1ae32a1SGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 141f1ae32a1SGerd Hoffmann .wMaxPacketSize = 64, 142f1ae32a1SGerd Hoffmann }, 143f1ae32a1SGerd Hoffmann } 144f1ae32a1SGerd Hoffmann }; 145f1ae32a1SGerd Hoffmann 146f1ae32a1SGerd Hoffmann static const USBDescDevice desc_device = { 147f1ae32a1SGerd Hoffmann .bcdUSB = 0x0200, 148f1ae32a1SGerd Hoffmann .bMaxPacketSize0 = 8, 149f1ae32a1SGerd Hoffmann .bNumConfigurations = 1, 150f1ae32a1SGerd Hoffmann .confs = (USBDescConfig[]) { 151f1ae32a1SGerd Hoffmann { 152f1ae32a1SGerd Hoffmann .bNumInterfaces = 1, 153f1ae32a1SGerd Hoffmann .bConfigurationValue = 1, 1545843b6b3SJason Andryuk .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, 155f1ae32a1SGerd Hoffmann .bMaxPower = 50, 156f1ae32a1SGerd Hoffmann .nif = 1, 157f1ae32a1SGerd Hoffmann .ifs = &desc_iface0, 158f1ae32a1SGerd Hoffmann }, 159f1ae32a1SGerd Hoffmann }, 160f1ae32a1SGerd Hoffmann }; 161f1ae32a1SGerd Hoffmann 162f1ae32a1SGerd Hoffmann static const USBDesc desc_serial = { 163f1ae32a1SGerd Hoffmann .id = { 164f1ae32a1SGerd Hoffmann .idVendor = 0x0403, 165f1ae32a1SGerd Hoffmann .idProduct = 0x6001, 166f1ae32a1SGerd Hoffmann .bcdDevice = 0x0400, 167f1ae32a1SGerd Hoffmann .iManufacturer = STR_MANUFACTURER, 168f1ae32a1SGerd Hoffmann .iProduct = STR_PRODUCT_SERIAL, 169f1ae32a1SGerd Hoffmann .iSerialNumber = STR_SERIALNUMBER, 170f1ae32a1SGerd Hoffmann }, 171f1ae32a1SGerd Hoffmann .full = &desc_device, 172f1ae32a1SGerd Hoffmann .str = desc_strings, 173f1ae32a1SGerd Hoffmann }; 174f1ae32a1SGerd Hoffmann 175f1ae32a1SGerd Hoffmann static const USBDesc desc_braille = { 176f1ae32a1SGerd Hoffmann .id = { 177f1ae32a1SGerd Hoffmann .idVendor = 0x0403, 178f1ae32a1SGerd Hoffmann .idProduct = 0xfe72, 179f1ae32a1SGerd Hoffmann .bcdDevice = 0x0400, 180f1ae32a1SGerd Hoffmann .iManufacturer = STR_MANUFACTURER, 181f1ae32a1SGerd Hoffmann .iProduct = STR_PRODUCT_BRAILLE, 182f1ae32a1SGerd Hoffmann .iSerialNumber = STR_SERIALNUMBER, 183f1ae32a1SGerd Hoffmann }, 184f1ae32a1SGerd Hoffmann .full = &desc_device, 185f1ae32a1SGerd Hoffmann .str = desc_strings, 186f1ae32a1SGerd Hoffmann }; 187f1ae32a1SGerd Hoffmann 188f1ae32a1SGerd Hoffmann static void usb_serial_reset(USBSerialState *s) 189f1ae32a1SGerd Hoffmann { 190f1ae32a1SGerd Hoffmann /* TODO: Set flow control to none */ 191f1ae32a1SGerd Hoffmann s->event_chr = 0x0d; 192f1ae32a1SGerd Hoffmann s->event_trigger = 0; 193f1ae32a1SGerd Hoffmann s->recv_ptr = 0; 194f1ae32a1SGerd Hoffmann s->recv_used = 0; 195f1ae32a1SGerd Hoffmann /* TODO: purge in char driver */ 196f1ae32a1SGerd Hoffmann } 197f1ae32a1SGerd Hoffmann 198f1ae32a1SGerd Hoffmann static void usb_serial_handle_reset(USBDevice *dev) 199f1ae32a1SGerd Hoffmann { 2008a0e4ee7SMark Cave-Ayland USBSerialState *s = USB_SERIAL(dev); 201ebb11320SMark Cave-Ayland USBBus *bus = usb_bus_from_device(dev); 202f1ae32a1SGerd Hoffmann 203ebb11320SMark Cave-Ayland trace_usb_serial_reset(bus->busnr, dev->addr); 204f1ae32a1SGerd Hoffmann 205f1ae32a1SGerd Hoffmann usb_serial_reset(s); 206f1ae32a1SGerd Hoffmann /* TODO: Reset char device, send BREAK? */ 207f1ae32a1SGerd Hoffmann } 208f1ae32a1SGerd Hoffmann 209f1ae32a1SGerd Hoffmann static uint8_t usb_get_modem_lines(USBSerialState *s) 210f1ae32a1SGerd Hoffmann { 211f1ae32a1SGerd Hoffmann int flags; 212f1ae32a1SGerd Hoffmann uint8_t ret; 213f1ae32a1SGerd Hoffmann 2145345fdb4SMarc-André Lureau if (qemu_chr_fe_ioctl(&s->cs, 215becdfa00SMarc-André Lureau CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) { 216f1ae32a1SGerd Hoffmann return FTDI_CTS | FTDI_DSR | FTDI_RLSD; 217becdfa00SMarc-André Lureau } 218f1ae32a1SGerd Hoffmann 219f1ae32a1SGerd Hoffmann ret = 0; 220adab8d48SMark Cave-Ayland if (flags & CHR_TIOCM_CTS) { 221f1ae32a1SGerd Hoffmann ret |= FTDI_CTS; 222adab8d48SMark Cave-Ayland } 223adab8d48SMark Cave-Ayland if (flags & CHR_TIOCM_DSR) { 224f1ae32a1SGerd Hoffmann ret |= FTDI_DSR; 225adab8d48SMark Cave-Ayland } 226adab8d48SMark Cave-Ayland if (flags & CHR_TIOCM_RI) { 227f1ae32a1SGerd Hoffmann ret |= FTDI_RI; 228adab8d48SMark Cave-Ayland } 229adab8d48SMark Cave-Ayland if (flags & CHR_TIOCM_CAR) { 230f1ae32a1SGerd Hoffmann ret |= FTDI_RLSD; 231adab8d48SMark Cave-Ayland } 232f1ae32a1SGerd Hoffmann 233f1ae32a1SGerd Hoffmann return ret; 234f1ae32a1SGerd Hoffmann } 235f1ae32a1SGerd Hoffmann 2369a77a0f5SHans de Goede static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, 237adab8d48SMark Cave-Ayland int request, int value, int index, 238adab8d48SMark Cave-Ayland int length, uint8_t *data) 239f1ae32a1SGerd Hoffmann { 2408a0e4ee7SMark Cave-Ayland USBSerialState *s = USB_SERIAL(dev); 241ebb11320SMark Cave-Ayland USBBus *bus = usb_bus_from_device(dev); 242f1ae32a1SGerd Hoffmann int ret; 243f1ae32a1SGerd Hoffmann 244ebb11320SMark Cave-Ayland trace_usb_serial_handle_control(bus->busnr, dev->addr, request, value); 245ebb11320SMark Cave-Ayland 246f1ae32a1SGerd Hoffmann ret = usb_desc_handle_control(dev, p, request, value, index, length, data); 247f1ae32a1SGerd Hoffmann if (ret >= 0) { 2489a77a0f5SHans de Goede return; 249f1ae32a1SGerd Hoffmann } 250f1ae32a1SGerd Hoffmann 251f1ae32a1SGerd Hoffmann switch (request) { 252f1ae32a1SGerd Hoffmann case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: 253f1ae32a1SGerd Hoffmann break; 254f1ae32a1SGerd Hoffmann 255f1ae32a1SGerd Hoffmann /* Class specific requests. */ 256f1ae32a1SGerd Hoffmann case DeviceOutVendor | FTDI_RESET: 257f1ae32a1SGerd Hoffmann switch (value) { 258f1ae32a1SGerd Hoffmann case FTDI_RESET_SIO: 259f1ae32a1SGerd Hoffmann usb_serial_reset(s); 260f1ae32a1SGerd Hoffmann break; 261f1ae32a1SGerd Hoffmann case FTDI_RESET_RX: 262f1ae32a1SGerd Hoffmann s->recv_ptr = 0; 263f1ae32a1SGerd Hoffmann s->recv_used = 0; 264f1ae32a1SGerd Hoffmann /* TODO: purge from char device */ 265f1ae32a1SGerd Hoffmann break; 266f1ae32a1SGerd Hoffmann case FTDI_RESET_TX: 267f1ae32a1SGerd Hoffmann /* TODO: purge from char device */ 268f1ae32a1SGerd Hoffmann break; 269f1ae32a1SGerd Hoffmann } 270f1ae32a1SGerd Hoffmann break; 271f1ae32a1SGerd Hoffmann case DeviceOutVendor | FTDI_SET_MDM_CTRL: 272f1ae32a1SGerd Hoffmann { 273f1ae32a1SGerd Hoffmann static int flags; 2745345fdb4SMarc-André Lureau qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); 275f1ae32a1SGerd Hoffmann if (value & FTDI_SET_RTS) { 276adab8d48SMark Cave-Ayland if (value & FTDI_RTS) { 277f1ae32a1SGerd Hoffmann flags |= CHR_TIOCM_RTS; 278adab8d48SMark Cave-Ayland } else { 279f1ae32a1SGerd Hoffmann flags &= ~CHR_TIOCM_RTS; 280f1ae32a1SGerd Hoffmann } 281adab8d48SMark Cave-Ayland } 282f1ae32a1SGerd Hoffmann if (value & FTDI_SET_DTR) { 283adab8d48SMark Cave-Ayland if (value & FTDI_DTR) { 284f1ae32a1SGerd Hoffmann flags |= CHR_TIOCM_DTR; 285adab8d48SMark Cave-Ayland } else { 286f1ae32a1SGerd Hoffmann flags &= ~CHR_TIOCM_DTR; 287f1ae32a1SGerd Hoffmann } 288adab8d48SMark Cave-Ayland } 2895345fdb4SMarc-André Lureau qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); 290f1ae32a1SGerd Hoffmann break; 291f1ae32a1SGerd Hoffmann } 292f1ae32a1SGerd Hoffmann case DeviceOutVendor | FTDI_SET_FLOW_CTRL: 293f1ae32a1SGerd Hoffmann /* TODO: ioctl */ 294f1ae32a1SGerd Hoffmann break; 295f1ae32a1SGerd Hoffmann case DeviceOutVendor | FTDI_SET_BAUD: { 296f1ae32a1SGerd Hoffmann static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 }; 297f1ae32a1SGerd Hoffmann int subdivisor8 = subdivisors8[((value & 0xc000) >> 14) 298f1ae32a1SGerd Hoffmann | ((index & 1) << 2)]; 299f1ae32a1SGerd Hoffmann int divisor = value & 0x3fff; 300f1ae32a1SGerd Hoffmann 301f1ae32a1SGerd Hoffmann /* chip special cases */ 302adab8d48SMark Cave-Ayland if (divisor == 1 && subdivisor8 == 0) { 303f1ae32a1SGerd Hoffmann subdivisor8 = 4; 304adab8d48SMark Cave-Ayland } 305adab8d48SMark Cave-Ayland if (divisor == 0 && subdivisor8 == 0) { 306f1ae32a1SGerd Hoffmann divisor = 1; 307adab8d48SMark Cave-Ayland } 308f1ae32a1SGerd Hoffmann 309f1ae32a1SGerd Hoffmann s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); 310*655ec806SMark Cave-Ayland trace_usb_serial_set_baud(bus->busnr, dev->addr, s->params.speed); 3115345fdb4SMarc-André Lureau qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); 312f1ae32a1SGerd Hoffmann break; 313f1ae32a1SGerd Hoffmann } 314f1ae32a1SGerd Hoffmann case DeviceOutVendor | FTDI_SET_DATA: 315f1ae32a1SGerd Hoffmann switch (value & FTDI_PARITY) { 316f1ae32a1SGerd Hoffmann case 0: 317f1ae32a1SGerd Hoffmann s->params.parity = 'N'; 318f1ae32a1SGerd Hoffmann break; 319f1ae32a1SGerd Hoffmann case FTDI_ODD: 320f1ae32a1SGerd Hoffmann s->params.parity = 'O'; 321f1ae32a1SGerd Hoffmann break; 322f1ae32a1SGerd Hoffmann case FTDI_EVEN: 323f1ae32a1SGerd Hoffmann s->params.parity = 'E'; 324f1ae32a1SGerd Hoffmann break; 325f1ae32a1SGerd Hoffmann default: 326ebb11320SMark Cave-Ayland trace_usb_serial_unsupported_parity(bus->busnr, dev->addr, 327ebb11320SMark Cave-Ayland value & FTDI_PARITY); 328f1ae32a1SGerd Hoffmann goto fail; 329f1ae32a1SGerd Hoffmann } 330adab8d48SMark Cave-Ayland 331f1ae32a1SGerd Hoffmann switch (value & FTDI_STOP) { 332f1ae32a1SGerd Hoffmann case FTDI_STOP1: 333f1ae32a1SGerd Hoffmann s->params.stop_bits = 1; 334f1ae32a1SGerd Hoffmann break; 335f1ae32a1SGerd Hoffmann case FTDI_STOP2: 336f1ae32a1SGerd Hoffmann s->params.stop_bits = 2; 337f1ae32a1SGerd Hoffmann break; 338f1ae32a1SGerd Hoffmann default: 339ebb11320SMark Cave-Ayland trace_usb_serial_unsupported_stopbits(bus->busnr, dev->addr, 340ebb11320SMark Cave-Ayland value & FTDI_STOP); 341f1ae32a1SGerd Hoffmann goto fail; 342f1ae32a1SGerd Hoffmann } 343adab8d48SMark Cave-Ayland 344*655ec806SMark Cave-Ayland trace_usb_serial_set_data(bus->busnr, dev->addr, s->params.parity, 345*655ec806SMark Cave-Ayland s->params.data_bits, s->params.stop_bits); 3465345fdb4SMarc-André Lureau qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); 347f1ae32a1SGerd Hoffmann /* TODO: TX ON/OFF */ 348f1ae32a1SGerd Hoffmann break; 349f1ae32a1SGerd Hoffmann case DeviceInVendor | FTDI_GET_MDM_ST: 350f1ae32a1SGerd Hoffmann data[0] = usb_get_modem_lines(s) | 1; 351647ee987SJason Andryuk data[1] = FTDI_THRE | FTDI_TEMT; 3529a77a0f5SHans de Goede p->actual_length = 2; 353f1ae32a1SGerd Hoffmann break; 354f1ae32a1SGerd Hoffmann case DeviceOutVendor | FTDI_SET_EVENT_CHR: 355f1ae32a1SGerd Hoffmann /* TODO: handle it */ 356f1ae32a1SGerd Hoffmann s->event_chr = value; 357f1ae32a1SGerd Hoffmann break; 358f1ae32a1SGerd Hoffmann case DeviceOutVendor | FTDI_SET_ERROR_CHR: 359f1ae32a1SGerd Hoffmann /* TODO: handle it */ 360f1ae32a1SGerd Hoffmann s->error_chr = value; 361f1ae32a1SGerd Hoffmann break; 362f1ae32a1SGerd Hoffmann case DeviceOutVendor | FTDI_SET_LATENCY: 363f1ae32a1SGerd Hoffmann s->latency = value; 364f1ae32a1SGerd Hoffmann break; 365f1ae32a1SGerd Hoffmann case DeviceInVendor | FTDI_GET_LATENCY: 366f1ae32a1SGerd Hoffmann data[0] = s->latency; 3679a77a0f5SHans de Goede p->actual_length = 1; 368f1ae32a1SGerd Hoffmann break; 369f1ae32a1SGerd Hoffmann default: 370f1ae32a1SGerd Hoffmann fail: 371ebb11320SMark Cave-Ayland trace_usb_serial_unsupported_control(bus->busnr, dev->addr, request, 372ebb11320SMark Cave-Ayland value); 3739a77a0f5SHans de Goede p->status = USB_RET_STALL; 374f1ae32a1SGerd Hoffmann break; 375f1ae32a1SGerd Hoffmann } 376f1ae32a1SGerd Hoffmann } 377f1ae32a1SGerd Hoffmann 3782bcf4e9fSJason Andryuk static void usb_serial_token_in(USBSerialState *s, USBPacket *p) 3792bcf4e9fSJason Andryuk { 38087db78f7SJason Andryuk const int max_packet_size = desc_iface0.eps[0].wMaxPacketSize; 38187db78f7SJason Andryuk int packet_len; 3822bcf4e9fSJason Andryuk uint8_t header[2]; 3832bcf4e9fSJason Andryuk 38487db78f7SJason Andryuk packet_len = p->iov.size; 38587db78f7SJason Andryuk if (packet_len <= 2) { 3862bcf4e9fSJason Andryuk p->status = USB_RET_NAK; 3872bcf4e9fSJason Andryuk return; 3882bcf4e9fSJason Andryuk } 38987db78f7SJason Andryuk 3902bcf4e9fSJason Andryuk header[0] = usb_get_modem_lines(s) | 1; 3912bcf4e9fSJason Andryuk /* We do not have the uart details */ 3922bcf4e9fSJason Andryuk /* handle serial break */ 3932bcf4e9fSJason Andryuk if (s->event_trigger && s->event_trigger & FTDI_BI) { 3942bcf4e9fSJason Andryuk s->event_trigger &= ~FTDI_BI; 3952bcf4e9fSJason Andryuk header[1] = FTDI_BI; 3962bcf4e9fSJason Andryuk usb_packet_copy(p, header, 2); 3972bcf4e9fSJason Andryuk return; 3982bcf4e9fSJason Andryuk } else { 3992bcf4e9fSJason Andryuk header[1] = 0; 4002bcf4e9fSJason Andryuk } 40187db78f7SJason Andryuk 40287db78f7SJason Andryuk if (!s->recv_used) { 40387db78f7SJason Andryuk p->status = USB_RET_NAK; 40487db78f7SJason Andryuk return; 40587db78f7SJason Andryuk } 40687db78f7SJason Andryuk 40787db78f7SJason Andryuk while (s->recv_used && packet_len > 2) { 40887db78f7SJason Andryuk int first_len, len; 40987db78f7SJason Andryuk 41087db78f7SJason Andryuk len = MIN(packet_len, max_packet_size); 4112bcf4e9fSJason Andryuk len -= 2; 4122bcf4e9fSJason Andryuk if (len > s->recv_used) { 4132bcf4e9fSJason Andryuk len = s->recv_used; 4142bcf4e9fSJason Andryuk } 41587db78f7SJason Andryuk 41687db78f7SJason Andryuk first_len = RECV_BUF - s->recv_ptr; 4172bcf4e9fSJason Andryuk if (first_len > len) { 4182bcf4e9fSJason Andryuk first_len = len; 4192bcf4e9fSJason Andryuk } 4202bcf4e9fSJason Andryuk usb_packet_copy(p, header, 2); 4212bcf4e9fSJason Andryuk usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); 4222bcf4e9fSJason Andryuk if (len > first_len) { 4232bcf4e9fSJason Andryuk usb_packet_copy(p, s->recv_buf, len - first_len); 4242bcf4e9fSJason Andryuk } 4252bcf4e9fSJason Andryuk s->recv_used -= len; 4262bcf4e9fSJason Andryuk s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; 42787db78f7SJason Andryuk packet_len -= len + 2; 42887db78f7SJason Andryuk } 4292bcf4e9fSJason Andryuk 4302bcf4e9fSJason Andryuk return; 4312bcf4e9fSJason Andryuk } 4322bcf4e9fSJason Andryuk 4339a77a0f5SHans de Goede static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) 434f1ae32a1SGerd Hoffmann { 4358a0e4ee7SMark Cave-Ayland USBSerialState *s = USB_SERIAL(dev); 436ebb11320SMark Cave-Ayland USBBus *bus = usb_bus_from_device(dev); 437f1ae32a1SGerd Hoffmann uint8_t devep = p->ep->nr; 438f1ae32a1SGerd Hoffmann struct iovec *iov; 4392bcf4e9fSJason Andryuk int i; 440f1ae32a1SGerd Hoffmann 441f1ae32a1SGerd Hoffmann switch (p->pid) { 442f1ae32a1SGerd Hoffmann case USB_TOKEN_OUT: 443adab8d48SMark Cave-Ayland if (devep != 2) { 444f1ae32a1SGerd Hoffmann goto fail; 445adab8d48SMark Cave-Ayland } 446f1ae32a1SGerd Hoffmann for (i = 0; i < p->iov.niov; i++) { 447f1ae32a1SGerd Hoffmann iov = p->iov.iov + i; 448adab8d48SMark Cave-Ayland /* 449adab8d48SMark Cave-Ayland * XXX this blocks entire thread. Rewrite to use 450adab8d48SMark Cave-Ayland * qemu_chr_fe_write and background I/O callbacks 451adab8d48SMark Cave-Ayland */ 4525345fdb4SMarc-André Lureau qemu_chr_fe_write_all(&s->cs, iov->iov_base, iov->iov_len); 453f1ae32a1SGerd Hoffmann } 4549a77a0f5SHans de Goede p->actual_length = p->iov.size; 455f1ae32a1SGerd Hoffmann break; 456f1ae32a1SGerd Hoffmann 457f1ae32a1SGerd Hoffmann case USB_TOKEN_IN: 458adab8d48SMark Cave-Ayland if (devep != 1) { 459f1ae32a1SGerd Hoffmann goto fail; 460adab8d48SMark Cave-Ayland } 4612bcf4e9fSJason Andryuk usb_serial_token_in(s, p); 462f1ae32a1SGerd Hoffmann break; 463f1ae32a1SGerd Hoffmann 464f1ae32a1SGerd Hoffmann default: 465ebb11320SMark Cave-Ayland trace_usb_serial_bad_token(bus->busnr, dev->addr); 466f1ae32a1SGerd Hoffmann fail: 4679a77a0f5SHans de Goede p->status = USB_RET_STALL; 468f1ae32a1SGerd Hoffmann break; 469f1ae32a1SGerd Hoffmann } 470f1ae32a1SGerd Hoffmann } 471f1ae32a1SGerd Hoffmann 472f1ae32a1SGerd Hoffmann static int usb_serial_can_read(void *opaque) 473f1ae32a1SGerd Hoffmann { 474f1ae32a1SGerd Hoffmann USBSerialState *s = opaque; 475da124e62SGerd Hoffmann 476da124e62SGerd Hoffmann if (!s->dev.attached) { 477da124e62SGerd Hoffmann return 0; 478da124e62SGerd Hoffmann } 479f1ae32a1SGerd Hoffmann return RECV_BUF - s->recv_used; 480f1ae32a1SGerd Hoffmann } 481f1ae32a1SGerd Hoffmann 482f1ae32a1SGerd Hoffmann static void usb_serial_read(void *opaque, const uint8_t *buf, int size) 483f1ae32a1SGerd Hoffmann { 484f1ae32a1SGerd Hoffmann USBSerialState *s = opaque; 485f1ae32a1SGerd Hoffmann int first_size, start; 486f1ae32a1SGerd Hoffmann 487f1ae32a1SGerd Hoffmann /* room in the buffer? */ 488adab8d48SMark Cave-Ayland if (size > (RECV_BUF - s->recv_used)) { 489f1ae32a1SGerd Hoffmann size = RECV_BUF - s->recv_used; 490adab8d48SMark Cave-Ayland } 491f1ae32a1SGerd Hoffmann 492f1ae32a1SGerd Hoffmann start = s->recv_ptr + s->recv_used; 493f1ae32a1SGerd Hoffmann if (start < RECV_BUF) { 494f1ae32a1SGerd Hoffmann /* copy data to end of buffer */ 495f1ae32a1SGerd Hoffmann first_size = RECV_BUF - start; 496adab8d48SMark Cave-Ayland if (first_size > size) { 497f1ae32a1SGerd Hoffmann first_size = size; 498adab8d48SMark Cave-Ayland } 499f1ae32a1SGerd Hoffmann 500f1ae32a1SGerd Hoffmann memcpy(s->recv_buf + start, buf, first_size); 501f1ae32a1SGerd Hoffmann 502f1ae32a1SGerd Hoffmann /* wrap around to front if needed */ 503adab8d48SMark Cave-Ayland if (size > first_size) { 504f1ae32a1SGerd Hoffmann memcpy(s->recv_buf, buf + first_size, size - first_size); 505adab8d48SMark Cave-Ayland } 506f1ae32a1SGerd Hoffmann } else { 507f1ae32a1SGerd Hoffmann start -= RECV_BUF; 508f1ae32a1SGerd Hoffmann memcpy(s->recv_buf + start, buf, size); 509f1ae32a1SGerd Hoffmann } 510f1ae32a1SGerd Hoffmann s->recv_used += size; 5115843b6b3SJason Andryuk 5125843b6b3SJason Andryuk usb_wakeup(s->intr, 0); 513f1ae32a1SGerd Hoffmann } 514f1ae32a1SGerd Hoffmann 515083b266fSPhilippe Mathieu-Daudé static void usb_serial_event(void *opaque, QEMUChrEvent event) 516f1ae32a1SGerd Hoffmann { 517f1ae32a1SGerd Hoffmann USBSerialState *s = opaque; 518f1ae32a1SGerd Hoffmann 519f1ae32a1SGerd Hoffmann switch (event) { 520f1ae32a1SGerd Hoffmann case CHR_EVENT_BREAK: 521f1ae32a1SGerd Hoffmann s->event_trigger |= FTDI_BI; 522f1ae32a1SGerd Hoffmann break; 523f1ae32a1SGerd Hoffmann case CHR_EVENT_OPENED: 524da124e62SGerd Hoffmann if (!s->dev.attached) { 5257334d650SGonglei usb_device_attach(&s->dev, &error_abort); 526da124e62SGerd Hoffmann } 527da124e62SGerd Hoffmann break; 528da124e62SGerd Hoffmann case CHR_EVENT_CLOSED: 529da124e62SGerd Hoffmann if (s->dev.attached) { 530da124e62SGerd Hoffmann usb_device_detach(&s->dev); 531da124e62SGerd Hoffmann } 532f1ae32a1SGerd Hoffmann break; 533c263158eSPhilippe Mathieu-Daudé case CHR_EVENT_MUX_IN: 534c263158eSPhilippe Mathieu-Daudé case CHR_EVENT_MUX_OUT: 535c263158eSPhilippe Mathieu-Daudé /* Ignore */ 536c263158eSPhilippe Mathieu-Daudé break; 537f1ae32a1SGerd Hoffmann } 538f1ae32a1SGerd Hoffmann } 539f1ae32a1SGerd Hoffmann 54038fff2c9SGonglei static void usb_serial_realize(USBDevice *dev, Error **errp) 541f1ae32a1SGerd Hoffmann { 542bdd5f27eSEduardo Habkost USBSerialState *s = USB_SERIAL(dev); 5437334d650SGonglei Error *local_err = NULL; 544f1ae32a1SGerd Hoffmann 5459d55d1adSGerd Hoffmann usb_desc_create_serial(dev); 546f1ae32a1SGerd Hoffmann usb_desc_init(dev); 547da124e62SGerd Hoffmann dev->auto_attach = 0; 548f1ae32a1SGerd Hoffmann 54930650701SAnton Nefedov if (!qemu_chr_fe_backend_connected(&s->cs)) { 55038fff2c9SGonglei error_setg(errp, "Property chardev is required"); 55138fff2c9SGonglei return; 552f1ae32a1SGerd Hoffmann } 553f1ae32a1SGerd Hoffmann 5547334d650SGonglei usb_check_attach(dev, &local_err); 5557334d650SGonglei if (local_err) { 5567334d650SGonglei error_propagate(errp, local_err); 5577334d650SGonglei return; 5587334d650SGonglei } 5597334d650SGonglei 5605345fdb4SMarc-André Lureau qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read, 56181517ba3SAnton Nefedov usb_serial_event, NULL, s, NULL, true); 562f1ae32a1SGerd Hoffmann usb_serial_handle_reset(dev); 563da124e62SGerd Hoffmann 56430650701SAnton Nefedov if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) { 5657334d650SGonglei usb_device_attach(dev, &error_abort); 5667d553f27SGonglei } 5675843b6b3SJason Andryuk s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); 568da124e62SGerd Hoffmann } 569f1ae32a1SGerd Hoffmann 570590ce74aSMarkus Armbruster static USBDevice *usb_braille_init(const char *unused) 571f1ae32a1SGerd Hoffmann { 572f1ae32a1SGerd Hoffmann USBDevice *dev; 5730ec7b3e7SMarc-André Lureau Chardev *cdrv; 574f1ae32a1SGerd Hoffmann 5754ad6f6cbSPaolo Bonzini cdrv = qemu_chr_new("braille", "braille", NULL); 576adab8d48SMark Cave-Ayland if (!cdrv) { 577f1ae32a1SGerd Hoffmann return NULL; 578adab8d48SMark Cave-Ayland } 579f1ae32a1SGerd Hoffmann 580590ce74aSMarkus Armbruster dev = usb_new("usb-braille"); 581f1ae32a1SGerd Hoffmann qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); 582f1ae32a1SGerd Hoffmann return dev; 583f1ae32a1SGerd Hoffmann } 584f1ae32a1SGerd Hoffmann 585f1ae32a1SGerd Hoffmann static const VMStateDescription vmstate_usb_serial = { 586f1ae32a1SGerd Hoffmann .name = "usb-serial", 587f1ae32a1SGerd Hoffmann .unmigratable = 1, 588f1ae32a1SGerd Hoffmann }; 589f1ae32a1SGerd Hoffmann 590f1ae32a1SGerd Hoffmann static Property serial_properties[] = { 591f1ae32a1SGerd Hoffmann DEFINE_PROP_CHR("chardev", USBSerialState, cs), 592f1ae32a1SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 593f1ae32a1SGerd Hoffmann }; 594f1ae32a1SGerd Hoffmann 595cdf0d769SGonglei static void usb_serial_dev_class_init(ObjectClass *klass, void *data) 596f1ae32a1SGerd Hoffmann { 597f1ae32a1SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 598f1ae32a1SGerd Hoffmann USBDeviceClass *uc = USB_DEVICE_CLASS(klass); 599f1ae32a1SGerd Hoffmann 60038fff2c9SGonglei uc->realize = usb_serial_realize; 601f1ae32a1SGerd Hoffmann uc->handle_reset = usb_serial_handle_reset; 602f1ae32a1SGerd Hoffmann uc->handle_control = usb_serial_handle_control; 603f1ae32a1SGerd Hoffmann uc->handle_data = usb_serial_handle_data; 604f1ae32a1SGerd Hoffmann dc->vmsd = &vmstate_usb_serial; 605125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 606f1ae32a1SGerd Hoffmann } 607f1ae32a1SGerd Hoffmann 608cdf0d769SGonglei static const TypeInfo usb_serial_dev_type_info = { 609cdf0d769SGonglei .name = TYPE_USB_SERIAL, 610cdf0d769SGonglei .parent = TYPE_USB_DEVICE, 611cdf0d769SGonglei .instance_size = sizeof(USBSerialState), 612cdf0d769SGonglei .abstract = true, 613cdf0d769SGonglei .class_init = usb_serial_dev_class_init, 614cdf0d769SGonglei }; 615cdf0d769SGonglei 616cdf0d769SGonglei static void usb_serial_class_initfn(ObjectClass *klass, void *data) 617cdf0d769SGonglei { 618cdf0d769SGonglei DeviceClass *dc = DEVICE_CLASS(klass); 619cdf0d769SGonglei USBDeviceClass *uc = USB_DEVICE_CLASS(klass); 620cdf0d769SGonglei 621cdf0d769SGonglei uc->product_desc = "QEMU USB Serial"; 622cdf0d769SGonglei uc->usb_desc = &desc_serial; 6234f67d30bSMarc-André Lureau device_class_set_props(dc, serial_properties); 624cdf0d769SGonglei } 625cdf0d769SGonglei 6268c43a6f0SAndreas Färber static const TypeInfo serial_info = { 627f1ae32a1SGerd Hoffmann .name = "usb-serial", 628cdf0d769SGonglei .parent = TYPE_USB_SERIAL, 629f1ae32a1SGerd Hoffmann .class_init = usb_serial_class_initfn, 630f1ae32a1SGerd Hoffmann }; 631f1ae32a1SGerd Hoffmann 632f1ae32a1SGerd Hoffmann static Property braille_properties[] = { 633f1ae32a1SGerd Hoffmann DEFINE_PROP_CHR("chardev", USBSerialState, cs), 634f1ae32a1SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 635f1ae32a1SGerd Hoffmann }; 636f1ae32a1SGerd Hoffmann 637f1ae32a1SGerd Hoffmann static void usb_braille_class_initfn(ObjectClass *klass, void *data) 638f1ae32a1SGerd Hoffmann { 639f1ae32a1SGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 640f1ae32a1SGerd Hoffmann USBDeviceClass *uc = USB_DEVICE_CLASS(klass); 641f1ae32a1SGerd Hoffmann 642f1ae32a1SGerd Hoffmann uc->product_desc = "QEMU USB Braille"; 643f1ae32a1SGerd Hoffmann uc->usb_desc = &desc_braille; 6444f67d30bSMarc-André Lureau device_class_set_props(dc, braille_properties); 645f1ae32a1SGerd Hoffmann } 646f1ae32a1SGerd Hoffmann 6478c43a6f0SAndreas Färber static const TypeInfo braille_info = { 648f1ae32a1SGerd Hoffmann .name = "usb-braille", 649cdf0d769SGonglei .parent = TYPE_USB_SERIAL, 650f1ae32a1SGerd Hoffmann .class_init = usb_braille_class_initfn, 651f1ae32a1SGerd Hoffmann }; 652f1ae32a1SGerd Hoffmann 653f1ae32a1SGerd Hoffmann static void usb_serial_register_types(void) 654f1ae32a1SGerd Hoffmann { 655cdf0d769SGonglei type_register_static(&usb_serial_dev_type_info); 656f1ae32a1SGerd Hoffmann type_register_static(&serial_info); 657f1ae32a1SGerd Hoffmann type_register_static(&braille_info); 658f1ae32a1SGerd Hoffmann usb_legacy_register("usb-braille", "braille", usb_braille_init); 659f1ae32a1SGerd Hoffmann } 660f1ae32a1SGerd Hoffmann 661f1ae32a1SGerd Hoffmann type_init(usb_serial_register_types) 662