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 Sat Aug 01 16:37:56 2020 -0700 @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include <9p.h> +#include "usb.h" +#include "serial.h" + +enum { + Kyocera = 0x0482, + AHK3001V = 0x0203, +}; + +enum { + ScACM = 2, + AT = 1, + None = 0, +}; + +enum { + LineCoding = 0x20, + SetBreak = 0x23, + ControlState = 0x22, +}; + +enum { + ClassCDCData = 0x0a, + SubclassData = 0, +}; + +enum { + DescSubCDCCM = 1, + DescSubCDCACM = 2, + DescSubCDCUnion = 6, +}; + +enum { + CMDoesCM = 1, + CMOverData = 2, +}; + +enum { + ACMHasFeature = 1, + ACMHasLine = 2, + ACMHasBreak = 4, + ACMHasNetwork = 8, +}; + +typedef struct CDCNotify { + uchar bmRequestType; + uchar bNotification; + ushort wValue; + ushort wIndex; + ushort wLength; + uchar data[16]; +} CDCNotify; + +enum { + Notification = 0xa1, + SerialState = 0x20, +}; + +enum { + DcdStatus = 0x01, + DsrStatus = 0x02, + BreakStatus = 0x04, + 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, p->ctl_ifc, vals, 7); + + return res; +} + +void +getcaps(Serialport *p) +{ + int i, j; + Desc *desc; + Iface *iface; + DDesc *ddesc; + 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 = &desc->data; + if (ddesc->bDescriptorType == Dfunction) { + switch (ddesc->bbytes[0]) { + case DescSubCDCCM: + p->data_ifc = ddesc->bbytes[2]; + break; + case DescSubCDCACM: + p->hasbreak = ddesc->bbytes[1] & ACMHasBreak? 1: 0; + break; + case DescSubCDCUnion: + p->data_ifc = ddesc->bbytes[2]; + break; + } + } + } + } + } +} + +void +statusreader(void *u) +{ + Serialport *p = u; + uchar buf[24]; + CDCNotify notify; + + threadsetname("statusreaderproc"); + + while(readn(p->epintr->dfd, buf, sizeof(buf)) == sizeof(buf)) { + notify.bmRequestType = buf[0]; + notify.bNotification = buf[1]; + notify.wValue = buf[2] | (buf[3] << 8); + notify.wIndex = buf[4] | (buf[5] << 8); + notify.wLength = buf[6] | (buf[7] << 8); + memcpy(notify.data, &buf[8], 16); + + if (notify.bmRequestType == Notification && notify.bNotification == SerialState && notify.wLength == 2) { + p->dcd = notify.data[0] & DcdStatus? 1: 0; + p->dsr = notify.data[0] & DsrStatus? 1: 0; + p->ring = notify.data[0] & RingStatus? 1: 0; + } 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), p->ctl_ifc, 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, p->ctl_ifc, 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 Sat Aug 01 16:37:56 2020 -0700 @@ -1,7 +1,7 @@ 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(ep->type == Eintr && ep->dir == Ein && epintr == -1) + epintr = ep->id; + } + } + /* * 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[ifc]->ep; + else + 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(ser->hasepintr && ep->type == Eintr && - ep->dir == Ein && epintr == -1) - epintr = ep->id; + if(!ser->isacm && 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; @@ -714,6 +728,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 +765,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 Sat Aug 01 16:37:56 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;