aboutsummaryrefslogtreecommitdiff
path: root/hw/usb
diff options
context:
space:
mode:
Diffstat (limited to 'hw/usb')
-rw-r--r--hw/usb/Kconfig5
-rw-r--r--hw/usb/Makefile.objs2
-rw-r--r--hw/usb/dev-network.c2
-rw-r--r--hw/usb/dev-serial.c95
-rw-r--r--hw/usb/dev-smartcard-reader.c4
-rw-r--r--hw/usb/imx-usb-phy.c225
-rw-r--r--hw/usb/quirks.c4
-rw-r--r--hw/usb/quirks.h22
8 files changed, 309 insertions, 50 deletions
diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig
index 5e70ed5f7b..464348ba14 100644
--- a/hw/usb/Kconfig
+++ b/hw/usb/Kconfig
@@ -91,3 +91,8 @@ config USB_STORAGE_MTP
bool
default y
depends on USB
+
+config IMX_USBPHY
+ bool
+ default y
+ depends on USB
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 2b10868937..66835e5bf7 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -61,3 +61,5 @@ common-obj-$(CONFIG_XEN) += xen-usb.o
xen-usb.o-cflags := $(LIBUSB_CFLAGS)
xen-usb.o-libs := $(LIBUSB_LIBS)
endif
+
+common-obj-$(CONFIG_IMX_USBPHY) += imx-usb-phy.o
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 9a78ad928b..6210427544 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -626,7 +626,7 @@ static const uint32_t oid_supported_list[] =
struct rndis_response {
QTAILQ_ENTRY(rndis_response) entries;
uint32_t length;
- uint8_t buf[0];
+ uint8_t buf[];
};
typedef struct USBNetState {
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index daac75b7ae..d2c03681b7 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -29,7 +29,7 @@ do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0)
#define DPRINTF(fmt, ...) do {} while(0)
#endif
-#define RECV_BUF 384
+#define RECV_BUF (512 - (2 * 8))
/* Commands */
#define FTDI_RESET 0
@@ -332,7 +332,7 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p,
break;
case DeviceInVendor | FTDI_GET_MDM_ST:
data[0] = usb_get_modem_lines(s) | 1;
- data[1] = 0;
+ data[1] = FTDI_THRE | FTDI_TEMT;
p->actual_length = 2;
break;
case DeviceOutVendor | FTDI_SET_EVENT_CHR:
@@ -358,13 +358,67 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p,
}
}
+static void usb_serial_token_in(USBSerialState *s, USBPacket *p)
+{
+ const int max_packet_size = desc_iface0.eps[0].wMaxPacketSize;
+ int packet_len;
+ uint8_t header[2];
+
+ packet_len = p->iov.size;
+ if (packet_len <= 2) {
+ p->status = USB_RET_NAK;
+ return;
+ }
+
+ 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;
+ }
+
+ if (!s->recv_used) {
+ p->status = USB_RET_NAK;
+ return;
+ }
+
+ 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;
+ }
+
+ return;
+}
+
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;
+ int i;
switch (p->pid) {
case USB_TOKEN_OUT:
@@ -382,38 +436,7 @@ static void usb_serial_handle_data(USBDevice *dev, USBPacket *p)
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;
+ usb_serial_token_in(s, p);
break;
default:
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index 02693a26ad..ef72738ced 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -227,7 +227,7 @@ typedef struct QEMU_PACKED CCID_Parameter {
typedef struct QEMU_PACKED CCID_DataBlock {
CCID_BULK_IN b;
uint8_t bChainParameter;
- uint8_t abData[0];
+ uint8_t abData[];
} CCID_DataBlock;
/* 6.1.4 PC_to_RDR_XfrBlock */
@@ -235,7 +235,7 @@ typedef struct QEMU_PACKED CCID_XferBlock {
CCID_Header hdr;
uint8_t bBWI; /* Block Waiting Timeout */
uint16_t wLevelParameter; /* XXX currently unused */
- uint8_t abData[0];
+ uint8_t abData[];
} CCID_XferBlock;
typedef struct QEMU_PACKED CCID_IccPowerOn {
diff --git a/hw/usb/imx-usb-phy.c b/hw/usb/imx-usb-phy.c
new file mode 100644
index 0000000000..e705a03a1f
--- /dev/null
+++ b/hw/usb/imx-usb-phy.c
@@ -0,0 +1,225 @@
+/*
+ * i.MX USB PHY
+ *
+ * Copyright (c) 2020 Guenter Roeck <linux@roeck-us.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * We need to implement basic reset control in the PHY control register.
+ * For everything else, it is sufficient to set whatever is written.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/usb/imx-usb-phy.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+static const VMStateDescription vmstate_imx_usbphy = {
+ .name = TYPE_IMX_USBPHY,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(usbphy, IMXUSBPHYState, USBPHY_MAX),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static void imx_usbphy_softreset(IMXUSBPHYState *s)
+{
+ s->usbphy[USBPHY_PWD] = 0x001e1c00;
+ s->usbphy[USBPHY_TX] = 0x10060607;
+ s->usbphy[USBPHY_RX] = 0x00000000;
+ s->usbphy[USBPHY_CTRL] = 0xc0200000;
+}
+
+static void imx_usbphy_reset(DeviceState *dev)
+{
+ IMXUSBPHYState *s = IMX_USBPHY(dev);
+
+ s->usbphy[USBPHY_STATUS] = 0x00000000;
+ s->usbphy[USBPHY_DEBUG] = 0x7f180000;
+ s->usbphy[USBPHY_DEBUG0_STATUS] = 0x00000000;
+ s->usbphy[USBPHY_DEBUG1] = 0x00001000;
+ s->usbphy[USBPHY_VERSION] = 0x04020000;
+
+ imx_usbphy_softreset(s);
+}
+
+static uint64_t imx_usbphy_read(void *opaque, hwaddr offset, unsigned size)
+{
+ IMXUSBPHYState *s = (IMXUSBPHYState *)opaque;
+ uint32_t index = offset >> 2;
+ uint32_t value;
+
+ switch (index) {
+ case USBPHY_PWD_SET:
+ case USBPHY_TX_SET:
+ case USBPHY_RX_SET:
+ case USBPHY_CTRL_SET:
+ case USBPHY_DEBUG_SET:
+ case USBPHY_DEBUG1_SET:
+ /*
+ * All REG_NAME_SET register access are in fact targeting the
+ * REG_NAME register.
+ */
+ value = s->usbphy[index - 1];
+ break;
+ case USBPHY_PWD_CLR:
+ case USBPHY_TX_CLR:
+ case USBPHY_RX_CLR:
+ case USBPHY_CTRL_CLR:
+ case USBPHY_DEBUG_CLR:
+ case USBPHY_DEBUG1_CLR:
+ /*
+ * All REG_NAME_CLR register access are in fact targeting the
+ * REG_NAME register.
+ */
+ value = s->usbphy[index - 2];
+ break;
+ case USBPHY_PWD_TOG:
+ case USBPHY_TX_TOG:
+ case USBPHY_RX_TOG:
+ case USBPHY_CTRL_TOG:
+ case USBPHY_DEBUG_TOG:
+ case USBPHY_DEBUG1_TOG:
+ /*
+ * All REG_NAME_TOG register access are in fact targeting the
+ * REG_NAME register.
+ */
+ value = s->usbphy[index - 3];
+ break;
+ default:
+ value = s->usbphy[index];
+ break;
+ }
+ return (uint64_t)value;
+}
+
+static void imx_usbphy_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMXUSBPHYState *s = (IMXUSBPHYState *)opaque;
+ uint32_t index = offset >> 2;
+
+ switch (index) {
+ case USBPHY_CTRL:
+ s->usbphy[index] = value;
+ if (value & USBPHY_CTRL_SFTRST) {
+ imx_usbphy_softreset(s);
+ }
+ break;
+ case USBPHY_PWD:
+ case USBPHY_TX:
+ case USBPHY_RX:
+ case USBPHY_STATUS:
+ case USBPHY_DEBUG:
+ case USBPHY_DEBUG1:
+ s->usbphy[index] = value;
+ break;
+ case USBPHY_CTRL_SET:
+ s->usbphy[index - 1] |= value;
+ if (value & USBPHY_CTRL_SFTRST) {
+ imx_usbphy_softreset(s);
+ }
+ break;
+ case USBPHY_PWD_SET:
+ case USBPHY_TX_SET:
+ case USBPHY_RX_SET:
+ case USBPHY_DEBUG_SET:
+ case USBPHY_DEBUG1_SET:
+ /*
+ * All REG_NAME_SET register access are in fact targeting the
+ * REG_NAME register. So we change the value of the REG_NAME
+ * register, setting bits passed in the value.
+ */
+ s->usbphy[index - 1] |= value;
+ break;
+ case USBPHY_PWD_CLR:
+ case USBPHY_TX_CLR:
+ case USBPHY_RX_CLR:
+ case USBPHY_CTRL_CLR:
+ case USBPHY_DEBUG_CLR:
+ case USBPHY_DEBUG1_CLR:
+ /*
+ * All REG_NAME_CLR register access are in fact targeting the
+ * REG_NAME register. So we change the value of the REG_NAME
+ * register, unsetting bits passed in the value.
+ */
+ s->usbphy[index - 2] &= ~value;
+ break;
+ case USBPHY_CTRL_TOG:
+ s->usbphy[index - 3] ^= value;
+ if ((value & USBPHY_CTRL_SFTRST) &&
+ (s->usbphy[index - 3] & USBPHY_CTRL_SFTRST)) {
+ imx_usbphy_softreset(s);
+ }
+ break;
+ case USBPHY_PWD_TOG:
+ case USBPHY_TX_TOG:
+ case USBPHY_RX_TOG:
+ case USBPHY_DEBUG_TOG:
+ case USBPHY_DEBUG1_TOG:
+ /*
+ * All REG_NAME_TOG register access are in fact targeting the
+ * REG_NAME register. So we change the value of the REG_NAME
+ * register, toggling bits passed in the value.
+ */
+ s->usbphy[index - 3] ^= value;
+ break;
+ default:
+ /* Other registers are read-only */
+ break;
+ }
+}
+
+static const struct MemoryRegionOps imx_usbphy_ops = {
+ .read = imx_usbphy_read,
+ .write = imx_usbphy_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static void imx_usbphy_realize(DeviceState *dev, Error **errp)
+{
+ IMXUSBPHYState *s = IMX_USBPHY(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &imx_usbphy_ops, s,
+ "imx-usbphy", 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+}
+
+static void imx_usbphy_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = imx_usbphy_reset;
+ dc->vmsd = &vmstate_imx_usbphy;
+ dc->desc = "i.MX USB PHY Module";
+ dc->realize = imx_usbphy_realize;
+}
+
+static const TypeInfo imx_usbphy_info = {
+ .name = TYPE_IMX_USBPHY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXUSBPHYState),
+ .class_init = imx_usbphy_class_init,
+};
+
+static void imx_usbphy_register_types(void)
+{
+ type_register_static(&imx_usbphy_info);
+}
+
+type_init(imx_usbphy_register_types)
diff --git a/hw/usb/quirks.c b/hw/usb/quirks.c
index 38a9c5634a..23ea7a23ea 100644
--- a/hw/usb/quirks.c
+++ b/hw/usb/quirks.c
@@ -22,10 +22,10 @@ static bool usb_id_match(const struct usb_device_id *ids,
uint8_t interface_protocol) {
int i;
- for (i = 0; ids[i].vendor_id != -1; i++) {
+ for (i = 0; ids[i].terminating_entry == 0; i++) {
if (ids[i].vendor_id == vendor_id &&
ids[i].product_id == product_id &&
- (ids[i].interface_class == -1 ||
+ (ids[i].interface_protocol_used == 0 ||
(ids[i].interface_class == interface_class &&
ids[i].interface_subclass == interface_subclass &&
ids[i].interface_protocol == interface_protocol))) {
diff --git a/hw/usb/quirks.h b/hw/usb/quirks.h
index 89480befd7..50ef2f9c2e 100644
--- a/hw/usb/quirks.h
+++ b/hw/usb/quirks.h
@@ -21,19 +21,23 @@
#include "quirks-pl2303-ids.h"
struct usb_device_id {
- int vendor_id;
- int product_id;
- int interface_class;
- int interface_subclass;
- int interface_protocol;
+ uint16_t vendor_id;
+ uint16_t product_id;
+ uint8_t interface_class;
+ uint8_t interface_subclass;
+ uint8_t interface_protocol;
+ uint8_t interface_protocol_used:1,
+ terminating_entry:1,
+ reserved:6;
};
#define USB_DEVICE(vendor, product) \
- .vendor_id = vendor, .product_id = product, .interface_class = -1,
+ .vendor_id = vendor, .product_id = product, .interface_protocol_used = 0,
#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, iclass, isubclass, iproto) \
.vendor_id = vend, .product_id = prod, .interface_class = iclass, \
- .interface_subclass = isubclass, .interface_protocol = iproto
+ .interface_subclass = isubclass, .interface_protocol = iproto, \
+ .interface_protocol_used = 1
static const struct usb_device_id usbredir_raw_serial_ids[] = {
/*
@@ -206,7 +210,7 @@ static const struct usb_device_id usbredir_raw_serial_ids[] = {
{ USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) },
{ USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) },
- { USB_DEVICE(-1, -1) } /* Terminating Entry */
+ { .terminating_entry = 1 } /* Terminating Entry */
};
static const struct usb_device_id usbredir_ftdi_serial_ids[] = {
@@ -906,7 +910,7 @@ static const struct usb_device_id usbredir_ftdi_serial_ids[] = {
{ USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) },
- { USB_DEVICE(-1, -1) } /* Terminating Entry */
+ { .terminating_entry = 1 } /* Terminating Entry */
};
#undef USB_DEVICE