9front - general discussion about 9front
 help / color / mirror / Atom feed
* CDC ACM nusb/serial driver
@ 2020-07-30 21:18 Eli Cohen
  2020-07-30 21:48 ` Eli Cohen
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Eli Cohen @ 2020-07-30 21:18 UTC (permalink / raw)
  To: 9front

[-- Attachment #1: Type: text/plain, Size: 867 bytes --]

I've cleaned up this driver to closely reflect the OpenBSD umodem
driver and address the issue of changing serial.c in a cleaner way.

cinap, we talked about having to support multiple serial interfaces on
one device, from the looks of it the OpenBSD driver may actually *not*
support this. this patch attempts to do so, but I have no such devices
to test it with...

the nusb/serial driver still needs work. I'm mounting 9p over the
serial port and it's flaky, it eventually screws up. I also did this
with an FTDI device and that was flaky too, before I made any changes.
I'm not sure what's going on, in kprint I see 9p errors when it screws
up... dropped data maybe...?

the devices I'm working with, Arduino Megas, work pretty well with
nusb/serial, both FTDI and with this patch CDC ACM devices, but like I
said above, I have no other devices to test this with

[-- Attachment #2: serial.patch --]
[-- Type: text/x-patch, Size: 9547 bytes --]

diff -r 23bc1d0e2dd2 sys/src/cmd/nusb/serial/cdc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/cmd/nusb/serial/cdc.c	Thu Jul 30 21:06:29 2020 -0700
@@ -0,0 +1,301 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include "usb.h"
+#include "serial.h"
+
+enum {
+	Kyocera		= 0x0482,
+	AHK3001V	= 0x0203,
+};
+
+enum {
+	ScACM	= 2,
+	AT		= 1,
+	None	= 0,
+	LineCoding	= 0x20,
+	SetBreak	= 0x23,
+	ControlState = 0x22,
+};
+
+enum {
+	ClassCDCData = 0x0a,
+	SubclassData = 0,
+};
+
+enum {
+	SubCDCCM	= 1,
+	SubCDCACM	= 2,
+	SubCDCUnion	= 6,
+};
+
+typedef struct raw_desc {
+	uchar bLength;
+	uchar bDescriptorType;
+	uchar bDescriptorSubtype;
+	uchar bbytes[1];
+} raw_desc;
+
+typedef struct cm_desc {
+	uchar bLength;
+	uchar bDescriptorType;
+	uchar bDescriptorSubtype;
+	uchar bmCapabilities;
+	uchar bDataInterface;
+} cm_desc;
+
+enum {
+	CMDoesCM	= 1,
+	CMOverData	= 2,
+};
+
+typedef struct acm_desc {
+	uchar bLength;
+	uchar bDescriptorType;
+	uchar bDescriptorSubtype;
+	uchar bmCapabilities;
+} acm_desc;
+
+enum {
+	ACMHasFeature	= 1,
+	ACMHasLine		= 2,
+	ACMHasBreak		= 4,
+	ACMHasNetwork	= 8,
+};
+
+typedef struct union_desc {
+	uchar bLength;
+	uchar bDescriptorType;
+	uchar bDescriptorSubtype;
+	uchar bMasterInterface;
+	uchar bSlaveInterface[1];
+} union_desc;
+
+typedef struct cdc_notify {
+	uchar bmRequestType;
+	uchar bNotification;
+	ushort wValue;
+	ushort wIndex;
+	ushort wLength;
+	uchar data[16];
+} cdc_notify;
+
+enum {
+	Notification = 0xa1,
+	SerialState = 0x20,
+};
+
+enum {
+	DcdStatus	= 0x01,
+	DsrStatus	= 0x02,
+	RingStatus	= 0x08,
+};
+
+static int
+cdcsetparam(Serialport *p)
+{
+	int res;
+	uchar vals[7];
+
+	PUT4(vals, p->baud);
+
+	vals[4] = 0;
+	if(p->stop == 1)
+		vals[4] = 0;
+	if(p->stop == 15)
+		vals[4] = 1;
+	if(p->stop == 2)
+		vals[4] = 2;
+
+	vals[5] = p->parity;
+
+	vals[6] = p->bits;
+
+	res = usbcmd(p->s->dev, Rh2d | Rclass | Riface, LineCoding, 0, 0, vals, 7);
+
+	return res;
+}
+
+void
+getcaps(Serialport *p)
+{
+	int i, j;
+	Desc *desc;
+	Iface *iface;
+	raw_desc ddesc;
+	cm_desc dcm;
+	acm_desc dacm;
+	union_desc dunion;
+	int cur_ifc;
+
+	for (i = 0; i < Niface; i++) {
+		iface = p->s->dev->usb->conf[0]->iface[i];
+		if (iface == nil)
+			continue;
+
+		cur_ifc = iface->id;
+		if (cur_ifc != p->ctl_ifc && Class(iface->csp) == ClassCDCData && Subclass(iface->csp) == SubclassData && p->data_ifc == -1)
+			p->data_ifc = cur_ifc;
+
+		if (cur_ifc == p->ctl_ifc)
+			for (j = 0; j < Nddesc; j++) {
+				desc = p->s->dev->usb->ddesc[j];
+				if (desc == nil)
+					continue;
+
+				ddesc = (raw_desc)desc->data;
+				if (ddesc.bDescriptorType == Dfunction) {
+					switch (ddesc.bDescriptorSubtype) {
+					case SubCDCCM:
+						dcm = (cm_desc)ddesc;
+						p->data_ifc = dcm.bDataInterface;
+						break;
+					case SubCDCACM:
+						dacm = (acm_desc)ddesc;
+						p->hasbreak = dacm.bmCapabilities & ACMHasBreak;
+						break;
+					case SubCDCUnion:
+						dunion = (union_desc)ddesc;
+						p->data_ifc = dunion.bSlaveInterface[0];
+						break;
+					}
+				}
+			}
+	}
+}
+
+void
+statusreader(void *u)
+{
+	Serialport *p = u;
+	uchar buf[sizeof(cdc_notify)];
+	cdc_notify *notify;
+
+	notify = (cdc_notify*)buf;
+	threadsetname("statusreaderproc");
+
+	while(read(p->epintr->dfd, buf, sizeof(cdc_notify)) > 0) {
+		if (notify->bmRequestType == Notification && notify->bNotification == SerialState && notify->wLength == 2) {
+			p->dcd = notify->data[0] & DcdStatus;
+			p->dsr = notify->data[0] & DsrStatus;
+			p->ring = notify->data[0] & RingStatus;
+		} else {
+			fprint(2, "serial: unknown intr message: reqtype:%02x notify:%02x len:%d state:%02x\n", notify->bmRequestType, notify->bNotification, notify->wLength, notify->data[0]);
+		}
+	}
+
+	fprint(2, "serial: statusreader exiting: %r\n");
+	closedev(p->s->dev);
+}
+
+static int
+cdcinit(Serialport *p)
+{
+	p->baud = 115200;
+	p->bits = 8;
+	p->parity = 0;
+	p->stop = 1;
+	cdcsetparam(p);
+
+	incref(p->s->dev);
+	proccreate(statusreader, p, 8*1024);
+
+	return 0;
+}
+
+static int
+cdcsetbreak(Serialport *p, int val)
+{
+	if(p->hasbreak == 0)
+		return 0;
+
+	return usbcmd(p->s->dev, Rh2d | Rclass | Riface, SetBreak, (val != 0? 0xffff: 0x0000), 0, nil, 0);
+}
+
+static int
+cdcsendlines(Serialport *p)
+{
+	if(p->rts)
+		p->ctlstate |= CtlRTS;
+	else
+		p->ctlstate &= ~CtlRTS;
+	if(p->dtr)
+		p->ctlstate |= CtlDTR;
+	else
+		p->ctlstate &= ~CtlDTR;
+
+	return usbcmd(p->s->dev, Rh2d | Rclass | Riface, ControlState, p->ctlstate, 0, nil, 0);
+}
+
+static int
+cdcclearpipes(Serialport *p)
+{
+	if(unstall(p->s->dev, p->epout, Eout) < 0)
+		dprint(2, "serial: unstall epout: %r\n");
+	if(unstall(p->s->dev, p->epin, Ein) < 0)
+		dprint(2, "serial: unstall epin: %r\n");
+	if(unstall(p->s->dev, p->epintr, Ein) < 0)
+		dprint(2, "serial: unstall epintr: %r\n");
+
+	return 0;
+}
+
+static int
+cdcwait4data(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 Serialops cdcops = {
+	.init		= cdcinit,
+	.setparam	= cdcsetparam,
+	.setbreak	= cdcsetbreak,
+	.sendlines	= cdcsendlines,
+	.clearpipes	= cdcclearpipes,
+	.wait4data	= cdcwait4data,
+};
+
+int
+cdcprobe(Serial *ser)
+{
+	int i, ifcs;
+	Usbdev *ud = ser->dev->usb;
+	Iface *iface;
+	ulong csp;
+
+	ifcs = 0;
+	if (ud->vid == Kyocera || ud->did == AHK3001V)
+		ifcs++;
+	else for (i = 0; i < Niface; i++) {
+		iface = ud->conf[0]->iface[i];
+		if (iface == nil)
+			continue;
+
+		csp = iface->csp;
+		if (Class(csp) == Clcomms && Subclass(csp) == ScACM && (Proto(csp) == AT || Proto(csp) == None)) {
+			ser->p[ifcs].ctl_ifc = i;
+			ser->p[ifcs].s = ser;
+			getcaps(&ser->p[ifcs]);
+			ifcs++;
+		}
+	}
+
+	if (ifcs == 0)
+		return -1;
+
+	ser->nifcs = ifcs;
+	ser->isacm = 1;
+	ser->outhdrsz = 0;
+	ser->hasepintr = 1;
+	ser->Serialops = cdcops;
+	return 0;
+}
diff -r 23bc1d0e2dd2 sys/src/cmd/nusb/serial/mkfile
--- a/sys/src/cmd/nusb/serial/mkfile	Tue Jun 09 12:23:24 2020 -0700
+++ b/sys/src/cmd/nusb/serial/mkfile	Thu Jul 30 21:06:29 2020 -0700
@@ -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 cdc.$O
 HFILES=\
 	../lib/usb.h\
 	serial.h\
diff -r 23bc1d0e2dd2 sys/src/cmd/nusb/serial/serial.c
--- a/sys/src/cmd/nusb/serial/serial.c	Tue Jun 09 12:23:24 2020 -0700
+++ b/sys/src/cmd/nusb/serial/serial.c	Thu Jul 30 21:06:29 2020 -0700
@@ -624,23 +624,46 @@
 
 	epintr = epin = epout = -1;
 
-	/*
-	 * interfc 0 means start from the start which is equiv to
-	 * iterate through endpoints probably, could be done better
-	 */
-	eps = ser->dev->usb->conf[0]->iface[ifc]->ep;
+	if(ser->isacm){
+		eps = ser->dev->usb->conf[0]->iface[ser->p[ifc].ctl_ifc]->ep;
 
-	for(i = 0; i < Nep; i++){
-		if((ep = eps[i]) == nil)
-			continue;
-		if(ser->hasepintr && ep->type == Eintr &&
-		    ep->dir == Ein && epintr == -1)
-			epintr = ep->id;
-		if(ep->type == Ebulk){
-			if((ep->dir == Ein || ep->dir == Eboth) && epin == -1)
-				epin = ep->id;
-			if((ep->dir == Eout || ep->dir == Eboth) && epout == -1)
-				epout = ep->id;
+		for(i = 0; i < Nep; i++){
+			if((ep = eps[i]) == nil)
+				continue;
+			if(ep->type == Eintr && ep->dir == Ein && epintr == -1)
+				epintr = ep->id;
+		}
+
+		eps = ser->dev->usb->conf[0]->iface[ser->p[ifc].data_ifc]->ep;
+		for(i = 0; i < Nep; i++){
+			if((ep = eps[i]) == nil)
+				continue;
+			if(ep->type == Ebulk){
+				if((ep->dir == Ein || ep->dir == Eboth) && epin == -1)
+					epin = ep->id;
+				if((ep->dir == Eout || ep->dir == Eboth) && epout == -1)
+					epout = ep->id;
+			}
+		}
+	} else {
+		/*
+		 * interfc 0 means start from the start which is equiv to
+		 * iterate through endpoints probably, could be done better
+		 */
+		eps = ser->dev->usb->conf[0]->iface[ifc]->ep;
+
+		for(i = 0; i < Nep; i++){
+			if((ep = eps[i]) == nil)
+				continue;
+			if(ser->hasepintr && ep->type == Eintr &&
+			    ep->dir == Ein && epintr == -1)
+				epintr = ep->id;
+			if(ep->type == Ebulk){
+				if((ep->dir == Ein || ep->dir == Eboth) && epin == -1)
+					epin = ep->id;
+				if((ep->dir == Eout || ep->dir == Eboth) && epout == -1)
+					epout = ep->id;
+			}
 		}
 	}
 	dprint(2, "serial[%d]: ep ids: in %d out %d intr %d\n", ifc, epin, epout, epintr);
@@ -714,6 +737,7 @@
 extern int slprobe(Serial *ser);
 extern int chprobe(Serial *ser);
 extern int uconsprobe(Serial *ser);
+extern int cdcprobe(Serial *ser);
 
 void
 threadmain(int argc, char* argv[])
@@ -750,7 +774,8 @@
 	&& ftprobe(ser)
 	&& slprobe(ser)
 	&& plprobe(ser)
-	&& chprobe(ser))
+	&& chprobe(ser)
+	&& cdcprobe(ser))
 		sysfatal("no serial devices found");
 
 	for(i = 0; i < ser->nifcs; i++){
diff -r 23bc1d0e2dd2 sys/src/cmd/nusb/serial/serial.h
--- a/sys/src/cmd/nusb/serial/serial.h	Tue Jun 09 12:23:24 2020 -0700
+++ b/sys/src/cmd/nusb/serial/serial.h	Thu Jul 30 21:06:29 2020 -0700
@@ -60,6 +60,10 @@
 
 	int	interfc;	/* interfc on the device for ftdi */
 
+	int hasbreak;	/* CDC ACM may not have break */
+	int ctl_ifc;	/* epintr iface for CDC ACM */
+	int data_ifc;	/* epin/out iface for CDC ACM */
+
 	Channel *w4data;
 	Channel *gotdata;
 	int	ndata;
@@ -81,6 +85,7 @@
 
 	int	jtag;		/* index of jtag interface, -1 none */
 	int	nifcs;		/* # of serial interfaces, including JTAG */
+	int isacm; 		/* CDC ACM interfaces handled differently */
 	Serialport p[Maxifc];
 	int	maxrtrans;
 	int	maxwtrans;

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2020-10-05 21:54 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-30 21:18 CDC ACM nusb/serial driver Eli Cohen
2020-07-30 21:48 ` Eli Cohen
2020-07-31 15:38   ` [9front] " ori
2020-07-31 11:18 ` [9front] " Ethan Gardener
2020-08-01  5:35   ` Anthony Martin
2020-08-01 15:34     ` Ethan Gardener
2020-08-01 16:45       ` Eli Cohen
2020-08-01 20:32         ` Ethan Gardener
2020-08-02 13:34           ` cinap_lenrek
2020-08-03  6:27             ` Eli Cohen
2020-10-05 21:53               ` Eli Cohen
2020-08-01 15:34 ` cinap_lenrek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).