USBDev_API
The Windows host application communicates with a vendor device through USBDev_API. The latter is a wrapper developed by Micrium allowing the application to access the WinUSB functionalities to manage a USB device. Windows USB (WinUSB) is a generic driver for USB devices. The WinUSB architecture consists of a kernel-mode driver (winusb.sys
) and a user-mode dynamic link library (winusb.dll
) that exposes WinUSB functions. USBDev_API eases the use of WinUSB by providing a comprehensive API (refer to the USBDev_API Functions Reference for the complete list). Figure - USBDev_API and WinUSB shows the USBDev_API library and WinUSB.
For more about WinUSB architecture, refer to Microsoft’s MSDN online documentation at: http://msdn.microsoft.com/en-us/library/ff540207(v=VS.85).aspx
Management
USBDev_API
offers the following functions to manage a device and its function’s pipes.
Function name | Operation |
---|---|
USBDev_DevQtyGet | Gets number of devices belonging to a specified Globally Unique IDentifier (GUID) and connected to the host. Refer to the GUID section for more details about the GUID. |
USBDev_Open | Opens a device. |
USBDev_Close | Closes a device. |
USBDev_BulkIn_Open | Opens a bulk IN pipe. |
USBDev_BulkOut_Open | Opens a bulk OUT pipe. |
USBDev_IntrIn_Open | Opens an interrupt IN pipe. |
USBDev_IntrOut_Open | Opens an interrupt OUT pipe. |
USBDev_PipeClose | Closes a pipe. |
Listing - USBDev_API Device and Pipe Management Example shows an example of device and pipe management. The steps to manage a device typically consist in:
- Opening the vendor device connected to the host.
- Opening required pipes for this device.
- Communicating with the device via the open pipes.
- Closing pipes.
- Closing the device.
HANDLE dev_handle; HANDLE bulk_in_handle; HANDLE bulk_out_handle; DWORD err; DWORD nbr_dev; nbr_dev = USBDev_DevQtyGet(USBDev_GUID, &err); (1) if (err != ERROR_SUCCESS) { /* $$$$ Handle the error. */ } dev_handle = USBDev_Open(USBDev_GUID, 1, &err); (2) if (dev_handle == INVALID_HANDLE_VALUE) { /* $$$$ Handle the error. */ } bulk_in_handle = USBDev_BulkIn_Open(dev_handle, 0, 0, &err); (3) if (bulk_in_handle == INVALID_HANDLE_VALUE) { /* $$$$ Handle the error. */ } bulk_out_handle = USBDev_BulkOut_Open(dev_handle, 0, 0, &err); (3) if (bulk_out_handle == INVALID_HANDLE_VALUE) { /* $$$$ Handle the error. */ } /* Communicate with the device. */ (4) USBDev_PipeClose(bulk_in_handle, &err); (5) if (err != ERROR_SUCCESS) { /* $$$$ Handle the error. */ } USBDev_PipeClose(bulk_out_handle, &err); if (err != ERROR_SUCCESS) { /* $$$$ Handle the error. */ } USBDev_Close(dev_handle, &err); (6) if (err != ERROR_SUCCESS) { /* $$$$ Handle the error. */ }
(1) Get the number of devices connected to the host under the specified GUID. A GUID provides a mechanism for applications to communicate with a driver assigned to devices in a class. The number of devices could be used in a loop to open at once all the devices. In this example, one device is assumed.
(2) Open the device by retrieving a general device handle. This handle will be used for pipe management and communication.
(3) Open a bulk pipe by retrieving a pipe handle. In the example, a bulk IN and a bulk OUT pipes are open. If the pipe does not exist for this device, an error is returned. When opening a pipe, the interface number and alternate setting number are specified. In the example, bulk IN and OUT pipes are part of the default interface. Opening an interrupt IN and OUT pipes with USBDev_IntIn_Open()
or USBDev_IntOut_Open()
is similar to bulk IN and OUT pipes.
(4) Transferring data on the open pipes can take place now. The pipe communication is described in the USBDev_API#Communication section.
(5) Close a pipe by passing the associated handle. The closing operation aborts any transfer in progress for the pipe and frees any allocated resources.
(6) Close the device by passing the associated handle. The operation frees any allocated resources for this device. If a pipe has not been closed by the application, this function will close any forgotten open pipes.
Communication
Synchronous
Synchronous communication means that the transfer is blocking. Upon function call, the application blocks until the end of transfer is completed with or without an error. A timeout can be specified to avoid waiting forever. Listing - USBDev_API Synchronous Read and Write Example presents a read and write example using a bulk IN pipe and a bulk OUT pipe.
UCHAR rx_buf[2]; UCHAR tx_buf[2]; DWORD err; (void)USBDev_PipeRd(bulk_in_handle, (1) &rx_buf[0], (2) 2u, 5000u, (3) &err); if (err != ERROR_SUCCESS) { /* $$$$ Handle the error. */ } (void)USBDev_PipeWr(bulk_out_handle, (1) &tx_buf[0], (4) 2u, 5000u, (3) &err); if (err != ERROR_SUCCESS) { /* $$$$ Handle the error. */ }
(1) The pipe handle gotten with USBDev_BulkIn_Open()
or USBDev_BulkOut_Open()
is passed to the function to schedule the transfer for the desired pipe.
(2) The application provides a receive buffer to store the data sent by the device.
(3) In order to avoid an infinite blocking situation, a timeout expressed in milliseconds can be specified. A value of ‘0’ makes the application thread wait forever. In the example, a timeout of 5 seconds is set.
(4) The application provides the transmit buffer that contains the data for the device.
Asynchronous
Asynchronous communication means that the transfer is non-blocking. Upon function call, the application passes the transfer information to the device stack and does not block. Other application processing can be done while the transfer is in progress over the USB bus. Once the transfer has completed, a callback is called by USBDev_API to inform the application about the transfer completion.
Listing - USBDev_API Asynchronous Read Example presents a read example. The asynchronous write is not offered by USBDev_API.
UCHAR rx_buf[2]; DWORD err; USBDev_PipeRdAsync( bulk_in_handle, (1) &rx_buf[0], (2) 2u, App_PipeRdAsyncComplete, (3) (void *)0u, (4) &err); if (err != ERROR_SUCCESS) { /* $$$$ Handle the error. */ } static void App_PipeRdAsyncComplete(void *p_buf, (3) DWORD buf_len, DWORD xfer_len, void *p_callback_arg, DWORD err) { (void)p_buf; (void)buf_len; (void)xfer_len; (void)p_callback_arg; (4) if (err == ERROR_SUCCESS) { /* $$$$ Process the received data. */ } else { /* $$$$ Handle the error. */ } }
(1) The pipe handle gotten with USBDev_BulkIn_Open()
is passed to the function to schedule the transfer for the desired pipe.
(2) The application provides a receive buffer to store the data sent by the device.
(3) The application provides a callback passed as a parameter. Upon completion of the transfer, USBDev_API calls this callback so that the application can finalize the transfer by analyzing the transfer result. For instance, upon read operation completion, the application may do a certain processing with the received data.
(4) An argument associated to the callback can also be passed. Then, in the callback context, some private information can be retrieved.
Control Transfer
You can communicate with the device through the default control endpoint by using the function
USBDev_CtrlReq()
. You will be able to define the three types of requests (standard, class or vendor) and to use the data stage or not of a control transfer to move data. More details about control transfers can be found in “Universal Serial Bus Specification, Revision 2.0, April 27, 2000”, section 5.5 and 9.3.