plan9fox/sys/man/2/nusb
cinap_lenrek 065d601916 nusb: Fix handling of interface altsetting.
The altsetting was handled only for a single endpoint
(per interface number), but has to be handled for each
endpoint (per interface *AND* altsetting number).

A multi function device (like a disk) can have
multiple interfaces, all with the same interface number
but varying altsetting numbers and each of these
interfaces would list distict endpoint configurations.

Multiple interfaces can even share some endpoints (they
use the same endpoint addresses), but
we still have to duplicate them for each
interface+altsetting number (as they'r part of
actually distict interfaces with distict endpoint
configurations).

It is also important to *NOT* make endpoints bi-directional
(dir == Eboth) when only one direction is used in a
interface/altsetting and the other direction in another.
This was the case for nusb/disk with some seagate drive
where endpoints where shared between the UAS and
usb storage class interface (but with distict altsettings).

The duplicate endpoints (as in using the same endpoint address)
are chained together by a next pointer and the head
is stored in Usbdev.ep[addr], where addr is the endpoint
address. These Ep structures will have distinct endpoint
numbers Ep.id (when they have conflicting types), but all
will share the endpoint address (lower 4 bits of the
endpoint number).

The consequence is that all of the endpoints configuration
(attributes, interval) is now stored in the Ep struct and
no more Altc struct is present.

A pointer to the Ep struct has to be passed to openep()
for it to configure the endpoint.

For the Iface struct, we will now create multiple of them:
one for each interface *AND* altsetting nunber,
chained together on a next pointer and the head being
stored in conf->iface[ifaceid].

--
cinap
2022-02-21 19:50:16 +00:00

407 lines
10 KiB
Text

