Attaching to devices
In the Linux Kernel, drivers provide callbacks to their own probe() and disconnect() functions. These are used to connect or disconnect from devices for the purposes of I/O. When connecting on QNX, use the insertion() and removal() callbacks, which serve a similar purpose.
Linux probe and disconnect callbacks recap
static int probe(struct usb_interface *interface, const struct usb_device_id *id);
static void disconnect(struct usb_interface *interface);
The probe() callback is ran once for any matching device on registration of the driver, and once anytime a matching device is inserted. It is provided with the default interface and device id for processing.
The probe() function is required to return a value indicating whether a connection to the device was successful or rejected for some reason (either failing to connect or an incompatible device/interface). As is standard, 0 represents EOK and any other value should be an error code.
The disconnect() callback is ran once when a device is removed. It should clean up any additional allocated data for the device, and call usb_deregister_dev() to deregister the device.
QNX insertion and removal callbacks
void insert(struct usbd_connection *conn, usbd_device_instance_t *inst);
void remove(struct usbd_connection *conn, usbd_device_instance_t *inst);
The insert() callback is ran once for any matching device on connection of the driver, and once anytime a matching device is inserted. It's provided with the connection and device instance.
Unlike probe(), insert() doesn't return any indication on whether
attachment to the device was successful - calling an attach function (as discussed later in this section) is enough for io-usb
to determine whether the
attachment was successful.
The remove() function serves the same purpose as Linux's disconnect, except it's also provided with the connection in addition to the device instance. It should also clean up any additional data, and call detach to detach the device.
Finally, it's also important to note that these can be set to NULL if asynchronous I/O is not to be supported by this driver.
To cover some QNX-specific details, the following section looks at the available attach and detach functions and structures:
usbd_attach() and usbd_detach()
/* QNX */
/* Attach/Detach Functions */
int usbd_attach( struct usbd_connection *connection,
usbd_device_instance_t *instance,
size_t extra,
struct usbd_device **device );
int usbd_detach( struct usbd_device *device );
/* See Below */
void *usbd_device_extra( struct usbd_device *device );
struct usbd_device *usbd_device_lookup(
struct usbd_connection *connection,
usbd_device_instance_t *instance );
/* usbd_device_instance_t structure */
typedef struct usbd_device_instance {
uint8_t path;
uint8_t devno;
uint16_t generation;
usbd_device_ident_t ident;
uint32_t config;
uint32_t iface;
uint32_t alternate;
} usbd_device_instance_t;
The usbd_attach() function attaches to a device in a specified connection and uses the following parameters:
- connection
A valid connection set up by usbd_connect().
- instance
A
usbd_device_instance_t
set to specify a specific device. Refer to the examples below for reference.- extra
Can be set above 0 to allocate some amount of shared memory that will be associated with the device. This is optional and can be set to 0.
- device
The address where a reference to the device instance can be stored. This handle will be treated as an opaque pointer.
The usbd_detach() function needs to be called and passed the corresponding device reference when ceasing I/O to a device, and must be called on a device before closing a connection via usbd_disconnect()
The usbd_device_extra() function returns a pointer to the shared memory allocated by usbd_attach() when passed a value greater than 0 into extra.
The function usbd_device_lookup() is a convenience macro that finds a device reference given its instance pointer and a connection (or NULL if one does not exist). This is primarily used in the removal callback.
The following examples cover what these fucntions might look like in a hotswapping implementation, alongside some convenience macros on each system:
/* Linux requires a class driver structure for i/o and registration.
* this will be recapped later - for now, treat this as opaque. */
static struct usb_class_driver class_driver;
/* In practice, you'd likely want more storage for devices */
static struct usb_device *device;
static int probe(struct usb_interface *interface, const struct usb_device_id *id){
device = interface_to_usbdev(interface);
int ret;
if((ret = usb_register_dev(interface, &class_driver)) < 0){
/* Error Handling */
}else{
/* Successfully attached, do additional processing */
}
return ret;
}
static void disconnect(struct usb_interface *interface){
usb_deregister_dev(interface, &class_driver);
}
void insert(struct usbd_connection *conn, usbd_device_instance_t *inst){
/* Device Reference */
struct usbd_device* device = NULL;
/* usbd attachment */
int ret = usbd_attach(conn, inst, 0, &device);
if(ret != EOK){
//Error Handling: This will occur if the device is already attached to another Exclusive Access I/O connection
}
/* Set up I/O here - See Parsing the USB Stack and Performing I/O at Endpoints */
}
void remove(struct usbd_connection *conn, usbd_device_instance_t *inst){
/* Any cleanup here */
/* Unallocate any additional resources here */
/* Detach the device (this also closes any associated pipes) */
// Detach takes struct usbd_device* which can be grabbed with a convenience macro
struct usbd_device * device;
device = usbd_device_lookup(conn, inst);
if(device == NULL){
// Handle a case where this device wasn't attached.
}else{
usbd_detach(device);
}
}
Attaching directly on QNX
If callbacks are not set, you can still access device configurations in
read-only mode by directly parsing the USB stack for devices. In this
case, you need to create a usbd_device_instance_t
.
In the following example, you scan for devices on all bus and device number entries and attempt connections:
Shared access example
// ... In Main Function, Connection Setup
uint8_t busno, devno;
usbd_device_instance_t instance;
for (busno = 0; busno < 10; ++busno) {
for (devno = 0; devno < 64; ++devno) {
memset(&instance, USBD_CONNECT_WILDCARD, sizeof(usbd_device_instance_t));
instance.path = busno, instance.devno = devno;
if (usbd_attach(connection, &instance, 0, &device) == EOK) {
// ... Parsing code
}
}
}
// ... Shutdown