285 lines · 5.8 KB · C
RawDownload
/* * Framework for USB devices. * Some of them may be embedded into usbd and some of * them may exist as /bin/usb/* binaries on their own. * * When embedded, devmain() is given a ref of an already * configured and open Dev. If devmain() * does not fail it should release this ref when done and * use incref to add further refs to it. */#include <u.h>#include <libc.h>#include <thread.h>#include "usb.h"#include "usbd.h" static Lock masklck;extern Devtab devtab[];static char* cputype; intgetdevnb(uvlong *maskp){	int i; 	lock(&masklck);	for(i = 0; i < 8 * sizeof *maskp; i++)		if((*maskp & (1ULL<<i)) == 0){			*maskp |= 1ULL<<i;			unlock(&masklck);			return i;		}	unlock(&masklck);	return -1;} voidputdevnb(uvlong *maskp, int id){	lock(&masklck);	if(id >= 0)		*maskp &= ~(1ULL<<id);	unlock(&masklck);}		static intcspmatch(Devtab *dt, int dcsp){	int	i;	int	csp; 	for(i = 0; i < nelem(dt->csps); i++)		if((csp=dt->csps[i]) != 0)		if(csp == dcsp)			return 1;		else if((csp&DCL) && (csp&~DCL) == Class(dcsp))			return 1;	return 0;} static intdevmatch(Devtab *dt, Usbdev *d){	int i;	int c;	Conf *cp; 	if(dt->noauto)		return 0;	if(dt->vid != -1 && d->vid != dt->vid)		return 0;	if(dt->did != -1 && d->did != dt->did)		return 0;	if(cspmatch(dt, d->csp))		return 1;	for(c = 0; c < Nconf; c++)		if((cp=d->conf[c]) != nil)			for(i = 0; i < Niface; i++)				if(cp->iface[i] != nil)					if(cspmatch(dt, cp->iface[i]->csp))						return 1;	return 0;} /* We can't use procexec to execute drivers, because * procexec mounts #| at /mnt/temp and we do *not* * have /mnt/temp at boot time. * Instead, we use access to guess if we can execute the file. * and reply as procexec. Be careful that the child inherits * all the shared state of the thread library. It should run unnoticed. */static voidxexec(Channel *c, char *nm, char *args[]){	int	pid; 	if(access(nm, AEXEC) == 0){		pid = rfork(RFFDG|RFREND|RFPROC);		switch(pid){		case 0:			exec(nm, args);			_exits("exec");		case -1:			break;		default:			sendul(c, pid);			threadexits(nil);		}	}} typedef struct Sarg Sarg;struct Sarg{	Port *pp;	Devtab* dt;	Channel*rc;	char fname[80];	char	args[128];	char	*argv[40];}; static voidstartdevproc(void *a){	Sarg	*sa = a;	Dev	*d;	Devtab *dt;	int	argc;	char *args, *argse, **argv;	char *fname; 	threadsetgrp(threadid());	d = sa->pp->dev;	dt = sa->dt;	args = sa->args;	argse = sa->args + sizeof sa->args;	argv = sa->argv;	fname = sa->fname;	sa->pp->devmaskp = &dt->devmask;	sa->pp->devnb = getdevnb(&dt->devmask);	if(sa->pp->devnb < 0){		sa->pp->devmaskp = nil;		sa->pp->devnb = 0;	}else		args = seprint(args, argse, "-N %d", sa->pp->devnb);	if(dt->args != nil)		seprint(args, argse, " %s", dt->args);	args = sa->args;	dprint(2, "%s: start: %s %s\n", argv0, dt->name, args);	argv[0] = dt->name;	argc = 1;	if(args[0] != 0)		argc += tokenize(args, argv+1, nelem(sa->argv)-2);	argv[argc] = nil;	if(dt->init == nil){		if(d->dfd > 0 ){			close(d->dfd);			d->dfd = -1;		}		rfork(RFCFDG);		open("/dev/null", OREAD);		open("/dev/cons", OWRITE);		open("/dev/cons", OWRITE); 		xexec(sa->rc, argv[0], argv);		snprint(fname, sizeof(sa->fname), "/bin/usb/%s", dt->name);		xexec(sa->rc, fname, argv);		snprint(fname, sizeof(sa->fname), "/boot/%s", dt->name);		xexec(sa->rc, fname, argv);		if(cputype == nil)			cputype = getenv("cputype");		if(cputype != nil){			snprint(fname, sizeof(sa->fname), "/%s/bin/%s",				cputype, dt->name);			argv[0] = fname;			xexec(sa->rc, fname, argv);		}		fprint(2, "%s: %s: not found. can't exec\n", argv0, dt->name);		sendul(sa->rc, -1);		threadexits("exec");	}else{		sa->pp->dev = opendev(d->dir);		sendul(sa->rc, 0);		if(dt->init(d, argc, argv) < 0)			fprint(2, "%s: %s: %r\n", argv0, dt->name);		closedev(d);		free(sa);	}	threadexits(nil);} static voidwriteinfo(Dev *d){	char buf[128];	char *s;	char *se;	Usbdev *ud;	Conf *c;	Iface *ifc;	int i, j; 	ud = d->usb;	s = buf;	se = buf+sizeof(buf);	s = seprint(s, se, "info %s csp %#08ulx", classname(ud->class), ud->csp);	for(i = 0; i < ud->nconf; i++){		c = ud->conf[i];		if(c == nil)			break;		for(j = 0; j < nelem(c->iface); j++){			ifc = c->iface[j];			if(ifc == nil)				break;			if(ifc->csp != ud->csp)				s = seprint(s, se, " csp %#08ulx", ifc->csp);		}	}	s = seprint(s, se, " vid %06#x did %06#x", ud->vid, ud->did);	seprint(s, se, " %q %q", ud->vendor, ud->product);	devctl(d, "%s", buf);} intstartdev(Port *pp){	Dev *d;	Usbdev *ud;	Devtab *dt;	Sarg *sa;	Channel *rc; 	d = pp->dev;	assert(d);	ud = d->usb;	assert(ud != nil); 	writeinfo(d); 	if(ud->class == Clhub){		/*		 * Hubs are handled directly by this process avoiding		 * concurrent operation so that at most one device		 * has the config address in use.		 * We cancel kernel debug for these eps. too chatty.		 */		pp->hub = newhub(d->dir, d);		if(pp->hub == nil)			fprint(2, "%s: %s: %r\n", argv0, d->dir);		else			fprint(2, "usb/hub... ");		if(usbdebug > 1)			devctl(d, "debug 0");	/* polled hubs are chatty */		return pp->hub == nil ? -1 : 0;	} 	for(dt = devtab; dt->name != nil; dt++)		if(devmatch(dt, ud))			break;	/*	 * From here on the device is for the driver.	 * When we return pp->dev contains a Dev just for us	 * with only the ctl open. Both devs are released on the last closedev:	 * driver's upon I/O errors and ours upon port dettach.	 */	if(dt->name == nil){		dprint(2, "%s: no configured entry for %s (csp %#08lx)\n",			argv0, d->dir, ud->csp);		close(d->dfd);		d->dfd = -1;		return 0;	}	sa = emallocz(sizeof(Sarg), 1);	sa->pp = pp;	sa->dt = dt;	rc = sa->rc = chancreate(sizeof(ulong), 1);	procrfork(startdevproc, sa, Stack, RFNOTEG);	if(recvul(rc) != 0)		free(sa);	chanfree(rc);	fprint(2, "usb/%s... ", dt->name); 	sleep(Spawndelay);		/* in case we re-spawn too fast */	return 0;}