2008-01-14 06:41:02 +03:00
|
|
|
/*
|
|
|
|
* FTDI FT232BM Device emulation
|
|
|
|
*
|
|
|
|
* Copyright (c) 2006 CodeSourcery.
|
|
|
|
* Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
|
|
|
|
* Written by Paul Brook, reused for FTDI by Samuel Thibault
|
|
|
|
*
|
2011-06-26 06:21:35 +04:00
|
|
|
* This code is licensed under the LGPL.
|
2008-01-14 06:41:02 +03:00
|
|
|
*/
|
|
|
|
|
2016-01-26 21:17:12 +03:00
|
|
|
#include "qemu/osdep.h"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 11:01:28 +03:00
|
|
|
#include "qapi/error.h"
|
2016-03-20 20:16:19 +03:00
|
|
|
#include "qemu/cutils.h"
|
2015-03-17 20:29:20 +03:00
|
|
|
#include "qemu/error-report.h"
|
2019-05-23 17:35:07 +03:00
|
|
|
#include "qemu/module.h"
|
2019-08-12 08:23:51 +03:00
|
|
|
#include "hw/qdev-properties.h"
|
2012-03-07 17:55:18 +04:00
|
|
|
#include "hw/usb.h"
|
2019-08-12 08:23:45 +03:00
|
|
|
#include "migration/vmstate.h"
|
2018-05-03 22:50:48 +03:00
|
|
|
#include "desc.h"
|
2017-01-26 16:33:39 +03:00
|
|
|
#include "chardev/char-serial.h"
|
2017-01-26 17:26:44 +03:00
|
|
|
#include "chardev/char-fe.h"
|
2020-09-03 23:43:22 +03:00
|
|
|
#include "qom/object.h"
|
2008-01-14 06:41:02 +03:00
|
|
|
|
|
|
|
//#define DEBUG_Serial
|
|
|
|
|
|
|
|
#ifdef DEBUG_Serial
|
2009-05-13 21:53:17 +04:00
|
|
|
#define DPRINTF(fmt, ...) \
|
|
|
|
do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0)
|
2008-01-14 06:41:02 +03:00
|
|
|
#else
|
2009-05-13 21:53:17 +04:00
|
|
|
#define DPRINTF(fmt, ...) do {} while(0)
|
2008-01-14 06:41:02 +03:00
|
|
|
#endif
|
|
|
|
|
2020-03-16 20:46:09 +03:00
|
|
|
#define RECV_BUF (512 - (2 * 8))
|
2008-01-14 06:41:02 +03:00
|
|
|
|
|
|
|
/* 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
|
2008-08-13 08:23:17 +04:00
|
|
|
#define FTDI_SET_DTR (FTDI_DTR << 8)
|
2008-01-14 06:41:02 +03:00
|
|
|
#define FTDI_RTS 2
|
2008-08-13 08:23:17 +04:00
|
|
|
#define FTDI_SET_RTS (FTDI_RTS << 8)
|
2008-01-14 06:41:02 +03:00
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
2020-09-03 23:43:22 +03:00
|
|
|
struct USBSerialState {
|
2008-01-14 06:41:02 +03:00
|
|
|
USBDevice dev;
|
2020-03-06 17:09:17 +03:00
|
|
|
USBEndpoint *intr;
|
2008-01-14 06:41:02 +03:00
|
|
|
uint8_t recv_buf[RECV_BUF];
|
2008-09-18 02:04:21 +04:00
|
|
|
uint16_t recv_ptr;
|
|
|
|
uint16_t recv_used;
|
2008-01-14 06:41:02 +03:00
|
|
|
uint8_t event_chr;
|
|
|
|
uint8_t error_chr;
|
|
|
|
uint8_t event_trigger;
|
|
|
|
QEMUSerialSetParams params;
|
|
|
|
int latency; /* ms */
|
2016-10-22 12:52:51 +03:00
|
|
|
CharBackend cs;
|
2020-09-03 23:43:22 +03:00
|
|
|
};
|
|
|
|
typedef struct USBSerialState USBSerialState;
|
2008-01-14 06:41:02 +03:00
|
|
|
|
2015-05-06 15:55:36 +03:00
|
|
|
#define TYPE_USB_SERIAL "usb-serial-dev"
|
2020-09-03 01:43:01 +03:00
|
|
|
DECLARE_INSTANCE_CHECKER(USBSerialState, USB_SERIAL,
|
2020-09-01 00:07:33 +03:00
|
|
|
TYPE_USB_SERIAL)
|
2015-05-06 15:55:36 +03:00
|
|
|
|
2010-11-17 13:05:32 +03:00
|
|
|
enum {
|
|
|
|
STR_MANUFACTURER = 1,
|
|
|
|
STR_PRODUCT_SERIAL,
|
|
|
|
STR_PRODUCT_BRAILLE,
|
|
|
|
STR_SERIALNUMBER,
|
2008-01-14 06:41:02 +03:00
|
|
|
};
|
|
|
|
|
2010-11-17 13:05:32 +03:00
|
|
|
static const USBDescStrings desc_strings = {
|
2012-05-30 07:35:51 +04:00
|
|
|
[STR_MANUFACTURER] = "QEMU",
|
2010-11-17 13:05:32 +03:00
|
|
|
[STR_PRODUCT_SERIAL] = "QEMU USB SERIAL",
|
2012-08-23 11:59:27 +04:00
|
|
|
[STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE",
|
2010-11-17 13:05:32 +03:00
|
|
|
[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,
|
2020-03-06 17:09:17 +03:00
|
|
|
.bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP,
|
2010-11-17 13:05:32 +03:00
|
|
|
.bMaxPower = 50,
|
2011-04-03 09:33:19 +04:00
|
|
|
.nif = 1,
|
2010-11-17 13:05:32 +03:00
|
|
|
.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,
|
2008-01-14 06:41:02 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
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? */
|
|
|
|
}
|
|
|
|
|
2008-08-13 08:23:17 +04:00
|
|
|
static uint8_t usb_get_modem_lines(USBSerialState *s)
|
|
|
|
{
|
|
|
|
int flags;
|
|
|
|
uint8_t ret;
|
|
|
|
|
2016-10-22 12:52:55 +03:00
|
|
|
if (qemu_chr_fe_ioctl(&s->cs,
|
2016-10-22 12:52:51 +03:00
|
|
|
CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
|
2008-08-13 08:23:17 +04:00
|
|
|
return FTDI_CTS|FTDI_DSR|FTDI_RLSD;
|
2016-10-22 12:52:51 +03:00
|
|
|
}
|
2008-08-13 08:23:17 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
static void usb_serial_handle_control(USBDevice *dev, USBPacket *p,
|
2011-02-02 18:33:13 +03:00
|
|
|
int request, int value, int index, int length, uint8_t *data)
|
2008-01-14 06:41:02 +03:00
|
|
|
{
|
|
|
|
USBSerialState *s = (USBSerialState *)dev;
|
2010-11-17 13:05:32 +03:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
DPRINTF("got control %x, value %x\n",request, value);
|
2011-02-02 18:33:13 +03:00
|
|
|
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
2010-11-17 13:05:32 +03:00
|
|
|
if (ret >= 0) {
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
return;
|
2010-11-17 13:05:32 +03:00
|
|
|
}
|
2008-01-14 06:41:02 +03:00
|
|
|
|
|
|
|
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:
|
2008-08-13 08:23:17 +04:00
|
|
|
{
|
|
|
|
static int flags;
|
2016-10-22 12:52:55 +03:00
|
|
|
qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
|
2008-08-13 08:23:17 +04:00
|
|
|
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;
|
|
|
|
}
|
2016-10-22 12:52:55 +03:00
|
|
|
qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
|
2008-01-14 06:41:02 +03:00
|
|
|
break;
|
2008-08-13 08:23:17 +04:00
|
|
|
}
|
2008-01-14 06:41:02 +03:00
|
|
|
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);
|
2016-10-22 12:52:55 +03:00
|
|
|
qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
|
2008-01-14 06:41:02 +03:00
|
|
|
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;
|
|
|
|
}
|
2016-10-22 12:52:55 +03:00
|
|
|
qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
|
2008-01-14 06:41:02 +03:00
|
|
|
/* TODO: TX ON/OFF */
|
|
|
|
break;
|
|
|
|
case DeviceInVendor | FTDI_GET_MDM_ST:
|
2008-08-13 08:23:17 +04:00
|
|
|
data[0] = usb_get_modem_lines(s) | 1;
|
2020-03-16 20:46:10 +03:00
|
|
|
data[1] = FTDI_THRE | FTDI_TEMT;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->actual_length = 2;
|
2008-01-14 06:41:02 +03:00
|
|
|
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;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->actual_length = 1;
|
2008-01-14 06:41:02 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fail:
|
|
|
|
DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_STALL;
|
2008-01-14 06:41:02 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-16 20:46:07 +03:00
|
|
|
static void usb_serial_token_in(USBSerialState *s, USBPacket *p)
|
|
|
|
{
|
2020-03-16 20:46:08 +03:00
|
|
|
const int max_packet_size = desc_iface0.eps[0].wMaxPacketSize;
|
|
|
|
int packet_len;
|
2020-03-16 20:46:07 +03:00
|
|
|
uint8_t header[2];
|
|
|
|
|
2020-03-16 20:46:08 +03:00
|
|
|
packet_len = p->iov.size;
|
|
|
|
if (packet_len <= 2) {
|
2020-03-16 20:46:07 +03:00
|
|
|
p->status = USB_RET_NAK;
|
|
|
|
return;
|
|
|
|
}
|
2020-03-16 20:46:08 +03:00
|
|
|
|
2020-03-16 20:46:07 +03:00
|
|
|
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);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
header[1] = 0;
|
|
|
|
}
|
2020-03-16 20:46:08 +03:00
|
|
|
|
|
|
|
if (!s->recv_used) {
|
2020-03-16 20:46:07 +03:00
|
|
|
p->status = USB_RET_NAK;
|
|
|
|
return;
|
|
|
|
}
|
2020-03-16 20:46:08 +03:00
|
|
|
|
|
|
|
while (s->recv_used && packet_len > 2) {
|
|
|
|
int first_len, len;
|
|
|
|
|
|
|
|
len = MIN(packet_len, max_packet_size);
|
|
|
|
len -= 2;
|
|
|
|
if (len > s->recv_used) {
|
|
|
|
len = s->recv_used;
|
|
|
|
}
|
|
|
|
|
|
|
|
first_len = RECV_BUF - s->recv_ptr;
|
|
|
|
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;
|
|
|
|
packet_len -= len + 2;
|
2020-03-16 20:46:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
static void usb_serial_handle_data(USBDevice *dev, USBPacket *p)
|
2008-01-14 06:41:02 +03:00
|
|
|
{
|
|
|
|
USBSerialState *s = (USBSerialState *)dev;
|
2012-01-12 16:23:01 +04:00
|
|
|
uint8_t devep = p->ep->nr;
|
2011-07-13 12:53:23 +04:00
|
|
|
struct iovec *iov;
|
2020-03-16 20:46:07 +03:00
|
|
|
int i;
|
2008-01-14 06:41:02 +03:00
|
|
|
|
|
|
|
switch (p->pid) {
|
|
|
|
case USB_TOKEN_OUT:
|
|
|
|
if (devep != 2)
|
|
|
|
goto fail;
|
2011-07-13 12:53:23 +04:00
|
|
|
for (i = 0; i < p->iov.niov; i++) {
|
|
|
|
iov = p->iov.iov + i;
|
2016-09-06 16:56:04 +03:00
|
|
|
/* XXX this blocks entire thread. Rewrite to use
|
|
|
|
* qemu_chr_fe_write and background I/O callbacks */
|
2016-10-22 12:52:55 +03:00
|
|
|
qemu_chr_fe_write_all(&s->cs, iov->iov_base, iov->iov_len);
|
2011-07-13 12:53:23 +04:00
|
|
|
}
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->actual_length = p->iov.size;
|
2008-01-14 06:41:02 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case USB_TOKEN_IN:
|
|
|
|
if (devep != 1)
|
|
|
|
goto fail;
|
2020-03-16 20:46:07 +03:00
|
|
|
usb_serial_token_in(s, p);
|
2008-01-14 06:41:02 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DPRINTF("Bad token\n");
|
|
|
|
fail:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->status = USB_RET_STALL;
|
2008-01-14 06:41:02 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-18 00:26:25 +04:00
|
|
|
static int usb_serial_can_read(void *opaque)
|
2008-01-14 06:41:02 +03:00
|
|
|
{
|
|
|
|
USBSerialState *s = opaque;
|
2012-10-17 11:54:25 +04:00
|
|
|
|
|
|
|
if (!s->dev.attached) {
|
|
|
|
return 0;
|
|
|
|
}
|
2008-01-14 06:41:02 +03:00
|
|
|
return RECV_BUF - s->recv_used;
|
|
|
|
}
|
|
|
|
|
2008-08-18 00:26:25 +04:00
|
|
|
static void usb_serial_read(void *opaque, const uint8_t *buf, int size)
|
2008-01-14 06:41:02 +03:00
|
|
|
{
|
|
|
|
USBSerialState *s = opaque;
|
2010-02-03 19:00:54 +03:00
|
|
|
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);
|
|
|
|
}
|
2008-01-14 06:41:02 +03:00
|
|
|
s->recv_used += size;
|
2020-03-06 17:09:17 +03:00
|
|
|
|
|
|
|
usb_wakeup(s->intr, 0);
|
2008-01-14 06:41:02 +03:00
|
|
|
}
|
|
|
|
|
chardev: Use QEMUChrEvent enum in IOEventHandler typedef
The Chardev events are listed in the QEMUChrEvent enum.
By using the enum in the IOEventHandler typedef we:
- make the IOEventHandler type more explicit (this handler
process out-of-band information, while the IOReadHandler
is in-band),
- help static code analyzers.
This patch was produced with the following spatch script:
@match@
expression backend, opaque, context, set_open;
identifier fd_can_read, fd_read, fd_event, be_change;
@@
qemu_chr_fe_set_handlers(backend, fd_can_read, fd_read, fd_event,
be_change, opaque, context, set_open);
@depends on match@
identifier opaque, event;
identifier match.fd_event;
@@
static
-void fd_event(void *opaque, int event)
+void fd_event(void *opaque, QEMUChrEvent event)
{
...
}
Then the typedef was modified manually in
include/chardev/char-fe.h.
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Acked-by: Corey Minyard <cminyard@mvista.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20191218172009.8868-15-philmd@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2019-12-18 20:20:09 +03:00
|
|
|
static void usb_serial_event(void *opaque, QEMUChrEvent event)
|
2008-01-14 06:41:02 +03:00
|
|
|
{
|
|
|
|
USBSerialState *s = opaque;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case CHR_EVENT_BREAK:
|
2009-05-18 19:00:26 +04:00
|
|
|
s->event_trigger |= FTDI_BI;
|
2008-01-14 06:41:02 +03:00
|
|
|
break;
|
2009-10-07 17:01:16 +04:00
|
|
|
case CHR_EVENT_OPENED:
|
2012-10-17 11:54:25 +04:00
|
|
|
if (!s->dev.attached) {
|
2014-09-19 11:25:21 +04:00
|
|
|
usb_device_attach(&s->dev, &error_abort);
|
2012-10-17 11:54:25 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHR_EVENT_CLOSED:
|
|
|
|
if (s->dev.attached) {
|
|
|
|
usb_device_detach(&s->dev);
|
|
|
|
}
|
2008-01-14 06:41:02 +03:00
|
|
|
break;
|
2019-12-18 20:19:59 +03:00
|
|
|
case CHR_EVENT_MUX_IN:
|
|
|
|
case CHR_EVENT_MUX_OUT:
|
|
|
|
/* Ignore */
|
|
|
|
break;
|
2008-01-14 06:41:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-19 10:48:34 +04:00
|
|
|
static void usb_serial_realize(USBDevice *dev, Error **errp)
|
2009-08-31 16:23:59 +04:00
|
|
|
{
|
2020-09-03 01:43:01 +03:00
|
|
|
USBSerialState *s = USB_SERIAL(dev);
|
2014-09-19 11:25:21 +04:00
|
|
|
Error *local_err = NULL;
|
2010-11-26 22:20:41 +03:00
|
|
|
|
2012-04-20 14:33:30 +04:00
|
|
|
usb_desc_create_serial(dev);
|
2010-11-26 22:20:41 +03:00
|
|
|
usb_desc_init(dev);
|
2012-10-17 11:54:25 +04:00
|
|
|
dev->auto_attach = 0;
|
2009-10-26 17:56:47 +03:00
|
|
|
|
2017-07-06 15:08:52 +03:00
|
|
|
if (!qemu_chr_fe_backend_connected(&s->cs)) {
|
2014-09-19 10:48:34 +04:00
|
|
|
error_setg(errp, "Property chardev is required");
|
|
|
|
return;
|
2010-05-28 19:03:22 +04:00
|
|
|
}
|
|
|
|
|
2014-09-19 11:25:21 +04:00
|
|
|
usb_check_attach(dev, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-22 12:52:55 +03:00
|
|
|
qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read,
|
2017-07-06 15:08:49 +03:00
|
|
|
usb_serial_event, NULL, s, NULL, true);
|
2009-10-26 17:56:47 +03:00
|
|
|
usb_serial_handle_reset(dev);
|
2012-10-17 11:54:25 +04:00
|
|
|
|
2017-07-06 15:08:52 +03:00
|
|
|
if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) {
|
2014-09-19 11:25:21 +04:00
|
|
|
usb_device_attach(dev, &error_abort);
|
2012-10-17 11:54:25 +04:00
|
|
|
}
|
2020-03-06 17:09:17 +03:00
|
|
|
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
2009-08-31 16:23:59 +04:00
|
|
|
}
|
|
|
|
|
2020-06-10 08:32:16 +03:00
|
|
|
static USBDevice *usb_braille_init(const char *unused)
|
2009-10-26 17:56:47 +03:00
|
|
|
{
|
|
|
|
USBDevice *dev;
|
2016-12-07 16:20:22 +03:00
|
|
|
Chardev *cdrv;
|
2009-10-26 17:56:47 +03:00
|
|
|
|
2019-02-13 16:18:13 +03:00
|
|
|
cdrv = qemu_chr_new("braille", "braille", NULL);
|
2009-10-26 17:56:47 +03:00
|
|
|
if (!cdrv)
|
|
|
|
return NULL;
|
2009-08-31 16:23:59 +04:00
|
|
|
|
2020-06-10 08:32:16 +03:00
|
|
|
dev = usb_new("usb-braille");
|
2009-10-26 17:56:47 +03:00
|
|
|
qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
|
|
|
|
return dev;
|
2008-01-14 06:41:02 +03:00
|
|
|
}
|
2009-08-31 16:23:59 +04:00
|
|
|
|
2011-07-20 12:06:18 +04:00
|
|
|
static const VMStateDescription vmstate_usb_serial = {
|
|
|
|
.name = "usb-serial",
|
|
|
|
.unmigratable = 1,
|
|
|
|
};
|
|
|
|
|
2011-12-08 07:34:16 +04:00
|
|
|
static Property serial_properties[] = {
|
|
|
|
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
|
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
|
|
};
|
|
|
|
|
2015-05-06 15:55:36 +03:00
|
|
|
static void usb_serial_dev_class_init(ObjectClass *klass, void *data)
|
2011-12-16 00:53:10 +04:00
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2011-12-16 00:53:10 +04:00
|
|
|
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
|
|
|
|
2015-05-06 15:55:36 +03:00
|
|
|
uc->realize = usb_serial_realize;
|
2011-12-16 00:53:10 +04:00
|
|
|
uc->handle_reset = usb_serial_handle_reset;
|
|
|
|
uc->handle_control = usb_serial_handle_control;
|
|
|
|
uc->handle_data = usb_serial_handle_data;
|
2011-12-08 07:34:16 +04:00
|
|
|
dc->vmsd = &vmstate_usb_serial;
|
2013-07-29 18:17:45 +04:00
|
|
|
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
2011-12-16 00:53:10 +04:00
|
|
|
}
|
|
|
|
|
2015-05-06 15:55:36 +03:00
|
|
|
static const TypeInfo usb_serial_dev_type_info = {
|
|
|
|
.name = TYPE_USB_SERIAL,
|
|
|
|
.parent = TYPE_USB_DEVICE,
|
|
|
|
.instance_size = sizeof(USBSerialState),
|
|
|
|
.abstract = true,
|
|
|
|
.class_init = usb_serial_dev_class_init,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void usb_serial_class_initfn(ObjectClass *klass, void *data)
|
|
|
|
{
|
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
|
|
|
|
|
|
|
uc->product_desc = "QEMU USB Serial";
|
|
|
|
uc->usb_desc = &desc_serial;
|
2020-01-10 18:30:32 +03:00
|
|
|
device_class_set_props(dc, serial_properties);
|
2015-05-06 15:55:36 +03:00
|
|
|
}
|
|
|
|
|
2013-01-10 19:19:07 +04:00
|
|
|
static const TypeInfo serial_info = {
|
2011-12-08 07:34:16 +04:00
|
|
|
.name = "usb-serial",
|
2015-05-06 15:55:36 +03:00
|
|
|
.parent = TYPE_USB_SERIAL,
|
2011-12-08 07:34:16 +04:00
|
|
|
.class_init = usb_serial_class_initfn,
|
|
|
|
};
|
|
|
|
|
|
|
|
static Property braille_properties[] = {
|
|
|
|
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
|
|
|
DEFINE_PROP_END_OF_LIST(),
|
2009-10-26 17:56:47 +03:00
|
|
|
};
|
|
|
|
|
2011-12-16 00:53:10 +04:00
|
|
|
static void usb_braille_class_initfn(ObjectClass *klass, void *data)
|
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2011-12-16 00:53:10 +04:00
|
|
|
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
|
|
|
|
|
|
|
uc->product_desc = "QEMU USB Braille";
|
|
|
|
uc->usb_desc = &desc_braille;
|
2020-01-10 18:30:32 +03:00
|
|
|
device_class_set_props(dc, braille_properties);
|
2011-12-16 00:53:10 +04:00
|
|
|
}
|
|
|
|
|
2013-01-10 19:19:07 +04:00
|
|
|
static const TypeInfo braille_info = {
|
2011-12-08 07:34:16 +04:00
|
|
|
.name = "usb-braille",
|
2015-05-06 15:55:36 +03:00
|
|
|
.parent = TYPE_USB_SERIAL,
|
2011-12-08 07:34:16 +04:00
|
|
|
.class_init = usb_braille_class_initfn,
|
2009-08-31 16:23:59 +04:00
|
|
|
};
|
|
|
|
|
2012-02-09 18:20:55 +04:00
|
|
|
static void usb_serial_register_types(void)
|
2009-08-31 16:23:59 +04:00
|
|
|
{
|
2015-05-06 15:55:36 +03:00
|
|
|
type_register_static(&usb_serial_dev_type_info);
|
2011-12-08 07:34:16 +04:00
|
|
|
type_register_static(&serial_info);
|
|
|
|
type_register_static(&braille_info);
|
2011-12-09 00:56:53 +04:00
|
|
|
usb_legacy_register("usb-braille", "braille", usb_braille_init);
|
2009-08-31 16:23:59 +04:00
|
|
|
}
|
2012-02-09 18:20:55 +04:00
|
|
|
|
|
|
|
type_init(usb_serial_register_types)
|