#include #include #include #include "usb.h" #include "usbfs.h" #include "serial.h" #include "prolific.h" 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 }, }; int plmatch(char *info) { Cinfo *ip; char buf[50]; for(ip = plinfo; ip->vid != 0; ip++){ snprint(buf, sizeof buf, "vid %#06x did %#06x", ip->vid, ip->did); dsprint(2, "serial: %s %s\n", buf, info); if(strstr(info, buf) != nil) return 0; } return -1; } static void statusreader(void *u); static void dumpbuf(uchar *buf, int bufsz) { int i; for(i=0; idev, Rd2h | Rvendor | Rdev, VendorReadReq, val, index, buf, 1); dsprint(2, "serial: vendorread res:%d\n", res); return res; } static int vendorwrite(Serial *ser, int val, int index) { int res; dsprint(2, "serial: vendorwrite val: 0x%x idx:%d\n", val, index); res = usbcmd(ser->dev, Rh2d | Rvendor | Rdev, VendorWriteReq, val, index, nil, 0); dsprint(2, "serial: vendorwrite res:%d\n", res); return res; } /* BUG: I could probably read Dcr0 and set only the bits */ static int plmodemctl(Serial *ser, int set) { if(set == 0){ ser->mctl = 0; vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init); return 0; } ser->mctl = 1; if(ser->type == TypeHX) vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcX); else vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcH); return 0; } static int plgetparam(Serial *ser) { uchar buf[ParamReqSz]; int res; res = usbcmd(ser->dev, Rd2h | Rclass | Riface, GetLineReq, 0, 0, buf, sizeof buf); ser->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) ser->stop = 1; else if(buf[4] == 2) ser->stop = 2; ser->parity = buf[5]; ser->bits = buf[6]; dsprint(2, "serial: getparam: "); if(serialdebug) dumpbuf(buf, sizeof buf); dsprint(2, "serial: getparam res: %d\n", res); return res; } static int plsetparam(Serial *ser) { uchar buf[ParamReqSz]; int res; PUT4(buf, ser->baud); if(ser->stop == 1) buf[4] = 0; else if(ser->stop == 2) buf[4] = 2; /* see comment in getparam */ buf[5] = ser->parity; buf[6] = ser->bits; dsprint(2, "serial: setparam: "); if(serialdebug) dumpbuf(buf, sizeof buf); res = usbcmd(ser->dev, Rh2d | Rclass | Riface, SetLineReq, 0, 0, buf, sizeof buf); plmodemctl(ser, ser->mctl); plgetparam(ser); /* make sure our state corresponds */ dsprint(2, "serial: setparam res: %d\n", res); return res; } 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(Serial *ser) { char *st; uchar *buf; ulong csp, maxpkt, dno; buf = emallocz(VendorReqSz, 1); qlock(ser); serialreset(ser); 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(ser, 0x8484, 0, buf); vendorwrite(ser, 0x0404, 0); vendorread(ser, 0x8484, 0, buf); vendorread(ser, 0x8383, 0, buf); vendorread(ser, 0x8484, 0, buf); vendorwrite(ser, 0x0404, 1); vendorread(ser, 0x8484, 0, buf); vendorread(ser, 0x8383, 0, buf); vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init); vendorwrite(ser, Dcr1Idx|DcrSet, Dcr1Init); if(ser->type == TypeHX) vendorwrite(ser, Dcr2Idx|DcrSet, Dcr2InitX); else vendorwrite(ser, Dcr2Idx|DcrSet, Dcr2InitH); plgetparam(ser); qunlock(ser); free(buf); st = emallocz(255, 1); qlock(ser); if(serialdebug) serdumpst(ser, st, 255); dsprint(2, st); qunlock(ser); free(st); /* ser gets freed by closedev, the process has a reference */ incref(ser->dev); proccreate(statusreader, ser, 8*1024); return 0; } static int plsetbreak(Serial *ser, int val) { return usbcmd(ser->dev, Rh2d | Rclass | Riface, (val != 0? BreakOn: BreakOff), val, 0, nil, 0); } static int plclearpipes(Serial *ser) { if(ser->type == TypeHX){ vendorwrite(ser, PipeDSRst, 0); vendorwrite(ser, PipeUSRst, 0); }else{ if(unstall(ser->dev, ser->epout, Eout) < 0) dprint(2, "disk: unstall epout: %r\n"); if(unstall(ser->dev, ser->epin, Ein) < 0) dprint(2, "disk: unstall epin: %r\n"); if(unstall(ser->dev, ser->epintr, Ein) < 0) dprint(2, "disk: unstall epintr: %r\n"); } return 0; } static int setctlline(Serial *ser, uchar val) { return usbcmd(ser->dev, Rh2d | Rclass | Riface, SetCtlReq, val, 0, nil, 0); } static void composectl(Serial *ser) { if(ser->rts) ser->ctlstate |= CtlRTS; else ser->ctlstate &= ~CtlRTS; if(ser->dtr) ser->ctlstate |= CtlDTR; else ser->ctlstate &= ~CtlDTR; } int plsendlines(Serial *ser) { int res; dsprint(2, "serial: sendlines: %#2.2x\n", ser->ctlstate); composectl(ser); res = setctlline(ser, ser->ctlstate); dsprint(2, "serial: sendlines res: %d\n", res); return 0; } static int plreadstatus(Serial *ser) { int nr, dfd; char err[40]; uchar buf[VendorReqSz]; qlock(ser); dsprint(2, "serial: reading from interrupt\n"); dfd = ser->epintr->dfd; qunlock(ser); nr = read(dfd, buf, sizeof buf); qlock(ser); snprint(err, sizeof err, "%r"); dsprint(2, "serial: interrupt read %d %r\n", nr); if(nr < 0 && strstr(err, "timed out") != nil){ dsprint(2, "serial: need to recover, status read %d %r\n", nr); if(serialrecover(ser, err) < 0){ qunlock(ser); return -1; } } if(nr < 0) dsprint(2, "serial: reading status: %r"); else if(nr >= sizeof buf - 1){ ser->dcd = buf[8] & DcdStatus; ser->dsr = buf[8] & DsrStatus; ser->cts = buf[8] & BreakerrStatus; ser->ring = buf[8] & RingStatus; ser->cts = buf[8] & CtsStatus; if(buf[8] & FrerrStatus) ser->nframeerr++; if(buf[8] & ParerrStatus) ser->nparityerr++; if(buf[8] & OvererrStatus) ser->novererr++; } else dsprint(2, "serial: bad status read %d\n", nr); dsprint(2, "serial: finished read from interrupt %d\n", nr); qunlock(ser); return 0; } static void statusreader(void *u) { Serial *ser; ser = u; threadsetname("statusreaderproc"); while(plreadstatus(ser) >= 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(Serial *ser) { devctl(ser->epin, "maxpkt 256"); devctl(ser->epout, "maxpkt 256"); return 0; } Serialops plops = { .init = plinit, .getparam = plgetparam, .setparam = plsetparam, .clearpipes = plclearpipes, .sendlines = plsendlines, .modemctl = plmodemctl, .setbreak = plsetbreak, .seteps = plseteps, };