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,