#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include "usb.h"
#include "serial.h"
enum {
/* flavours of the device */
TypeH,
TypeHX,
TypeUnk,
RevH = 0x0202,
RevX = 0x0300,
RevHX = 0x0400,
Rev1 = 0x0001,
/* usbcmd parameters */
SetLineReq = 0x20,
SetCtlReq = 0x22,
BreakReq = 0x23,
BreakOn = 0xffff,
BreakOff = 0x0000,
GetLineReq = 0x21,
VendorWriteReq = 0x01, /* BUG: is this a standard request? */
VendorReadReq = 0x01,
ParamReqSz = 7,
VendorReqSz = 10,
/* status read from interrupt endpoint */
DcdStatus = 0x01,
DsrStatus = 0x02,
BreakerrStatus = 0x04,
RingStatus = 0x08,
FrerrStatus = 0x10,
ParerrStatus = 0x20,
OvererrStatus = 0x40,
CtsStatus = 0x80,
DcrGet = 0x80,
DcrSet = 0x00,
Dcr0Idx = 0x00,
Dcr0Init = 0x0001,
Dcr0HwFcH = 0x0040,
Dcr0HwFcX = 0x0060,
Dcr1Idx = 0x01,
Dcr1Init = 0x0000,
Dcr1InitH = 0x0080,
Dcr1InitX = 0x0000,
Dcr2Idx = 0x02,
Dcr2InitH = 0x0024,
Dcr2InitX = 0x0044,
PipeDSRst = 0x08,
PipeUSRst = 0x09,
};
enum {
PL2303Vid = 0x067b,
PL2303Did = 0x2303,
PL2303DidRSAQ2 = 0x04bb,
PL2303DidDCU11 = 0x1234,
PL2303DidPHAROS = 0xaaa0,
PL2303DidRSAQ3 = 0xaaa2,
PL2303DidALDIGA = 0x0611,
PL2303DidMMX = 0x0612,
PL2303DidGPRS = 0x0609,
ATENVid = 0x0557,
ATENVid2 = 0x0547,
ATENDid = 0x2008,
IODATAVid = 0x04bb,
IODATADid = 0x0a03,
IODATADidRSAQ5 = 0x0a0e,
ELCOMVid = 0x056e,
ELCOMDid = 0x5003,
ELCOMDidUCSGT = 0x5004,
ITEGNOVid = 0x0eba,
ITEGNODid = 0x1080,
ITEGNODid2080 = 0x2080,
MA620Vid = 0x0df7,
MA620Did = 0x0620,
RATOCVid = 0x0584,
RATOCDid = 0xb000,
TRIPPVid = 0x2478,
TRIPPDid = 0x2008,
RADIOSHACKVid = 0x1453,
RADIOSHACKDid = 0x4026,
DCU10Vid = 0x0731,
DCU10Did = 0x0528,
SITECOMVid = 0x6189,
SITECOMDid = 0x2068,
/* Alcatel OT535/735 USB cable */
ALCATELVid = 0x11f7,
ALCATELDid = 0x02df,
/* Samsung I330 phone cradle */
SAMSUNGVid = 0x04e8,
SAMSUNGDid = 0x8001,
SIEMENSVid = 0x11f5,
SIEMENSDidSX1 = 0x0001,
SIEMENSDidX65 = 0x0003,
SIEMENSDidX75 = 0x0004,
SIEMENSDidEF81 = 0x0005,
SYNTECHVid = 0x0745,
SYNTECHDid = 0x0001,
/* Nokia CA-42 Cable */
NOKIACA42Vid = 0x078b,
NOKIACA42Did = 0x1234,
/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */
CA42CA42Vid = 0x10b5,
CA42CA42Did = 0xac70,
SAGEMVid = 0x079b,
SAGEMDid = 0x0027,
/* Leadtek GPS 9531 (ID 0413:2101) */
LEADTEKVid = 0x0413,
LEADTEK9531Did = 0x2101,
/* USB GSM cable from Speed Dragon Multimedia, Ltd */
SPEEDDRAGONVid = 0x0e55,
SPEEDDRAGONDid = 0x110b,
/* DATAPILOT Universal-2 Phone Cable */
BELKINVid = 0x050d,
BELKINDid = 0x0257,
/* Belkin "F5U257" Serial Adapter */
DATAPILOTU2Vid = 0x0731,
DATAPILOTU2Did = 0x2003,
ALCORVid = 0x058F,
ALCORDid = 0x9720,
/* Willcom WS002IN Data Driver (by NetIndex Inc.) */,
WS002INVid = 0x11f6,
WS002INDid = 0x2001,
/* Corega CG-USBRS232R Serial Adapter */,
COREGAVid = 0x07aa,
COREGADid = 0x002a,
/* Y.C. Cable U.S.A., Inc - USB to RS-232 */,
YCCABLEVid = 0x05ad,
YCCABLEDid = 0x0fba,
/* "Superial" USB - Serial */,
SUPERIALVid = 0x5372,
SUPERIALDid = 0x2303,
/* Hewlett-Packard LD220-HP POS Pole Display */,
HPVid = 0x03f0,
HPLD220Did = 0x3524,
};
Cinfo plinfo[] = {
{ PL2303Vid, PL2303Did },
{ PL2303Vid, PL2303DidRSAQ2 },
{ PL2303Vid, PL2303DidDCU11 },
{ PL2303Vid, PL2303DidRSAQ3 },
{ PL2303Vid, PL2303DidPHAROS },
{ PL2303Vid, PL2303DidALDIGA },
{ PL2303Vid, PL2303DidMMX },
{ PL2303Vid, PL2303DidGPRS },
{ IODATAVid, IODATADid },
{ IODATAVid, IODATADidRSAQ5 },
{ ATENVid, ATENDid },
{ ATENVid2, ATENDid },
{ ELCOMVid, ELCOMDid },
{ ELCOMVid, ELCOMDidUCSGT },
{ ITEGNOVid, ITEGNODid },
{ ITEGNOVid, ITEGNODid2080 },
{ MA620Vid, MA620Did },
{ RATOCVid, RATOCDid },
{ TRIPPVid, TRIPPDid },
{ RADIOSHACKVid,RADIOSHACKDid },
{ DCU10Vid, DCU10Did },
{ SITECOMVid, SITECOMDid },
{ ALCATELVid, ALCATELDid },
{ SAMSUNGVid, SAMSUNGDid },
{ SIEMENSVid, SIEMENSDidSX1 },
{ SIEMENSVid, SIEMENSDidX65 },
{ SIEMENSVid, SIEMENSDidX75 },
{ SIEMENSVid, SIEMENSDidEF81 },
{ SYNTECHVid, SYNTECHDid },
{ NOKIACA42Vid, NOKIACA42Did },
{ CA42CA42Vid, CA42CA42Did },
{ SAGEMVid, SAGEMDid },
{ LEADTEKVid, LEADTEK9531Did },
{ SPEEDDRAGONVid,SPEEDDRAGONDid },
{ DATAPILOTU2Vid,DATAPILOTU2Did },
{ BELKINVid, BELKINDid },
{ ALCORVid, ALCORDid },
{ WS002INVid, WS002INDid },
{ COREGAVid, COREGADid },
{ YCCABLEVid, YCCABLEDid },
{ SUPERIALVid, SUPERIALDid },
{ HPVid, HPLD220Did },
{ 0, 0 },
};
static Serialops plops;
int
plprobe(Serial *ser)
{
Usbdev *ud = ser->dev->usb;
if(matchid(plinfo, ud->vid, ud->did) == nil)
return -1;
ser->hasepintr = 1;
ser->Serialops = plops;
return 0;
}
static void statusreader(void *u);
static void
dumpbuf(uchar *buf, int bufsz)
{
int i;
for(i=0; i<bufsz; i++)
print("buf[%d]=%#ux ", i, buf[i]);
print("\n");
}
static int
vendorread(Serialport *p, int val, int index, uchar *buf)
{
int res;
Serial *ser;
ser = p->s;
dsprint(2, "serial: vendorread val: 0x%x idx:%d buf:%p\n",
val, index, buf);
res = usbcmd(ser->dev, Rd2h | Rvendor | Rdev, VendorReadReq,
val, index, buf, 1);
if(res != 1) fprint(2, "serial: vendorread failed with res=%d\n", res);
return res;
}
static int
vendorwrite(Serialport *p, int val, int index)
{
int res;
Serial *ser;
ser = p->s;
dsprint(2, "serial: vendorwrite val: 0x%x idx:%d\n", val, index);
res = usbcmd(ser->dev, Rh2d | Rvendor | Rdev, VendorWriteReq,
val, index, nil, 0);
if(res != 8) fprint(2, "serial: vendorwrite failed with res=%d\n", res);
return res;
}
/* BUG: I could probably read Dcr0 and set only the bits */
static int
plmodemctl(Serialport *p, int set)
{
Serial *ser;
ser = p->s;
if(set == 0){
p->mctl = 0;
vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init);
return 0;
}
p->mctl = 1;
if(ser->type == TypeHX)
vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcX);
else
vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcH);
return 0;
}
static int
plgetparam(Serialport *p)
{
uchar buf[ParamReqSz];
int res;
Serial *ser;
ser = p->s;
res = usbcmd(ser->dev, Rd2h | Rclass | Riface, GetLineReq,
0, 0, buf, sizeof buf);
if(res != ParamReqSz)
memset(buf, 0, sizeof(buf));
p->baud = GET4(buf);
/*
* with the Pl9 interface it is not possible to set `1.5' as stop bits
* for the prologic:
* 0 is 1 stop bit
* 1 is 1.5 stop bits
* 2 is 2 stop bits
*/
if(buf[4] == 1)
fprint(2, "warning, stop bit set to 1.5 unsupported");
else if(buf[4] == 0)
p->stop = 1;
else if(buf[4] == 2)
p->stop = 2;
p->parity = buf[5];
p->bits = buf[6];
dsprint(2, "serial: getparam: ");
if(serialdebug)
dumpbuf(buf, sizeof buf);
if(res == ParamReqSz)
return 0;
fprint(2, "serial: plgetparam failed with res=%d\n", res);
if(res >= 0) werrstr("plgetparam failed with res=%d", res);
return -1;
}
static int
plsetparam(Serialport *p)
{
uchar buf[ParamReqSz];
int res;
Serial *ser;
ser = p->s;
PUT4(buf, p->baud);
if(p->stop == 1)
buf[4] = 0;
else if(p->stop == 2)
buf[4] = 2; /* see comment in getparam */
buf[5] = p->parity;
buf[6] = p->bits;
dsprint(2, "serial: setparam: ");
if(serialdebug)
dumpbuf(buf, sizeof buf);
res = usbcmd(ser->dev, Rh2d | Rclass | Riface, SetLineReq,
0, 0, buf, sizeof buf);
if(res != 8+ParamReqSz){
fprint(2, "serial: plsetparam failed with res=%d\n", res);
if(res >= 0) werrstr("plsetparam failed with res=%d", res);
return -1;
}
plmodemctl(p, p->mctl);
if(plgetparam(p) < 0) /* make sure our state corresponds */
return -1;
return 0;
}
static int
revid(ulong devno)
{
switch(devno){
case RevH:
return TypeH;
case RevX:
case RevHX:
case Rev1:
return TypeHX;
default:
return TypeUnk;
}
}
/* linux driver says the release id is not always right */
static int
heuristicid(ulong csp, ulong maxpkt)
{
if(Class(csp) == 0x02)
return TypeH;
else if(maxpkt == 0x40)
return TypeHX;
else if(Class(csp) == 0x00 || Class(csp) == 0xFF)
return TypeH;
else{
fprint(2, "serial: chip unknown, setting to HX version\n");
return TypeHX;
}
}
static int
plinit(Serialport *p)
{
char *st;
uchar *buf;
ulong csp, maxpkt, dno;
Serial *ser;
ser = p->s;
buf = emallocz(VendorReqSz, 1);
dsprint(2, "plinit\n");
csp = ser->dev->usb->csp;
maxpkt = ser->dev->maxpkt;
dno = ser->dev->usb->dno;
if((ser->type = revid(dno)) == TypeUnk)
ser->type = heuristicid(csp, maxpkt);
dsprint(2, "serial: type %d\n", ser->type);
vendorread(p, 0x8484, 0, buf);
vendorwrite(p, 0x0404, 0);
vendorread(p, 0x8484, 0, buf);
vendorread(p, 0x8383, 0, buf);
vendorread(p, 0x8484, 0, buf);
vendorwrite(p, 0x0404, 1);
vendorread(p, 0x8484, 0, buf);
vendorread(p, 0x8383, 0, buf);
vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init);
vendorwrite(p, Dcr1Idx|DcrSet, Dcr1Init);
if(ser->type == TypeHX)
vendorwrite(p, Dcr2Idx|DcrSet, Dcr2InitX);
else
vendorwrite(p, Dcr2Idx|DcrSet, Dcr2InitH);
plgetparam(p);
qunlock(ser);
free(buf);
qlock(ser);
if(serialdebug){
st = emallocz(255, 1);
serdumpst(p, st, 255);
dsprint(2, "%s", st);
free(st);
}
/* p gets freed by closedev, the process has a reference */
incref(ser->dev);
proccreate(statusreader, p, 8*1024);
return 0;
}
static int
plsetbreak(Serialport *p, int val)
{
Serial *ser;
ser = p->s;
return usbcmd(ser->dev, Rh2d | Rclass | Riface,
(val != 0? BreakOn: BreakOff), val, 0, nil, 0);
}
static int
plclearpipes(Serialport *p)
{
Serial *ser;
ser = p->s;
if(ser->type == TypeHX){
vendorwrite(p, PipeDSRst, 0);
vendorwrite(p, PipeUSRst, 0);
}else{
if(unstall(ser->dev, p->epout, Eout) < 0)
dprint(2, "disk: unstall epout: %r\n");
if(unstall(ser->dev, p->epin, Ein) < 0)
dprint(2, "disk: unstall epin: %r\n");
if(unstall(ser->dev, p->epintr, Ein) < 0)
dprint(2, "disk: unstall epintr: %r\n");
}
return 0;
}
static int
setctlline(Serialport *p, uchar val)
{
Serial *ser;
ser = p->s;
return usbcmd(ser->dev, Rh2d | Rclass | Riface, SetCtlReq,
val, 0, nil, 0);
}
static void
composectl(Serialport *p)
{
if(p->rts)
p->ctlstate |= CtlRTS;
else
p->ctlstate &= ~CtlRTS;
if(p->dtr)
p->ctlstate |= CtlDTR;
else
p->ctlstate &= ~CtlDTR;
}
static int
plsendlines(Serialport *p)
{
int res;
dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate);
composectl(p);
res = setctlline(p, p->ctlstate);
dsprint(2, "serial: sendlines res: %d\n", res);
return 0;
}
static int
plreadstatus(Serialport *p)
{
int nr, dfd;
char err[ERRMAX];
uchar buf[VendorReqSz];
Serial *ser;
ser = p->s;
qlock(ser);
dfd = p->epintr->dfd;
qunlock(ser);
nr = read(dfd, buf, sizeof buf);
qlock(ser);
rerrstr(err, sizeof err);
if(nr < 0 && strstr(err, "timed out") == nil){
if(serialrecover(ser, nil, nil, err) < 0){
qunlock(ser);
return -1;
}
}
if(nr < 0)
dsprint(2, "serial: reading status: %r\n");
else if(nr >= sizeof buf - 1){
p->dcd = buf[8] & DcdStatus;
p->dsr = buf[8] & DsrStatus;
p->cts = buf[8] & BreakerrStatus;
p->ring = buf[8] & RingStatus;
p->cts = buf[8] & CtsStatus;
if(buf[8] & FrerrStatus)
p->nframeerr++;
if(buf[8] & ParerrStatus)
p->nparityerr++;
if(buf[8] & OvererrStatus)
p->novererr++;
} else
dsprint(2, "serial: bad status read %d\n", nr);
qunlock(ser);
return 0;
}
static void
statusreader(void *u)
{
Serialport *p;
Serial *ser;
p = u;
ser = p->s;
threadsetname("statusreaderproc");
while(plreadstatus(p) >= 0)
;
fprint(2, "serial: statusreader exiting\n");
closedev(ser->dev);
}
/*
* Maximum number of bytes transferred per frame
* The output buffer size cannot be increased due to the size encoding
*/
static int
plseteps(Serialport *p)
{
devctl(p->epin, "maxpkt 256");
devctl(p->epout, "maxpkt 256");
return 0;
}
static Serialops plops = {
.init = plinit,
.getparam = plgetparam,
.setparam = plsetparam,
.clearpipes = plclearpipes,
.sendlines = plsendlines,
.modemctl = plmodemctl,
.setbreak = plsetbreak,
.seteps = plseteps,
};