git: plan9front

Download patch

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,
+};
--