065d601916
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
407 lines
10 KiB
Text
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.
|