Jelajahi Sumber

WinUSB initial

WinUSB (WCID) driverless firmware.  1.0 OS descriptor specs w/device interface guid
Travis Robinson 8 tahun lalu
induk
melakukan
4b60c9a3d0
2 mengubah file dengan 508 tambahan dan 0 penghapusan
  1. 372 0
      demo/winusb_loop.c
  2. 136 0
      inc/ms_os.h

+ 372 - 0
demo/winusb_loop.c

@@ -0,0 +1,372 @@
+/* This file is the part of the Lightweight USB device Stack for STM32 microcontrollers
+ *
+ * Copyright ©2018 Travis Robinson <libusbdotnet[at]gmail[dot]com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32f1xx_hal.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include "usb.h"
+#include "ms_os.h"
+
+#ifndef _STM32_WUSB_CONFIG_
+
+// YOUR VENDOR ID
+// http://pid.codes/1209/
+#define WUSB_VEND_ID 0x1209
+
+// YOUR PRODUCT ID
+#define WUSB_PROD_ID 0x0777
+
+// YOUR MANUFACTURER NAME
+#define WUSB_MANUFACTURER_STR		"github.TravisRo"
+
+// YOUR PRODUCT NAME
+#define WUSB_PRODUCT_STR			"WinUSB Communication Device"
+
+// YOUR DEVICE-INTERFACE-GUID
+// NOTE: A '\0' terminator is required!  (This is a multi-sz field)
+#define WUSB_DEVICE_INTERFACE_GUID	"{B7705C49-EAC6-4C73-AE94-ACAEF841D599}\0"
+
+#else
+#endif
+
+/////////////////////////////////// DEMO ENDPOINT SETUP ////////////////////////////////////
+#define WUSB_EP0_MAXPACKET_SIZE	(8)
+#define WUSB_EP1_MAXPACKET_SIZE	(64)
+#define WUSB_EP1_RX				(0x01)
+#define WUSB_EP1_TX				(0x82)
+
+//////////////////////////////////////// VARIABLES ////////////////////////////////////////
+// USB device
+usbd_device udev;
+
+// USB device buffer (buffer for ep0 rx requests [usbd_ctlreq])
+// 32 bytes gives us an extra 24 bytes for an incoming data payload.
+// This can bet set as low as 2! (8 bytes to hold the setup
+uint32_t wusb_control_buffer[8]; 
+
+
+// STM32CUBE RTC handle
+RTC_HandleTypeDef hrtc;
+
+// DEMO endpoint buffer for loopback
+uint8_t fifo[WUSB_EP1_MAXPACKET_SIZE * 4];
+uint32_t fpos = 0;
+
+////////////////////////////////////// DEMO PROTOTYPES //////////////////////////////////////
+static void cdc_loopback(usbd_device* dev, uint8_t event, uint8_t ep);
+
+//////////////////////////////////////// DESCRIPTORS ////////////////////////////////////////
+struct winusb_config {
+    struct usb_config_descriptor cfg;
+    struct usb_interface_descriptor intf1;
+    struct usb_endpoint_descriptor eprx1;
+    struct usb_endpoint_descriptor eptx1;
+} __attribute__((packed));
+
+static const struct usb_device_descriptor device_desc = {
+    .bLength = sizeof(struct usb_device_descriptor),
+    .bDescriptorType = USB_DTYPE_DEVICE,
+    .bcdUSB = VERSION_BCD(2, 0, 0),
+    .bDeviceClass = USB_CLASS_PER_INTERFACE,
+    .bDeviceSubClass = USB_SUBCLASS_NONE,
+    .bDeviceProtocol = USB_PROTO_NONE,
+    .bMaxPacketSize0 = 8,
+    .idVendor = WUSB_VEND_ID,
+    .idProduct = WUSB_PROD_ID,
+    .bcdDevice = VERSION_BCD(1, 0, 0),
+    .iManufacturer = 1,
+    .iProduct = 2,
+    .iSerialNumber = INTSERIALNO_DESCRIPTOR,
+    .bNumConfigurations = 1,
+};
+
+static const struct winusb_config config_desc = {
+    .cfg = {
+        .bLength = sizeof(struct usb_config_descriptor),
+        .bDescriptorType = USB_DTYPE_CONFIGURATION,
+        .wTotalLength = sizeof(struct winusb_config),
+        .bNumInterfaces = 1,
+        .bConfigurationValue = 1,
+        .iConfiguration = NO_DESCRIPTOR,
+        .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED,
+        .bMaxPower = USB_CFG_POWER_MA(100),
+    },
+
+    .intf1 = {
+        .bLength = sizeof(struct usb_interface_descriptor),
+        .bDescriptorType = USB_DTYPE_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bAlternateSetting = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = USB_CLASS_APP_SPEC,
+        .bInterfaceSubClass = 0x01,
+        .bInterfaceProtocol = 0x07,
+        .iInterface = NO_DESCRIPTOR,
+    },
+    .eprx1 = {
+        .bLength = sizeof(struct usb_endpoint_descriptor),
+        .bDescriptorType = USB_DTYPE_ENDPOINT,
+        .bEndpointAddress = WUSB_EP1_RX,
+        .bmAttributes = USB_EPTYPE_BULK,
+        .wMaxPacketSize = WUSB_EP1_MAXPACKET_SIZE,
+        .bInterval = 0x00,
+    },
+    .eptx1 = {
+        .bLength = sizeof(struct usb_endpoint_descriptor),
+        .bDescriptorType = USB_DTYPE_ENDPOINT,
+        .bEndpointAddress = WUSB_EP1_TX,
+        .bmAttributes = USB_EPTYPE_BULK,
+        .wMaxPacketSize = WUSB_EP1_MAXPACKET_SIZE,
+        .bInterval = 0x00,
+    },
+};
+
+#define MS_VENDOR_CODE 0x20
+static const struct usb_string_descriptor ms_os_desc = USB_STRING_DESC("MSFT100\x20");
+
+static const struct usb_string_descriptor lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
+static const struct usb_string_descriptor manuf_desc_en = USB_STRING_DESC(WUSB_MANUFACTURER_STR);
+static const struct usb_string_descriptor prod_desc_en = USB_STRING_DESC(WUSB_PRODUCT_STR);
+
+static const struct usb_string_descriptor*const dtable[] = {
+    &lang_desc,
+    &manuf_desc_en,
+    &prod_desc_en,
+};
+
+static const struct wusb_devguid_extprop_feature_descriptor devguid_ext_prop_feature_desc = {
+    .dwLength = sizeof(struct wusb_devguid_extprop_feature_descriptor),
+    .bcdVersion = VERSION_BCD(1,0,0),
+    .wIndex = 5,
+    .wCount = 1,
+    .dev_guid_property = {
+        .dwPropertyLength = sizeof(struct wusb_devguid_property),
+        .dwPropertyType = 7, // (7 = Unicode REG_MULTI_SZ)
+        .wPropertyNameLength = 42,
+        .wPropertyName = STRING_TO_WCHARS("DeviceInterfaceGUIDs"),
+        .dwPropertyDataLength = 80,
+        .wPropertyData = STRING_TO_WCHARS(WUSB_DEVICE_INTERFACE_GUID)
+    }
+};
+
+static const struct ms_os_compatid_descriptor compatid_desc = {
+    .dwLength = sizeof(struct ms_os_compatid_descriptor),
+    .bcdVersion = VERSION_BCD(1,0,0),
+    .wIndex = 4,
+    .bCount = 1,
+    ._reservedA = {0, 0, 0, 0, 0, 0, 0},
+    .bInterfaceNumber = 0,
+    ._reservedB = {0},
+    .chCompatibleID = {'W', 'I', 'N', 'U', 'S', 'B', 0, 0},
+    .chSubCompatibleID = {0, 0, 0, 0, 0, 0, 0, 0},
+    ._reservedC = {0, 0, 0, 0, 0, 0}
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////// USB Core callbacks //////////////////////////////////////////////
+static usbd_respond wusb_getdesc(usbd_ctlreq* req, void** address, uint16_t* length) {
+    const uint8_t dtype = req->wValue >> 8;
+    const uint8_t dnumber = req->wValue & 0xFF;
+    const void* desc;
+    uint16_t len = 0;
+    switch (dtype) {
+    case USB_DTYPE_DEVICE:
+        desc = &device_desc;
+        break;
+    case USB_DTYPE_CONFIGURATION:
+        desc = &config_desc;
+        len = sizeof(config_desc);
+        break;
+    case USB_DTYPE_STRING:
+        if (dnumber < 3) {
+            desc = dtable[dnumber];
+        }
+        else if (dnumber == 0xEE) {
+            // Microsoft OS String Descriptor
+            desc = &ms_os_desc;
+
+            /* MSDN NOTES
+            After the operating system requests a Microsoft OS String Descriptor from a device, it creates the following registry key:
+            HLKM\SYSTEM\CurrentControlSet\Control\UsbFlags\*vvvvpppprrrrr*
+
+            The operating system creates a registry entry, named osvc, under this registry key that indicates whether the device supports
+            Microsoft OS Descriptors. If the device does not provide a valid response the first time that the operating system queries it
+            for a Microsoft OS String Descriptor, the operating system will make no further requests for that descriptor.
+
+            [TR] Once it gets a successful enuration, it will make no further requests for it PERIOD.
+            [TR] You can change bcdDevice in the device descriptor to get a re-request or delete the UsbFlags key for your dev.
+            */
+        }
+        else {
+            return usbd_fail;
+        }
+        break;
+    default:
+        return usbd_fail;
+    }
+    if (len == 0) {
+        len = ((struct usb_header_descriptor*)desc)->bLength;
+    }
+    *address = (void*)desc;
+    *length = len;
+    return usbd_ack;
+}
+
+static usbd_respond wusb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
+
+    if ((req->bmRequestType == (USB_REQ_DEVTOHOST | USB_REQ_VENDOR | USB_REQ_DEVICE)) ||
+        (req->bmRequestType == (USB_REQ_DEVTOHOST | USB_REQ_VENDOR | USB_REQ_INTERFACE))) {
+        if (req->bRequest == MS_VENDOR_CODE) {
+            switch (req->wIndex) {
+
+            case 0x04: // Compatible ID Feature Descriptor
+                // NOTE: data_ptr and data_count are the same as the address and length params
+                // in the get descriptor request.
+                dev->status.data_ptr = (void*)&compatid_desc;
+                dev->status.data_count = sizeof(compatid_desc);
+                return usbd_ack;
+
+            case 0x05: // Microsoft Extended Properties Feature Descriptor
+                dev->status.data_ptr = (void*)&devguid_ext_prop_feature_desc;
+                dev->status.data_count = sizeof(devguid_ext_prop_feature_desc);
+                return usbd_ack;
+
+            default: ;
+            }
+        }
+    }
+
+    return usbd_fail;
+}
+
+
+
+static usbd_respond wusb_setconf(usbd_device* dev, uint8_t cfg) {
+    switch (cfg) {
+    case 0:
+        /* deconfiguring device */
+        usbd_ep_deconfig(dev, WUSB_EP1_TX);
+        usbd_ep_deconfig(dev, WUSB_EP1_RX);
+        usbd_reg_endpoint(dev, WUSB_EP1_RX, 0);
+        usbd_reg_endpoint(dev, WUSB_EP1_TX, 0);
+        return usbd_ack;
+    case 1:
+        /* configuring device */
+        usbd_ep_config(dev, WUSB_EP1_RX, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, WUSB_EP1_MAXPACKET_SIZE);
+        usbd_ep_config(dev, WUSB_EP1_TX, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, WUSB_EP1_MAXPACKET_SIZE);
+
+        usbd_reg_endpoint(dev, WUSB_EP1_RX, cdc_loopback);
+        usbd_reg_endpoint(dev, WUSB_EP1_TX, cdc_loopback);
+
+        usbd_ep_write(dev, WUSB_EP1_TX, 0, 0);
+        return usbd_ack;
+    default:
+        return usbd_fail;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////// DEMO MAIN //////////////////////////////////////////////////
+static void cdc_loopback(usbd_device* dev, uint8_t event, uint8_t ep) {
+	int _t;
+	switch (event) {
+	case usbd_evt_eptx:
+		_t = usbd_ep_write(dev,
+		                   WUSB_EP1_TX,
+		                   &fifo[0],
+		                   (fpos < WUSB_EP1_MAXPACKET_SIZE) ? fpos : WUSB_EP1_MAXPACKET_SIZE);
+		if (_t > 0) {
+			memmove(&fifo[0], &fifo[_t], fpos - _t);
+			fpos -= _t;
+		}
+	case usbd_evt_eprx:
+		if (fpos < (sizeof(fifo) - WUSB_EP1_MAXPACKET_SIZE)) {
+			_t = usbd_ep_read(dev, WUSB_EP1_RX, &fifo[fpos], WUSB_EP1_MAXPACKET_SIZE);
+			if (_t > 0) {
+				fpos += _t;
+			}
+		}
+	default:
+		break;
+	}
+}
+
+static void wusb_init_usbd(void) {
+
+    usbd_init(&udev, &usbd_hw, WUSB_EP0_MAXPACKET_SIZE, wusb_control_buffer, sizeof(wusb_control_buffer));
+    usbd_reg_config(&udev, wusb_setconf);
+    usbd_reg_control(&udev, wusb_control);
+    usbd_reg_descr(&udev, wusb_getdesc);
+}
+
+#if defined(WUSB_USE_IRQ)
+#if defined(STM32L052xx)
+#define USB_HANDLER     USB_IRQHandler
+#define USB_NVIC_IRQ    USB_IRQn
+#elif defined(STM32L100xC)
+#define USB_HANDLER     USB_LP_IRQHandler
+#define USB_NVIC_IRQ    USB_LP_IRQn
+#elif defined(STM32L476xx)
+#define USB_HANDLER     OTG_FS_IRQHandler
+#define USB_NVIC_IRQ    OTG_FS_IRQn
+#elif defined(STM32F103x6)
+#define USB_HANDLER     USB_LP_CAN1_RX0_IRQHandler
+#define USB_NVIC_IRQ    USB_LP_CAN1_RX0_IRQn
+#elif defined(STM32F103xE)
+#define USB_HANDLER     USB_LP_CAN1_RX0_IRQHandler
+#define USB_NVIC_IRQ    USB_LP_CAN1_RX0_IRQn
+#elif defined(STM32F429xx)
+#define USB_HANDLER     OTG_FS_IRQHandler
+#define USB_NVIC_IRQ    OTG_FS_IRQn
+#else
+#error Not supported
+#endif
+#endif
+
+void USB_HANDLER(void) {
+    usbd_poll(&udev);
+}
+
+#ifdef CUBE_CALLS_MAIN
+	void wusb_main(void) {
+#else
+	void main(void) {
+#endif

+    wusb_init_usbd();
+
+#ifdef USB_NVIC_IRQ
+    NVIC_EnableIRQ(USB_NVIC_IRQ);
+#endif
+    usbd_enable(&udev, true);
+    usbd_connect(&udev, true);
+    uint32_t curTick = HAL_GetTick();
+    while (true) {
+
+#ifndef USB_NVIC_IRQ
+		usbd_poll(&udev);
+#endif
+
+        const uint32_t nextTick = HAL_GetTick();
+        if (curTick + 1000 < nextTick) {
+            curTick = nextTick;
+            HAL_GPIO_TogglePin(LED_1_GPIO_Port, LED_1_Pin);
+        }
+#ifdef USB_NVIC_IRQ
+        __WFI();
+#endif
+    }
+}

+ 136 - 0
inc/ms_os.h

@@ -0,0 +1,136 @@
+#ifndef _MS_OS_H_
+#define _MS_OS_H_
+/** 
+ * Extended Properties OS Feature Descriptor Specification 
+ * Extended Compat ID OS Feature Descriptor Specification
+ *  v1.0
+ */
+
+/* This file is the part of the Lightweight USB device Stack for STM32 microcontrollers
+ *
+ * Copyright ©2018 Travis Robinson <libusbdotnet[at]gmail[dot]com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** Macro to create a unicode char array */
+#define STRING_TO_WCHARS(s)	{CAT(u,s)}
+
+/** Macro to return the total byte length of a string as if it were a unicode char array */
+#define SIZE_OF_WSTRING(s)	sizeof(CAT(u,s))
+
+/** Total Length of a custom property name (in bytes).
+ *
+ * This can be hard-coded at 42 for a 'DeviceInterFaceGUIDs' property name.
+ */
+#define MSOS_DEVGUID_NAME_BLEN  (SIZE_OF_WSTRING("DeviceInterfaceGUIDs"))
+
+/** Total Length of a custom property value (in bytes).
+ *
+ * This can be hard-coded at 80 for a 'DeviceInterFaceGUIDs' property value.
+ */
+#define MSOS_DEVGUID_VALUE_BLEN (SIZE_OF_WSTRING("{00000000-0000-0000-0000-000000000000}\0"))
+
+/**
+ * \brief windows (winusb) Extended Compat ID OS Feature Descriptor
+ * 
+ * Stores one or more compatible IDs and subcompatible IDs in device firmware on a per-interface or per-function
+ * basis. Windows can then use these values much like class and subclass values to select an appropriate driver.
+ * (WinUSB for example)
+ */
+struct ms_os_compatid_descriptor {
+
+	/**<\brief The length, in bytes, of the complete descriptor (DWORD LE). (40 bytes) */
+	uint32_t dwLength;
+
+	/**<\brief The descriptor’s version number, in binary coded decimal (BCD) format. (v1.00) */
+	uint16_t bcdVersion;
+
+	/**<\brief The index for extended properties OS descriptors. (always 0x04 for extended compat ID descriptors) */
+	uint16_t wIndex;
+
+	/**<\brief The number of custom property sections that follow the header section. (1 property) */
+	uint8_t bCount;
+
+	/**<\brief Not used.  fill with zeros. */
+	uint8_t _reservedA[7];
+
+	/**<\brief Interface or function that is associated with the IDs in this section */
+	uint8_t bInterfaceNumber;
+
+	/**<\brief Not used.  fill with zeros. */
+	uint8_t _reservedB[1];
+
+	/**<\brief A fixed string assign by Microsoft. 8 chars. fill extras with zero. (See MSDN) */
+	uint8_t chCompatibleID[8];
+
+	/**<\brief A fixed string assign by Microsoft. 8 chars. fill extras with zero. (See MSDN) */
+	uint8_t chSubCompatibleID[8];
+
+	/**<\brief Not used.  fill with zeros. */
+	uint8_t _reservedC[6];
+}__attribute__((packed));
+
+/**
+ * \brief windows (winusb) \bDeviceInterfaceGUIDs custom property
+ * 
+ * This is a DeviceInterfaceGUIDs property section used in \ref wusb_devguid_extprop_feature_descriptor.
+ * 
+ */
+struct wusb_devguid_property {
+	/**<\brief The size of this custom properties section, in bytes (136 bytes) */
+	uint32_t dwPropertyLength;
+
+	/**<\brief Property data format (7 = Unicode REG_MULTI_SZ) */
+	uint32_t dwPropertyType;
+
+	/**<\brief The total length of wPropertyName, in bytes (42) */
+	uint16_t wPropertyNameLength;
+
+	/**<\brief The property name, as a NULL-terminated Unicode string (42 bytes) */
+	uint16_t wPropertyName[MSOS_DEVGUID_NAME_BLEN / 2];
+
+	/**<\brief The total length of wPropertyData, in bytes (80) */
+	uint32_t dwPropertyDataLength;
+
+	/**<\brief (format-dependent) sz = Unicode strings (REG_MULTI_SZ, 76wchars + wnull + wnull = 80 bytes) */
+	uint16_t wPropertyData[MSOS_DEVGUID_VALUE_BLEN / 2];
+
+} __attribute__((packed));
+
+/**
+ * \brief windows (winusb) \bDeviceInterfaceGUIDs extended property feature descriptor
+ * 
+ * This is an extended property feature descriptor containing a DeviceInterfaceGUIDs property section.
+ * 
+ * Store DeviceInterfaceGUIDs in firmware and use them on Windows to locate and open a device.
+ * 
+ */
+struct wusb_devguid_extprop_feature_descriptor {
+
+	/**<\brief The length, in bytes, of the complete extended properties descriptor (DWORD LE). (146 bytes) */
+	uint32_t dwLength;
+
+	/**<\brief The descriptor’s version number, in binary coded decimal (BCD) format. */
+	uint16_t bcdVersion;
+
+	/**<\brief The index for extended properties OS descriptors. (always 5) */
+	uint16_t wIndex;
+
+	/**<\brief The number of custom property sections that follow the header section. (1 property) */
+	uint16_t wCount;
+
+	/**<\brief device interface guid custom property */
+	struct wusb_devguid_property dev_guid_property;
+
+}__attribute__((packed));
+
+#endif