Embedded USB driver-free device communication method based on WinUSB
[Copy link]
In order to simplify the development of USB devices and access to PC systems, Microsoft developed WinUSB, which can install Winusb.sys as a device function driver and provide WinUSB API for applications to access devices. For a long time, except for USB HID devices, other types of devices need to install drivers to work in the WINDOWS environment. To achieve driver-free USB devices, you can only use HID devices. However, HID devices have slow transmission speeds. In some occasions, when bulk type must be used for batch transmission, you must use a third-party driver or develop a driver yourself, which makes project development very troublesome. Now, since Microsoft launched WinUSB, it has become very convenient and fast to implement simple Bulk type batch transmission on Microsoft's latest operating system. It is very applicable and easy to implement in the R&D process or in some occasions where differentiation requirements are not high. This article is dedicated to implementing a simple WinUSB communication system to meet such needs.
How to enumerate embedded devices as WinUSB devices
The system uses USB descriptors to determine which USB Class type to work with. If you want WINDOWS to be able to identify the embedded device as a WinUSB device, its descriptor should contain at least the following fields:
1. Support OS string descriptors:
In order for the USB driver stack to know that the device supports extended feature descriptors, the device must define an OS string descriptor stored at string index 0xEE. During enumeration, the driver stack queries the string descriptor. If the descriptor exists, the driver stack assumes that the device contains one or more OS feature descriptors and the data required to retrieve these feature descriptors. The retrieved string descriptor has a bMS_VendorCode field value. This value of 1 indicates that the USB driver stack must use the vendor code to retrieve the extended feature descriptor.
0x00, 0x01,
// langId : 0x0000 const U8 OS_StringDescritpor[ ] =
{ 0x12, 0x03, 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, bMS_VendorCode, 0 }; 2. Set the compatible ID feature descriptor:
const U8 WINUSB_ExtendedCompatId_Descritpor[ ] = {
0x28, 0x00, 0x00, 0x00, // dwLength
0x00, 0x01, // bcdVersion
0x04, 0x00, // wIndex
0x01, // bCount
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved[7]
0x00, // bFirstInterfaceNumber
0x01, // RESERVED ( 0x01 )
'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x0 0, // compactiableID[8]
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subCompactiableID[8]
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved[6]
};
Note: WinUSB also supports composite devices. For the simplest system with a single transmission type, we can ignore the requirement of composite devices. The compatibleID field must specify "WINUSB" as the field value. Others can be changed as required.
3. Register device interface GUID descriptor:
This descriptor is used to distinguish different WinUSB devices.
const U8 WINUSB_ExtendedProperty_InterfaceGUID_Descritpor[ ] ={
0x8E, 0x00, 0x00, 0x00, // dwTotalSize = Header + All sections0x00, 0x01, // bcdVersion
0x05, 0x00, // wIndex
0x01, 0x00, // wCount
0x84, 0x00, 0x00, 0x00, // dwSize -- this section0x01, 0x00, 0x00, 0x00, // dwPropertyDataType0x28, 0x00,// wPropertyNameLength 'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,'I',0,'n',0x00,'t',0,'e',0,'r',0,'f',0,'a',0,'c',0,'e',0, 'G',0,'U',0,'I',0,'D',0,0,0,0x 4E, 0x00, 0x00, 0x00, // dwPropertyDataLength : 78 Bytes = 0x0000004E'{',0,'1',0,'2',0,'3',0,'4',0, '5',0,'6',0,'7',0,'8',0,'-',0,'1',0,'2',0,'3',0,'4',0,'-',0,'1',0,'3',0,'4',0,'4',0,'-',0,'1',0,'2',0,'3',0,'4',0,'-',0,'1' ,0,'2',0,'3',0,'4',0,'5',0,'6',0,'7',0,'8',0,'9',0,'A',0,'B',0,'C',0,'}',0,0,0};// bPropertyData : WCHAR : L"{12345678-1234-1234-1234-123456789ABC}"4. Endpoint descriptor:
Configure the number and type of endpoints according to actual needs to complete the descriptor configuration of the embedded device.
Generally, the firmware program can be modified through the sample program provided by the MCU manufacturer, and the description of the USB firmware function is omitted here. As long as the required fields in the above three descriptors are included, it can be successfully enumerated as a USB DevICe. After successful enumeration, similar devices can be seen in the device WINDOWS device manager, as shown in Figure 1 below.
Figure 1 Successfully enumerated as USB Device
How to write PC applications to communicate with embedded devices via USB PC software is relatively simple, and Microsoft has also provided sample code. The only thing to note is that the GUID parameter of the corresponding software program to obtain the WinUSB device handle must be consistent with the GUID in the descriptor of the embedded device. GUID is the unique identifier used by WinUSB to distinguish devices. GUID, short for Globally Unique Identifier, is a binary data generated by an algorithm with a length of 128 bits. The
specific implementation steps are as follows:
1. Create a file handle for the device:
call SetupDiGetClassDevs to obtain the handle of the device information set; call SetupDiEnumDeviceInterfaces to enumerate the device interfaces in the device information set and obtain information about the device interfaces; call SetupDiGetDeviceInterfaceDetail to obtain detailed information about the device interface, and the information obtained is returned through the SP_DEVICE_INTERFACE_DETAIL_DATA structure. Since the size of the structure cannot be obtained in advance, the function needs to be called twice in succession. When the second call is made, the interface details will be filled into the buffer of the size determined by the return value of the first call. The "device path" can be obtained through the DevicePath member of the structure in the buffer.
2. Get the WinUSB interface handle of the device:
Call WinUsb_Initialize by passing the file handle created in the file handle of the device creation.
3. Query the device to obtain the USB descriptor:
Next, query the device to obtain USB-specific information, such as device speed, interface descriptor, related endpoints and their pipes. Call WinUsb_QueryDeviceInformation to request information from the device descriptor of the device. Call WinUsb_QueryInterfaceSettings and pass the interface handle of the device to obtain the corresponding interface descriptor. Call WinUsb_QueryPipe to obtain information about each endpoint of each interface. This step is not necessary because the endpoint direction and transmission characteristics are determined by the embedded device descriptor and are known.
4. Send a control transfer to the default endpoint:
This step is also not necessary. Generally, the payload is not sent through the default endpoint.
5. Send I/O request:
Send data to the bulk input and bulk output endpoints of the device, which can be used for read requests and write requests respectively. Call WinUsb_ReadPipe to read data from the bulk input endpoint of the device. Call WinUsb_WritePipe to write data to the device through the bulk output endpoint. After writing data in the output endpoint of the embedded device, the data can be read out on the PC side. Conversely, if data is written to the input endpoint of the embedded device on the PC side, the embedded device will generate a USB endpoint write event. How to capture this event is determined by the product hardware of the MCU manufacturer, and the corresponding interrupt information is generated for the interrupt service program to judge. Generally speaking, chip manufacturers will provide MCU's USB communication basic example program, and simple modifications and adaptations can be made on its basis.
6. Release device handles
After completing all necessary calls to the device, release the device's file handle and WinUSB interface handle. CloseHandle releases the handle created by CreateFile.
WinUsb_Free releases the device's WinUSB interface handle returned by WinUsb_Initialize.
At this point, the USB code porting of the embedded device firmware and the writing of the PC application have been completed, and the communication method of the USB driver-free device can be realized.
|