Home Contents | # USBHID | Rawio | Core4 Lua Event System

## DESCRIPTION

As of SDK version 1.7.0 and later, the V4 and V4½ support USB HID device mode for connecting to a PC.

The controllers have one USB device port implemented as a USB Type B socket.

After startup, the port is disabled and any connection to a PC or other host will be ignored.

To enable the port, it is necessary to configure the USB properties.

At the end of this document some example initialization code is shown.

The example has the VID/PID identifiers replaced by asterisks. You will have to provide your own registered vendor ID and make up a unused product ID. Also the vendor and product names need to be replaced by proper values. Note how the code passes the controller's serial number on to USB.

The HID device sets up the fastest possible polling interval (1ms). The HID standard limits the allowed packet sizes to 64 bytes even if the controller hardware could support more. We're playing it safe here and stick to 64 bytes.

The example HID report descriptor looks complicated. All this does is telling the PC to expect messages of constant 64 bytes going in both directions. Unfortunately HID does not support variable message lengths.

The example code also enables a special packet mode provided by the Core4 SDK which helps to overcome the fixed message length requirement. More below.

Once the port has been initialized like this, a cable connection to a PC will make the PC detect the controller as a HID device. Typically, a windows machine will briefly display a balloon popup the first time the connection is plugged in. The popup shows the vendor and product strings, so it's a good idea to put something meaningful there.

After initialization, the port is ready for data transfer.

To send data to the host, simply write() it to the handle. When not using the packet mode, then the written data must be exactly the length as specified in the insize parameter or an error will be returned.

When the host sends data to the controller, it can be read() from the device handle. Each read call will return one message. The usual event signalling is also available.

The SDK can buffer a few messages arriving from the host if the application does not read them quickly enough. Once the buffer is full, the controller will stop accepting data from the host until the application reads some messages from the buffer. (Technically, the controller NAKs any further messages until it can accept more.)

For the host side, there is free framework available for download from the web going by the name HIDAPI.

## PACKET MODE

To enable support for variable length messages, there is a built in optional packet mode.

The packet mode will wrap any data that is written to the handle like this:

LEN DATA... CRCH CRCL

The data is prefixed by a length byte, which indicates the number of bytes following (including the CRC). At the end, the system adds a CRC16.

In packet mode, therefore the available packet size is reduced by three.

When sending to a PC, the code can write any data length to the handle, up to the length limit. The system will automatically add length byte and CRC. If necessary, the data is then padded up with zeroes up to the fixed message size before sending it to the PC.

The other direction, host to device, uses the same packet format. The system strips away the length byte and CRC and returs a variable size result to the read() call. If an error is detected (e.g. wrong CRC), the error is recorded in the message receive buffer instead. The read call will return EPROTO once for each error.

## CRC CALCULATION IN PACKET MODE

The CRC is calculated using the algorithm provided by cipher.crc16(). The starting value is 0xFFFF. The length byte is NOT included in the CRC.

On transmit, the system feeds all data bytes through the CRC, followed by two zero bytes. The result is appended to the message, high byte first.

On receive, the system again feeds all data bytes through the CRC, and then also feeds the two received CRC bytes in the order received. For a correct packet, the result will be zero.

## EXAMPLE CODE

function usb_hid_init() local handle, errtxt = rawio.open("/dev/usb/devhid") if (not handle) then print("USB Device port failed to open") print(errtxt) return nil end -- Configure a non-standard HID device with 64/64 byte block size handle:usbInfo{ vid = 0x****, pid = 0x****, vendor = "ACME USB Devices Inc.", product = "ACME HID Device", serial = os.uname().ssn, } handle:usbHidConfig{ insize = 64, outsize = 64, interval = 1, packetmode = true, report = "\x06\x00\xFF" -- USAGE_PAGE (Vendor Specific) "\x09\x01" -- USAGE (Vendor Usage 1) "\xA1\x01" -- COLLECTION (Application) "\x06\x00\xFF" -- USAGE_PAGE (Vendor Specific) "\x09\x01" -- USAGE (Vendor Usage 1) "\x15\x00" -- LOGICAL_MINIMUM (0) "\x26\xFF\x00" -- LOGICAL_MAXIMUM (255) "\x75\x08" -- REPORT_SIZE (8) "\x95\x40" -- REPORT_COUNT (64) "\x81\x02" -- INPUT (Data"Var,Abs) "\x09\x01" -- USAGE (Vendor Usage 1) "\x75\x08" -- REPORT_SIZE (8) "\x95\x40" -- REPORT_COUNT (64) "\x91\x02" -- OUTPUT (Data,Var,Abs) "\xC0", -- END_COLLECTION } return handle end

## SEE ALSO

rawio:usbHidConfig, rawio:usbInfo