This is a follow-up to my previous question, Need to write driver for USB peripheral device?
Background
I'm working on designing a USB peripheral using an STM32 microcontroller (bare metal / no OS). The device will occasionally be connected to a Windows PC, and transfer a few KB of data in each direction. There will be a custom PC application that controls the data transfers, using a proprietary protocol (i.e. for the USB payloads).
The PC will always be the master (initiator) - it'll send commands, and the device will issue responses, with up to a few hundred bytes of data going in either direction in a single command or response. I think I'll want to use USB Bulk Transfer mode.
Option 1 - USB CDC
From what I understand, one option is I could use the USB Communications Device Class (CDC). On the device side, I could use sample code from ST for the USB CDC, e.g. from STM32Cube. On the PC side, the device would present as a Virtual COM Port (VCP). Then in software I'd basically have a raw, bidirectional stream, on top of which I'd have to define my messsage formats, commands, etc.
Have I explained this correctly?
Option 2 - WinUSB
I'm having trouble wrapping my mind around exactly what this is, and how to work with it.
What is the relationship of WinUSB to USB device classes? It seems to function as a "generic" USB class, but I can't find any documentation that spells it out as such.
Does WinUSB provide any built-in message delimiters? e.g. Does WinUsb_WritePipe send the contents of the buffer down to the device as an atomic unit? Or do I just get raw streams like a VCP/UART?
How does one implement WinUSB on the device? Is there sample code available? (Preferably for STM32.)
Choosing
What are the relevant considerations in choosing between options 1 and 2 for my application?
Yes, you described USC CDC ACM correctly.
WinUSB exists to support devices that do not have a particular device class. If your device implements a device class such as Human Interface Device, Mass Storage Device, or Communications Device (CDC), you can just use the drivers that come with your operating system to talk to that device. If you want a more customizable and flexible USB interface that doesn't conform to one of those classes, you can use WinUSB to talk to your device. You could also write your own driver if you want, but that is going to be a lot of work and I wouldn't recommend it. Some people have written drivers that are alternatives to WinUSB: you can look up libusbK, libusb0.sys, and UsbDK for examples. WinUSB has the advantage that it comes with Windows so I wouldn't use one of those other drivers unless it has a specific feature you really need.
I believe that WinUSB_WritePipe sends the data as a single USB transfer. A transfer has a specific definition in the USB specification. You can tell when a transfer ends because you will get a short packet at the end of the transfer. A short packet is a packet that is smaller than the maximum packet size of the endpoint, and it could possibly be zero-length. You should double check whether that is actually true though; try sending a transfer that is a multiple of the maximum packet size and make sure that Windows sends a zero-length packet at the end of that. By the way, you should consider sending your data as a control transfer or series of control transfers on endpoint 0. Control transfers have a built-in concept of a request and a response to the request. Despite the name, control transfers can be used to transfer large amounts of data; they are commonly used in the USB bootloaders (see the DFU class). Another advantage of using control transfers instead of non-zero endpoints is that you don't have to add extra endpoint descriptors and initialize the endpoints in your firmware. Your USB stack should have the machinery for handling custom control transfers and you should be able to make custom control transfers just by writing a few callback functions.
To implement WinUSB on the device side, you would need to write your own USB descriptors and then use low-level USB transfer commands to read and write data from endpoints, or to handle vendor-specific control transfers on endpoint zero. I am not familiar with the STM32 USB libraries, but you should be able to identify the core component of it that implements control transfers and IN and OUT endpoints, without doing anything specific to a device class. That is the component that you would need to learn how to use.
By default, you should use WinUSB instead of USB CDC ACM. The only reason to use USB CDC ACM is if your device is actually a serial port or if you want to make it easier for people to talk to your device in a variety of programming languages and environments. Most programming languages support serial ports, but the user would still have to write the code on top of that that generates your particular command format, so it doesn't actually give you that much. One problem with USB CDC ACM is that if the device gets disconnected while you have a handle open to it, and then it gets reconnected, the various USB CDC ACM drivers can often get into a bad state. In particular, usbser.sys prior to Windows 10 does not handle this well, and you usually have to unplug and replug the device to make the COM port usable again. USB CDC ACM uses bulk endpoints for its data transfer, so there is no latency guarantee. With WinUSB, you have the option to use Interrupt endpoints so that you can be guaranteed to always have one packet transferred for every USB frame.
WinUSB consists of two parts:
WinUsb.sys is a kernel-mode driver that can be installed as either a filter or function driver, above the protocol drivers in a USB device’s kernel-mode device stack.
WinUsb.dll is a user-mode DLL that exposes the WinUSB API. Applications can use this API to communicate with WinUsb.sys when it is installed as a device’s function driver.
WinUSB API—exposed by WinUSB.dll. WinUSB is included in the Windows Driver Kit (WDK) in the form of a co-installer package, WinUSBCoInstaller.dll, located in WinDDK\BuildNumber\Redist\Winusb.
To use the WinUSB API in an application:
Include WinUsb.h
Add WinUsb.lib to the list of libraries that are linked to your application.
Usb100.h contains declarations for some useful macros.
Use the device interface GUID to obtain the device path. The correct GUID is the one that you specified in the INF that was used to install WinUsb.sys.
Get a handle to the device information set by passing the device interface GUID that you defined in the INF to SetupDiGetClassDevs. The function returns an HDEVINFO handle.
Call SetupDiEnumDeviceInterfaces to enumerate the system’s device interfaces and obtain information on your device interface.
Call SetupDiGetDeviceInterfaceDetail to get detailed data for the device interface.
Call the GetDevicePath function to obtain the device path.
Pass the device path to CreateFile to obtain a file handle for the device. Use ReadFile and WriteFile to communicate with device.
Pass the file handle to WinUsb_Initialize to initialize WinUSB and obtain a WinUSB handle. You use the device’s WinUSB handle to identify the device when you call WinUSB API functions, not the device’s file handle.
For more advanced solutions - use functions:
WinUsb_QueryDeviceInformation to obtain the device’s speed.
WinUsb_QueryInterfaceSettings to obtain the corresponding interface descriptors. The WinUSB handle corresponds to the first interface.
WinUsb_QueryPipe gets information about each endpoint.
WinUsb_WritePipe writes the buffer to the device - default behavior: zero-length writes are forwarded down the stack. If the transfer length is greater than a maximum transfer length, WinUSB divides the request into smaller requests of maximum transfer length and submits them serially.
more functions and info: http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/winusb_howto.docx
For debugging purposes You probably need:
winusbtrace_tool https://blogs.msdn.microsoft.com/usbcoreblog/2010/02/05/how-to-generate-and-view-a-winusb-debug-trace-log/;
Wireshark https://www.wireshark.org with USBPcap plugin.
Other Example:
http://searchingforbit.blogspot.com/2012/04/winusb-communication-with-stm32-part-1.html.
Sample template comes with Visual Studio.
You need also have knowledge of writing .inf files.
Another easy way to communicate with USB - libusb-win32 https://sourceforge.net/projects/libusb-win32/
My simple example console application sends small (for keep-alive) chunks of data to device:
#include "stdafx.h"
#include <SetupAPI.h>
#include <Hidsdi.h>
#include <devguid.h>
#include <winusb.h>
#include <usb.h>
#pragma comment(lib, "hid.lib")
#pragma comment(lib, "setupapi.lib")
#pragma comment(lib, "winusb.lib")
#include <iUString.h>
iString<char> DevicePath;
bool WinusbHandle_Open=false;
bool DeviceHandle_Open = false;
WINUSB_INTERFACE_HANDLE WinusbHandle;
HANDLE DeviceHandle;
UCHAR usb_out_buffer[64];
DEFINE_GUID(GUID_DEVCLASS_WINUSB, 0x88bae032L, 0x5a81, 0x49f0, 0xbc, 0x3d, 0xa4, 0xff, 0x13, 0x82, 0x16, 0xd6);
DEFINE_GUID(GUID_DEVCLASS_STL, 0xf177724dL, 0x74d3, 0x430e, 0x86, 0xb5, 0xf0, 0x36, 0x89, 0x10, 0xeb, 0x23);
GUID winusb_guid;
GUID stl_guid;
bool connectusb();
void disconnectusb();
int main()
{
DWORD n;
DWORD numEvents;
HANDLE rHnd;
WinusbHandle_Open = false;
DeviceHandle_Open = false;
winusb_guid = GUID_DEVCLASS_WINUSB;
stl_guid = GUID_DEVCLASS_STL;
usb_out_buffer[0] = 0;
usb_out_buffer[1] = 1;
usb_out_buffer[2] = 2;
usb_out_buffer[3] = 3;
ULONG bytesWritten;
ULONG timeout;
timeout = 100;
rHnd = GetStdHandle(STD_INPUT_HANDLE);
WinUsb_SetPipePolicy(WinusbHandle, 0x01, PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout);
timeout = TRUE;
WinUsb_SetPipePolicy(WinusbHandle, 0x01, AUTO_CLEAR_STALL, sizeof(ULONG), &timeout);
timeout = TRUE;
WinUsb_SetPipePolicy(WinusbHandle, 0x01, RAW_IO, sizeof(ULONG), &timeout);//Bypasses queuing and error handling to boost performance for multiple read requests.
while (true)
{
if ((!WinusbHandle_Open) || (!WinusbHandle_Open)) { if (!connectusb())Sleep(2000); }
if ((!WinusbHandle_Open) || (!WinusbHandle_Open))continue;
bytesWritten = 0;
if (!WinUsb_WritePipe(WinusbHandle, 0x01, &usb_out_buffer[0], 2, &bytesWritten, NULL))
{
n = GetLastError();
disconnectusb();
}
Sleep(2000);
}
disconnectusb();
return 0;
}
bool connectusb()
{
BOOL bResult = FALSE;
HDEVINFO deviceInfo;
SP_DEVICE_INTERFACE_DATA interfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL;
DWORD n;
SP_DEVINFO_DATA devinfo;
BYTE devdetailbuffer[4096];
bool found;
deviceInfo = SetupDiGetClassDevs(&stl_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (deviceInfo == INVALID_HANDLE_VALUE) { return false; }
found = false;
for (n = 0;; n++)
{
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (!SetupDiEnumDeviceInterfaces(deviceInfo, NULL, &stl_guid, n, &interfaceData))
{
n = GetLastError();
break;
}
detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)devdetailbuffer;
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
devinfo.cbSize = sizeof(devinfo);
if (!SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, detailData, sizeof(devdetailbuffer), NULL, &devinfo)) { printf("SetupDiGetDeviceInterfaceDetail: %u\n", GetLastError()); break; }
if (IsEqualGUID(devinfo.ClassGuid, winusb_guid))
{
if ((-1 != iStrPos(detailData->DevicePath, "VID_0483")) || (-1 != iStrPos(detailData->DevicePath, "vid_0483")))
{
if ((-1 != iStrPos(detailData->DevicePath, "PID_576B")) || (-1 != iStrPos(detailData->DevicePath, "pid_576b")))
{
DevicePath = detailData->DevicePath;
found = true;
break;
}
}
}
}
SetupDiDestroyDeviceInfoList(deviceInfo);
if (!found)return false;
DeviceHandle = CreateFile(DevicePath.Buffer() ,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == DeviceHandle) {
n = GetLastError();
}
if (INVALID_HANDLE_VALUE == DeviceHandle) return false;
DeviceHandle_Open = true;
if (!WinUsb_Initialize(DeviceHandle, &WinusbHandle))
{
n = GetLastError();
CloseHandle(DeviceHandle); DeviceHandle_Open = false;
return false;
}
WinusbHandle_Open = true;
return true;
}
void disconnectusb()
{
if (WinusbHandle_Open) { WinUsb_Free(WinusbHandle); WinusbHandle_Open = false; }
if (DeviceHandle_Open) { CloseHandle(DeviceHandle); DeviceHandle_Open = false; }
}
I also have to do your exact same requirement: PC <==> STM
Microsoft has lots of documentation on WinUSB. Here is what I've seen that answers your questions...
Custom USB device sample
https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/CustomUsbDeviceAccess
Developing Windows applications for USB devices -- C# AND VB
https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/developing-windows-applications-that-communicate-with-a-usb-device
Windows desktop app for a USB device
https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/windows-desktop-app-for-a-usb-device
How to Access a USB Device by Using WinUSB Functions
https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/using-winusb-api-to-communicate-with-a-usb-device
Developing Windows drivers for USB host controllers
https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/developing-windows-drivers-for-usb-host-controllers
Windows.Devices.Usb Windows.Devices.Usb Namespace
https://learn.microsoft.com/en-us/uwp/api/windows.devices.usb
I solved this request so I understand your intention is about to make your firmware could be enumerated as a WINUSB (winusb generic driver) device automatically by Windows while plugging-in.
I believe it'd be clear if you have a demo and code so I made one for you :)
My KEIL project using the STM32F4 Discovery board working with WINUSB as an USB CDC device.
You can see more information and have the source code from my GitHub.
For deploying WINUSB it's necessary to manually modify USB device and middleware layers source code in the KEIL project.
You can focus on usbd_ctlreq.c, usbd_cdc.c and usbd_desc.c to have a comparison with origins generated by CubeMX.
Related
I have a kernel module that would like to send pre-fabricated ethernet frames from user space such as custom ARP, and other protocols (I'm trying to bypass tcp/ip stack on linux and create custom one for my needs). Frames are valid and complete with all necessary things. The only part that remains is to send them somehow to the queue on eth0 interface. What is the best solution to do this?
For snatching incoming packets I am using netfilter API with the earliest hook possible. I can not use raw sockets from user space due to the need of sudo and also due to my custom requirements.
Edit: I was able to achieve my goals with dev_queue_xmit(). However, I am still wondering if there is another solution that accesses the driver directly.
static void SendFrame(void)
{
struct sk_buff* skb = dev_alloc_skb(1518);
skb->dev = __dev_get_by_name(&init_net, "eth0");
skb_reserve(skb, NET_IP_ALIGN);
skb->data = skb_put(skb, ethFrameBytes);
memcpy(skb->data, pEthFrame, ethFrameBytes);
if (dev_queue_xmit(skb) != NET_XMIT_SUCCESS)
{
printk(KERN_ERR, KERN_ERR "Error: unable to send the frame\n");
}
}
How do I upload firmware (microsemi) via the Spi port?
How do I then start, stop and check the status of said firmware?
Android Things Version: 0.4.1-devpreview (could not get the display to work on the newer builds)
Issue: I am a hardware noob. I have a python driver used for uploading firmware and a config file via the Spi port. My goal is to port this to Android Things leveraging the SpiDevice class.
The python version of the driver strips off headers and checks block size ect. I'm not sure if I need to do this with Android Things SpiDevice.Write(buffer, length) method.
Once I have uploaded the firmware and config, I will need to start it. In total I will need to upload firmware, start firmware, check if firmware is running and stop firmware.
I have started writing a SpiDeviceManager, and have naively began to flesh out the methods. (see below).
*public void LoadFirmware()
{
WriteFile(_firmwareFilePath);
WriteFile(_configurationFilePath);
}
private void WriteFile(string filename)
{
using (System.IO.Stream stream = _assetManager.Open(filename))
{
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
_spiDevice.Write(buffer, bytesRead);
}
}
}*
If anyone can point me at some docs or examples I would really appreciate it.
Also, if anyone has any advice about getting the display drivers working for the latest version of Things I could really use your help. I have the device specs for the config.txt it just does not work.
Thanks,
Andrew
The SpiDevice just sends the data along.
Its up to me to pack the bytes in there in such a way that the chip can make sense of it.
I am sifting through the data sheet to that end.
#Nick Felker, thanks for the info about the Android Things display. Cant wait for the next release to drop.
I try to implement an application in snow Leopard, Reading Data from USB/HID device.In my application i tried following steps:
IOHIDManagerCreate()
CreateDeviceMatchingDictionary()
IOHIDManagerSetDeviceMatching()
IOHIDManagerOpen()
IOHIDManagerCopyDevices()
Create a Reference for device(IOHIDDeviceRef)
Based On the IOHIDDeviceRef i Fetch device details such as(Device ProductIDKey, Device VendorIDKey,Device ProductKey,Device Serial NumberKey,Device VersionNumberKey ect.)
IOHIDDeviceOpen(),ie :Using IOHIDDeviceRef i opened Device;
IOHIDDeviceCopyMatchingElements(); Ie Copy Matching Elements from the Device;
Create a Reference element(ie IOHIDElementRef);
Using IOHIDElementRef i retrieved Device Usage,Device Cocookie,Device UsagePage etc.
Up to this My application working Fine.
My doubts are
How can read data From Endpoint 1, My device is special purpose device having only one End point(Interrupt no synchronization data end point)?
Is their any Pipe associated with end point 1(HID Device)?;
Is their any ReadPipe and WritePipe functions in HIDManager Based Application?
Is it possible to retrieve data from USB/HID using IOHIDDeviceSetReoprtWithCallback()?
Every thing Did based on this Link:
http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/HID/new_api_10_5/tn2187.html#//apple_ref/doc/uid/TP40000970-CH214-SW7 ...
Thank you so much for your help ..
You are trying to use HID functions with non-HID device.
HID is Human Interface Device. Any HID device must conform "Device Class Definition for HID".
I suspect, your device does not conforms this specification.
So you should use other OS interface to work with your device. I suggest to use libusb. This is cross-platform library for working with USB devices on low level. It will allow you to read/write your endpoint directly.
I need to configure USB OTG on iMX31 for device mode. We need a raw channel between the host and target and usb seems to be the best suited. However I haven't been able to correctly configure the OTG controller. I dont know what I am missing. I have performed the steps mentioned in section 32.14.1 of iMX31 Reference Manual. I have also configured PORTSC1 register for ULPI.
Can any one help me out here? any poineters/code/any thing that can help me is welcome.
Thanks
The litekit is supported by the vanilla Linux kernel.
It's pretty easy to declare the OTG for device mode. You just need to declare it as device when you register your device:
static struct fsl_usb2_platform_data usb_pdata = {
.operating_mode = FSL_USB2_DR_DEVICE,
.phy_mode = FSL_USB2_PHY_ULPI,
};
Register code:
mxc_register_device(&mxc_otg_udc_device, &usb_pdata);
Don't forget to configure the pads for the physical ULPI lines, and eventually make the initial transactions for your transceiver.
You can find all the necessary code as I did it for the moboard platform and the marxbot board file.
I have a USB Joystick, and I want to write my own HID driver for it. Notably I want to inject delay between when the joystick input is received by Windows and when my application is notified of that input event. I would also like to change the identity of the joystick percieved by my application. I have never written a driver, let alone an HID driver, for Windows. Can anyone provide me with advice or pointers on how to go about doing this?
When you press knobs on the Joystick the electric signals reach the operating system (and onto the game) in the form of IRP's through the drivers chain. Intercepting these IRP's at some point and delaying the forwarding to the next driver can delay the joystick input. This can be achieved with driver filters.
To write windows drivers you need to use WinDDK.
The entrypoint of a windows driver is the DriverEntry function. In this function you will be hooking what IRP's you want to intercept and the callback functions that deal with them, in our case, the callback functions that delay the forwarding.
For example, say our IRP to be delayed is IRP_MJ_READ and our callback function is called CallbackDelayForwarding:
// the function that delays the IRP
NTSTATUS CallbackDelayForwarding(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
){
// delay the forwarding
}
// this is the driver entrypoint, similar to "main" in user-mode executables
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING RegistryPath
){
pDriverObject->MajorFunction[IRP_MJ_READ] = CallbackDelayForwarding;
...
}
To delay the forwarding inside CallbackDelayForwarding, you must use functions from the KeInitializeTimer family to simulate some sort of sleep (maybe in conjunction with locks), KeDelayExecutionThread etc.
To install your filter driver in the joystick driver chain, you can use .inf files.
Check the toaster filter driver sample in the WinDDK, you can find it at INSTALL_DIR/src/general/toaster/ see also here.
Related links:
http://www.rootkit.com/newsread.php?newsid=187
http://www.techtalkz.com/microsoft-device-drivers/269654-toaster-filter-driver.html
What you are trying to do is a filter driver.
Filter drivers are optional drivers that add value to or modify the behavior of a device. A filter driver can service one or more devices.
source: http://msdn.microsoft.com/en-us/library/ff545890.aspx
I think the WDK contains samples You ould have to download the Windows Driver Kit from here: http://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx
The WDK probably contains a sample code to create a filter.
There is also the following download on microsoft's site : http://support.microsoft.com/kb/176417
It is probably a little bit outdated since USB was not supported pre-NT5.0, but maybe it is still relevant.
You might want to use Autohotkey. This is a script language for windows which can remap every keys of mouse/keyboard/joysticks. If you capture the key through a script and insert a delay before sending the right key/macro to the active application, you might have a part of a solution to your problem.
I was under the impression you could use <dinput.h>
joySetCapture(wnd, JOYSTICKID1) or joySetCapture(wnd,JOYSTICKID2)
then Joystick events would fire WM_JOYMOVE events, though what the wParam and lParam for the message would be I have no idea.
Then when your program closes or you no longer want joyReleaseCapture(JOYSTICKID1) etc.
it could just be empty params which then tell you to use joyGetPos or joyGetPosEx functions
to find the data for yourself from the USB device.