1465 lines · 29.1 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * USB device driver framework. * * This is in charge of providing access to actual HCIs * and providing I/O to the various endpoints of devices. * A separate user program (usbd) is in charge of * enumerating the bus, setting up endpoints and * starting devices (also user programs). * * The interface provided is a violation of the standard: * you're welcome. * * The interface consists of a root directory with several files * plus a directory (epN.M) with two files per endpoint. * A device is represented by its first endpoint, which * is a control endpoint automatically allocated for each device. * Device control endpoints may be used to create new endpoints. * Devices corresponding to hubs may also allocate new devices, * perhaps also hubs. Initially, a hub device is allocated for * each controller present, to represent its root hub. Those can * never be removed. * * All endpoints refer to the first endpoint (epN.0) of the device, * which keeps per-device information, and also to the HCI used * to reach them. Although all endpoints cache that information. * * epN.M/data files permit I/O and are considered DMEXCL. * epN.M/ctl files provide status info and accept control requests. * * Endpoints may be given file names to be listed also at #u, * for those drivers that have nothing to do after configuring the * device and its endpoints. * * Drivers for different controllers are kept at usb[oue]hci.c * It's likely we could factor out much from controllers into * a generic controller driver, the problem is that details * regarding how to handle toggles, tokens, Tds, etc. will * get in the way. Thus, code is probably easier the way it is. */ #include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"io.h"#include	"../port/error.h"#include	"../port/usb.h" typedef struct Hcitype Hcitype; enum{	/* Qid numbers */	Qdir = 0,		/* #u */	Qusbdir,			/* #u/usb */	Qctl,			/* #u/usb/ctl - control requests */ 	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */	Qep0dummy,		/* give 4 qids to each endpoint */ 	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */	Qepio,			/* to identify which file for the endpoint */	Qepctl, 	/* ... */ 	/* Usb ctls. */	CMdebug = 0,		/* debug on|off */	CMdump,			/* dump (data structures for debug) */ 	/* Ep. ctls */	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */	CMhub,			/* hub (set the device as a hub) */	CMspeed,		/* speed full|low|high|no */	CMmaxpkt,		/* maxpkt size */	CMntds,			/* ntds nb (max nb. of tds per µframe) */	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */	CMpollival,		/* pollival interval (interrupt/iso) */	CMhz,			/* hz n (samples/sec; iso) */	CMsamplesz,		/* samplesz n (sample size; iso) */	CMinfo,			/* info infostr (ke.ep info for humans) */	CMdetach,		/* detach (abort I/O forever on this ep). */	CMaddress,		/* address (address is assigned) */	CMdebugep,		/* debug n (set/clear debug for this ep) */	CMname,			/* name str (show up as #u/name as well) */	CMtmout,		/* timeout n (activate timeouts for ep) */	CMpreset,		/* reset the port */ 	/* Hub feature selectors */	Rportenable	= 1,	Rportreset	= 4, }; struct Hcitype{	char*	type;	int	(*reset)(Hci*);}; #define QID(q)	((int)(q).path) static Cmdtab usbctls[] ={	{CMdebug,	"debug",	2},	{CMdump,	"dump",		1},}; static Cmdtab epctls[] ={	{CMnew,		"new",		4},	{CMnewdev,	"newdev",	3},	{CMhub,		"hub",		1},	{CMspeed,	"speed",	2},	{CMmaxpkt,	"maxpkt",	2},	{CMntds,	"ntds",		2},	{CMpollival,	"pollival",	2},	{CMsamplesz,	"samplesz",	2},	{CMhz,		"hz",		2},	{CMinfo,	"info",		0},	{CMdetach,	"detach",	1},	{CMaddress,	"address",	1},	{CMdebugep,	"debug",	2},	{CMclrhalt,	"clrhalt",	1},	{CMname,	"name",		2},	{CMtmout,	"timeout",	2},	{CMpreset,	"reset",	1},}; static Dirtab usbdir[] ={	"ctl",		{Qctl},		0,	0666,}; char *usbmodename[] ={	[OREAD]	"r",	[OWRITE]	"w",	[ORDWR]	"rw",}; static char *ttname[] ={	[Tnone]	"none",	[Tctl]	"control",	[Tiso]	"iso",	[Tintr]	"interrupt",	[Tbulk]	"bulk",}; static char *spname[] ={	[Fullspeed]	"full",	[Lowspeed]	"low",	[Highspeed]	"high",	[Nospeed]	"no",}; static int	debug;static Hcitype	hcitypes[Nhcis];static Hci*	hcis[Nhcis];static QLock	epslck;		/* add, del, lookup endpoints */static Ep*	eps[Neps];	/* all endpoints known */static int	epmax;		/* 1 + last endpoint index used  */static int	usbidgen;	/* device address generator */ /* * Is there something like this in a library? should it be? */char*seprintdata(char *s, char *se, uchar *d, int n){	int i, l; 	s = seprint(s, se, " %#p[%d]: ", d, n);	l = n;	if(l > 10)		l = 10;	for(i=0; i<l; i++)		s = seprint(s, se, " %2.2ux", d[i]);	if(l < n)		s = seprint(s, se, "...");	return s;} static intname2speed(char *name){	int i; 	for(i = 0; i < nelem(spname); i++)		if(strcmp(name, spname[i]) == 0)			return i;	return Nospeed;} static intname2ttype(char *name){	int i; 	for(i = 0; i < nelem(ttname); i++)		if(strcmp(name, ttname[i]) == 0)			return i;	/* may be a std. USB ep. type */	i = strtol(name, nil, 0);	switch(i+1){	case Tctl:	case Tiso:	case Tbulk:	case Tintr:		return i+1;	default:		return Tnone;	}} static intname2mode(char *mode){	int i; 	for(i = 0; i < nelem(usbmodename); i++)		if(strcmp(mode, usbmodename[i]) == 0)			return i;	return -1;} static intqid2epidx(int q){	q = (q-Qep0dir)/4;	if(q < 0 || q >= epmax || eps[q] == nil)		return -1;	return q;} static intisqtype(int q, int type){	if(q < Qep0dir)		return 0;	q -= Qep0dir;	return (q & 3) == type;} voidaddhcitype(char* t, int (*r)(Hci*)){	static int ntype; 	if(ntype == Nhcis)		panic("too many USB host interface types");	hcitypes[ntype].type = t;	hcitypes[ntype].reset = r;	ntype++;} static char*seprintep(char *s, char *se, Ep *ep, int all){	static char* dsnames[] = { "config", "enabled", "detached", "reset" };	Udev *d;	int i;	int di; 	d = ep->dev; 	qlock(ep);	if(waserror()){		qunlock(ep);		nexterror();	}	di = ep->dev->nb;	if(all)		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);	s = seprint(s, se, "%s", dsnames[ep->dev->state]);	s = seprint(s, se, " %s", ttname[ep->ttype]);	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);	s = seprint(s, se, " %s", usbmodename[ep->mode]);	s = seprint(s, se, " speed %s", spname[d->speed]);	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);	s = seprint(s, se, " pollival %ld", ep->pollival);	s = seprint(s, se, " samplesz %ld", ep->samplesz);	s = seprint(s, se, " hz %ld", ep->hz);	s = seprint(s, se, " hub %d", ep->dev->hub);	s = seprint(s, se, " port %d", ep->dev->port);	if(ep->inuse)		s = seprint(s, se, " busy");	else		s = seprint(s, se, " idle");	if(all){		s = seprint(s, se, " load %uld", ep->load);		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);		s = seprint(s, se, " idx %d", ep->idx);		if(ep->name != nil)			s = seprint(s, se, " name '%s'", ep->name);		if(ep->tmout != 0)			s = seprint(s, se, " tmout");		if(ep == ep->ep0){			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);			s = seprint(s, se, " eps:");			for(i = 0; i < nelem(d->eps); i++)				if(d->eps[i] != nil)					s = seprint(s, se, " ep%d.%d", di, i);		}	}	if(ep->info != nil)		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);	else		s = seprint(s, se, "\n");	qunlock(ep);	poperror();	return s;} static Ep*epalloc(Hci *hp){	Ep *ep;	int i; 	ep = smalloc(sizeof(Ep));	ep->ref = 1;	qlock(&epslck);	for(i = 0; i < Neps; i++)		if(eps[i] == nil)			break;	if(i == Neps){		qunlock(&epslck);		free(ep);		print("usb: bug: too few endpoints.\n");		return nil;	}	ep->idx = i;	if(epmax <= i)		epmax = i+1;	eps[i] = ep;	ep->hp = hp;	ep->maxpkt = 8;	ep->ntds = 1;	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */	qunlock(&epslck);	return ep;} static Ep*getep(int i){	Ep *ep; 	if(i < 0 || i >= epmax || eps[i] == nil)		return nil;	qlock(&epslck);	ep = eps[i];	if(ep != nil)		incref(ep);	qunlock(&epslck);	return ep;} static voidputep(Ep *ep){	Udev *d; 	if(ep != nil && decref(ep) == 0){		d = ep->dev;		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);		qlock(&epslck);		eps[ep->idx] = nil;		if(ep->idx == epmax-1)			epmax--;		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)			usbidgen--;		qunlock(&epslck);		if(d != nil){			qlock(ep->ep0);			d->eps[ep->nb] = nil;			qunlock(ep->ep0);		}		if(ep->ep0 != ep){			putep(ep->ep0);			ep->ep0 = nil;		}		free(ep->info);		free(ep->name);		free(ep);	}} static voiddumpeps(void){	int i;	static char buf[512];	char *s;	char *e;	Ep *ep; 	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);	for(i = 0; i < epmax; i++){		s = buf;		e = buf+sizeof(buf);		ep = getep(i);		if(ep != nil){			if(waserror()){				putep(ep);				nexterror();			}			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);			seprintep(s, e, ep, 1);			print("%s", buf);			ep->hp->seprintep(buf, e, ep);			print("%s", buf);			poperror();			putep(ep);		}	}	print("usb dump hcis:\n");	for(i = 0; i < Nhcis; i++)		if(hcis[i] != nil)			hcis[i]->dump(hcis[i]);} static intnewusbid(Hci *){	int id; 	qlock(&epslck);	id = ++usbidgen;	if(id >= 0x7F)		print("#u: too many device addresses; reuse them more\n");	qunlock(&epslck);	return id;} /* * Create endpoint 0 for a new device */static Ep*newdev(Hci *hp, int ishub, int isroot){	Ep *ep;	Udev *d; 	ep = epalloc(hp);	d = ep->dev = smalloc(sizeof(Udev));	d->nb = newusbid(hp);	d->eps[0] = ep;	ep->nb = 0;	ep->toggle[0] = ep->toggle[1] = 0;	d->ishub = ishub;	d->isroot = isroot;	if(hp->highspeed != 0)		d->speed = Highspeed;	else		d->speed = Fullspeed;	d->state = Dconfig;		/* address not yet set */	ep->dev = d;	ep->ep0 = ep;			/* no ref counted here */	ep->ttype = Tctl;	ep->tmout = Xfertmout;	ep->mode = ORDWR;	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);	return ep;} /* * Create a new endpoint for the device * accessed via the given endpoint 0. */static Ep*newdevep(Ep *ep, int i, int tt, int mode){	Ep *nep;	Udev *d; 	d = ep->dev;	if(d->eps[i] != nil)		error("endpoint already in use");	nep = epalloc(ep->hp);	incref(ep);	d->eps[i] = nep;	nep->nb = i;	nep->toggle[0] = nep->toggle[1] = 0;

Lines 1–500 of 1465.