User Tools

Site Tools


projects:qtusb

This is an old revision of the document!


Developing multi-platform USB applications with Qt

24.05.2013 Freising, Germany

This page is under construction

<html> <?xml version=“1.0” encoding=“UTF-8” standalone=“no”?> <!– Created with Inkscape (http://www.inkscape.org/) –>

</html>

USB is very popular and user-friendly computer interface. Unfortunately it is far from the most easy ones to use. This article is meant to provide a guidelines and samples on building multi-platform application from the side of device and host as well.

In many cases CDC(virtual serial) class device is enough but there might be situations where user wan to avoid using it (for example to obscure communication and block third part software access). I will show how to create true plug and play generic bulk device on Windows and how to create Linux version of the same software. I believe that this way is much more professional than HID “hack” (using HID class for both way communication).

The application will allow to control Launchpad RGB led colour using simple GUI on PC.

There are several software projects involved:

  • TI StellarisWARE - provides embedded USB stack and basic examples.
  • Qt - provides multi platform application framework and widget toolkit.
  • libusbX - provides generic access to USB devices on multiple platforms.

There are multiple USB libraries available. For example:

  • Microsoft winusb - Native for windows (platform-dependent)
  • libusb 0.1 - Now legacy. Unix (platform-dependent)
  • libusb-win32 - Now legacy. Windows (platform-dependent)
  • libusb 1.0 - multi platform solution, alternative to libusbX

What are the advantages of libusbx over libusb?

Quoting http://libusbx.org/:

Apart from frequent releases, which include regular bugfixes as well as exciting new features (please check our roadmap), you should find that we are a lot more responsive and that, rather than focus our efforts on elements that are of little interest to you, or an ever delayed promise of “better” features that fail to materialize, we strive to bring you the best possible user and developer experience today.
Also, unlike libusb, we fully subscribe to the Release Early, Release Often (RERO) philosophy, upon which the success of the Linux kernel and countless other Open Source projects is based.
Finally, if there's anything the failure of libusb has taught us, it's that a project should never fail to listen to you, its user… As such, libusbx is as much your library as it is ours, and we hope that you will engage with us to help make it even greater!

libusbx supports Windows, Linux, MacOS X, OpenBSD and NetBSD. Multiple language bindings exist including Python and C#.

What about VID/PID?

If you are planning commercial product you need to have custom VID1)/PID2). You may buy your VID from USB Implementers Forum or use one from MCU manufacturer.

Software

Embedded

Code for embedded device is based on Generic Bulk example provided by TI. Instead of echoing data back to PC data is processed and LEDs are set according to it. Additionally device provide OS Descriptors unlike TI example. Please view my USB CDC + Microsoft descriptors project for more information on what OS Descriptors are and how to use them with TI StellarisWARE. Software for MCU does not change for any PC platform.

There are 4 commands that MCU can understand:

  1. 0x0E - Enable LEDs
  2. 0x0D - Disable LEDs
  3. 0x0C - Set colour of LEDs (followed by 6 bytes: 2x Red, 2x Green, 2x Blue)
  4. 0x0A - Host ask for status. Device will reply with 1 byte indicating LED current state

Windows

GUI with device connected under Windows.

This sample application was written in Qt 5. GUI controls are disabled as long as device is not connected. Connection/disconnection is fully automatic. Program pools device status every 500[ms] to check if user disabled LED by pressing on-board switch (SW1). Button LEDs ON is toogle-type. When LEDs are active button glows.

GUI Windows source code

Linking libusbx

