Skip to main content

USB Driver Framework

Latest Version: 1.0.0

Electric Imp’s USB Driver Framework is an easily extensible foundation for the delivery of USB device drivers. It is implemented as a code library, but comprises a number of classes and data structures, which are described below.

If you are developing an application which makes use of one or more existing drivers, please see the USB Application Development Guide for instructions on how to make use of the USB Driver Framework in your code.

If you are developing a driver, please see the USB Driver Development Guide.

You can view the latest version of the library’s source code on GitHub. Click here to see information on other versions of this library.

To use this library, add #require "USB.device.lib.nut:1.0.0" to the top of your device code.

USB.Host Class Usage

This is the main interface you use to start working with USB devices and drivers.

If you have more then one USB port in your product or development board, you should create a USB.Host instance for each of them.

USB.Host(usb, drivers[, autoConfigPins])

This method instantiates the USB.Host class. USB.Host is a wrapper over the native imp API USB implementation.

It should be instantiated only once per physical port for any application. There are some Electric Imp boards which do not have a USB port, therefore an exception will be thrown on any attempt to instantiate USB.Host in code running on these boards.

Note When using the USB Drivers Framework, do not access the imp API hardware.usb object directly.

Parameters

Parameter Type Required Description
usb Object Yes The imp API usb object representing a Universal Serial Bus (USB) interface
drivers Array of USB.Driver objects Yes An array of pre-defined driver classes
autoConfigPins Boolean No Indicate whether to configure imp005 pins R and W, which must be configured for USB to work on the imp005. Default: true

Example

#require "USB.device.lib.nut:1.0.0"

class MyCustomDriver1 extends USB.Driver {
  ...
}

class MyCustomDriver2 extends USB.Driver {
  ...
}

// Instantiate the USB host and register our drivers
usbHost <- USB.Host(hardware.usb, [MyCustomDriver1, MyCustomDriver2]);

USB.Host Class Methods

setDriverListener(listener)

This method instructs the host to begin listening for driver events. The application will then be notified via the supplied listener function if a driver is started or stopped.

Passing in null clears any previously assigned listener.

Parameters

Parameter Type Required Description
listener Function Yes A function to be called when a driver event occurs

Listener Function Parameters

Parameter Type Description
eventType String The driver event type: USB_DRIVER_STATE_STARTED or USB_DRIVER_STATE_STOPPED
driver USB.Driver instance The driver triggering the event

Return Value

Nothing.

Example

usbHost.setDriverListener(function (eventType, driver) {
  switch (eventType) {
    case USB_DRIVER_STATE_STARTED:
      server.log("Driver found and started " + (typeof driver));
      break;
    case USB_DRIVER_STATE_STOPPED:
      server.log("Driver stopped " + (typeof driver));
      break;
  }
});

setDeviceListener(listener)

This method instructs the host to begin listening for runtime device events, such as plugging or unplugging a peripheral. The application will then be notified via the supplied listener function.

Passing in null clears any previously assigned listener.

Parameters

Parameter Type Required Description
listener Function Yes A function to be called when a device event occurs

Listener Function Parameters

Parameter Type Description
eventType String The device event type: USB_DEVICE_STATE_CONNECTED or USB_DEVICE_STATE_DISCONNECTED
device USB.Device instance The device triggering the event

Return Value

Nothing.

Example

// Subscribe to USB connection events
usbHost.setDeviceListener(function (eventType, device) {
  switch (eventType) {
    case USB_DEVICE_STATE_CONNECTED:
      server.log("New device found");
      break;
    case USB_DEVICE_STATE_DISCONNECTED:
      server.log("Device detached");
      break;
  }
});

reset()

This method resets the USB host. The effect of this action is similar to the physical reconnection of all connected devices. It disables USB, and cleans up all drivers and devices. This results in the execution of any corresponding driver and device listeners. All devices will have a new device object instances and different addresses after a reset.

It can be used by a driver or application in response to an unrecoverable error, such as a timed out bulk transfer, or a halt condition encountered during control transfers.

Return Value

Nothing.

Example

class MyCustomDriver extends USB.Driver {
  ...
}

host <- USB.Host(hardware.usb, [MyCustomDriver]);

host.setDeviceListener(function(eventName, eventDetails) {
  if (eventName == USB_DEVICE_STATE_CONNECTED && host.getAttachedDevices().len() != 1) {
    server.log("Only one device could be attached");
  }
});

// Reset after two seconds
imp.wakeup(2, function() {
  host.reset();
}.bindenv(this));

getAttachedDevices()

This method returns a list of attached devices.

Return Value

Array of USB.Device objects.

USB.Device Class Usage

This class represents attached USB devices. Please refer to the USB specification for details of USB devices' descriptions.

