xref: /plan9/sys/src/cmd/usb/usbd/dev.c (revision 0cc6832d7c845a6250c7353daab06d4af2dfe5cb)
1906943f9SDavid du Colombier /*
2906943f9SDavid du Colombier  * Framework for USB devices.
3906943f9SDavid du Colombier  * Some of them may be embedded into usbd and some of
4906943f9SDavid du Colombier  * them may exist as /bin/usb/* binaries on their own.
5906943f9SDavid du Colombier  *
6906943f9SDavid du Colombier  * When embedded, devmain() is given a ref of an already
7906943f9SDavid du Colombier  * configured and open Dev. If devmain()
8906943f9SDavid du Colombier  * does not fail it should release this ref when done and
9906943f9SDavid du Colombier  * use incref to add further refs to it.
10906943f9SDavid du Colombier  */
11906943f9SDavid du Colombier #include <u.h>
12906943f9SDavid du Colombier #include <libc.h>
13906943f9SDavid du Colombier #include <thread.h>
14906943f9SDavid du Colombier #include "usb.h"
15906943f9SDavid du Colombier #include "usbd.h"
16906943f9SDavid du Colombier 
17ed868a7cSDavid du Colombier static Lock masklck;
18906943f9SDavid du Colombier extern Devtab devtab[];
19906943f9SDavid du Colombier static char* cputype;
20906943f9SDavid du Colombier 
21ed868a7cSDavid du Colombier int
getdevnb(uvlong * maskp)22ed868a7cSDavid du Colombier getdevnb(uvlong *maskp)
23ed868a7cSDavid du Colombier {
24ed868a7cSDavid du Colombier 	int i;
25ed868a7cSDavid du Colombier 
26ed868a7cSDavid du Colombier 	lock(&masklck);
27ed868a7cSDavid du Colombier 	for(i = 0; i < 8 * sizeof *maskp; i++)
28ed868a7cSDavid du Colombier 		if((*maskp & (1ULL<<i)) == 0){
29ed868a7cSDavid du Colombier 			*maskp |= 1ULL<<i;
30ed868a7cSDavid du Colombier 			unlock(&masklck);
31ed868a7cSDavid du Colombier 			return i;
32ed868a7cSDavid du Colombier 		}
33ed868a7cSDavid du Colombier 	unlock(&masklck);
34ed868a7cSDavid du Colombier 	return -1;
35ed868a7cSDavid du Colombier }
36ed868a7cSDavid du Colombier 
37ed868a7cSDavid du Colombier void
putdevnb(uvlong * maskp,int id)38ed868a7cSDavid du Colombier putdevnb(uvlong *maskp, int id)
39ed868a7cSDavid du Colombier {
40ed868a7cSDavid du Colombier 	lock(&masklck);
41ed868a7cSDavid du Colombier 	if(id >= 0)
42ed868a7cSDavid du Colombier 		*maskp &= ~(1ULL<<id);
43ed868a7cSDavid du Colombier 	unlock(&masklck);
44ed868a7cSDavid du Colombier }
45ed868a7cSDavid du Colombier 
46906943f9SDavid du Colombier static int
cspmatch(Devtab * dt,int dcsp)47906943f9SDavid du Colombier cspmatch(Devtab *dt, int dcsp)
48906943f9SDavid du Colombier {
49906943f9SDavid du Colombier 	int	i;
50906943f9SDavid du Colombier 	int	csp;
51906943f9SDavid du Colombier 
52906943f9SDavid du Colombier 	for(i = 0; i < nelem(dt->csps); i++)
53906943f9SDavid du Colombier 		if((csp=dt->csps[i]) != 0)
54906943f9SDavid du Colombier 		if(csp == dcsp)
55906943f9SDavid du Colombier 			return 1;
56906943f9SDavid du Colombier 		else if((csp&DCL) && (csp&~DCL) == Class(dcsp))
57906943f9SDavid du Colombier 			return 1;
58906943f9SDavid du Colombier 	return 0;
59906943f9SDavid du Colombier }
60906943f9SDavid du Colombier 
61906943f9SDavid du Colombier static int
devmatch(Devtab * dt,Usbdev * d)62906943f9SDavid du Colombier devmatch(Devtab *dt, Usbdev *d)
63906943f9SDavid du Colombier {
64906943f9SDavid du Colombier 	int i;
65906943f9SDavid du Colombier 	int c;
66906943f9SDavid du Colombier 	Conf *cp;
67906943f9SDavid du Colombier 
68*0cc6832dSDavid du Colombier 	if(dt->noauto)
69*0cc6832dSDavid du Colombier 		return 0;
70906943f9SDavid du Colombier 	if(dt->vid != -1 && d->vid != dt->vid)
71906943f9SDavid du Colombier 		return 0;
72906943f9SDavid du Colombier 	if(dt->did != -1 && d->did != dt->did)
73906943f9SDavid du Colombier 		return 0;
74906943f9SDavid du Colombier 	if(cspmatch(dt, d->csp))
75906943f9SDavid du Colombier 		return 1;
76906943f9SDavid du Colombier 	for(c = 0; c < Nconf; c++)
77906943f9SDavid du Colombier 		if((cp=d->conf[c]) != nil)
78906943f9SDavid du Colombier 			for(i = 0; i < Niface; i++)
79906943f9SDavid du Colombier 				if(cp->iface[i] != nil)
80906943f9SDavid du Colombier 					if(cspmatch(dt, cp->iface[i]->csp))
81906943f9SDavid du Colombier 						return 1;
82906943f9SDavid du Colombier 	return 0;
83906943f9SDavid du Colombier }
84906943f9SDavid du Colombier 
85906943f9SDavid du Colombier /* We can't use procexec to execute drivers, because
86906943f9SDavid du Colombier  * procexec mounts #| at /mnt/temp and we do *not*
87906943f9SDavid du Colombier  * have /mnt/temp at boot time.
88906943f9SDavid du Colombier  * Instead, we use access to guess if we can execute the file.
89906943f9SDavid du Colombier  * and reply as procexec. Be careful that the child inherits
90906943f9SDavid du Colombier  * all the shared state of the thread library. It should run unnoticed.
91906943f9SDavid du Colombier  */
92906943f9SDavid du Colombier static void
xexec(Channel * c,char * nm,char * args[])93906943f9SDavid du Colombier xexec(Channel *c, char *nm, char *args[])
94906943f9SDavid du Colombier {
95906943f9SDavid du Colombier 	int	pid;
96906943f9SDavid du Colombier 
97906943f9SDavid du Colombier 	if(access(nm, AEXEC) == 0){
98906943f9SDavid du Colombier 		pid = rfork(RFFDG|RFREND|RFPROC);
99906943f9SDavid du Colombier 		switch(pid){
100906943f9SDavid du Colombier 		case 0:
101906943f9SDavid du Colombier 			exec(nm, args);
102906943f9SDavid du Colombier 			_exits("exec");
103906943f9SDavid du Colombier 		case -1:
104906943f9SDavid du Colombier 			break;
105906943f9SDavid du Colombier 		default:
106906943f9SDavid du Colombier 			sendul(c, pid);
107906943f9SDavid du Colombier 			threadexits(nil);
108906943f9SDavid du Colombier 		}
109906943f9SDavid du Colombier 	}
110906943f9SDavid du Colombier }
111906943f9SDavid du Colombier 
112906943f9SDavid du Colombier typedef struct Sarg Sarg;
113906943f9SDavid du Colombier struct Sarg{
114906943f9SDavid du Colombier 	Port *pp;
115906943f9SDavid du Colombier 	Devtab* dt;
116906943f9SDavid du Colombier 	Channel*rc;
117906943f9SDavid du Colombier 	char fname[80];
118906943f9SDavid du Colombier 	char	args[128];
119906943f9SDavid du Colombier 	char	*argv[40];
120906943f9SDavid du Colombier };
121906943f9SDavid du Colombier 
122906943f9SDavid du Colombier static void
startdevproc(void * a)123906943f9SDavid du Colombier startdevproc(void *a)
124906943f9SDavid du Colombier {
125906943f9SDavid du Colombier 	Sarg	*sa = a;
126906943f9SDavid du Colombier 	Dev	*d;
127906943f9SDavid du Colombier 	Devtab *dt;
128906943f9SDavid du Colombier 	int	argc;
129ed868a7cSDavid du Colombier 	char *args, *argse, **argv;
130906943f9SDavid du Colombier 	char *fname;
131906943f9SDavid du Colombier 
132ed868a7cSDavid du Colombier 	threadsetgrp(threadid());
133906943f9SDavid du Colombier 	d = sa->pp->dev;
134906943f9SDavid du Colombier 	dt = sa->dt;
135906943f9SDavid du Colombier 	args = sa->args;
136ed868a7cSDavid du Colombier 	argse = sa->args + sizeof sa->args;
137906943f9SDavid du Colombier 	argv = sa->argv;
138906943f9SDavid du Colombier 	fname = sa->fname;
139ed868a7cSDavid du Colombier 	sa->pp->devmaskp = &dt->devmask;
140ed868a7cSDavid du Colombier 	sa->pp->devnb = getdevnb(&dt->devmask);
141ed868a7cSDavid du Colombier 	if(sa->pp->devnb < 0){
142ed868a7cSDavid du Colombier 		sa->pp->devmaskp = nil;
143ed868a7cSDavid du Colombier 		sa->pp->devnb = 0;
144ed868a7cSDavid du Colombier 	}else
145ed868a7cSDavid du Colombier 		args = seprint(args, argse, "-N %d", sa->pp->devnb);
146906943f9SDavid du Colombier 	if(dt->args != nil)
147ed868a7cSDavid du Colombier 		seprint(args, argse, " %s", dt->args);
148ed868a7cSDavid du Colombier 	args = sa->args;
149906943f9SDavid du Colombier 	dprint(2, "%s: start: %s %s\n", argv0, dt->name, args);
150906943f9SDavid du Colombier 	argv[0] = dt->name;
151906943f9SDavid du Colombier 	argc = 1;
152906943f9SDavid du Colombier 	if(args[0] != 0)
153906943f9SDavid du Colombier 		argc += tokenize(args, argv+1, nelem(sa->argv)-2);
154906943f9SDavid du Colombier 	argv[argc] = nil;
155906943f9SDavid du Colombier 	if(dt->init == nil){
156906943f9SDavid du Colombier 		if(d->dfd > 0 ){
157906943f9SDavid du Colombier 			close(d->dfd);
158906943f9SDavid du Colombier 			d->dfd = -1;
159906943f9SDavid du Colombier 		}
160906943f9SDavid du Colombier 		rfork(RFCFDG);
161906943f9SDavid du Colombier 		open("/dev/null", OREAD);
162906943f9SDavid du Colombier 		open("/dev/cons", OWRITE);
163906943f9SDavid du Colombier 		open("/dev/cons", OWRITE);
164906943f9SDavid du Colombier 
165906943f9SDavid du Colombier 		xexec(sa->rc, argv[0], argv);
166906943f9SDavid du Colombier 		snprint(fname, sizeof(sa->fname), "/bin/usb/%s", dt->name);
167906943f9SDavid du Colombier 		xexec(sa->rc, fname, argv);
168906943f9SDavid du Colombier 		snprint(fname, sizeof(sa->fname), "/boot/%s", dt->name);
169906943f9SDavid du Colombier 		xexec(sa->rc, fname, argv);
170906943f9SDavid du Colombier 		if(cputype == nil)
171906943f9SDavid du Colombier 			cputype = getenv("cputype");
172906943f9SDavid du Colombier 		if(cputype != nil){
173906943f9SDavid du Colombier 			snprint(fname, sizeof(sa->fname), "/%s/bin/%s",
174906943f9SDavid du Colombier 				cputype, dt->name);
175906943f9SDavid du Colombier 			argv[0] = fname;
176906943f9SDavid du Colombier 			xexec(sa->rc, fname, argv);
177906943f9SDavid du Colombier 		}
178906943f9SDavid du Colombier 		fprint(2, "%s: %s: not found. can't exec\n", argv0, dt->name);
179906943f9SDavid du Colombier 		sendul(sa->rc, -1);
180906943f9SDavid du Colombier 		threadexits("exec");
181906943f9SDavid du Colombier 	}else{
182906943f9SDavid du Colombier 		sa->pp->dev = opendev(d->dir);
183906943f9SDavid du Colombier 		sendul(sa->rc, 0);
184d5789509SDavid du Colombier 		if(dt->init(d, argc, argv) < 0)
185906943f9SDavid du Colombier 			fprint(2, "%s: %s: %r\n", argv0, dt->name);
186906943f9SDavid du Colombier 		closedev(d);
187906943f9SDavid du Colombier 		free(sa);
188906943f9SDavid du Colombier 	}
189906943f9SDavid du Colombier 	threadexits(nil);
190906943f9SDavid du Colombier }
191906943f9SDavid du Colombier 
192906943f9SDavid du Colombier static void
writeinfo(Dev * d)193906943f9SDavid du Colombier writeinfo(Dev *d)
194906943f9SDavid du Colombier {
195906943f9SDavid du Colombier 	char buf[128];
196906943f9SDavid du Colombier 	char *s;
197906943f9SDavid du Colombier 	char *se;
198906943f9SDavid du Colombier 	Usbdev *ud;
199906943f9SDavid du Colombier 	Conf *c;
200906943f9SDavid du Colombier 	Iface *ifc;
201906943f9SDavid du Colombier 	int i, j;
202906943f9SDavid du Colombier 
203906943f9SDavid du Colombier 	ud = d->usb;
204906943f9SDavid du Colombier 	s = buf;
205906943f9SDavid du Colombier 	se = buf+sizeof(buf);
206906943f9SDavid du Colombier 	s = seprint(s, se, "info %s csp %#08ulx", classname(ud->class), ud->csp);
207906943f9SDavid du Colombier 	for(i = 0; i < ud->nconf; i++){
208906943f9SDavid du Colombier 		c = ud->conf[i];
209906943f9SDavid du Colombier 		if(c == nil)
210906943f9SDavid du Colombier 			break;
211906943f9SDavid du Colombier 		for(j = 0; j < nelem(c->iface); j++){
212906943f9SDavid du Colombier 			ifc = c->iface[j];
213906943f9SDavid du Colombier 			if(ifc == nil)
214906943f9SDavid du Colombier 				break;
215906943f9SDavid du Colombier 			if(ifc->csp != ud->csp)
216906943f9SDavid du Colombier 				s = seprint(s, se, " csp %#08ulx", ifc->csp);
217906943f9SDavid du Colombier 		}
218906943f9SDavid du Colombier 	}
219906943f9SDavid du Colombier 	s = seprint(s, se, " vid %06#x did %06#x", ud->vid, ud->did);
220906943f9SDavid du Colombier 	seprint(s, se, " %q %q", ud->vendor, ud->product);
221906943f9SDavid du Colombier 	devctl(d, "%s", buf);
222906943f9SDavid du Colombier }
223906943f9SDavid du Colombier 
224906943f9SDavid du Colombier int
startdev(Port * pp)225906943f9SDavid du Colombier startdev(Port *pp)
226906943f9SDavid du Colombier {
227906943f9SDavid du Colombier 	Dev *d;
228906943f9SDavid du Colombier 	Usbdev *ud;
229906943f9SDavid du Colombier 	Devtab *dt;
230906943f9SDavid du Colombier 	Sarg *sa;
231906943f9SDavid du Colombier 	Channel *rc;
232906943f9SDavid du Colombier 
233906943f9SDavid du Colombier 	d = pp->dev;
234906943f9SDavid du Colombier 	assert(d);
235906943f9SDavid du Colombier 	ud = d->usb;
236906943f9SDavid du Colombier 	assert(ud != nil);
237906943f9SDavid du Colombier 
238906943f9SDavid du Colombier 	writeinfo(d);
239906943f9SDavid du Colombier 
240906943f9SDavid du Colombier 	if(ud->class == Clhub){
241906943f9SDavid du Colombier 		/*
242906943f9SDavid du Colombier 		 * Hubs are handled directly by this process avoiding
243906943f9SDavid du Colombier 		 * concurrent operation so that at most one device
244906943f9SDavid du Colombier 		 * has the config address in use.
245906943f9SDavid du Colombier 		 * We cancel kernel debug for these eps. too chatty.
246906943f9SDavid du Colombier 		 */
247906943f9SDavid du Colombier 		pp->hub = newhub(d->dir, d);
248906943f9SDavid du Colombier 		if(pp->hub == nil)
249906943f9SDavid du Colombier 			fprint(2, "%s: %s: %r\n", argv0, d->dir);
250906943f9SDavid du Colombier 		else
251906943f9SDavid du Colombier 			fprint(2, "usb/hub... ");
252906943f9SDavid du Colombier 		if(usbdebug > 1)
253906943f9SDavid du Colombier 			devctl(d, "debug 0");	/* polled hubs are chatty */
254906943f9SDavid du Colombier 		return pp->hub == nil ? -1 : 0;
255906943f9SDavid du Colombier 	}
256906943f9SDavid du Colombier 
257906943f9SDavid du Colombier 	for(dt = devtab; dt->name != nil; dt++)
258906943f9SDavid du Colombier 		if(devmatch(dt, ud))
259906943f9SDavid du Colombier 			break;
260906943f9SDavid du Colombier 	/*
261906943f9SDavid du Colombier 	 * From here on the device is for the driver.
262906943f9SDavid du Colombier 	 * When we return pp->dev contains a Dev just for us
26339dc1420SDavid du Colombier 	 * with only the ctl open. Both devs are released on the last closedev:
264906943f9SDavid du Colombier 	 * driver's upon I/O errors and ours upon port dettach.
265906943f9SDavid du Colombier 	 */
266906943f9SDavid du Colombier 	if(dt->name == nil){
267906943f9SDavid du Colombier 		dprint(2, "%s: no configured entry for %s (csp %#08lx)\n",
268906943f9SDavid du Colombier 			argv0, d->dir, ud->csp);
269906943f9SDavid du Colombier 		close(d->dfd);
270906943f9SDavid du Colombier 		d->dfd = -1;
271906943f9SDavid du Colombier 		return 0;
272906943f9SDavid du Colombier 	}
273906943f9SDavid du Colombier 	sa = emallocz(sizeof(Sarg), 1);
274906943f9SDavid du Colombier 	sa->pp = pp;
275906943f9SDavid du Colombier 	sa->dt = dt;
276906943f9SDavid du Colombier 	rc = sa->rc = chancreate(sizeof(ulong), 1);
277906943f9SDavid du Colombier 	procrfork(startdevproc, sa, Stack, RFNOTEG);
278906943f9SDavid du Colombier 	if(recvul(rc) != 0)
279906943f9SDavid du Colombier 		free(sa);
280906943f9SDavid du Colombier 	chanfree(rc);
281906943f9SDavid du Colombier 	fprint(2, "usb/%s... ", dt->name);
282906943f9SDavid du Colombier 
283906943f9SDavid du Colombier 	sleep(Spawndelay);		/* in case we re-spawn too fast */
284906943f9SDavid du Colombier 	return 0;
285906943f9SDavid du Colombier }
286