I have downloaded the most recent release of library (http://sourceforge.net/projects/libusbx/) and decompressed it. There are 2 options on linking it: static or dynamic. I am choose static to reduce output file count.

Now we need to:

  1. Add header file to our project by coping […]\include\libusbx-1.0\libusb.h to our project
  2. Copy compiled library to our project folder […]\MinGW32\static\libusb-1.0.a
  3. Add library binary by adding following line to ***.pro file of our project
    LIBS += -L$$PWD -lusb-1.0

Detecting device mount/remove

It is possible to get a notification from Windows when our device is connected or disconnected from the system.

#include <dbt.h>
 
[...]
 
//
// Register for device connect notification
//
DEV_BROADCAST_DEVICEINTERFACE devInt;
ZeroMemory( &devInt, sizeof(devInt) );
devInt.dbcc_size        = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
devInt.dbcc_devicetype  = DBT_DEVTYP_DEVICEINTERFACE;
devInt.dbcc_classguid   = GUID_DEVINTERFACE_LAUNCHPAD;
//
HDEVNOTIFY m_hDeviceNotify =
            RegisterDeviceNotification((HANDLE)winId(),&devInt, DEVICE_NOTIFY_WINDOW_HANDLE );
//
if(m_hDeviceNotify == NULL)
{
    qDebug() << "Error: Failed to register device notification!";
    qApp->quit();
}

Structure devInt consists variable GUID_DEVINTERFACE_LAUNCHPAD. This variable stores GUID of device interface.

static const GUID GUID_DEVINTERFACE_LAUNCHPAD =
{ 0xfd96fadb, 0x9246, 0x4017, { 0x8d, 0x76, 0x3e, 0x30, 0x77, 0x80, 0xf6, 0xeb } };
//{{fd96fadb-9246-4017-8d76-3e307780f6eb}}

Device interface GUID usually is stored in driver .inf file. Since we are using OS Descriptors we can define it in OS Properties descriptor

const g_sOSProperties g_pOSProperties =
{
 dwLength: 			sizeof(g_pOSProperties),
 bcdVersion:			0x0100,
 wIndex:			5,
 wCount:			3,
 
 dwSize:			sizeof(L"DeviceInterfaceGUID")+sizeof(L"{4d36e978-e325-11ce-bfc1-08002be10318}")+14,//132,
 dwPropertyDataType: 		1,	//(Unicode string
 wPropertyNameLength: 		sizeof(L"DeviceInterfaceGUID"),
 bPropertyName: 		L"DeviceInterfaceGUID",
 dwPropertyDataLength:		sizeof(L"{fd96fadb-9246-4017-8d76-3e307780f6eb}"),
 bPropertyData: 		L"{fd96fadb-9246-4017-8d76-3e307780f6eb}", 
[...]

Handling of the messages is being performed in a standard way. In case message is interesting for us we emit signal.

// Function that receive messages 
// This is windows-specific
bool MainWindow::nativeEvent(const QByteArray& eventType, void* message,
                             long* result)
{
    Q_UNUSED( result );
    Q_UNUSED( eventType );
 
    MSG* msg = reinterpret_cast<MSG*>(message);
 
    // Does this specific message interest us?
    if(msg->message == WM_DEVICECHANGE)
    {
        switch(msg->wParam)
        {
        case DBT_DEVICEARRIVAL:
            emit signal_DeviceConnected();
            break;
 
        case DBT_DEVICEREMOVECOMPLETE:
            emit signal_DeviceDisconnected();
            break;
        }
    }
 
    // Qt handles the rest
    return false;
}

Linux

I am using Debian 6.0.7 (virtual machine).

We cannot use windows-specific routines under Linux. To check if device is connected or not we will pool udev monitor. Additionally we need to set udev rules to allow non-root users access device.

  • Qt Linux source code [TODO]

Building libusbx

  1. Download the code from libusbx source forge download page
  2. Extract files
  3. Run standard sequence:
    1. ./configure
    2. make
    3. make install

After that user should find libusb-1.0.a in local libraries catalog /usr/local/lib

It might be necessary to install required packages, see README.

Building systemd (libudev)

  1. Download most recent source from http://www.freedesktop.org/software/systemd/
  2. Extract
  3. Go to project directory
  4. Build library (additional packages might be required I had to build libdbus, see README):
    1. ./configure
    2. make
    3. make install

TODO

udev Rules

TODO

Start with creating a rule for device

nano /etc/udev/rules.d/usb_led_bulk.rules
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1cbe", ATTRS{idProduct}=="0003", GROUP="users", MODE="0666", NAME="rgb_led"

This will allow non-root users to access the device. Also device will be accessible through path /dev/rgb_led

Restart udev service for changes to take effect /etc/init.d/udev restart

References

1)
Vendor ID
2)
Product ID
projects/qtusb.1370118996.txt.gz · Last modified: 2013/06/01 22:36 by mkucia