Typically, applications don't use device objects directly, but they can use by drivers to acquire required endpoints. Neither applications nor drivers should explicitly create USB.Device objects — they are instantiated by the USB Drivers Framework automatically.

When using the USB Drivers Framework, all management of USB device configurations, interfaces and endpoints must go through the device object rather than via the imp API hardware.usb object.

USB.Device Class Methods

getDescriptor()

This method returns the device’s descriptor. It throws an exception if the device is not currently connected.

Return Value

Table — the device descriptor.

getVendorId()

This method returns the device vendor ID. It throws an exception if the device is not currently connected.

Return Value

String — the device’s vendor ID.

getProductId()

This method returns the device product ID. It throws an exception if the device is not currently connected.

Return Value

String — the device’s product ID.

getAssignedDrivers()

This method returns an array of drivers for the attached device. It throws an exception if the device is not currently connected.

Each USB device may provide a number of interfaces which could be supported by a one or more drivers. For example, a keyboard with an integrated touchpad could have keyboard and touchpad drivers assigned.

Return Value

Array — a set of USB.Driver objects.

getEndpointZero()

This method returns a proxy for the device’s Control Endpoint 0. The endpoint 0 is a special type of endpoint that implicitly exists for every device. The method throws an exception if the device is not currently connected.

Return Value

USB.ControlEndpoint — the zero endpoint.

USB.ControlEndpoint Class Usage

This class represents USB control endpoints. Class instances are managed by instances of USB.Device and should be acquired by calling getEndpointZero() on a USB.Device instance.

Note Neither applications nor drivers should explicitly create USB.ControlEndpoint objects — they are instantiated by the USB Drivers Framework automatically.

Example

The following code sends a request to the control endpoint 0 to reset the state (clear error codes) of a functional endpoint specified by functionalEndpointAddress:

device
  .getEndpointZero()
  .transfer(USB_SETUP_RECIPIENT_ENDPOINT | USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD,
            USB_REQUEST_CLEAR_FEATURE,
            0,
            functionalEndpointAddress);

USB.ControlEndpoint Class Methods

transfer(requestType, request, value, index[, data])

This is a generic method for transferring data over a control endpoint.

Parameters

Parameter Type Required Description
requestType Integer Yes USB request type. Please see Control Endpoint Request Types for more details
request Integer Yes The specific USB request. Please see Control Endpoint Requests for more details
value Integer Yes A value determined by the specific USB request
index Integer Yes An index value determined by the specific USB request
data Blob No Optional storage for incoming or outgoing payload. Default: null

getEndpointAddr()

This method returns the endpoint address, which is required by a device control operation performed over control endpoint 0.

Return Value

Integer — the endpoint address.

USB.FuncEndpoint Class Usage

This class represents all non-control endpoints, ie. bulk, interrupt and isochronous endpoints. It is managed by the USB.Device class and should be acquired only through USB.Device instances. Neither applications nor drivers should explicitly create USB.FuncEndpoint objects — they are instantiated by the USB Drivers Framework automatically.

USB.FuncEndpoint Class Methods

write(data, onComplete)

This method asynchronously writes data through the endpoint. It throws an exception if the endpoint is closed or doesn't support USB_DIRECTION_OUT.

Parameters

Parameter Type Required Description
data Blob Yes Payload data to be sent through this endpoint
onComplete Function Yes A function to be called when the transfer is complete

onComplete Parameters

Parameter Type Description
endpoint USB.FuncEndpoint instance The endpoint used
state Integer USB transfer state. Please see USB Transfer States for more details
data Blob The payload data being sent
length Integer The length of the written data

Return Value

Nothing.

Example

class MyCustomDriver extends USB.Driver {
  constructor(device, interfaces) {
    try {
      local payload = blob(16);
      local endpoint = interfaces[0].endpoints[1].get();
      endpoint.write(payload, function(ep, state, data, len) {
        if (len > 0) server.log(len + " bytes sent");
      }.bindenv(this));
    }
    catch(err) {
      server.error(err);
    }
  } // constructor

  function match(device, interfaces) {
    return MyCustomDriver(device, interfaces);
  }
}

read(data, onComplete)

This method asynchronously reads data from the endpoint. It throws an exception if the endpoint is closed, has an incompatible type, or is already busy.

The method sets an upper limit of five seconds for any command for the bulk endpoint to be processed.

Parameters

Parameter Type Required Description
data Blob Yes Blob to read data into
onComplete Function Yes A function to be called when the transfer is complete

onComplete Parameters

Parameter Type Description
endpoint USB.FuncEndpoint instance The endpoint used
state Integer USB transfer state. Please see USB Transfer States for more details
data Blob The payload data being received
length Integer The length of the written data

Return Value

Nothing.

Example

