shithub: plan9front

Download patch

ref: 556eea1f5a6cd0f1eeb74156ff3899803d107822
parent: 5bce05b759c695fdd5bc71fe04c00407406e644b
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Thu Jan 5 01:34:12 EST 2023

nusb/usbd: add /dev/usbhubctl file to control port power and led indicators

Experimental feature:

echo portpower <hub> <port> on/off > /dev/usbhubctl
echo portindicator <hub> <port> on/off > /dev/usbhubctl

Where <hub> is the device number of the hub (as in /dev/usb/epX)
and <port> is the port index (1..n).

Hub and port numbers can be found by reading /dev/usb/ctl.

--- a/sys/src/cmd/nusb/usbd/fns.h
+++ b/sys/src/cmd/nusb/usbd/fns.h
@@ -4,3 +4,4 @@
 Hub*	newhub(char *, Dev*);
 int	hname(char *);
 void	checkidle(void);
+int	portfeature(Hub*, int, int, int);
--- a/sys/src/cmd/nusb/usbd/hub.c
+++ b/sys/src/cmd/nusb/usbd/hub.c
@@ -13,8 +13,8 @@
 
 static char *dsname[] = { "disabled", "attached", "configed" };
 
-static int
-hubfeature(Hub *h, int port, int f, int on)
+int
+portfeature(Hub *h, int port, int f, int on)
 {
 	int cmd;
 
@@ -202,12 +202,12 @@
 		devctl(h->dev, "info hub csp %#08ulx ports %d vid %#.4ux did %#.4ux %q %q",
 			ud->csp, h->nport, ud->vid, ud->did, ud->vendor, ud->product);
 		for(i = 1; i <= h->nport; i++)
-			if(hubfeature(h, i, Fportpower, 1) < 0)
+			if(portfeature(h, i, Fportpower, 1) < 0)
 				fprint(2, "%s: %s: power: %r\n", argv0, fn);
 		sleep(h->pwrms);
 		for(i = 1; i <= h->nport; i++)
 			if(h->leds != 0)
-				hubfeature(h, i, Fportindicator, 1);
+				portfeature(h, i, Fportindicator, 1);
 	}
 	h->next = hubs;
 	hubs = h;
