USB(2)                                                     USB(2)

     NAME
          usbcmd, classname, closedev, configdev, devctl, finddevs,
          loaddevstr, matchdevcsp, opendev, opendevdata, openep,
          startdevs, unstall, class, subclass, proto, CSP - USB device
          driver library

     SYNOPSIS
          #include <u.h>
          #include <libc.h>
          #include <thread.h>
          #include "../lib/usb.h"

          struct Dev {
                  Ref;
                  char*   dir;            /* path for the endpoint dir */
                  int     id;             /* usb id for device or ep. number */
                  int     dfd;            /* descriptor for the data file */
                  int     cfd;            /* descriptor for the control file */
                  int     maxpkt;         /* cached from usb description */
                  Usbdev* usb;            /* USB description */
                  void*   aux;            /* for the device driver */
                  void    (*free)(void*); /* idem. to release aux */
          };
          struct Usbdev {
                  ulong   csp;            /* USB class/subclass/proto */
                  int     vid;            /* vendor id */
                  int     did;            /* product (device) id */
                  int     dno;            /* device release number */
                  char*   vendor;
                  char*   product;
                  char*   serial;
                  int     ls;             /* low speed */
                  int     class;          /* from descriptor */
                  int     nconf;          /* from descriptor */
                  Conf*   conf[Nconf];    /* configurations */
                  Ep*     ep[Nep];        /* all endpoints in device */
                  Desc*   ddesc[Nddesc];  /* (raw) device specific descriptors */
          };

          struct Ep {
                  uchar   addr;           /* endpt address */
                  uchar   dir;            /* direction, Ein/Eout */
                  uchar   type;           /* Econtrol, Eiso, Ebulk, Eintr */
                  uchar   isotype;        /* Eunknown, Easync, Eadapt, Esync */
                  int     id;
                  int     maxpkt;         /* max. packet size */
                  Conf*   conf;           /* the endpoint belongs to */
                  Iface*  iface;          /* the endpoint belongs to */
          };
          struct Altc {

     Page 1                       Plan 9             (printed 3/28/24)

     USB(2)                                                     USB(2)

                  int     attrib;
                  int     interval;
                  void*   aux;            /* for the driver program */
          };

          struct Iface {
                  int     id;             /* interface number */
                  ulong   csp;            /* USB class/subclass/proto */
                  Altc*   altc[Naltc];
                  Ep*     ep[Nep];
                  void*   aux;            /* for the driver program */
          };
          struct Conf {
                  int     cval;           /* value for set configuration */
                  int     attrib;
                  int     milliamps;      /* maximum power in this config. */
                  Iface*  iface[Niface];  /* up to 16 interfaces */
          };

          struct Desc {
                  Conf*   conf;           /* where this descriptor was read */
                  Iface*  iface;          /* last iface before desc in conf. */
                  Ep*     ep;             /* last endpt before desc in conf. */
                  Altc*   altc;           /* last alt.c. before desc in conf. */
                  DDesc   data;           /* unparsed standard USB descriptor */
          };
          struct DDesc {
                  uchar   bLength;
                  uchar   bDescriptorType;
                  uchar   bbytes[1];
                  /* extra bytes allocated here to keep the rest of it */
          };

          #define Class(csp)      ((csp)&0xff)
          #define Subclass(csp)   (((csp)>>8)&0xff)
          #define Proto(csp)      (((csp)>>16)&0xff)
          #define CSP(c, s, p)    ((c) | ((s)<<8) | ((p)<<16))
          #define GET2(p)         ...
          #define PUT2(p,v)       ...
          #define GET4(p)         ...
          #define PUT4(p,v)       ...
          #define dprint   if(usbdebug)fprint
          #define ddprint if(usbdebug > 1)fprint
          int     Ufmt(Fmt *f);
          char*   classname(int c);
          void    closedev(Dev *d);
          int     configdev(Dev *d);
          int     devctl(Dev *dev, char *fmt, ...);
          void*   emallocz(ulong size, int zero);
          char*   estrdup(char *s);
          int     finddevs(int (*matchf)(char*,void*), void *farg, char** dirs, int ndirs);
          char*   hexstr(void *a, int n);

     Page 2                       Plan 9             (printed 3/28/24)

     USB(2)                                                     USB(2)

          char*   loaddevstr(Dev *d, int sid);
          int     matchdevcsp(char *info, void *a);
          Dev*    opendev(char *fn);
          int     opendevdata(Dev *d, int mode);
          Dev*    openep(Dev *d, int id);
          void    startdevs(char *args, char *argv[], int argc,
                          int (*mf)(char*,void*), void*ma, int (*df)(Dev*,int,char**));
          int     unstall(Dev *dev, Dev *ep, int dir);
          int     usbcmd(Dev *d, int type, int req,
                          int value, int index, uchar *data, int count);

          extern int usbdebug;    /* more messages for bigger values */

     DESCRIPTION
          This library provides convenience structures and functions
          to write USB device drivers.  It is not intended for user
          programs using USB devices.  See usb(3) for a description of
          the interfaces provided for that purpose.  For drivers that
          provide a file system and may be embedded into usbd, the
          library includes a file system implementation toolkit
          described in usbfs(2).

          Usb drivers rely on usb(3) to perform I/O through USB as
          well as on usbd(4) to perform the initial configuration for
          the device's setup endpoint.  The rest of the work is up to
          the driver and is where this library may help.

          In most cases, a driver locates the devices of interest and
          configures them by calling startdevs and then sets up addi-
          tional endpoints as needed (by calling openep) to finally
          perform I/O by reading and writing the data files for the
          endpoints.

          An endpoint as provided by usb(3) is represented by a Dev
          data structure.  The setup endpoint for a device represents
          the USB device, because it is the means to configure and
          operate the device.  This structure is reference counted.
          Functions creating Devs adjust the number of references to
          one, initially.  The driver is free to call incref (in
          lock(2)) to add references and closedev to drop references
          (and release resources when the last one vanishes).  As an
          aid to the driver, the field aux may keep driver-specific
          data and the function free will be called (if not null) to
          release the aux structure when the reference count goes down
          to zero.

          Dev.dir holds the path for the endpoint's directory.

          The field id keeps the device number for setup endpoints and
          the endpoint number for all other endpoints.  For example,
          it would be 3 for /dev/usb/ep3.0 and 1 for /dev/usb/ep3.1.
          It is easy to remember this because the former is created to
          operate on the device, while the later has been created as a

     Page 3                       Plan 9             (printed 3/28/24)

     USB(2)                                                     USB(2)

          particular endpoint to perform I/O.

          Fields dfd and cfd keep the data and control file descrip-
          tors, respectively.  When a Dev is created the control file
          is open, initially.  Opening the data file requires calling
          opendevdata with the appropriate mode.

          When the device configuration information has been loaded
          (see below), maxpkt holds the maximum packet size (in bytes)
          for the endpoint and usb keeps the rest of the USB informa-
          tion.

          Most of the information in usb comes from parsing various
          device and configuration descriptors provided by the device,
          by calling one of the functions described later.  Only
          descriptors unknown to the library are kept unparsed at
          usb.ddesc as an aid for the driver (which should know how to
          parse them and what to do with the information).

        Configuration
          Startdevs is a wrapper that locates devices of interest,
          loads their configuration information, and starts a
          thread(2)'s proc for each device located so that it executes
          f as its main entry point. The entry point is called with a
          pointer to the Dev for the device it has to process, argc,
          and argv.  Devices are located either from the arguments
          (after options) in argv, if any, or by calling the helper
          function mf with the argument ma to determine (for each
          device available) if the device belongs to the driver or
          not. If the function returns -1 then the device is not for
          us.

          In many cases, matchdevcsp may be supplied as mf along with
          a (null terminated) vector of CSP values supplied as ma.
          This function returns 0 for any device with a CSP matching
          one in the vector supplied as an argument and -1 otherwise.
          In other cases (eg., when a particular vendor and device ids
          are the ones identifying the device) the driver must include
          its own function and supply it as an argument to startdevs.
          The first argument of the function corresponds to the infor-
          mation known about the device (the second line in its ctl
          file).  Openep creates the endpoint number id for the device
          d and returns a Dev structure to operate on it (with just
          the control file open).

          Opendev creates a Dev for the endpoint with directory fn.
          Usually, the endpoint is a setup endpoint representing a
          device. The endpoint control file is open, but the data file
          is not. The USB description is void.  In most cases drivers
          call startdevs and openep and do not call this function
          directly.

     Page 4                       Plan 9             (printed 3/28/24)

     USB(2)                                                     USB(2)

          Configdev opens the data file for the device supplied and
          loads and parses its configuration information.  After call-
          ing it, the device is ready for I/O and the USB description
          in Dev.usb is valid.  When using startdevs it is not desir-
          able to call this function (because startdevs already calls
          it).

          Control requests for an endpoint may be written by calling
          devctl in the style of print(2). It is better not to call
          print directly because the control request should be issued
          as a single write system call.  See usb(3) for a list of
          available control requests (not to be confused with USB con-
          trol transfers performed on a control endpoint).

        Input/Output
          Opendevdata opens the data file for the device according to
          the given mode. The mode must match that of the endpoint,
          doing otherwise is considered an error.  Actual I/O is per-
          formed by reading/writing the descriptor kept in the dfd
          field of Dev.

          For control endpoints, it is not necessary to call read and
          write directly.  Instead, usbcmd issues a USB control
          request to the device d (not to be confused with a usb(3)
          control request sent to its control file).  Usbcmd retries
          the control request several times upon failure because some
          devices require it.  The format of requests is fixed per the
          USB standard: type is the type of request and req identifies
          the request. Arguments value and index are parameters to the
          request and the last two arguments, data and count, are sim-
          ilar to read and write arguments.  However, data may be nil
          if no transfer (other than the control request) has to take
          place.  The library header file includes numerous symbols
          defined to help writing the type and arguments for a
          request.

          The return value from usbcmd is the number of bytes trans-
          ferred, zero to indicate a stall and -1 to indicate an
          error.

          A common request is to unstall an endpoint that has been
          stalled due to some reason by the device (eg., when read or
          write indicate a count of zero bytes read or written on the
          endpoint). The function unstall does this.  It is given the
          device that stalled the endpoint, dev, the stalled endpoint,
          ep, and the direction of the stall (one of Ein or Eout).
          The function takes care of notifying the device of the
          unstall as well as notifying the kernel.

        Tools
          Class returns the class part of the number given, represent-
          ing a CSP.  Subclass does the same for the device subclass

     Page 5                       Plan 9             (printed 3/28/24)

     USB(2)                                                     USB(2)

          and Proto for the protocol.  The counterpart is CSP, which
          builds a CSP from the device class, subclass, and protocol.
          For some classes, classname knows the name (for those with
          constants in the library header file).

          The macros GET2 and PUT2 get and put a (little-endian) two-
          byte value and are useful to parse descriptors and replies
          for control requests.

          Functions emallocz and estrdup are similar to mallocz and
          strdup but abort program operation upon failure.

          The function Ufmt is a format routine suitable for
          fmtinstall(2) to print a Dev data structure.  The auxiliary
          hexstr returns a string representing a dump (in hexadecimal)
          of n bytes starting at a. The string is allocated using
          malloc(2) and memory must be released by the caller.

          Loaddevstr returns the string obtained by reading the device
          string descriptor number sid.

     SOURCE
          /sys/src/cmd/usb/lib

     SEE ALSO
          usbfs(2), usb(3), usb(4), usbd(4).

     BUGS
          Not heavily exercised yet.

     Page 6                       Plan 9             (printed 3/28/24)