From 04ac48dea1a0db5e26edd9716bc92c1be2606992 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 12 Oct 2012 19:57:09 +0200 Subject: usb-mbim: WIP: working MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork --- hw/usb/Makefile.objs | 2 +- hw/usb/dev-mbim.c | 551 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 552 insertions(+), 1 deletion(-) create mode 100644 hw/usb/dev-mbim.c 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 + * + * 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) -- cgit v1.2.3