Plan 9 from Bell Labs’s /usr/web/sources/patch/applied/usbdusage/usbd.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <thread.h>
#include "usb.h"

#include "dat.h"
#include "fns.h"

#define STACKSIZE 128*1024

static int dontfork;

Ref	busy;

int debug;

typedef struct Enum Enum;
struct Enum
{
	Hub *hub;
	int port;
};

void (*dprinter[])(Device *, int, ulong, void *b, int n) = {
	[STRING] pstring,
	[DEVICE] pdevice,
	[0x29] phub,
};

static void
usage(void)
{
	fprint(2, "usage: usbd [-dfDV]\n");
	threadexitsall("usage");
}

void
work(void *a)
{
	int port;
	Hub *hub;
	Enum *arg;

	hub = a;
	for (port = 1; port <= hub->nport; port++) {
		if (debug)
			fprint(2, "enumerate port %H.%d\n", hub, port);
		arg = emallocz(sizeof(Enum), 1);
		arg->hub = hub;
		arg->port = port;
		incref(&busy);
		threadcreate(enumerate, arg, STACKSIZE);
	}

	decref(&busy);
	for(;;) {
		yield();
		if (busy.ref == 0)
			sleep(2000);
	}
}

void
threadmain(int argc, char **argv)
{
	int i;
	Hub *h;

	ARGBEGIN{
	case 'f':
		dontfork=1;
		break;
	case 'D':
		debugdebug++;
		break;
	case 'd':
		debug = atoi(EARGF(usage()));
		break;
	case 'V':
		verbose = 1;
		break;
	}ARGEND
	if (argc)
		usage();
	if (access("/dev/usb0", 0) < 0 && bind("#U", "/dev", MBEFORE) < 0)
		sysfatal("%s: can't bind -b #U /dev: %r\n", argv0);

	usbfmtinit();
	fmtinstall('H', Hfmt);

	/* always fork off usb[1—n] */
	for(i=1; (h = roothub(i)) != nil; i++) {
		incref(&busy);
		proccreate(work, h, STACKSIZE);
	}

	/* usb0 might be handled in this proc */
	if((h = roothub(0)) != nil){
		incref(&busy);
		if (dontfork) {
			work(h);
		} else {
			rfork(RFNOTEG);
			proccreate(work, h, STACKSIZE);
			/* don't hold window open */
			close(0);
			close(1);
			if(!debug && !verbose)
				close(2);
		}
	}
	if (debug)
		fprint(2, "done\n");
	while (busy.ref)
		sleep(100);
	threadexits(nil);
}

void
enumerate(void *v)
{
	int i, port;
	Device *d;
	Enum *arg;
	Hub *h, *nh;

	arg = v;
	h = arg->hub;
	port = arg->port;
	free(arg);

	for (;;) {
		if((portstatus(h, port) & (1<<PORT_CONNECTION)) == 0) {
			decref(&busy);
			if (verbose)
				fprint(2, "usbd: %H: port %d empty\n", h, port);
			do {
				yield();
				if (debugdebug)
					fprint(2, "usbd: probing %H.%d\n",
						h, port);
				sleep(500);
			} while((portstatus(h, port) & (1<<PORT_CONNECTION)) == 0);
			incref(&busy);
		}
		if(verbose)
			fprint(2, "usbd: %H: port %d attached\n", h, port);
		d = configure(h, port);
		if(d == nil) {
			if(verbose)
				fprint(2, "usbd: can't configure port %H.%d\n", h, port);
			decref(&busy);
			threadexits("configure");
		}
		if(d->class == Hubclass) {
			if(debug)
				fprint(2, "usbd: %H.%d: hub %d attached\n",
					h, port, d->id);
			setconfig(d, 1);
			nh = newhub(h, d);
			if(nh == nil) {
				detach(h, port);
				decref(&busy);
				threadexits("describehub");
			}
			if(debug)
				fprint(2, "usbd: traversing hub %H\n", nh);
			/* TO DO: initialise status endpoint */
			for(i=1; i<=nh->nport; i++)
				portpower(nh, i, 1);
			sleep(nh->pwrms);
			for(i=1; i<=nh->nport; i++) {
				arg = emallocz(sizeof(Enum), 1);
				arg->hub = nh;
				arg->port = i;
				incref(&busy);
				threadcreate(enumerate, arg, STACKSIZE);
			}
		}else{
			if(debug)
				fprint(2,
					"usbd: %H.%d: %d: not hub, %s speed\n",
					h, port, d->id, d->ls?"low":"high");
			setconfig(d, 1);	/* TO DO */
//unconscionable kludge (testing camera)
if(d->class == 10) setup0(d, RH2D|Rinterface, SET_INTERFACE, 10, 0, 0);
		}
		decref(&busy);
		while(portstatus(h, port) & (1<<PORT_CONNECTION)) {
			if (debugdebug)
				fprint(2, "checking %H.%d\n", h, port);
			yield();
			if (d->state == Detached) {
				if (verbose)
					fprint(2,
					 "%H: port %d detached by parent hub\n",
						h, port);
				/* parent hub died */
				threadexits(nil);
			}
		}
		if(verbose)
			fprint(2, "%H: port %d detached\n", h, port);
		detach(h, port);
	}
}

Device*
configure(Hub *h, int port)
{
	Port *p;
	Device *d;
	int i, s, maxpkt, ls;

	portenable(h, port, 1);
	sleep(20);
	portreset(h, port);
	sleep(20);
	s = portstatus(h, port);
	if (debug)
		fprint(2, "%H.%d status %#ux\n", h, port, s);
	if ((s & (1<<PORT_CONNECTION)) == 0)
		return nil;
	if ((s & (1<<PORT_SUSPEND)) == 0) {
		if (debug)
			fprint(2, "enabling port %H.%d\n", h, port);
		portenable(h, port, 1);
		s = portstatus(h, port);
		if (debug)
			fprint(2, "%H.%d status now %#ux\n", h, port, s);
	}

	ls = (s & (1<<PORT_LOW_SPEED)) != 0;
	devspeed(h->dev0, ls);
	maxpkt = getmaxpkt(h->dev0);
	if(debugdebug)
		fprint(2, "%H.%d maxpkt: %d\n", h, port, maxpkt);
	if(maxpkt < 0) {
Error0:
		portenable(h, port, 0);
		return nil;
	}
	d = opendev(h->ctlrno, -1);
	d->ls = ls;
	d->state = Enabled;
	d->ep[0]->maxpkt = maxpkt;
	if(fprint(d->ctl, "maxpkt 0 %d", maxpkt) < 0) {
Error1:
		closedev(d);
		goto Error0;
	}
	if(setaddress(h->dev0, d->id) < 0)
		goto Error1;
	d->state = Assigned;
	devspeed(d, ls);
	if(describedevice(d) < 0)
		goto Error1;

	/* read configurations 0 to n */
	for(i=0; i<d->nconf; i++) {
		if(d->config[i] == nil)
			d->config[i] = mallocz(sizeof(*d->config[i]),1);
		loadconfig(d, i);
	}
	for(i=0; i<16; i++)
		setdevclass(d, i);
	p = &h->port[port-1];
	p->d = d;
	return d;
}

void
detach(Hub *h, int port)
{
	Port *p;
	Device *d;

	p = &h->port[port-1];
	if(p->hub != nil) {
		freehub(p->hub);
		p->hub = nil;
	}
	d = p->d;
	d->state = Detached;	/* return i/o error on access */
	closedev(d);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.