ref: 81f4d1d7fc259f1e05dd101586fe98b7c38c211f
parent: 30cccba7ec86185fa726c61a67560cf8406a6393
author: Arne <cgnarne@netcologne.de>
date: Sat Jun 14 15:56:22 EDT 2025
nusb/serial: add support for cdc serial devices add support for usb cdc serial devices compatible with the Abstract Control Model interface. this also introduces a new member to struct Serialops for a custom method to set up usb endpoints.
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/acm.c
@@ -1,0 +1,149 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include "usb.h"
+#include "serial.h"
+
+enum {
+ SetCommReq = 0x02,
+ SetLineReq = 0x20,
+ BreakReq = 0x23,
+
+ BreakOn = 0xffff,
+ BreakOff = 0x0000,
+
+ ParamReqSz = 7,
+
+ Fncm = 0x1, /* call management */
+ Fnacm = 0x2, /* abstract control management */
+};
+
+static Serialops acmops;
+static int did;
+static int acmsetparam(Serialport *);
+static int acmmultiplex(Serial *);
+static int acmsetbreak(Serialport *, int);
+
+int acmprobe(Serial *ser)
+{
+ int i, acmcap;
+ Usbdev *ud;
+ uchar *b;
+ Desc *dd;
+
+ acmcap = did = -1;
+ ud = ser->dev->usb;
+ for(i = 0; i < nelem(ud->ddesc); i++)
+ if((dd = ud->ddesc[i]) != nil){
+ b = (uchar *)&dd->data;
+ if(b[1] == Dfunction && b[2] == Fncm){
+ did = b[4];
+ }
+ else if(b[1] == Dfunction && b[2] == Fnacm)
+ acmcap = b[3];
+ }
+
+ if(did < 0 || acmcap < 0)
+ return -1;
+
+ ser->Serialops = acmops;
+
+ return 0;
+}
+
+static int
+acmsetparam(Serialport *p)
+{
+ uchar buf[ParamReqSz];
+ int res;
+ Serial *ser;
+
+ ser = p->s;
+
+ PUT4(buf, p->baud);
+
+ buf[4] = p->stop;
+ buf[5] = p->parity;
+ buf[6] = p->bits;
+
+ dsprint(2, "serial: setparam: ");
+ res = usbcmd(ser->dev, Rh2d | Rclass | Riface, SetLineReq,
+ 0, 0, buf, sizeof buf);
+ if(res != ParamReqSz){
+ fprint(2, "serial: acmsetparam failed with res=%d\n", res);
+ if(res >= 0) werrstr("acmsetparam failed with res=%d", res);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+acminit(Serialport *p)
+{
+ p->baud = 9600;
+ p->stop = 1;
+ p->bits = 8;
+ acmops.setparam(p);
+ return 0;
+}
+
+static int
+acmwait4data(Serialport *p, uchar *data, int count)
+{
+ int n;
+
+ qunlock(p->s);
+ while ((n = read(p->epin->dfd, data, count)) == 0)
+ ;
+ qlock(p->s);
+ return n;
+}
+
+static int
+acmfindeps(Serial *ser, int ifc)
+{
+ Ep **eps, *ep, *epin, *epout, *epintr;
+ Usbdev *ud;
+ Conf *c;
+ int i;
+
+ eps = nil;
+ ud = ser->dev->usb;
+ c = ud->conf[0];
+ for(i = 0; i < nelem(c->iface); i++)
+ if(c->iface[i] != nil && c->iface[i]->id == did){
+ eps = c->iface[i]->ep;
+ break;
+ }
+
+ epintr = epin = epout = nil;
+ for(i = 0; i < Nep; i++){
+ if((ep = eps[i]) == nil)
+ break;
+ if(ser->hasepintr && ep->type == Eintr && ep->dir == Ein && epintr == nil)
+ epintr = ep;
+ if(ep->type == Ebulk){
+ if((ep->dir == Ein || ep->dir == Eboth) && epin == nil)
+ epin = ep;
+ if((ep->dir == Eout || ep->dir == Eboth) && epout == nil)
+ epout = ep;
+ }
+ }
+ if(epin == nil || epout == nil)
+ return -1;
+ if(openeps(&ser->p[ifc], epin, epout, nil) < 0)
+ return -1;
+
+ return 0;
+}
+
+static Serialops acmops = {
+ .init = acminit,
+ .setparam = acmsetparam,
+ .wait4data = acmwait4data,
+ .findeps = acmfindeps,
+};
+
--- a/sys/src/cmd/nusb/serial/ch340.c
+++ b/sys/src/cmd/nusb/serial/ch340.c
@@ -168,4 +168,5 @@
static Serialops chops = {
.init = chinit,
.setparam = chsetparam,
+ .findeps = findendpoints,
};
--- a/sys/src/cmd/nusb/serial/ftdi.c
+++ b/sys/src/cmd/nusb/serial/ftdi.c
@@ -1568,6 +1568,7 @@
static Serialops ftops = {
.init = ftinit,
.seteps = ftseteps,
+ .findeps = findendpoints,
.setparam = ftsetparam,
.clearpipes = ftclearpipes,
.reset = ftreset,
--- a/sys/src/cmd/nusb/serial/mkfile
+++ b/sys/src/cmd/nusb/serial/mkfile
@@ -1,7 +1,7 @@
</$objtype/mkfile
TARG=serial
-OFILES=ftdi.$O serial.$O prolific.$O ucons.$O silabs.$O ch340.$O
+OFILES=ftdi.$O serial.$O prolific.$O ucons.$O silabs.$O ch340.$O acm.$O
HFILES=\
../lib/usb.h\
serial.h\
--- a/sys/src/cmd/nusb/serial/prolific.c
+++ b/sys/src/cmd/nusb/serial/prolific.c
@@ -615,4 +615,5 @@
.modemctl = plmodemctl,
.setbreak = plsetbreak,
.seteps = plseteps,
+ .findeps = findendpoints,
};
--- a/sys/src/cmd/nusb/serial/serial.c
+++ b/sys/src/cmd/nusb/serial/serial.c
@@ -561,7 +561,7 @@
qunlock(ser);
}
-static int
+int
openeps(Serialport *p, Ep *epin, Ep *epout, Ep *epintr)
{
Serial *ser;
@@ -614,7 +614,7 @@
return 0;
}
-static int
+int
findendpoints(Serial *ser, int ifc)
{
Ep **eps, *ep, *epin, *epout, *epintr;
@@ -708,6 +708,7 @@
extern int slprobe(Serial *ser);
extern int chprobe(Serial *ser);
extern int uconsprobe(Serial *ser);
+extern int acmprobe(Serial *ser);
void
threadmain(int argc, char* argv[])
@@ -745,7 +746,8 @@
&& ftprobe(ser)
&& slprobe(ser)
&& plprobe(ser)
- && chprobe(ser))
+ && chprobe(ser)
+ && acmprobe(ser))
sysfatal("no serial devices found");
for(i = 0; i < ser->nifcs; i++){
@@ -755,7 +757,8 @@
p->s = ser;
if(i == ser->jtag)
p->isjtag++;
- if(findendpoints(ser, i) < 0)
+ assert(ser->findeps != nil);
+ if(ser->findeps(ser, i) < 0)
sysfatal("no endpoints found for ifc %d", i);
p->w4data = chancreate(sizeof(ulong), 0);
p->gotdata = chancreate(sizeof(ulong), 0);
--- a/sys/src/cmd/nusb/serial/serial.h
+++ b/sys/src/cmd/nusb/serial/serial.h
@@ -4,6 +4,7 @@
struct Serialops {
int (*seteps)(Serialport*);
+ int (*findeps)(Serial*, int);
int (*init)(Serialport*);
int (*getparam)(Serialport*);
int (*setparam)(Serialport*);
@@ -120,5 +121,7 @@
int serialrecover(Serial *ser, Serialport *p, Dev *ep, char *err);
int serialreset(Serial *ser);
+int findendpoints(Serial *ser, int ifc);
+int openeps(Serialport *p, Ep *epin, Ep *epout, Ep *epintr);
char *serdumpst(Serialport *p, char *buf, int bufsz);
Cinfo *matchid(Cinfo *tab, int vid, int did);
--- a/sys/src/cmd/nusb/serial/silabs.c
+++ b/sys/src/cmd/nusb/serial/silabs.c
@@ -130,4 +130,5 @@
.getparam = slgetparam,
.setparam = slsetparam,
.wait4data = wait4data,
+ .findeps = findendpoints,
};
--- a/sys/src/cmd/nusb/serial/ucons.c
+++ b/sys/src/cmd/nusb/serial/ucons.c
@@ -20,6 +20,8 @@
{ 0, 0, 0 },
};
+static Serialops uconsops;
+
int
uconsprobe(Serial *ser)
{
@@ -29,5 +31,10 @@
if((ip = matchid(uconsinfo, ud->vid, ud->did)) == nil)
return -1;
ser->nifcs = ip->cid;
+ ser->Serialops = uconsops;
return 0;
}
+
+static Serialops uconsops = {
+ .findeps = findendpoints,
+};
--
⑨