@@ -374,7 +374,7 @@
 		sp = "super";
 	} else {
 		sleep(Enabledelay);
-		if(hubfeature(h, p, Fportreset, 1) < 0){
+		if(portfeature(h, p, Fportreset, 1) < 0){
 			dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
 			goto Fail;
 		}
@@ -384,7 +384,7 @@
 			goto Fail;
 		if((sts & PSenable) == 0){
 			dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
-			hubfeature(h, p, Fportenable, 1);
+			portfeature(h, p, Fportenable, 1);
 			sts = portstatus(h, p);
 			if((sts & PSenable) == 0)
 				goto Fail;
@@ -468,7 +468,7 @@
 	if(pp->hub != nil)
 		pp->hub = nil;	/* hub closed by enumhub */
 	if(!h->dev->isusb3)
-		hubfeature(h, p, Fportenable, 0);
+		portfeature(h, p, Fportenable, 0);
 	if(nd != nil)
 		devctl(nd, "detach");
 	closedev(nd);
@@ -549,7 +549,7 @@
 	d = h->dev;
 	pp = &h->port[p];
 	dprint(2, "%s: %s: port %d: resetting\n", argv0, d->dir, p);
-	if(hubfeature(h, p, Fportreset, 1) < 0){
+	if(portfeature(h, p, Fportreset, 1) < 0){
 		dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
 		goto Fail;
 	}
@@ -587,7 +587,7 @@
 	pp->sts = 0;
 	portdetach(h, p);
 	if(!d->isusb3)
-		hubfeature(h, p, Fportenable, 0);
+		portfeature(h, p, Fportenable, 0);
 }
 
 static int
@@ -627,7 +627,7 @@
 	onhubs = nhubs;
 	if(!h->dev->isusb3){
 		if((sts & PSsuspend) != 0){
-			if(hubfeature(h, p, Fportsuspend, 0) < 0)
+			if(portfeature(h, p, Fportsuspend, 0) < 0)
 				dprint(2, "%s: %s: port %d: unsuspend: %r\n", argv0, d->dir, p);
 			sleep(Enabledelay);
 			sts = portstatus(h, p);
--- a/sys/src/cmd/nusb/usbd/usbd.c
+++ b/sys/src/cmd/nusb/usbd/usbd.c
@@ -10,6 +10,7 @@
 enum {
 	Qroot,
 	Qusbevent,
+	Qusbhubctl,
 	Qmax
 };
 
@@ -16,9 +17,11 @@
 char *names[] = {
 	"",
 	"usbevent",
+	"usbhubctl",
 };
 
 static char Enonexist[] = "does not exist";
+static char Eperm[] = "permission denied";
 
 typedef struct Event Event;
 
@@ -146,12 +149,19 @@
 		return -1;
 	d->qid.path = n + 1;
 	d->qid.vers = 0;
-	if(n >= 0){
-		d->qid.type = 0;
-		d->mode = 0444;
-	}else{
+	switch((ulong)d->qid.path){
+	case Qroot:
 		d->qid.type = QTDIR;
 		d->mode = 0555 | DMDIR;
+		break;
+	case Qusbevent:
+		d->qid.type = 0;
+		d->mode = 0444;
+		break;
+	case Qusbhubctl:
+		d->qid.type = 0;
+		d->mode = 0222;
+		break;
 	}
 	d->uid = estrdup9p(getuser());
 	d->gid = estrdup9p(d->uid);
@@ -211,6 +221,69 @@
 }
 
 static void
+usbdwrite(Req *req)
+{
+	extern QLock hublock;
+	extern Hub *hubs;
+	Hub *hub;
+	Cmdbuf *cb;
+	int hubid, port, feature, on;
+
+	if((long)req->fid->qid.path != Qusbhubctl){
+		respond(req, Enonexist);
+		return;
+	}
+	cb = parsecmd(req->ifcall.data, req->ifcall.count);
+	if(cb->nf < 4){
+		respond(req, "not enougth arguments");
+		goto out;
+	}
+	if(strcmp(cb->f[0], "portpower") == 0)
+		feature = Fportpower;
+	else if(strcmp(cb->f[0], "portindicator") == 0)
+		feature = Fportindicator;
+	else {
+		respond(req, "unnown feature");
+		goto out;
+	}
+	hubid = atoi(cb->f[1]);
+	port = atoi(cb->f[2]);
+	if(strcmp(cb->f[3], "on") == 0)
+		on = 1;
+	else if(strcmp(cb->f[3], "off") == 0)
+		on = 0;
+	else
+		on = atoi(cb->f[3]) != 0;
+
+	qlock(&hublock);
+	for(hub = hubs; hub != nil; hub = hub->next)
+		if(hub->dev->id == hubid)
+			break;
+	if(hub == nil){
+		qunlock(&hublock);
+		respond(req, "unknown hub");
+		goto out;
+	}
+	if(port < 1 || port > hub->nport){
+		qunlock(&hublock);
+		respond(req, "unknown port");
+		goto out;
+	}
+	if(feature == Fportpower && hub->pwrmode != 1
+	|| feature == Fportindicator && !hub->leds){
+		qunlock(&hublock);
+		respond(req, "not supported");
+		goto out;
+	}
+	portfeature(hub, port, feature, on);
+	qunlock(&hublock);
+	req->ofcall.count = req->ifcall.count;
+	respond(req, nil);
+out:
+	free(cb);
+}
+
+static void
 usbdstat(Req *req)
 {
 	if(dirgen(req->fid->qid.path - 1, &req->d, nil) < 0)
@@ -265,10 +338,14 @@
 usbdopen(Req *req)
 {
 	extern QLock hublock;
+	Event *e;
 
-	if(req->fid->qid.path == Qusbevent){
-		Event *e;
-
+	switch((ulong)req->fid->qid.path){
+	case Qusbevent:
+		if(req->ifcall.mode != OREAD){
+			respond(req, Eperm);
+			return;
+		}
 		qlock(&hublock);
 		qlock(&evlock);
 
@@ -279,6 +356,13 @@
 
 		qunlock(&evlock);
 		qunlock(&hublock);
+		break;
+	case Qusbhubctl:
+		if((req->ifcall.mode&~OTRUNC) != OWRITE){
+			respond(req, Eperm);
+			return;
+		}
+		break;
 	}
 	respond(req, nil);
 }
@@ -350,6 +434,7 @@
 	.attach = usbdattach,
 	.walk1 = usbdwalk,
 	.read = usbdread,
+	.write = usbdwrite,
 	.stat = usbdstat,
 	.open = usbdopen,
 	.flush = usbdflush,