class MyCustomDriver extends USB.Driver {
  constructor(device, interfaces) {
    try {
      local payload = blob(16);
      local endpoint = interfaces[0].endpoints[0].get();
      endpoint.read(payload, function(ep, state, data, len) {
        if (len > 0) server.log(len + " bytes read");
      }.bindenv(this));
    }
    catch(err) {
      server.error(err);
    }
  } // constructor

  function match(device, interfaces) {
    return MyCustomDriver(device, interfaces);
  }
}

getEndpointAddr()

This method returns the endpoint address, which is required by a device control operation performed over control endpoint 0.

Return Value

Integer — the endpoint address.

USB.Driver Class Usage

This class is the base for all drivers that are developed using the USB Drivers Framework. It contains one method, match() that must be be implemented by every USB driver, and two further methods, release() and _typeof(), which are optional but recommended.

Note Applications should not explicitly create USB.Driver objects — they are instantiated by the USB Drivers Framework automatically.

USB.Driver Class Methods

match(deviceObject, interfaces)

This method is used to check if the driver can support the specified device and its exposed interfaces.

If the driver can support the device, the method should return a new driver instance or an array of driver instances (when multiple interfaces are supported and a driver instance is created for each of them).

If the driver can’t support the device, return null.

The driver-device matching procedure can be based on checking Vendor ID, Product ID, device class and/or subclass, and/or interfaces.

Parameters

Parameter Type Required Description
device USB.Device instance Yes The object representing the attached device
interfaces Array of tables Yes A set of tables each of which describes the interfaces supported by the attached device

Return Value

USB.Driver instance, array of USB.Driver instances, or null.

release()

This method releases all of the resources instantiated by the driver. It should be used by the driver to clean up its resources and free external resources if necessary.

It is called by the USB Drivers Framework when a USB device is disconnected.

Do not access USB.Driver or endpoint instances from any callback once release() is called, as they may have already been partially released. Any attempts to access these objects and their members from the callback may therefore throw exceptions.

_typeof()

This metamethod is used to return a class name when typeof <instance> is invoked. It can be used to identify the driver instance type as runtime: for example, for debugging purposes.

Example

local usbHost = USB.Host(hardware.usb, [MyCustomDriver1, MyCustomDriver2]);

usbHost.setDriverListener(function(eventName, eventDetails) {
  if (eventName == "started" && typeof eventDetails == "MyCustomDriver2") {
    server.log("MyCustomDriver2 initialized");
  }
});

USB Drivers Framework Structures

Endpoint Constants

These are constants that may be useful for endpoint search functions.

Constant Name Value Description
USB_ENDPOINT_CONTROL 0x00 Control Endpoint type value
USB_ENDPOINT_ISOCHRONOUS 0x01 Isochronous Endpoint type value
USB_ENDPOINT_BULK 0x02 Bulk Endpoint type value
USB_ENDPOINT_INTERRUPT 0x03 Interrupt Endpoint type value
USB_ENDPOINT_TYPE_MASK 0x03 A mask value that covers all endpoint types
USB_DIRECTION_OUT 0x00 A bit value that indicates OUTPUT endpoint direction
USB_DIRECTION_IN 0x80 A bit value that indicates INPUT endpoint direction
USB_DIRECTION_MASK 0x80 A mask to extract the endpoint direction from an endpoint address

Control Endpoint Request Types

These are possible values for the ControlEndpoint.transfer() method’s parameter requestType.

Constant Name Value Description
USB_SETUP_HOST_TO_DEVICE 0x00 Transfer direction: host to device
USB_SETUP_DEVICE_TO_HOST 0x80 Transfer direction: device to host
USB_SETUP_TYPE_STANDARD 0x00 Type: standard
USB_SETUP_TYPE_CLASS 0x20 Type: class
USB_SETUP_TYPE_VENDOR 0x40 Type: vendor
USB_SETUP_RECIPIENT_DEVICE 0x00 Recipient: device
USB_SETUP_RECIPIENT_INTERFACE 0x01 Recipient: interface
USB_SETUP_RECIPIENT_ENDPOINT 0x02 Recipient: endpoint
USB_SETUP_RECIPIENT_OTHER 0x03 Recipient: other

Control Endpoint Requests

These are possible values for the ControlEndpoint.transfer() method’s parameter request.

Constant Name Value Description
USB_REQUEST_GET_STATUS 0 Get status
USB_REQUEST_CLEAR_FEATURE 1 Clear feature
USB_REQUEST_SET_FEATURE 3 Set feature
USB_REQUEST_SET_ADDRESS 5 Set address
USB_REQUEST_GET_DESCRIPTOR 6 Get descriptor
USB_REQUEST_SET_DESCRIPTOR 7 Set descriptor
USB_REQUEST_GET_CONFIGURATION 8 Get configuration
USB_REQUEST_SET_CONFIGURATION 9 Set configuration
USB_REQUEST_GET_INTERFACE 10 Get interface
USB_REQUEST_SET_INTERFACE 11 Set interface
USB_REQUEST_SYNCH_FRAME 12 Sync frame

