From owner-svn-soc-all@FreeBSD.ORG Tue Aug 13 22:42:31 2013 Return-Path: Delivered-To: svn-soc-all@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id 10847FCD for ; Tue, 13 Aug 2013 22:42:31 +0000 (UTC) (envelope-from syuu@FreeBSD.org) Received: from socsvn.freebsd.org (socsvn.freebsd.org [IPv6:2001:1900:2254:206a::50:2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id F05852B90 for ; Tue, 13 Aug 2013 22:42:30 +0000 (UTC) Received: from socsvn.freebsd.org ([127.0.1.124]) by socsvn.freebsd.org (8.14.7/8.14.7) with ESMTP id r7DMgUTB032267 for ; Tue, 13 Aug 2013 22:42:30 GMT (envelope-from syuu@FreeBSD.org) Received: (from www@localhost) by socsvn.freebsd.org (8.14.7/8.14.6/Submit) id r7DMgUwT032263 for svn-soc-all@FreeBSD.org; Tue, 13 Aug 2013 22:42:30 GMT (envelope-from syuu@FreeBSD.org) Date: Tue, 13 Aug 2013 22:42:30 GMT Message-Id: <201308132242.r7DMgUwT032263@socsvn.freebsd.org> X-Authentication-Warning: socsvn.freebsd.org: www set sender to syuu@FreeBSD.org using -f From: syuu@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r255905 - in soc2013/syuu/bhyve_usb/usr.sbin/bhyve: . usb/gpl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-soc-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the entire Summer of Code repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 13 Aug 2013 22:42:31 -0000 Author: syuu Date: Tue Aug 13 22:42:30 2013 New Revision: 255905 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=255905 Log: import LGPL tainted dev-serial.c Added: soc2013/syuu/bhyve_usb/usr.sbin/bhyve/usb/gpl/char.h soc2013/syuu/bhyve_usb/usr.sbin/bhyve/usb/gpl/dev-serial.c Modified: soc2013/syuu/bhyve_usb/usr.sbin/bhyve/Makefile Modified: soc2013/syuu/bhyve_usb/usr.sbin/bhyve/Makefile ============================================================================== --- soc2013/syuu/bhyve_usb/usr.sbin/bhyve/Makefile Tue Aug 13 21:56:16 2013 (r255904) +++ soc2013/syuu/bhyve_usb/usr.sbin/bhyve/Makefile Tue Aug 13 22:42:30 2013 (r255905) @@ -19,7 +19,7 @@ CFLAGS+=-I${.CURDIR}/usb/include -I${.CURDIR} SRCS+= core.c hcd-uhci.c host-libusb.c dev-hub.c .PATH: ${.CURDIR}/usb/gpl -SRCS+= desc.c +SRCS+= desc.c dev-serial.c NO_MAN= Added: soc2013/syuu/bhyve_usb/usr.sbin/bhyve/usb/gpl/char.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2013/syuu/bhyve_usb/usr.sbin/bhyve/usb/gpl/char.h Tue Aug 13 22:42:30 2013 (r255905) @@ -0,0 +1,303 @@ +#ifndef QEMU_CHAR_H +#define QEMU_CHAR_H + +//#include "qemu-common.h" +//#include "qemu/queue.h" +//#include "qemu/option.h" +//#include "qemu/config-file.h" +//#include "block/aio.h" +//#include "qapi/qmp/qobject.h" +//#include "qapi/qmp/qstring.h" +//#include "qemu/main-loop.h" + +/* character device */ + +#define CHR_EVENT_BREAK 0 /* serial break char */ +#define CHR_EVENT_FOCUS 1 /* focus to this terminal (modal input needed) */ +#define CHR_EVENT_OPENED 2 /* new connection established */ +#define CHR_EVENT_MUX_IN 3 /* mux-focus was set to this terminal */ +#define CHR_EVENT_MUX_OUT 4 /* mux-focus will move on */ +#define CHR_EVENT_CLOSED 5 /* connection closed */ + + +#define CHR_IOCTL_SERIAL_SET_PARAMS 1 +typedef struct { + int speed; + int parity; + int data_bits; + int stop_bits; +} QEMUSerialSetParams; + +#define CHR_IOCTL_SERIAL_SET_BREAK 2 + +#define CHR_IOCTL_PP_READ_DATA 3 +#define CHR_IOCTL_PP_WRITE_DATA 4 +#define CHR_IOCTL_PP_READ_CONTROL 5 +#define CHR_IOCTL_PP_WRITE_CONTROL 6 +#define CHR_IOCTL_PP_READ_STATUS 7 +#define CHR_IOCTL_PP_EPP_READ_ADDR 8 +#define CHR_IOCTL_PP_EPP_READ 9 +#define CHR_IOCTL_PP_EPP_WRITE_ADDR 10 +#define CHR_IOCTL_PP_EPP_WRITE 11 +#define CHR_IOCTL_PP_DATA_DIR 12 + +#define CHR_IOCTL_SERIAL_SET_TIOCM 13 +#define CHR_IOCTL_SERIAL_GET_TIOCM 14 + +#define CHR_TIOCM_CTS 0x020 +#define CHR_TIOCM_CAR 0x040 +#define CHR_TIOCM_DSR 0x100 +#define CHR_TIOCM_RI 0x080 +#define CHR_TIOCM_DTR 0x002 +#define CHR_TIOCM_RTS 0x004 + +#if 0 +typedef void IOEventHandler(void *opaque, int event); + +struct CharDriverState { + void (*init)(struct CharDriverState *s); + int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len); + GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond); + void (*chr_update_read_handler)(struct CharDriverState *s); + int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); + int (*get_msgfd)(struct CharDriverState *s); + int (*chr_add_client)(struct CharDriverState *chr, int fd); + IOEventHandler *chr_event; + IOCanReadHandler *chr_can_read; + IOReadHandler *chr_read; + void *handler_opaque; + void (*chr_close)(struct CharDriverState *chr); + void (*chr_accept_input)(struct CharDriverState *chr); + void (*chr_set_echo)(struct CharDriverState *chr, bool echo); + void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open); + void *opaque; + char *label; + char *filename; + int be_open; + int fe_open; + int explicit_fe_open; + int explicit_be_open; + int avail_connections; + int is_mux; + QemuOpts *opts; + QTAILQ_ENTRY(CharDriverState) next; +}; + +/** + * @qemu_chr_new_from_opts: + * + * Create a new character backend from a QemuOpts list. + * + * @opts see qemu-config.c for a list of valid options + * @init not sure.. + * + * Returns: a new character backend + */ +CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, + void (*init)(struct CharDriverState *s), + Error **errp); + +/** + * @qemu_chr_new: + * + * Create a new character backend from a URI. + * + * @label the name of the backend + * @filename the URI + * @init not sure.. + * + * Returns: a new character backend + */ +CharDriverState *qemu_chr_new(const char *label, const char *filename, + void (*init)(struct CharDriverState *s)); + +/** + * @qemu_chr_delete: + * + * Destroy a character backend. + */ +void qemu_chr_delete(CharDriverState *chr); + +/** + * @qemu_chr_fe_set_echo: + * + * Ask the backend to override its normal echo setting. This only really + * applies to the stdio backend and is used by the QMP server such that you + * can see what you type if you try to type QMP commands. + * + * @echo true to enable echo, false to disable echo + */ +void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo); + +/** + * @qemu_chr_fe_set_open: + * + * Set character frontend open status. This is an indication that the + * front end is ready (or not) to begin doing I/O. + */ +void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open); + +/** + * @qemu_chr_fe_printf: + * + * Write to a character backend using a printf style interface. + * + * @fmt see #printf + */ +void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + +int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, + GIOFunc func, void *user_data); + +/** + * @qemu_chr_fe_write: + * + * Write data to a character backend from the front end. This function will + * send data from the front end to the back end. + * + * @buf the data + * @len the number of bytes to send + * + * Returns: the number of bytes consumed + */ +int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len); + +/** + * @qemu_chr_fe_write_all: + * + * Write data to a character backend from the front end. This function will + * send data from the front end to the back end. Unlike @qemu_chr_fe_write, + * this function will block if the back end cannot consume all of the data + * attempted to be written. + * + * @buf the data + * @len the number of bytes to send + * + * Returns: the number of bytes consumed + */ +int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len); + +/** + * @qemu_chr_fe_ioctl: + * + * Issue a device specific ioctl to a backend. + * + * @cmd see CHR_IOCTL_* + * @arg the data associated with @cmd + * + * Returns: if @cmd is not supported by the backend, -ENOTSUP, otherwise the + * return value depends on the semantics of @cmd + */ +int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg); + +/** + * @qemu_chr_fe_get_msgfd: + * + * For backends capable of fd passing, return the latest file descriptor passed + * by a client. + * + * Returns: -1 if fd passing isn't supported or there is no pending file + * descriptor. If a file descriptor is returned, subsequent calls to + * this function will return -1 until a client sends a new file + * descriptor. + */ +int qemu_chr_fe_get_msgfd(CharDriverState *s); + +/** + * @qemu_chr_fe_claim: + * + * Claim a backend before using it, should be called before calling + * qemu_chr_add_handlers(). + * + * Returns: -1 if the backend is already in use by another frontend, 0 on + * success. + */ +int qemu_chr_fe_claim(CharDriverState *s); + +/** + * @qemu_chr_fe_claim_no_fail: + * + * Like qemu_chr_fe_claim, but will exit qemu with an error when the + * backend is already in use. + */ +void qemu_chr_fe_claim_no_fail(CharDriverState *s); + +/** + * @qemu_chr_fe_claim: + * + * Release a backend for use by another frontend. + * + * Returns: -1 if the backend is already in use by another frontend, 0 on + * success. + */ +void qemu_chr_fe_release(CharDriverState *s); + +/** + * @qemu_chr_be_can_write: + * + * Determine how much data the front end can currently accept. This function + * returns the number of bytes the front end can accept. If it returns 0, the + * front end cannot receive data at the moment. The function must be polled + * to determine when data can be received. + * + * Returns: the number of bytes the front end can receive via @qemu_chr_be_write + */ +int qemu_chr_be_can_write(CharDriverState *s); + +/** + * @qemu_chr_be_write: + * + * Write data from the back end to the front end. Before issuing this call, + * the caller should call @qemu_chr_be_can_write to determine how much data + * the front end can currently accept. + * + * @buf a buffer to receive data from the front end + * @len the number of bytes to receive from the front end + */ +void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len); + + +/** + * @qemu_chr_be_event: + * + * Send an event from the back end to the front end. + * + * @event the event to send + */ +void qemu_chr_be_event(CharDriverState *s, int event); + +void qemu_chr_add_handlers(CharDriverState *s, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque); + +void qemu_chr_be_generic_open(CharDriverState *s); +void qemu_chr_accept_input(CharDriverState *s); +int qemu_chr_add_client(CharDriverState *s, int fd); +void qemu_chr_info_print(Monitor *mon, const QObject *ret_data); +void qemu_chr_info(Monitor *mon, QObject **ret_data); +CharDriverState *qemu_chr_find(const char *name); + +QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); + +void register_char_driver(const char *name, CharDriverState *(*open)(QemuOpts *)); +void register_char_driver_qapi(const char *name, ChardevBackendKind kind, + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp)); + +/* add an eventfd to the qemu devices that are polled */ +CharDriverState *qemu_chr_open_eventfd(int eventfd); + +extern int term_escape_char; + +CharDriverState *qemu_char_get_next_serial(void); + +/* msmouse */ +CharDriverState *qemu_chr_open_msmouse(void); + +/* baum.c */ +CharDriverState *chr_baum_init(void); +#endif + +#endif Added: soc2013/syuu/bhyve_usb/usr.sbin/bhyve/usb/gpl/dev-serial.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2013/syuu/bhyve_usb/usr.sbin/bhyve/usb/gpl/dev-serial.c Tue Aug 13 22:42:30 2013 (r255905) @@ -0,0 +1,667 @@ +/* + * FTDI FT232BM Device emulation + * + * Copyright (c) 2006 CodeSourcery. + * Copyright (c) 2008 Samuel Thibault + * Written by Paul Brook, reused for FTDI by Samuel Thibault + * + * This code is licensed under the LGPL. + */ + +#include +#include +#include +#include +#include + +//#include "qemu-common.h" +//#include "qemu/error-report.h" +#include "hw/usb.h" +#include "usb/gpl/desc.h" +#include "usb/gpl/char.h" + +int strstart(const char *str, const char *val, const char **ptr); +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + + +//#define DEBUG_Serial + +#ifdef DEBUG_Serial +#define DPRINTF(fmt, ...) \ +do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#endif + +#define RECV_BUF 384 + +/* Commands */ +#define FTDI_RESET 0 +#define FTDI_SET_MDM_CTRL 1 +#define FTDI_SET_FLOW_CTRL 2 +#define FTDI_SET_BAUD 3 +#define FTDI_SET_DATA 4 +#define FTDI_GET_MDM_ST 5 +#define FTDI_SET_EVENT_CHR 6 +#define FTDI_SET_ERROR_CHR 7 +#define FTDI_SET_LATENCY 9 +#define FTDI_GET_LATENCY 10 + +#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) +#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) + +/* RESET */ + +#define FTDI_RESET_SIO 0 +#define FTDI_RESET_RX 1 +#define FTDI_RESET_TX 2 + +/* SET_MDM_CTRL */ + +#define FTDI_DTR 1 +#define FTDI_SET_DTR (FTDI_DTR << 8) +#define FTDI_RTS 2 +#define FTDI_SET_RTS (FTDI_RTS << 8) + +/* SET_FLOW_CTRL */ + +#define FTDI_RTS_CTS_HS 1 +#define FTDI_DTR_DSR_HS 2 +#define FTDI_XON_XOFF_HS 4 + +/* SET_DATA */ + +#define FTDI_PARITY (0x7 << 8) +#define FTDI_ODD (0x1 << 8) +#define FTDI_EVEN (0x2 << 8) +#define FTDI_MARK (0x3 << 8) +#define FTDI_SPACE (0x4 << 8) + +#define FTDI_STOP (0x3 << 11) +#define FTDI_STOP1 (0x0 << 11) +#define FTDI_STOP15 (0x1 << 11) +#define FTDI_STOP2 (0x2 << 11) + +/* GET_MDM_ST */ +/* TODO: should be sent every 40ms */ +#define FTDI_CTS (1<<4) // CTS line status +#define FTDI_DSR (1<<5) // DSR line status +#define FTDI_RI (1<<6) // RI line status +#define FTDI_RLSD (1<<7) // Receive Line Signal Detect + +/* Status */ + +#define FTDI_DR (1<<0) // Data Ready +#define FTDI_OE (1<<1) // Overrun Err +#define FTDI_PE (1<<2) // Parity Err +#define FTDI_FE (1<<3) // Framing Err +#define FTDI_BI (1<<4) // Break Interrupt +#define FTDI_THRE (1<<5) // Transmitter Holding Register +#define FTDI_TEMT (1<<6) // Transmitter Empty +#define FTDI_FIFO (1<<7) // Error in FIFO + +typedef struct { + USBDevice dev; + uint8_t recv_buf[RECV_BUF]; + uint16_t recv_ptr; + uint16_t recv_used; + uint8_t event_chr; + uint8_t error_chr; + uint8_t event_trigger; + QEMUSerialSetParams params; + int latency; /* ms */ +// CharDriverState *cs; +} USBSerialState; + +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT_SERIAL, + STR_PRODUCT_BRAILLE, + STR_SERIALNUMBER, +}; + +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU", + [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL", + [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE", + [STR_SERIALNUMBER] = "1", +}; + +static const USBDescIface desc_iface0 = { + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + }, + } +}; + +static const USBDescDevice desc_device = { + .bcdUSB = 0x0200, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .bmAttributes = 0x80, + .bMaxPower = 50, + .nif = 1, + .ifs = &desc_iface0, + }, + }, +}; + +static const USBDesc desc_serial = { + .id = { + .idVendor = 0x0403, + .idProduct = 0x6001, + .bcdDevice = 0x0400, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_SERIAL, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device, + .str = desc_strings, +}; + +static const USBDesc desc_braille = { + .id = { + .idVendor = 0x0403, + .idProduct = 0xfe72, + .bcdDevice = 0x0400, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_BRAILLE, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device, + .str = desc_strings, +}; + +static void usb_serial_reset(USBSerialState *s) +{ + /* TODO: Set flow control to none */ + s->event_chr = 0x0d; + s->event_trigger = 0; + s->recv_ptr = 0; + s->recv_used = 0; + /* TODO: purge in char driver */ +} + +static void usb_serial_handle_reset(USBDevice *dev) +{ + USBSerialState *s = (USBSerialState *)dev; + + DPRINTF("Reset\n"); + + usb_serial_reset(s); + /* TODO: Reset char device, send BREAK? */ +} + +static uint8_t usb_get_modem_lines(USBSerialState *s) +{ + int flags; + uint8_t ret; + +// if (qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) +// return FTDI_CTS|FTDI_DSR|FTDI_RLSD; + + ret = 0; + if (flags & CHR_TIOCM_CTS) + ret |= FTDI_CTS; + if (flags & CHR_TIOCM_DSR) + ret |= FTDI_DSR; + if (flags & CHR_TIOCM_RI) + ret |= FTDI_RI; + if (flags & CHR_TIOCM_CAR) + ret |= FTDI_RLSD; + + return ret; +} + +static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) +{ + USBSerialState *s = (USBSerialState *)dev; + int ret; + + DPRINTF("got control %x, value %x\n",request, value); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); + if (ret >= 0) { + return; + } + + switch (request) { + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: + break; + + /* Class specific requests. */ + case DeviceOutVendor | FTDI_RESET: + switch (value) { + case FTDI_RESET_SIO: + usb_serial_reset(s); + break; + case FTDI_RESET_RX: + s->recv_ptr = 0; + s->recv_used = 0; + /* TODO: purge from char device */ + break; + case FTDI_RESET_TX: + /* TODO: purge from char device */ + break; + } + break; + case DeviceOutVendor | FTDI_SET_MDM_CTRL: + { + static int flags; +// qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); + if (value & FTDI_SET_RTS) { + if (value & FTDI_RTS) + flags |= CHR_TIOCM_RTS; + else + flags &= ~CHR_TIOCM_RTS; + } + if (value & FTDI_SET_DTR) { + if (value & FTDI_DTR) + flags |= CHR_TIOCM_DTR; + else + flags &= ~CHR_TIOCM_DTR; + } +// qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); + break; + } + case DeviceOutVendor | FTDI_SET_FLOW_CTRL: + /* TODO: ioctl */ + break; + case DeviceOutVendor | FTDI_SET_BAUD: { + static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 }; + int subdivisor8 = subdivisors8[((value & 0xc000) >> 14) + | ((index & 1) << 2)]; + int divisor = value & 0x3fff; + + /* chip special cases */ + if (divisor == 1 && subdivisor8 == 0) + subdivisor8 = 4; + if (divisor == 0 && subdivisor8 == 0) + divisor = 1; + + s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); +// qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); + break; + } + case DeviceOutVendor | FTDI_SET_DATA: + switch (value & FTDI_PARITY) { + case 0: + s->params.parity = 'N'; + break; + case FTDI_ODD: + s->params.parity = 'O'; + break; + case FTDI_EVEN: + s->params.parity = 'E'; + break; + default: + DPRINTF("unsupported parity %d\n", value & FTDI_PARITY); + goto fail; + } + switch (value & FTDI_STOP) { + case FTDI_STOP1: + s->params.stop_bits = 1; + break; + case FTDI_STOP2: + s->params.stop_bits = 2; + break; + default: + DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP); + goto fail; + } +// qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); + /* TODO: TX ON/OFF */ + break; + case DeviceInVendor | FTDI_GET_MDM_ST: + data[0] = usb_get_modem_lines(s) | 1; + data[1] = 0; + p->actual_length = 2; + break; + case DeviceOutVendor | FTDI_SET_EVENT_CHR: + /* TODO: handle it */ + s->event_chr = value; + break; + case DeviceOutVendor | FTDI_SET_ERROR_CHR: + /* TODO: handle it */ + s->error_chr = value; + break; + case DeviceOutVendor | FTDI_SET_LATENCY: + s->latency = value; + break; + case DeviceInVendor | FTDI_GET_LATENCY: + data[0] = s->latency; + p->actual_length = 1; + break; + default: + fail: + DPRINTF("got unsupported/bogus control %x, value %x\n", request, value); + p->status = USB_RET_STALL; + break; + } +} + +static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) +{ + USBSerialState *s = (USBSerialState *)dev; + uint8_t devep = p->ep->nr; + struct iovec *iov; + uint8_t header[2]; + int i, first_len, len; + + switch (p->pid) { + case USB_TOKEN_OUT: + if (devep != 2) + goto fail; + for (i = 0; i < p->iov.niov; i++) { + iov = p->iov.iov + i; +// qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len); + } + p->actual_length = p->iov.size; + break; + + case USB_TOKEN_IN: + if (devep != 1) + goto fail; + first_len = RECV_BUF - s->recv_ptr; + len = p->iov.size; + if (len <= 2) { + p->status = USB_RET_NAK; + break; + } + header[0] = usb_get_modem_lines(s) | 1; + /* We do not have the uart details */ + /* handle serial break */ + if (s->event_trigger && s->event_trigger & FTDI_BI) { + s->event_trigger &= ~FTDI_BI; + header[1] = FTDI_BI; + usb_packet_copy(p, header, 2); + break; + } else { + header[1] = 0; + } + len -= 2; + if (len > s->recv_used) + len = s->recv_used; + if (!len) { + p->status = USB_RET_NAK; + break; + } + if (first_len > len) + first_len = len; + usb_packet_copy(p, header, 2); + usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); + if (len > first_len) + usb_packet_copy(p, s->recv_buf, len - first_len); + s->recv_used -= len; + s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; + break; + + default: + DPRINTF("Bad token\n"); + fail: + p->status = USB_RET_STALL; + break; + } +} + +static int usb_serial_can_read(void *opaque) +{ + USBSerialState *s = opaque; + + if (!s->dev.attached) { + return 0; + } + return RECV_BUF - s->recv_used; +} + +static void usb_serial_read(void *opaque, const uint8_t *buf, int size) +{ + USBSerialState *s = opaque; + int first_size, start; + + /* room in the buffer? */ + if (size > (RECV_BUF - s->recv_used)) + size = RECV_BUF - s->recv_used; + + start = s->recv_ptr + s->recv_used; + if (start < RECV_BUF) { + /* copy data to end of buffer */ + first_size = RECV_BUF - start; + if (first_size > size) + first_size = size; + + memcpy(s->recv_buf + start, buf, first_size); + + /* wrap around to front if needed */ + if (size > first_size) + memcpy(s->recv_buf, buf + first_size, size - first_size); + } else { + start -= RECV_BUF; + memcpy(s->recv_buf + start, buf, size); + } + s->recv_used += size; +} + +static void usb_serial_event(void *opaque, int event) +{ + USBSerialState *s = opaque; + + switch (event) { + case CHR_EVENT_BREAK: + s->event_trigger |= FTDI_BI; + break; + case CHR_EVENT_FOCUS: + break; + case CHR_EVENT_OPENED: + if (!s->dev.attached) { + usb_device_attach(&s->dev); + } + break; + case CHR_EVENT_CLOSED: + if (s->dev.attached) { + usb_device_detach(&s->dev); + } + break; + } +} + +#if 0 +static int usb_serial_initfn(USBDevice *dev) +{ + USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev); + + usb_desc_create_serial(dev); + usb_desc_init(dev); + dev->auto_attach = 0; + + if (!s->cs) { + error_report("Property chardev is required"); + return -1; + } + + qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read, + usb_serial_event, s); + usb_serial_handle_reset(dev); + + if (s->cs->be_open && !dev->attached) { + usb_device_attach(dev); + } + return 0; +} +#endif + +static USBDevice *usb_serial_init(USBBus *bus, const char *filename) +{ + USBDevice *dev; +// CharDriverState *cdrv; + uint32_t vendorid = 0, productid = 0; + char label[32]; + static int index; + + while (*filename && *filename != ':') { + const char *p; + char *e; + if (strstart(filename, "vendorid=", &p)) { + vendorid = strtol(p, &e, 16); + if (e == p || (*e && *e != ',' && *e != ':')) { +// error_report("bogus vendor ID %s", p); + return NULL; + } + filename = e; + } else if (strstart(filename, "productid=", &p)) { + productid = strtol(p, &e, 16); + if (e == p || (*e && *e != ',' && *e != ':')) { +// error_report("bogus product ID %s", p); + return NULL; + } + filename = e; + } else { +// error_report("unrecognized serial USB option %s", filename); + return NULL; + } + while(*filename == ',') + filename++; + } + if (!*filename) { +// error_report("character device specification needed"); + return NULL; + } + filename++; + + snprintf(label, sizeof(label), "usbserial%d", index++); +// cdrv = qemu_chr_new(label, filename, NULL); +// if (!cdrv) +// return NULL; + + dev = usb_create(bus, "usb-serial"); + if (!dev) { + return NULL; + } +// qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); +// if (vendorid) +// qdev_prop_set_uint16(&dev->qdev, "vendorid", vendorid); +// if (productid) +// qdev_prop_set_uint16(&dev->qdev, "productid", productid); +// qdev_init_nofail(&dev->qdev); + + return dev; +} + +static USBDevice *usb_braille_init(USBBus *bus, const char *unused) +{ + USBDevice *dev; +// CharDriverState *cdrv; + +// cdrv = qemu_chr_new("braille", "braille", NULL); +// if (!cdrv) +// return NULL; + + dev = usb_create(bus, "usb-braille"); +// qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); +// qdev_init_nofail(&dev->qdev); + + return dev; +} + +#if 0 +static const VMStateDescription vmstate_usb_serial = { + .name = "usb-serial", + .unmigratable = 1, +}; + +static Property serial_properties[] = { + DEFINE_PROP_CHR("chardev", USBSerialState, cs), + DEFINE_PROP_END_OF_LIST(), +}; + +static void usb_serial_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->init = usb_serial_initfn; + uc->product_desc = "QEMU USB Serial"; + uc->usb_desc = &desc_serial; + uc->handle_reset = usb_serial_handle_reset; + uc->handle_control = usb_serial_handle_control; + uc->handle_data = usb_serial_handle_data; + dc->vmsd = &vmstate_usb_serial; + dc->props = serial_properties; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo serial_info = { + .name = "usb-serial", + .parent = TYPE_USB_DEVICE, + .instance_size = sizeof(USBSerialState), + .class_init = usb_serial_class_initfn, +}; + +static Property braille_properties[] = { + DEFINE_PROP_CHR("chardev", USBSerialState, cs), + DEFINE_PROP_END_OF_LIST(), +}; + +static void usb_braille_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->init = usb_serial_initfn; + uc->product_desc = "QEMU USB Braille"; + uc->usb_desc = &desc_braille; + uc->handle_reset = usb_serial_handle_reset; + uc->handle_control = usb_serial_handle_control; + uc->handle_data = usb_serial_handle_data; + dc->vmsd = &vmstate_usb_serial; + dc->props = braille_properties; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo braille_info = { + .name = "usb-braille", + .parent = TYPE_USB_DEVICE, + .instance_size = sizeof(USBSerialState), + .class_init = usb_braille_class_initfn, +}; + +static void usb_serial_register_types(void) +{ + type_register_static(&serial_info); + usb_legacy_register("usb-serial", "serial", usb_serial_init); + type_register_static(&braille_info); + usb_legacy_register("usb-braille", "braille", usb_braille_init); +} + +type_init(usb_serial_register_types) +#endif