aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2012-10-12 19:57:09 +0200
committerBjørn Mork <bjorn@mork.no>2012-10-12 19:57:09 +0200
commit04ac48dea1a0db5e26edd9716bc92c1be2606992 (patch)
tree28c56a46258602779497e866c86ad0feea3584fb
parent4d9367b76f71c6d938cf8201392abe4bfb1136cb (diff)
usb-mbim: WIP: working
Signed-off-by: Bjørn Mork <bjorn@mork.no>
-rw-r--r--hw/usb/Makefile.objs2
-rw-r--r--hw/usb/dev-mbim.c551
2 files changed, 552 insertions, 1 deletions
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 6425c1ff7..78659ecb6 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -11,4 +11,4 @@ common-obj-y += core.o bus.o desc.o dev-hub.o
common-obj-y += host-$(HOST_USB).o dev-bluetooth.o
common-obj-y += dev-hid.o dev-storage.o dev-wacom.o
common-obj-y += dev-serial.o dev-network.o dev-audio.o
-common-obj-y += dev-uas.o
+common-obj-y += dev-uas.o dev-mbim.o
diff --git a/hw/usb/dev-mbim.c b/hw/usb/dev-mbim.c
new file mode 100644
index 000000000..13ede661a
--- /dev/null
+++ b/hw/usb/dev-mbim.c
@@ -0,0 +1,551 @@
+/*
+ * CDC MBIM Device emulation
+ *
+ * Copyright (c) Bjørn Mork <bjorn@mork.no>
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+
+#define DEBUG_MBIM
+
+#ifdef DEBUG_MBIM
+#define DPRINTF(fmt, ...) \
+ do { printf("usb-mbim: %s:" fmt , __FUNCTION__, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define RECV_BUF 384
+
+#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+
+enum {
+ STR_MANUFACTURER = 1,
+ STR_PRODUCT_MBIM,
+ STR_SERIALNUMBER,
+ STR_MACADDRESS,
+ STR_COMMANDSET,
+};
+
+static const USBDescStrings desc_strings = {
+ [STR_MANUFACTURER] = "QEMU",
+ [STR_PRODUCT_MBIM] = "QEMU USB CDC MBIM",
+ [STR_SERIALNUMBER] = "1",
+ [STR_MACADDRESS] = "5254DEADBEEF",
+ [STR_COMMANDSET] = "S_NCM_CMD_FUNC",
+};
+
+/* CDC functional descriptors: */
+static const uint8_t cdc_header[] = {
+ 0x05, /* bLength; */
+ 0x21, /* bDescriptorType; Functional */
+ 0x00, /* bDescriptorSubType */
+ 0x10, 0x01, /* bcdCDC 1.10 */
+};
+static const uint8_t cdc_mbim[] = {
+ 0x0c, /* bLength; */
+ 0x21, /* bDescriptorType; Functional */
+ 0x1b, /* bDescriptorSubType */
+ 0x00, 0x01, /* bcdMBIMVersion 1.00 */
+ 0x00, 0x06, /* wMaxControlMessage 1536 */
+ 0x10, /* bNumberFilters 16 */
+ 0x28, /* bMaxFilterSize 40 */
+ 0x00, 0x10, /* wMaxSegmentSize 4096 */
+ 0x20, /* bmNetworkCapabilities 0x20 */
+};
+static const uint8_t cdc_union[] = {
+ 0x05, /* bLength; */
+ 0x21, /* bDescriptorType; Functional */
+ 0x06, /* bDescriptorSubType */
+ 0x00, /* bMasterInterface 0 */
+ 0x01, /* bSlaveInterface 1 */
+};
+static const uint8_t cdc_ethernet[] = {
+ 0x10, /* bLength; */
+ 0x21, /* bDescriptorType; Functional */
+ 0x0f, /* bDescriptorSubType */
+ STR_MACADDRESS, /* iMacAddress */
+ 0x00, /* bmEthernetStatistics 0x00000000 */
+ 0xea, 0x05, /* wMaxSegmentSize 1514 */
+ 0x40, 0x80, /* wNumberMCFilters 0x8040 */
+ 0x01, /* bNumberPowerFilters 1 */
+};
+static const uint8_t cdc_ncm[] = {
+ 0x06, /* bLength; */
+ 0x21, /* bDescriptorType; Functional */
+ 0x1a, /* bDescriptorSubType */
+ 0x00, 0x01, /* bcdNcmVersion 1.00 */
+ 0x1b, /* bmNetworkCapabilities 0x1b */
+};
+static const uint8_t cdc_commandset[] = {
+ 0x16, /* bLength; */
+ 0x21, /* bDescriptorType; Functional */
+ 0x16, /* bDescriptorSubType */
+ 0x00, 0x01, /* bcdVersion 1.00 */
+ STR_COMMANDSET, /* iCommandSet 13 S_NCM_CMD_FUNC */
+ // bGUID {2e23bbae-c188-11df-bcdc-d9c6dfd72085}
+// 0x1b, /* bmNetworkCapabilities 0x1b */
+};
+/*
+ CDC Command Set:
+ bcdVersion 1.00
+ iCommandSet 13 S_NCM_CMD_FUNC
+ bGUID {2e23bbae-c188-11df-bcdc-d9c6dfd72085}
+*/
+
+/*
+MBIM with NCM 1.0 compatibility setting
+=======================================
+
+ I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=cdc_ncm
+ E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=2ms
+ I:* If#= 0 Alt= 1 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_ncm
+ E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=2ms
+ I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm
+ I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm
+ E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+ E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+ I:* If#= 1 Alt= 2 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_ncm
+ E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+ E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms
+
+*/
+
+static const USBDescIface desc_iface_mbim[] = {
+{
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = 0x02,
+ .bInterfaceSubClass = 0x0d, /* NCM */
+ .bInterfaceProtocol = 0x00,
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ },
+ }
+},{
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = 0x02,
+ .bInterfaceSubClass = 0x0e, /* MBIM */
+ .bInterfaceProtocol = 0x00,
+ .ndesc = 3,
+ .descs = (USBDescOther[]) {
+ {
+ .data = cdc_header,
+ },
+ {
+ .data = cdc_mbim,
+ },
+ {
+ .data = cdc_union,
+ },
+ },
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ },
+ }
+},{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = 0x0a, /* CDC Data */
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x01, /* NCM */
+ .eps = (USBDescEndpoint[]) {
+ }
+},{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0x0a, /* CDC Data */
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x01, /* NCM */
+ .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,
+ },
+ }
+},{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 2,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0x0a, /* CDC Data */
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x02, /* MBIM */
+ .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 USBDescIface desc_iface_ncm[] = {
+{
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = 0x02,
+ .bInterfaceSubClass = 0x0d, /* NCM */
+ .bInterfaceProtocol = 0x00,
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ },
+ }
+},{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = 0x0a, /* CDC Data */
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x01, /* NCM */
+ .eps = (USBDescEndpoint[]) {
+ }
+},{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0x0a, /* CDC Data */
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x01, /* NCM */
+ .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 USBDescIface desc_iface_mbim_nocompat[] = {
+{
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = 0x02,
+ .bInterfaceSubClass = 0x0e, /* MBIM */
+ .bInterfaceProtocol = 0x00,
+ .descs = (USBDescOther[]) {
+ {
+ .data = cdc_header,
+ },
+ {
+ .data = cdc_mbim,
+ },
+ {
+ .data = cdc_union,
+ },
+ },
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ },
+ }
+},{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = 0x0a, /* CDC Data */
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x02, /* MBIM */
+ .eps = (USBDescEndpoint[]) {
+ }
+},{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0x0a, /* CDC Data */
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x02, /* MBIM */
+ .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_mbim = {
+ .bcdUSB = 0x0200,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 2,
+ .bConfigurationValue = 1,
+ .bmAttributes = 0x80,
+ .bMaxPower = 50,
+ .nif = ARRAY_SIZE(desc_iface_mbim),
+ .ifs = desc_iface_mbim,
+ },
+ },
+};
+static const USBDescDevice desc_device_ncm = {
+ .bcdUSB = 0x0200,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 2,
+ .bConfigurationValue = 1,
+ .bmAttributes = 0x80,
+ .bMaxPower = 50,
+ .nif = ARRAY_SIZE(desc_iface_ncm),
+ .ifs = desc_iface_ncm,
+ },
+ },
+};
+static const USBDescDevice desc_device_nocompat = {
+ .bcdUSB = 0x0200,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 2,
+ .bConfigurationValue = 1,
+ .bmAttributes = 0x80,
+ .bMaxPower = 50,
+ .nif = ARRAY_SIZE(desc_iface_mbim_nocompat),
+ .ifs = desc_iface_mbim_nocompat,
+ },
+ },
+};
+
+static const USBDesc desc_mbim = {
+ .id = {
+ .idVendor = 0xff03,
+ .idProduct = 0xf001,
+ .bcdDevice = 0x0100,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = 0,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_mbim,
+ .str = desc_strings,
+};
+
+static const USBDesc desc_ncm = {
+ .id = {
+ .idVendor = 0xff03,
+ .idProduct = 0xf002,
+ .bcdDevice = 0x0100,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = 0,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_ncm,
+ .str = desc_strings,
+};
+static const USBDesc desc_mbim_nocompat = {
+ .id = {
+ .idVendor = 0xff03,
+ .idProduct = 0xf003,
+ .bcdDevice = 0x0100,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = 0,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_nocompat,
+ .str = desc_strings,
+};
+
+static void usb_mbim_handle_reset(USBDevice *dev)
+{
+
+ DPRINTF("Reset\n");
+}
+
+static int usb_mbim_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
+{
+ 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 ret;
+ }
+
+ ret = 0;
+ switch (request) {
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ ret = 0;
+ break;
+ default:
+ DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_mbim_handle_data(USBDevice *dev, USBPacket *p)
+{
+ int ret = 0;
+ uint8_t devep = p->ep->nr;
+
+ DPRINTF("ep=0x%02x\n",devep);
+
+ switch (p->pid) {
+ case USB_TOKEN_OUT:
+ break;
+
+ case USB_TOKEN_IN:
+ break;
+
+ default:
+ DPRINTF("Bad token\n");
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usb_mbim_handle_destroy(USBDevice *dev)
+{
+ DPRINTF("\n");
+}
+
+static int usb_mbim_initfn(USBDevice *dev)
+{
+ DPRINTF("\n");
+ usb_desc_create_serial(dev);
+ usb_desc_init(dev);
+ usb_mbim_handle_reset(dev);
+ return 0;
+}
+
+static const VMStateDescription vmstate_usb_mbim = {
+ .name = "usb-mbim",
+ .unmigratable = 1,
+};
+
+static const VMStateDescription vmstate_usb_ncm = {
+ .name = "usb-ncm",
+ .unmigratable = 1,
+};
+static const VMStateDescription vmstate_usb_mbim_nocompat = {
+ .name = "usb-mbim-nocompat",
+ .unmigratable = 1,
+};
+
+static void usb_mbim_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+ uc->init = usb_mbim_initfn;
+ uc->product_desc = "QEMU USB CDC MBIM";
+ uc->usb_desc = &desc_mbim;
+ uc->handle_reset = usb_mbim_handle_reset;
+ uc->handle_control = usb_mbim_handle_control;
+ uc->handle_data = usb_mbim_handle_data;
+ uc->handle_destroy = usb_mbim_handle_destroy;
+ dc->vmsd = &vmstate_usb_mbim;
+}
+
+static void usb_ncm_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+ uc->init = usb_mbim_initfn;
+ uc->product_desc = "QEMU USB CDC NCM";
+ uc->usb_desc = &desc_ncm;
+ uc->handle_reset = usb_mbim_handle_reset;
+ uc->handle_control = usb_mbim_handle_control;
+ uc->handle_data = usb_mbim_handle_data;
+ uc->handle_destroy = usb_mbim_handle_destroy;
+ dc->vmsd = &vmstate_usb_mbim;
+}
+
+static void usb_mbim_nocompat_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+ uc->init = usb_mbim_initfn;
+ uc->product_desc = "QEMU USB CDC MBIM";
+ uc->usb_desc = &desc_mbim_nocompat;
+ uc->handle_reset = usb_mbim_handle_reset;
+ uc->handle_control = usb_mbim_handle_control;
+ uc->handle_data = usb_mbim_handle_data;
+ uc->handle_destroy = usb_mbim_handle_destroy;
+ dc->vmsd = &vmstate_usb_mbim;
+}
+
+static TypeInfo mbim_info = {
+ .name = "usb-mbim",
+ .parent = TYPE_USB_DEVICE,
+ .instance_size = sizeof(USBDevice),
+ .class_init = usb_mbim_class_initfn,
+};
+
+static TypeInfo ncm_info = {
+ .name = "usb-ncm",
+ .parent = TYPE_USB_DEVICE,
+ .instance_size = sizeof(USBDevice),
+ .class_init = usb_ncm_class_initfn,
+};
+
+static TypeInfo mbim_nocompat_info = {
+ .name = "usb-mbim-nocompat",
+ .parent = TYPE_USB_DEVICE,
+ .instance_size = sizeof(USBDevice),
+ .class_init = usb_mbim_nocompat_class_initfn,
+};
+
+static void usb_mbim_register_types(void)
+{
+ type_register_static(&mbim_info);
+ usb_legacy_register("usb-mbim", "mbim", NULL);
+ type_register_static(&ncm_info);
+ usb_legacy_register("usb-ncm", "ncm", NULL);
+ type_register_static(&mbim_nocompat_info);
+ usb_legacy_register("usb-mbim-nocompat", "mbim-nocompat", NULL);
+}
+
+type_init(usb_mbim_register_types)