Host Software

Interface based on libusb or libusb-win32

Libusb offers a simple interface for sending and receiving data on all types of endpoints.

Finding and opening the device

Before you can communicate a device, you must first find it on the bus. Devices are usually identified by their USB IDs (Product- and Vendor-ID). If you use Objective Development's shared IDs, the device is identified by the USB IDs AND by the textual vendor- and product description.

The following code iterates over all devices:

struct usb_bus      *bus;
struct usb_device   *dev;
 
usb_init();             // initialize libusb
usb_find_busses();
usb_find_devices();
for(bus=usb_get_busses(); bus; bus=bus->next){
    for(dev=bus->devices; dev; dev=dev->next){
        // use dev->descriptor to identify device here
    }
}

Once you have identified your device, open it with usb_open(dev). An example code which also checks the textual vendor and product descriptions can be found in the PowerSwitch code. See function usbOpenDevice() in the command line tool's source code.

If you want to use other endpoints than the default control endpoint 0, you must set a configuration and claim an interface. The default descriptors in V-USB define only one configuration and one interface. You would therefore use:

usb_dev_handle *handle;
 
if(!usbOpenDevice(&handle, VENDOR_ID, NULL, PRODUCT_ID, NULL))
    abortWithDeviceWasNotFondError();
usb_set_configuration(handle, 1);
usb_claim_interface(handle, 0);

Sending and receiving data on Control endpoint 0

Control data is sent and received with the function usb_control_msg(). To request data from the device, use

int realNumBytes = usb_control_msg(
        handle,             // handle obtained with usb_open()
        USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, // bRequestType
        MY_REQUEST_ID,      // bRequest
        value,              // wValue
        index,              // wIndex
        buffer,             // pointer to destination buffer
        numBytesRequested,  // wLength
        timeoutInMilliseconds
    );

usb_control_msg() returns the number of bytes actually received from the device. You may pass any information you like in the parameters value and index to the device.

Sending a block of data to the device is similar:

int realNumBytes = usb_control_msg(
        handle,             // handle obtained with usb_open()
        USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, // bRequestType
        MY_REQUEST_ID,      // bRequest
        value,              // wValue
        index,              // wIndex
        buffer,             // pointer to buffer containing data
        numBytesInBuffer,   // wLength
        timeoutInMilliseconds
    );

The function returns the number of bytes accepted by the device.

Sending and receiving data on Interrupt- and Bulk-endpoints

Interrupt- and Bulk endpoints have simple read/write semantics. Just use usb_interrupt_write() or usb_bulk_write() to write data and usb_interrupt_read() or usb_bulk_read() to read data.

Example: To wait for interrupt data on interrupt endpoint 1, one would use:

char buffer[16];
numBytes = usb_interrupt_read(
        handle,             // handle obtained with usb_open()
        USB_ENDPOINT_IN | 1,// identifies endpoint 1
        buffer,             // data buffer
        sizeof(buffer),     // maximum amount to read
        timeoutInMilliseconds
    );

Non-blocking variants of read and write are not available in libusb. You must use multi-threading instead.

HID interface on Windows

The basic steps are the same as with libusb when using the native Windows API for HID devices: You must first find the device, then you can query it's features or send and receive data. An example of using this API can be found in the Automator project in the file usb-windows.c. You must include the system headers windows.h, setupapi.h, hidsdi.h and ddk/hidpi.h to use this interface.

Windows sends output reports as interrupt-out data if possible and expects input reports on the interrupt-in endpoint. Only feature reports are always communicated as control transfers on endpoint 0. It is therefore advisable to use feature reports for your data. We will only cover feature reports here.

Note: You can also access HID class devices with libusb-win32 on Windows, as long as you only need access to control endpoint 0.

Finding and opening the device

The code for opening a HID device on Windows looks like this:

GUID                                hidGuid;        /* GUID for HID driver */
HDEVINFO                            deviceInfoList;
SP_DEVICE_INTERFACE_DATA            deviceInfo;
char                                buffer[16324];
SP_DEVICE_INTERFACE_DETAIL_DATA     *deviceDetails = (void *)buffer;
DWORD                               size;
int                                 i;
HANDLE                              handle = INVALID_HANDLE_VALUE;
HIDD_ATTRIBUTES                     deviceAttributes;
 
HidD_GetHidGuid(&hidGuid);
deviceInfoList = SetupDiGetClassDevs(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
deviceInfo.cbSize = sizeof(deviceInfo);
for(i=0;;i++){
    if(handle != INVALID_HANDLE_VALUE){
        CloseHandle(handle);
        handle = INVALID_HANDLE_VALUE;
    }
    if(!SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo))
        break;  /* no more entries */
    deviceDetails->cbSize = sizeof(*deviceDetails);
    size = sizeof(buffer);
    SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL);
    /* attempt opening for R/W -- we don't care about devices which can't be accessed */
    handle = CreateFile(deviceDetails->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    if(handle != INVALID_HANDLE_VALUE){
        deviceAttributes.Size = sizeof(deviceAttributes);
        HidD_GetAttributes(handle, &deviceAttributes);
        if(deviceAttributes.VendorID == MY_VID && deviceAttributes.ProductID == MY_PID)
            break;  /* we have found the device we are looking for! */
    }
}
SetupDiDestroyDeviceInfoList(deviceInfoList);

This code sets handle either to a valid handle for the driver or leaves it as INVALID_HANDLE_VALUE if no matching device was found. This is a simplified example. For real-world code see the file usb-windows.c in the Automator project.

Sending reports

Feature reports are simply sent with HidD_SetFeature(). However, Windows checks the structure of your data. You must therefore be careful to stay within the limits of your HID Report descriptor.

Example:

char buffer[REPORT_SIZE + 1];
 
buffer[0] = MY_REPORT_ID;
HidD_SetFeature(handle, buffer, REPORT_SIZE + 1);

If you use report IDs (which is necessary if you have more than one report type), the first byte must always be the report ID.

Requesting reports

Feature reports are requested with HidD_GetFeature():

char buffer[REPORT_SIZE + 1];
 
buffer[0] = MY_REPORT_ID;
HidD_GetFeature(handle, buffer, REPORT_SIZE + 1);

If you use report IDs, you must pass the ID in the first byte of buffer.

Receiving report streams

No documentation yet. If you know how to send and receive data asynchronously on Windows, please update this section.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License