USB Transfer States

These are possible values for the ControlEndpoint.transfer() method’s parameter state.

Not all of the non-zero state values indicate errors. For example, USB_TYPE_FREE (15) and USB_TYPE_IDLE (16) are not errors. The range of possible error values you may encounter will depend on which type of USB device you are connecting. More details here.

Constant Name Value
OK 0
USB_TYPE_CRC_ERROR 1
USB_TYPE_BIT_STUFFING_ERROR 2
USB_TYPE_DATA_TOGGLE_MISMATCH_ERROR 3
USB_TYPE_STALL_ERROR 4
USB_TYPE_DEVICE_NOT_RESPONDING_ERROR 5
USB_TYPE_PID_CHECK_FAILURE_ERROR 6
USB_TYPE_UNEXPECTED_PID_ERROR 7
USB_TYPE_DATA_OVERRUN_ERROR 8
USB_TYPE_DATA_UNDERRUN_ERROR 9
USB_TYPE_UNKNOWN_ERROR 10
USB_TYPE_UNKNOWN_ERROR 11
USB_TYPE_BUFFER_OVERRUN_ERROR 12
USB_TYPE_BUFFER_UNDERRUN_ERROR 13
USB_TYPE_DISCONNECTED 14
USB_TYPE_FREE 15
USB_TYPE_IDLE 16
USB_TYPE_BUSY 17
USB_TYPE_INVALID_ENDPOINT 18
USB_TYPE_TIMEOUT 19
USB_TYPE_INTERNAL_ERROR 20

USB Drivers Framework Event Structures

The USB Drivers Framework uses tables named descriptors which contain a description of the attached device, its interfaces and endpoint. endpoint and interface descriptors are used only at the driver probing stage, while device descriptors may be acquired from a USB.Device instance.

Device Descriptor

A device descriptor contains whole the device specification in addition to the Vendor ID and Product Id. The descriptor table has the following keys:

Key Type Description
usb Integer The USB specification to which the device conforms. It is a binary coded decimal value. For example, 0x0110 is USB 1.1
class Integer The USB class assigned by the USB-IF. If 0x00, each interface specifies its own class. If 0xFF, the class is vendor specific
subclass Integer The USB subclass (assigned by the USB-IF)
protocol Integer The USB protocol (assigned by the USB-IF)
vendorid Integer The vendor ID (assigned by the USB-IF)
productid Integer The product ID (assigned by the vendor)
device Integer The device version number as BCD
manufacturer Integer Index to a string descriptor containing the manufacturer string
product Integer Index to a string descriptor containing the product string
serial Integer Index to a string descriptor containing the serial number string
numofconfigurations Integer The number of possible configurations

Interface Descriptor

When probed, a driver’s match() method receives two objects: a USB.Device instance and an array of the interfaces exposed by this device. Each interface is presented as a descriptor table with the following keys:

Key Type Description
interfacenumber Integer The number representing this interface
altsetting Integer The alternative setting of this interface
class Integer The interface class
subclass Integer The interface subclass
protocol Integer The interface class protocol
interface Integer The index of the string descriptor describing this interface
endpoints Array of table The endpoint descriptors
find Function Auxiliary function to search for endpoints with specified attributes
getDevice Function Returns the USB.Device instance that is the owner of this interface

find()

The interface descriptor’s find() function signature is as follows:

function(endpointType, endpointDirection) {
  ...
}
Parameter Type Accepted Values
endpointType Integer USB_ENDPOINT_CONTROL, USB_ENDPOINT_BULK, USB_ENDPOINT_INTERRUPT
endpointDirection Integer USB_DIRECTION_IN, USB_DIRECTION_OUT

It returns an instance of either the ControlEndpoint class or the FuncEndpoint class, or null if no endpoints were found.

Endpoint Descriptor

Each endpoints table contains the following keys:

Key Type Description
address Integer bitfield The endpoint address:
D0-3: Endpoint number
D4-6: Reserved
D7: Direction (0 out, 1 in)
attributes Integer bitfield Transfer type:
00: control
01: isochronous
10: bulk
11: interrupt
maxpacketsize Integer The maximum size of packet this endpoint can send or receive
interval Integer Only relevant for Interrupt In endpoints
get Function A function that returns an instance of either USB.FuncEndpoint or USB.ControlEndpoint depending on information stored in the attributes and address fields

Release History

The Electric Imp Dev Center documents the latest version of the library. For past versions, please see the Electric Imp public GitHub repos listed below.

Version Source Code Notes
0.1.0 GitHub Initial pre-release
1.0.0 GitHub Initial release

License

This library is licensed under the MIT License.