Changes between Initial Version and Version 1 of USBDrivers


Ignore:
Timestamp:
2018-03-01T11:40:58Z (6 years ago)
Author:
Ondra Hlavatý
Comment:

Initialize from the documentation of HelUSB3 project

Legend:

Unmodified
Added
Removed
Modified
  • USBDrivers

    v1 v1  
     1{{{#!comment
     2        Credit for textual content goes to Petr Mánek.
     3}}}
     4
     5== Basics
     6
     7This section gives details on the basic structure of USB device
     8drivers, their role in the system and components they usually interact with.
     9
     10=== Framework
     11
     12USB device drivers use the generic //Device Driver Framework//
     13available in HelenOS. Because all USB drivers have similar initialization
     14routines, a thin layer – specific to USB devices – was added above the generic
     15one. This layer mainly serves as a middleware for easy communication with USB
     16host controller drivers, performing USB specific resource management and
     17enabling device drivers to initialize endpoint pipes and interact with USB
     18devices. For those reasons, USB device drivers are recommended to be linked
     19with libdrv and libusbdev, which contain both aforementioned layers
     20respectively.
     21
     22It is expected that USB device drivers specify in advance not only their
     23relevant match identifiers, which are used by the Device Manager to pair new
     24devices with available drivers, but also all endpoints, which shall be present
     25on the device through a USB driver structure. Later when a new device is found,
     26a specialized device structure is prepared and pipe abstractions are
     27initialized.
     28
     29Device drivers live the same life cycle as any other drivers controlled by the
     30Device Manager. A quick summary follows:
     31
     321. The driver is started at the convenience of the Device Manager if and when a compatible device is found. At startup, the driver registers with the USB framework, which in turn registers also with the Device Driver Framework.
     332. During its lifetime, the driver receives callbacks from the USB Framework, informing it about relevant device events. On the basis of these events, the driver then communicates with the device and exposes various interfaces to other tasks in the system. At this point, the controlled device usually becomes visible and useful to the user.
     343. When there is no more need for the driver to run (i.e. no devices to control), the Device Manager may terminate the driver to save resources.
     35
     36In the Device Driver Framework, drivers are the //consumers// of devices and
     37providers of //functions//. This paradigm allows them to expose an unlimited number
     38of nodes (representing logical or physical units) for every device they
     39control. The same basic principle holds for USB drivers as well.
     40
     41=== Device Callbacks
     42As explained in the previous section, USB device drivers are informed about
     43relevant device events by asynchronous callbacks from the USB framework. To
     44simplify usage, these callbacks are identical to those of the Device Driver
     45Framework:
     46
     47 Add Device:: This event notifies the driver that a new device has been
     48             discovered and matched to it. From this point on, the driver is
     49             allowed to communicate with the device in order to configure it and expose its
     50             functions to the rest of the system. Further communication with the device will
     51             likely depend on remote calls originating from other system tasks utilizing the
     52             exposed interface.
     53 Remove Device:: This event instructs the driver to immediately disallow new
     54                user operations on a device, terminate all currently running operations in
     55                a timely manner, and hand device control back to the system, as the device will
     56                likely be physically removed from the bus in the foreseeable future.
     57 Device Gone:: This event informs the driver that a device has been physically
     58              disconnected from the system without a prior Remove Device event. Since the
     59              device is no longer reachable, the driver is to force interrupt all user
     60              operations, which were running at the time of receiving the event and
     61              report failure to the callers.
     62 Offline Function:: By receiving this event, the driver is asked by the system
     63                   to explicitly transition a specific function exposed by one of its
     64                   controlled devices into the Offline state. The meaning of such transition
     65                   might depend on the interpretation of the function. For more information,
     66                   see the [wiki:DeviceDrivers the device drivers guide].
     67 Online Function:: By receiving this event, the driver is asked by the system to
     68                  explicitly transition a specific function exposed by one of its controlled
     69                  devices into the Online state. Again, the meaning of such transition might
     70                  depend on the interpretation of the function. For more information, see the
     71                  [wiki:DeviceDrivers the device drivers guide].
     72
     73The listed events are delivered to device drivers through function calls to
     74event handlers specified in the main USB driver operations structure (see
     75the following minimal example). The order of event delivery models the
     76physical lifecycle of the device within the system. For instance, it is not
     77possible for the //Add Device// event to occur more than once for the same device
     78in a row.
     79
     80{{{#!c
     81static int device_add(usb_device_t *dev)
     82{
     83        usb_log_info("Device '%s' added.", usb_device_get_name(dev));
     84        return EOK;
     85}
     86
     87static int device_remove(usb_device_t *dev)
     88{
     89        usb_log_info("Device '%s' removed.", usb_device_get_name(dev));
     90        return EOK;
     91}
     92
     93static int device_gone(usb_device_t *dev)
     94{
     95        usb_log_info("Device '%s' gone.", usb_device_get_name(dev));
     96        return EOK;
     97}
     98
     99static const usb_driver_ops_t driver_ops = {
     100        .device_add = device_add,
     101        .device_remove = device_remove,
     102        .device_gone = device_gone,
     103};
     104}}}
     105
     106{{{#!comment
     107TODO: Figure
     108For reference, the exact device states and transitions between them (corresponding to the events with respective names) are shown in Figure A.1.
     109}}}
     110
     111Furthermore, since there are no guarantees on the synchronization between
     112calls, the driver is explicitly responsible for synchronizing accesses to its
     113private data structures as well as device communication.
     114
     115== Device Communication
     116
     117In USB, a pipe is an abstraction primitive for a communication channel between
     118a device and a host computer. For the sake of simplicity, the provided
     119framework relies on a mechanism based on the same abstraction in order to
     120facilitate communication between device drivers and devices.
     121
     122== Endpoint Description
     123
     124In order to use pipes, device drivers must first define at least one
     125//endpoint description//. The purpose of such definition is to specify all
     126device endpoints, which will be used for communication by the device driver
     127throughout its lifecycle. For that reason, all descriptions ought to be
     128specified in advance and referenced in the main device driver structure. An
     129example of possible endpoint description definition follows:
     130
     131{{{#!c
     132static const usb_endpoint_description_t bulk_in_ep = {
     133        .transfer_type = USB_TRANSFER_BULK,
     134        .direction = USB_DIRECTION_IN,
     135        .interface_class = USB_CLASS_MASS_STORAGE,
     136        .interface_subclass = USB_MASSSTOR_SUBCLASS_SCSI,
     137        .interface_protocol = USB_MASSSTOR_PROTOCOL_BBB,
     138        .flags = 0
     139};
     140
     141static const usb_endpoint_description_t bulk_out_ep = {
     142        .transfer_type = USB_TRANSFER_BULK,
     143        .direction = USB_DIRECTION_OUT,
     144        .interface_class = USB_CLASS_MASS_STORAGE,
     145        .interface_subclass = USB_MASSSTOR_SUBCLASS_SCSI,
     146        .interface_protocol = USB_MASSSTOR_PROTOCOL_BBB,
     147        .flags = 0
     148};
     149
     150static const usb_endpoint_description_t *endpoints[] = {
     151        &bulk_in_ep, &bulk_out_ep, NULL
     152};
     153}}}
     154
     155Endpoint description contains information which can be matched to USB endpoint
     156descriptors in very much the same way as match identifiers are used to pair
     157devices with device drivers in HelenOS. Following this scheme, the presented
     158USB framework does most of the heavy lifting for device drivers. When a new
     159device is added, prior to delivery of the //Add Device// event, all
     160endpoint descriptions provided by the driver are matched to device endpoints,
     161resulting in two possible outcomes for every description:
     162
     163
     1641. A device endpoint matching the provided description is found and //an endpoint mapping// is created.
     1652. No device endpoint matches the provided description, hence no mapping is created.
     166
     167Device drivers can later query the result of the matching, and if successful,
     168retrieve the created endpoint mapping containing a fully initialized pipe
     169instance. An example of this is shown in the following snippet:
     170
     171{{{#!c
     172static int device_add(usb_device_t *dev)
     173{
     174        /* Find mapping for the endpoint description. */
     175        usb_endpoint_mapping_t *mapping = usb_device_get_mapped_ep_desc(dev, &bulk_out_ep);
     176
     177        /* Determine if the mapping was successful. */
     178        if (!mapping || !mapping->present) {
     179                usb_log_error("Endpoint mapping failed!");
     180                return ENOENT;
     181        }
     182
     183        usb_pipe_t *pipe = &mapping->pipe;
     184
     185        /* Now we can write to `pipe`. */
     186        return write_data(pipe);
     187}
     188}}}
     189
     190=== Pipe I/O
     191
     192Provided that an endpoint description has been defined, successfully matched
     193and an endpoint mapping has been retrieved along with a pipe instance, a device
     194driver can schedule data transfers to a device.
     195
     196Pipes offer a very simple synchronous command interface similar to that of an
     197open file descriptor, namely `usb_pipe_read` and `usb_pipe_write` for reading
     198from IN endpoints and writing to OUT endpoints respectively. While semantics of
     199these functions depends on endpoint types (consult USB specification for
     200details), their basic usage is as follows:
     201
     2021. Device driver allocates a buffer of appropriate size (and fills it with data if writing).
     2032. Either the `usb_pipe_read` or `usb_pipe_write` function is called, scheduling transfers to the device. The function receives a pipe pointer, pointer to the user-allocated buffer and data size.
     2043. The calling fibril is blocked until transfers succeed or fail (see error code). If reading, the number of read bytes is returned as well.
     2054. Device driver recycles or disposes of the allocated buffer.
     206
     207Note that while the write function blocks the calling fibril until the entire
     208buffer is transferred to the device, the read function might return
     209successfully with a lower number of bytes read than the actual data size
     210requested, and may thus have to be called multiple times.
     211
     212In addition, due to the multiplatform nature of HelenOS, it is not advisable to
     213assume endianity of the host system. Instead, the `uint{16|32}_host2usb`
     214and `uint{16|32}_usb2host` macros serve to convert host system endianity
     215to and from the transfer endianity defined in the USB Protocol Specification at
     216driver's convenience. A complete example of pipe write is shown in the following code:
     217
     218{{{#!c
     219static int write_data(usb_pipe_t *pipe)
     220{
     221        int rc = EOK;
     222        /* Allocate memory. */
     223        static const size_t size = 64;
     224        char *buffer = (char *) malloc(size);
     225        if (!buffer) {
     226                rc = ENOMEM;
     227                goto err;
     228        }
     229
     230        /* Fill `buffer` with arithmetic sequence of bytes. */
     231        for (int i = 0; i < size; ++i) buffer[i] = (char) i;
     232
     233        /* Write data. */
     234        rc = usb_pipe_write(pipe, buffer, size);
     235        if (rc != EOK) {
     236                usb_log_error("Write failed with error: %s", str_error(rc));
     237                goto err_buffer;
     238        }
     239
     240        /* Clean up. */
     241err_buffer:
     242        free(buffer);
     243err:
     244        return rc;
     245}
     246}}}
     247
     248=== Automated Polling
     249
     250In USB devices which often interact with a human user, it is important to wait
     251for user input by spinning (or polling) on some //Interrupt IN// endpoint. In
     252the abstraction used, this translates to calling `usb_pipe_read` in a perpetual
     253loop and responding to incoming data or errors. Since this behavior is common
     254to a significant class of drivers, a reusable unified implementation is
     255provided by the USB framework.
     256
     257The implementation is represented by the `usb_polling_t` structure and its
     258associated functions. Similarly to device event handlers, device drivers are
     259expected to configure this structure with polling handler functions. Later when
     260polling should be initiated, a call to the `usb_polling_start` function will
     261spawn a separate fibril, spinning on the pipe and calling handlers when data
     262arrive or `usb_pipe_read` fails with error.
     263
     264When polling is to be ceased, a call to the `usb_polling_join` function will
     265spontaneously wake up the polling fibril and block the caller fibril until its
     266termination. If polling is started, a call to this function must //always//
     267occur no later than in the //Remove Device// or //Device Gone// event
     268handler in order to prevent the fibril from outlasting the device lifecycle
     269within driver private data structures..
     270
     271Furthermore, note that once any device driver starts polling on a pipe, it
     272transfers the pipe ownership to the polling fibril, relinquishing all control
     273over it. That prohibits subsequent calls to `usb_pipe_read` or other relevant
     274functions. It is also not feasible to poll only for a limited time period, then
     275join and reuse the pipe for arbitrary data reads. This is due to the fact that
     276once `usb_polling_join` is called, the pipe is left closed for all
     277communication, destroying the underlying endpoint mapping.
     278
     279An example usage of the pipe polling interface follows:
     280
     281{{{#!c
     282static usb_polling_t polling;
     283static uint8_t buffer[13];
     284
     285static bool callback(usb_device_t *dev, uint8_t *buffer, size_t size, void *arg)
     286{
     287        printf("Have data!/n");
     288
     289        // Return true if we wish to continue polling.
     290        return true;
     291}
     292
     293static void demo()
     294{
     295        // Initialize.
     296        usb_polling_init(&polling);
     297
     298        // Configure.
     299        polling.device = /* some usb_device_t here */;
     300        polling.ep_mapping = /* some interrupt(in) endpoint of the device */;
     301        polling.buffer = buffer;
     302        polling.request_size = sizeof(buffer);
     303        polling.on_data = callback;
     304
     305        // Start polling.
     306        usb_polling_start(&polling);
     307
     308        // Sleep synchronously for a while.
     309        async_usleep(10000);
     310
     311        // End polling and clean up.
     312        usb_polling_join(&polling);
     313        usb_polling_fini(&polling);
     314}
     315}}}
     316
     317== Miscellaneous
     318
     319This section features general suggestions and recommendations for USB device
     320driver implementation.
     321
     322=== Device-specific Data Structures
     323
     324Device drivers often need to maintain stateful and descriptive information
     325related to their controlled devices. A common practice is to store this kind of
     326information in device-specific data structures. As their name suggests, such
     327structures usually correspond to a single controlled device, and can thus
     328closely follow its lifecycle, being allocated and configured when the device is
     329added and being freed upon its removal.
     330
     331The presented USB framework offers device drivers a straightforward mechanism to
     332associate any of their data structures with USB devices by specifying custom
     333//device data/ in the `usb_device_t` structure, which is present in
     334all device-related events. Device data is allocated at most once for every
     335device and can have any driver-specified, albeit constant size. When the device
     336is later removed from the driver, device data is automatically freed if
     337present.
     338
     339Allocation of device data is performed by a call to the
     340`usb_device_data_alloc` function. This function has the same return value
     341and semantics as the `calloc` function. In addition, the pointer returned by
     342this function is stored within the `usb_device_t` structure and can be
     343retrieved from that point onward until the device is removed by a call to the
     344`usb_device_data_get` function. Example usage of this mechanism can be seen here:
     345
     346{{{#!c
     347typedef struct my_data {
     348        /* Any device-specific data here. */
     349        usb_polling_t polling;
     350} my_data_t;
     351
     352static int device_add(usb_device_t *dev)
     353{
     354        /* Allocate device data. */
     355        my_data_t *data = (my_data_t *) usb_device_data_alloc(dev, sizeof(my_data_t));
     356        if (!data) {
     357                return ENOMEM;
     358        }
     359
     360        usb_polling_init(&data->polling);
     361        /* ... configure polling ... */
     362        usb_polling_start(&data->polling);
     363
     364        return EOK;
     365}
     366
     367static int device_remove(usb_device_t *dev)
     368{
     369        /* Retrieve device data. */
     370        my_data_t *data = (my_data_t *) usb_device_data_get(dev);
     371
     372        /* Stop polling. */
     373        usb_polling_join(&data->polling);
     374        usb_polling_fini(&data->polling);
     375
     376        /* After returning, device data is freed automatically. */
     377        return EOK;
     378}
     379}}}
     380
     381In this case, the `my_data_t` device-specific data structure is used
     382to keep track of automated pipe polling. Note that some error conditions have
     383been intentionally ignored for the sake of brevity.
     384
     385=== Logging
     386
     387Another useful feature of the USB device driver framework is a set of logging
     388macros. These macros copy the scheme common to the HelenOS logging framework,
     389offering various log levels a format string syntax for a variable number of
     390arguments consistent with the \fnc{printf} function syntax. With decreasing
     391severity, the presented logging macros are named as follows:
     392
     393* `usb_log_fatal` for fatal errors,
     394* `usb_log_error` for recoverable errors,
     395* `usb_log_warning` for warnings,
     396* `usb_log_info` for informational messages (produces a log message of the `Note` level),
     397* `usb_log_debug` for debugging messages,
     398* `usb_log_debug2` for verbose debugging messages.
     399
     400Prior to printing the first log message, the `log_init` function must be
     401called. During runtime, the default HelenOS log level can be adjusted by a
     402call to the `logctl_set_log_level` function. Keep in mind that in the
     403current implementation of HelenOS, every call to a logging macro results in an
     404IPC call. For that reason, it is not advisable to perform logging in
     405performance-sensitive parts of the code, even if the log level is sufficiently high.
     406
     407For more information about logging in HelenOS, consult the HelenOS
     408Documentation.
     409
     410=== Exposing an Interface
     411
     412In order for controlled hardware to become usable to system tasks, HelenOS
     413device drivers can expose IPC interfaces to abstract hardware-specific features
     414and provide a set of well-defined logical operations for device interaction.
     415
     416In HelenOS IPC, one of the preferred ways to achieve this is by interacting with
     417//services// -- system tasks, which run as daemons, and whose purpose is to
     418connect user tasks with device drivers. Since services usually keep track of
     419devices of the same category, it is necessary to first identify the appropriate
     420service for a driver and consult its documentation.
     421
     422While the specifics of exposing IPC interfaces may vary for different device
     423categories, the general scheme remains the same:
     424
     4251. For a DDF function, fill a specific structure with configuration and call handlers Then register them with the respective system service.
     4262. Respond to service-specific calls in compliance with service specification. Handling of these calls will likely result in communication with the device.
     4273. When the device is removed, cease handling service calls and unregister the device from the service.
     428
     429=== Descriptor Parser
     430
     431While they are sometimes stored in a serialized form, USB descriptors can be
     432viewed as a tree. For instance, configuration descriptor is followed by
     433interface and endpoint descriptors, which are not directly accessible. To aid
     434with problems such as this, a simple descriptor parser is provided by the
     435presented framework.
     436
     437The parser has been designed to be as generic as possible and its interface
     438might thus be somewhat terse. It operates on a byte array and offers functions
     439for finding the first nested descriptor and its next sibling (i.e. descriptor on
     440the same depth of nesting). The parser expects that the input is an array of
     441bytes where the descriptors are sorted in //prefix tree traversal// order.
     442Next, it expects that each descriptor has its length stored in the first byte
     443and the descriptor type in the second byte. That corresponds to standard
     444descriptor layout. The parser determines the nesting from a list of parent-child
     445pairs that is given to it during parser initialization. This list is terminated
     446with a pair where both parent and child are set to -1.
     447
     448The parser uses the `usb_dp_parser_t` structure, which contains array
     449with possible descriptor nesting (`usb_dp_descriptor_nesting_t`). The
     450data for the parser are stored in the `usb_dp_parser_data_t`, the arg
     451field is intended for custom data.
     452
     453For processing the actual data, two functions are available. The
     454`usb_dp_get_nested_descriptor` function takes a parser, parser data and
     455a pointer to parent as parameters. For retrieving the sibling descriptor (i.e.
     456descriptor at the same depth) one can use `usb_dp_get_sibling_descriptor`.
     457Whereas this function takes the same arguments, it also requires two extra
     458arguments — pointer to the parent descriptor (i.e. parent of both nested ones)
     459and a pointer to the preceding descriptor. Such pointer must always point to
     460the first byte of the descriptor (i.e. to the length of the descriptor, not to
     461its type).
     462
     463Additionally, there exists a simple iterator over the descriptors. The
     464function `usb_dp_walk_simple` takes a callback as a parameter, which is
     465executed for each found descriptor and receives thee current depth (starting
     466with 0) and a pointer to current descriptor.