.TH NUSB 2
.SH NAME
usbcmd,
classname,
closedev,
configdev,
devctl,
getdev,
loaddevstr,
opendev,
opendevdata,
openep,
unstall - USB device driver library
.SH SYNOPSIS
.EX
.ta 8n +8n +8n +8n +8n +8n +8n
#include <u.h>
#include <libc.h>
#include "../lib/usb.h"
.sp 0.3v
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 isusb3; /* this is a usb3 device */
int depth; /* hub depth for usb3 hubs */
int maxpkt; /* cached from usb description */
Usbdev* usb; /* USB description */
Ep* ep; /* endpoint from epopen() */
void* aux; /* for the device driver */
char* hname; /* hash name, unique for device */
};
.sp 0.3v
struct Usbdev {
int ver; /* usb version */
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 vsid;
int psid;
int ssid;
int class; /* from descriptor */
int nconf; /* from descriptor */
Conf* conf[Nconf]; /* configurations */
Ep* ep[Epmax+1]; /* all endpoints in device (chained), indexed by address */
Desc* ddesc[Nddesc]; /* (raw) device specific descriptors */
};
.sp 0.3v
struct Ep {
Iface* iface; /* the endpoint belongs to */
Conf* conf; /* the endpoint belongs to */
int id; /* endpoint number: (id & Epmax) == endpoint address */
uchar dir; /* direction, Ein/Eout/Eboth */
uchar type; /* Econtrol, Eiso, Ebulk, Eintr */
int attrib;
int pollival;
int maxpkt; /* max. packet size */
int ntds; /* nb. of Tds per µframe */
/* chain of endpoints with same address (used in different interfaces/altsettings) */
Ep* next;
void* aux; /* for the driver program */
};
.sp 0.3v
struct Iface {
int id; /* interface number */
int alt; /* altsetting for this interface */
ulong csp; /* USB class/subclass/proto */
Iface* next; /* chain of interfaces of different altsettings */
Ep* ep[Nep]; /* consecutive array of endpoints in this interface (not including ep0) */
void* aux; /* for the driver program */
};
.sp 0.3v
struct Conf {
int cval; /* value for set configuration */
int attrib;
int milliamps; /* maximum power in this config. */
Iface* iface[Niface]; /* up to 16 interfaces */
};
.sp 0.3v
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. */
DDesc data; /* unparsed standard USB descriptor */
};
.sp 0.3v
struct DDesc {
uchar bLength;
uchar bDescriptorType;
uchar bbytes[1];
/* extra bytes allocated here to keep the rest of it */
};
.sp 0.3v
#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
.sp 0.3v
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);
char* hexstr(void *a, int n);
char* loaddevstr(Dev *d, int sid);
Dev* opendev(char *fn);
int opendevdata(Dev *d, int mode);
Dev* openep(Dev *d, Ep *e);
int unstall(Dev *dev, Dev *ep, int dir);
int usbcmd(Dev *d, int type, int req,
int value, int index, uchar *data, int count);
Dev* getdev(char *devid);
.sp 0.3v
extern int usbdebug; /* more messages for bigger values */
.EE
.SH DESCRIPTION
This library provides convenience structures and functions to write
USB device drivers.
It is not intended for user programs using USB devices.
See
.IR usb (3)
for a description of the interfaces provided for that purpose.
.PP
Usb drivers rely on
.IR usb (3)
to perform I/O through USB as well as on
.I usbd
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.
.PP
An endpoint as provided by
.IR usb (3)
is represented by a
.B 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
.B Devs
adjust the number of references to one, initially.
The driver is free to call
.I incref
(in
.IR lock (2))
to add references and
.I closedev
to drop references (and release resources when the last one vanishes).
As an aid to the driver, the field
.B aux
may keep driver-specific data.
.PP
.I Dev.dir
holds the path for the endpoint's directory.
.PP
The field
.B id
keeps the device number for setup endpoints and the endpoint number
for all other endpoints.
The endpoint number identifies
.I devusb
endpoint and is unique within a device.
For example, it would be
.B 3
for
.B /dev/usb/ep3.0
and
.B 1
for
.BR /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 particular endpoint
to perform I/O.
.PP
The field
.B ep
holds the endpoint structure that was passed in
.I epopen
which gives easy access to the endpoint configuration.
.PP
Fields
.B dfd
and
.B cfd
keep the data and
control file descriptors, respectively.
When a
.B Dev
is created the control file is open, initially.
Opening the data
file requires calling
.I opendevdata
with the appropriate mode.
.PP
When the device configuration information has been loaded (see below),
.B maxpkt
holds the maximum packet size (in bytes) for the endpoint and
.B usb
keeps the rest of the USB information.
.PP
Most of the information in
.B 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
.B usb.ddesc
as an aid for the driver
(which should know how to parse them and what to do with the information).
.SS Configuration
.PP
.I Getdev
is the primary entry point for device setup. It takes a
numeric device address or device path which usually gets
passed to drivers as a program argument and sets up the device,
retuning a configured
.B Dev
representing the setup endpoint of the device.
.PP
.I Opendev
creates a
.B Dev
for the endpoint with directory
.IR 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
.I getdev
and
.I openep
and do not call this function directly.
.PP
.I Configdev
opens the data file for the device supplied and
loads and parses its configuration information.
After calling it, the device is ready for I/O and the USB description in
.B Dev.usb
is valid.
In most cases drivers call
.I getdev
and do not call this function directly.
.PP
Control requests for an endpoint may be written by calling
.I devctl
in the style of
.IR print (2).
It is better not to call
.I print
directly because the control request should be issued as a single
.I write
system call.
See
.IR usb (3)
for a list of available control requests (not to be confused with
USB control transfers performed on a control endpoint).
.SS Input/Output
.I Opendevdata
opens the data file for the device according to the given
.IR mode .
The mode must match that of the endpoint, doing otherwise is considered
an error.
Actual I/O is performed by reading/writing the descriptor kept in the
.B dfd
field of
.BR Dev .
.PP
For control endpoints,
it is not necessary to call
.I read
and
.I write
directly.
Instead,
.I usbcmd
issues a USB control request to the device
.I d
(not to be confused with a
.IR usb (3)
control request sent to its control file).
.I Usbcmd
retries the control request several times upon failure because some devices
require it.
The format of requests is fixed per the USB standard:
.I type
is the type of request and
.I req
identifies the request. Arguments
.I value
and
.I index
are parameters to the request and the last two arguments,
.I data
and
.IR count ,
are similar to
.I read
and
.I write
arguments.
However,
.I data
may be
.B 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.
.PP
The return value from
.I usbcmd
is the number of bytes transferred, zero to indicate a stall and -1
to indicate an error.
.PP
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
.I unstall
does this.
It is given the device that stalled the endpoint,
.IR dev ,
the
stalled endpoint,
.IR ep ,
and the direction of the stall (one of
.B Ein
or
.BR Eout ).
The function takes care of notifying the device of the unstall as well
as notifying the kernel.
.SS Tools
.I Class
returns the class part of the number given, representing a CSP.
.I Subclass
does the same for the device subclass and
.I Proto
for the protocol.
The counterpart is
.IR CSP ,
which builds a CSP from the device class, subclass, and protocol.
For some classes,
.I classname
knows the name (for those with constants in the library header file).
.PP
The macros
.I GET2
and
.I PUT2
get and put a (little-endian) two-byte value and are useful to
parse descriptors and replies for control requests.
.PP
Functions
.I emallocz
and
.I estrdup
are similar to
.I mallocz
and
.I strdup
but abort program operation upon failure.
.PP
The function
.I Ufmt
is a format routine suitable for
.IR fmtinstall (2)
to print a
.B Dev
data structure.
The auxiliary
.I hexstr
returns a string representing a dump (in hexadecimal) of
.I n
bytes starting at
.IR a .
The string is allocated using
.IR malloc (2)
and memory must be released by the caller.
.PP
.I Loaddevstr
returns the string obtained by reading the device string descriptor number
.IR sid .
.SH SOURCE
.B /sys/src/cmd/nusb/lib
.SH "SEE ALSO"
.IR usb (3),
.IR nusb (4).
.SH BUGS
Not heavily exercised yet.