/* Future Technology Devices International serial ports */
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "usb.h"
#include "usbfs.h"
#include "serial.h"
#include "ftdi.h"
/*
* BUG: This keeps growing, there has to be a better way, but without
* devices to try it... We can probably simply look for FTDI in the
* string, or use regular expressions somehow.
*/
Cinfo ftinfo[] = {
{ FTVid, FTACTZWAVEDid },
{ FTSheevaVid, FTSheevaDid },
{ FTVid, FTOpenRDUltDid},
{ FTVid, FTIRTRANSDid },
{ FTVid, FTIPLUSDid },
{ FTVid, FTSIODid },
{ FTVid, FT8U232AMDid },
{ FTVid, FT8U232AMALTDid },
{ FTVid, FT8U2232CDid },
{ FTVid, FTRELAISDid },
{ INTERBIOMVid, INTERBIOMIOBRDDid },
{ INTERBIOMVid, INTERBIOMMINIIOBRDDid },
{ FTVid, FTXF632Did },
{ FTVid, FTXF634Did },
{ FTVid, FTXF547Did },
{ FTVid, FTXF633Did },
{ FTVid, FTXF631Did },
{ FTVid, FTXF635Did },
{ FTVid, FTXF640Did },
{ FTVid, FTXF642Did },
{ FTVid, FTDSS20Did },
{ FTNFRICVid, FTNFRICDid },
{ FTVid, FTVNHCPCUSBDDid },
{ FTVid, FTMTXORB0Did },
{ FTVid, FTMTXORB1Did },
{ FTVid, FTMTXORB2Did },
{ FTVid, FTMTXORB3Did },
{ FTVid, FTMTXORB4Did },
{ FTVid, FTMTXORB5Did },
{ FTVid, FTMTXORB6Did },
{ FTVid, FTPERLEULTRAPORTDid },
{ FTVid, FTPIEGROUPDid },
{ SEALEVELVid, SEALEVEL2101Did },
{ SEALEVELVid, SEALEVEL2102Did },
{ SEALEVELVid, SEALEVEL2103Did },
{ SEALEVELVid, SEALEVEL2104Did },
{ SEALEVELVid, SEALEVEL22011Did },
{ SEALEVELVid, SEALEVEL22012Did },
{ SEALEVELVid, SEALEVEL22021Did },
{ SEALEVELVid, SEALEVEL22022Did },
{ SEALEVELVid, SEALEVEL22031Did },
{ SEALEVELVid, SEALEVEL22032Did },
{ SEALEVELVid, SEALEVEL24011Did },
{ SEALEVELVid, SEALEVEL24012Did },
{ SEALEVELVid, SEALEVEL24013Did },
{ SEALEVELVid, SEALEVEL24014Did },
{ SEALEVELVid, SEALEVEL24021Did },
{ SEALEVELVid, SEALEVEL24022Did },
{ SEALEVELVid, SEALEVEL24023Did },
{ SEALEVELVid, SEALEVEL24024Did },
{ SEALEVELVid, SEALEVEL24031Did },
{ SEALEVELVid, SEALEVEL24032Did },
{ SEALEVELVid, SEALEVEL24033Did },
{ SEALEVELVid, SEALEVEL24034Did },
{ SEALEVELVid, SEALEVEL28011Did },
{ SEALEVELVid, SEALEVEL28012Did },
{ SEALEVELVid, SEALEVEL28013Did },
{ SEALEVELVid, SEALEVEL28014Did },
{ SEALEVELVid, SEALEVEL28015Did },
{ SEALEVELVid, SEALEVEL28016Did },
{ SEALEVELVid, SEALEVEL28017Did },
{ SEALEVELVid, SEALEVEL28018Did },
{ SEALEVELVid, SEALEVEL28021Did },
{ SEALEVELVid, SEALEVEL28022Did },
{ SEALEVELVid, SEALEVEL28023Did },
{ SEALEVELVid, SEALEVEL28024Did },
{ SEALEVELVid, SEALEVEL28025Did },
{ SEALEVELVid, SEALEVEL28026Did },
{ SEALEVELVid, SEALEVEL28027Did },
{ SEALEVELVid, SEALEVEL28028Did },
{ SEALEVELVid, SEALEVEL28031Did },
{ SEALEVELVid, SEALEVEL28032Did },
{ SEALEVELVid, SEALEVEL28033Did },
{ SEALEVELVid, SEALEVEL28034Did },
{ SEALEVELVid, SEALEVEL28035Did },
{ SEALEVELVid, SEALEVEL28036Did },
{ SEALEVELVid, SEALEVEL28037Did },
{ SEALEVELVid, SEALEVEL28038Did },
{ IDTECHVid, IDTECHIDT1221UDid },
{ OCTVid, OCTUS101Did },
{ FTVid, FTHETIRA1Did }, /* special quirk div = 240 baud = B38400 rtscts = 1 */
{ FTVid, FTUSBUIRTDid }, /* special quirk div = 77, baud = B38400 */
{ FTVid, PROTEGOSPECIAL1 },
{ FTVid, PROTEGOR2X0 },
{ FTVid, PROTEGOSPECIAL3 },
{ FTVid, PROTEGOSPECIAL4 },
{ FTVid, FTGUDEADSE808Did },
{ FTVid, FTGUDEADSE809Did },
{ FTVid, FTGUDEADSE80ADid },
{ FTVid, FTGUDEADSE80BDid },
{ FTVid, FTGUDEADSE80CDid },
{ FTVid, FTGUDEADSE80DDid },
{ FTVid, FTGUDEADSE80EDid },
{ FTVid, FTGUDEADSE80FDid },
{ FTVid, FTGUDEADSE888Did },
{ FTVid, FTGUDEADSE889Did },
{ FTVid, FTGUDEADSE88ADid },
{ FTVid, FTGUDEADSE88BDid },
{ FTVid, FTGUDEADSE88CDid },
{ FTVid, FTGUDEADSE88DDid },
{ FTVid, FTGUDEADSE88EDid },
{ FTVid, FTGUDEADSE88FDid },
{ FTVid, FTELVUO100Did },
{ FTVid, FTELVUM100Did },
{ FTVid, FTELVUR100Did },
{ FTVid, FTELVALC8500Did },
{ FTVid, FTPYRAMIDDid },
{ FTVid, FTELVFHZ1000PCDid },
{ FTVid, FTELVCLI7000Did },
{ FTVid, FTELVPPS7330Did },
{ FTVid, FTELVTFM100Did },
{ FTVid, FTELVUDF77Did },
{ FTVid, FTELVUIO88Did },
{ FTVid, FTELVUAD8Did },
{ FTVid, FTELVUDA7Did },
{ FTVid, FTELVUSI2Did },
{ FTVid, FTELVT1100Did },
{ FTVid, FTELVPCD200Did },
{ FTVid, FTELVULA200Did },
{ FTVid, FTELVCSI8Did },
{ FTVid, FTELVEM1000DLDid },
{ FTVid, FTELVPCK100Did },
{ FTVid, FTELVRFP500Did },
{ FTVid, FTELVFS20SIGDid },
{ FTVid, FTELVWS300PCDid },
{ FTVid, FTELVFHZ1300PCDid },
{ FTVid, FTELVWS500Did },
{ FTVid, LINXSDMUSBQSSDid },
{ FTVid, LINXMASTERDEVEL2Did },
{ FTVid, LINXFUTURE0Did },
{ FTVid, LINXFUTURE1Did },
{ FTVid, LINXFUTURE2Did },
{ FTVid, FTCCSICDU200Did },
{ FTVid, FTCCSICDU401Did },
{ FTVid, INSIDEACCESSO },
{ INTREDidVid, INTREDidVALUECANDid },
{ INTREDidVid, INTREDidNEOVIDid },
{ FALCOMVid, FALCOMTWISTDid },
{ FALCOMVid, FALCOMSAMBADid },
{ FTVid, FTSUUNTOSPORTSDid },
{ FTVid, FTRMCANVIEWDid },
{ BANDBVid, BANDBUSOTL4Did },
{ BANDBVid, BANDBUSTL4Did },
{ BANDBVid, BANDBUSO9ML2Did },
{ FTVid, EVERECOPROCDSDid },
{ FTVid, FT4NGALAXYDE0Did },
{ FTVid, FT4NGALAXYDE1Did },
{ FTVid, FT4NGALAXYDE2Did },
{ FTVid, XSENSCONVERTER0Did },
{ FTVid, XSENSCONVERTER1Did },
{ FTVid, XSENSCONVERTER2Did },
{ FTVid, XSENSCONVERTER3Did },
{ FTVid, XSENSCONVERTER4Did },
{ FTVid, XSENSCONVERTER5Did },
{ FTVid, XSENSCONVERTER6Did },
{ FTVid, XSENSCONVERTER7Did },
{ MOBILITYVid, MOBILITYUSBSERIALDid },
{ FTVid, FTACTIVEROBOTSDid },
{ FTVid, FTMHAMKWDid },
{ FTVid, FTMHAMYSDid },
{ FTVid, FTMHAMY6Did },
{ FTVid, FTMHAMY8Did },
{ FTVid, FTMHAMICDid },
{ FTVid, FTMHAMDB9Did },
{ FTVid, FTMHAMRS232Did },
{ FTVid, FTMHAMY9Did },
{ FTVid, FTTERATRONIKVCPDid },
{ FTVid, FTTERATRONIKD2XXDid },
{ EVOLUTIONVid, EVOLUTIONER1Did },
{ FTVid, FTARTEMISDid },
{ FTVid, FTATIKATK16Did },
{ FTVid, FTATIKATK16CDid },
{ FTVid, FTATIKATK16HRDid },
{ FTVid, FTATIKATK16HRCDid },
{ KOBILVid, KOBILCONVB1Did },
{ KOBILVid, KOBILCONVKAANDid },
{ POSIFLEXVid, POSIFLEXPP7000Did },
{ FTVid, FTTTUSBDid },
{ FTVid, FTECLOCOM1WIREDid },
{ FTVid, FTWESTREXMODEL777Did },
{ FTVid, FTWESTREXMODEL8900FDid },
{ FTVid, FTPCDJDAC2Did },
{ FTVid, FTRRCIRKITSLOCOBUFFERDid },
{ FTVid, FTASKRDR400Did },
{ ICOMID1Vid, ICOMID1Did },
{ PAPOUCHVid, PAPOUCHTMUDid },
{ FTVid, FTACGHFDUALDid },
{ FT8U232AMDid, FT4232HDid },
{ 0, 0 },
};
enum {
Packsz = 64, /* default size */
Maxpacksz = 512,
Bufsiz = 4 * 1024,
};
static int
ftdiread(Serialport *p, int val, int index, int req, uchar *buf)
{
int res;
Serial *ser;
ser = p->s;
if(req != FTGETE2READ)
index |= p->interfc + 1;
dsprint(2, "serial: ftdiread %#p [%d] req: %#x val: %#x idx:%d buf:%p\n",
p, p->interfc, req, val, index, buf);
res = usbcmd(ser->dev, Rd2h | Rftdireq | Rdev, req, val, index, buf, 1);
dsprint(2, "serial: ftdiread res:%d\n", res);
return res;
}
static int
ftdiwrite(Serialport *p, int val, int index, int req)
{
int res;
Serial *ser;
ser = p->s;
index |= p->interfc + 1;
dsprint(2, "serial: ftdiwrite %#p [%d] req: %#x val: %#x idx:%d\n",
p, p->interfc, req, val, index);
res = usbcmd(ser->dev, Rh2d | Rftdireq | Rdev, req, val, index, nil, 0);
dsprint(2, "serial: ftdiwrite res:%d\n", res);
return res;
}
static int
ftmodemctl(Serialport *p, int set)
{
if(set == 0){
p->mctl = 0;
ftdiwrite(p, 0, 0, FTSETMODEMCTRL);
return 0;
}
p->mctl = 1;
ftdiwrite(p, 0, FTRTSCTSHS, FTSETFLOWCTRL);
return 0;
}
static ushort
ft232ambaudbase2div(int baud, int base)
{
int divisor3;
ushort divisor;
divisor3 = (base / 2) / baud;
if((divisor3 & 7) == 7)
divisor3++; /* round x.7/8 up to x+1 */
divisor = divisor3 >> 3;
divisor3 &= 7;
if(divisor3 == 1)
divisor |= 0xc000; /* 0.125 */
else if(divisor3 >= 4)
divisor |= 0x4000; /* 0.5 */
else if(divisor3 != 0)
divisor |= 0x8000; /* 0.25 */
if( divisor == 1)
divisor = 0; /* special case for maximum baud rate */
return divisor;
}
enum{
ClockNew = 48000000,
ClockOld = 12000000 / 16,
HetiraDiv = 240,
UirtDiv = 77,
};
static ushort
ft232ambaud2div(int baud)
{
return ft232ambaudbase2div(baud, ClockNew);
}
static ulong divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7};
static ulong
ft232bmbaudbase2div(int baud, int base)
{
int divisor3;
u32int divisor;
divisor3 = (base / 2) / baud;
divisor = divisor3 >> 3 | divfrac[divisor3 & 7] << 14;
/* Deal with special cases for highest baud rates. */
if( divisor == 1)
divisor = 0; /* 1.0 */
else if( divisor == 0x4001)
divisor = 1; /* 1.5 */
return divisor;
}
static ulong
ft232bmbaud2div (int baud)
{
return ft232bmbaudbase2div (baud, ClockNew);
}
static int
customdiv(Serial *ser)
{
if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did)
return HetiraDiv;
else if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTUSBUIRTDid)
return UirtDiv;
fprint(2, "serial: weird custom divisor\n");
return 0; /* shouldn't happen, break as much as I can */
}
static ulong
ftbaudcalcdiv(Serial *ser, int baud)
{
int cusdiv;
ulong divval;
if(baud == 38400 && (cusdiv = customdiv(ser)) != 0)
baud = ser->baudbase / cusdiv;
if(baud == 0)
baud = 9600;
switch(ser->type) {
case SIO:
switch(baud) {
case 300:
divval = FTb300;
break;
case 600:
divval = FTb600;
break;
case 1200:
divval = FTb1200;
break;
case 2400:
divval = FTb2400;
break;
case 4800:
divval = FTb4800;
break;
case 9600:
divval = FTb9600;
break;
case 19200:
divval = FTb19200;
break;
case 38400:
divval = FTb38400;
break;
case 57600:
divval = FTb57600;
break;
case 115200:
divval = FTb115200;
break;
default:
divval = FTb9600;
break;
}
break;
case FT8U232AM:
if(baud <= 3000000)
divval = ft232ambaud2div(baud);
else
divval = ft232ambaud2div(9600);
break;
case FT232BM:
case FT2232C:
case FTKINDR:
case FT2232H:
case FT4232H:
if(baud <= 3000000)
divval = ft232bmbaud2div(baud);
else
divval = ft232bmbaud2div(9600);
break;
default:
divval = ft232bmbaud2div(9600);
break;
}
return divval;
}
static int
ftsetparam(Serialport *p)
{
int res;
ushort val;
ulong bauddiv;
val = 0;
if(p->stop == 1)
val |= FTSETDATASTOPBITS1;
else if(p->stop == 2)
val |= FTSETDATASTOPBITS2;
else if(p->stop == 15)
val |= FTSETDATASTOPBITS15;
switch(p->parity){
case 0:
val |= FTSETDATAParNONE;
break;
case 1:
val |= FTSETDATAParODD;
break;
case 2:
val |= FTSETDATAParEVEN;
break;
case 3:
val |= FTSETDATAParMARK;
break;
case 4:
val |= FTSETDATAParSPACE;
break;
};
dsprint(2, "serial: setparam\n");
res = ftdiwrite(p, val, 0, FTSETDATA);
if(res < 0)
return res;
res = ftmodemctl(p, p->mctl);
if(res < 0)
return res;
bauddiv = ftbaudcalcdiv(p->s, p->baud);
res = ftdiwrite(p, bauddiv, (bauddiv>>16) & 1, FTSETBaudRate);
dsprint(2, "serial: setparam res: %d\n", res);
return res;
}
/* ser locked */
static void
ftgettype(Serial *ser)
{
int i, outhdrsz, dno, pksz;
ulong baudbase;
Conf *cnf;
pksz = Packsz;
/* Assume it is not the original SIO device for now. */
baudbase = ClockNew / 2;
outhdrsz = 0;
dno = ser->dev->usb->dno;
cnf = ser->dev->usb->conf[0];
ser->nifcs = 0;
for(i = 0; i < Niface; i++)
if(cnf->iface[i] != nil)
ser->nifcs++;
if(ser->nifcs > 1) {
/*
* Multiple interfaces. default assume FT2232C,
*/
if(dno == 0x500)
ser->type = FT2232C;
else if(dno == 0x600)
ser->type = FTKINDR;
else if(dno == 0x700){
ser->type = FT2232H;
pksz = Maxpacksz;
} else if(dno == 0x800){
ser->type = FT4232H;
pksz = Maxpacksz;
} else
ser->type = FT2232C;
ser->jtag = 0;
/*
* BM-type devices have a bug where dno gets set
* to 0x200 when serial is 0.
*/
if(dno < 0x500)
fprint(2, "serial: warning: dno %d too low for "
"multi-interface device\n", dno);
} else if(dno < 0x200) {
/* Old device. Assume it is the original SIO. */
ser->type = SIO;
baudbase = ClockOld/16;
outhdrsz = 1;
} else if(dno < 0x400)
/*
* Assume its an FT8U232AM (or FT8U245AM)
* (It might be a BM because of the iSerialNumber bug,
* but it will still work as an AM device.)
*/
ser->type = FT8U232AM;
else /* Assume it is an FT232BM (or FT245BM) */
ser->type = FT232BM;
ser->maxrtrans = ser->maxwtrans = pksz;
ser->baudbase = baudbase;
ser->outhdrsz = outhdrsz;
ser->inhdrsz = 2;
dsprint (2, "serial: detected type: %#x\n", ser->type);
}
int
ftmatch(Serial *ser, char *info)
{
Cinfo *ip;
char buf[50];
for(ip = ftinfo; 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){
if(ser != nil){
qlock(ser);
ftgettype(ser);
qunlock(ser);
}
return 0;
}
}
return -1;
}
static int
ftuseinhdr(Serialport *p, uchar *b)
{
if(b[0] & FTICTS)
p->cts = 1;
else
p->cts = 0;
if(b[0] & FTIDSR)
p->dsr = 1;
else
p->dsr = 0;
if(b[0] & FTIRI)
p->ring = 1;
else
p->ring = 0;
if(b[0] & FTIRLSD)
p->rlsd = 1;
else
p->rlsd = 0;
if(b[1] & FTIOE)
p->novererr++;
if(b[1] & FTIPE)
p->nparityerr++;
if(b[1] & FTIFE)
p->nframeerr++;
if(b[1] & FTIBI)
p->nbreakerr++;
return 0;
}
static int
ftsetouthdr(Serialport *p, uchar *b, int len)
{
if(p->s->outhdrsz != 0)
b[0] = FTOPORT | (FTOLENMSK & len);
return p->s->outhdrsz;
}
static int
wait4data(Serialport *p, uchar *data, int count)
{
int d;
Serial *ser;
ser = p->s;
qunlock(ser);
d = sendul(p->w4data, 1);
qlock(ser);
if(d <= 0)
return -1;
if(p->ndata >= count)
p->ndata -= count;
else{
count = p->ndata;
p->ndata = 0;
}
assert(count >= 0);
assert(p->ndata >= 0);
memmove(data, p->data, count);
if(p->ndata != 0)
memmove(p->data, p->data+count, p->ndata);
recvul(p->gotdata);
return count;
}
static int
wait4write(Serialport *p, uchar *data, int count)
{
int off, fd;
uchar *b;
Serial *ser;
ser = p->s;
b = emallocz(count+ser->outhdrsz, 1);
off = ftsetouthdr(p, b, count);
memmove(b+off, data, count);
fd = p->epout->dfd;
qunlock(ser);
count = write(fd, b, count+off);
qlock(ser);
free(b);
return count;
}
typedef struct Packser Packser;
struct Packser{
int nb;
uchar b[Bufsiz];
};
typedef struct Areader Areader;
struct Areader{
Serialport *p;
Channel *c;
};
static void
shutdownchan(Channel *c)
{
Packser *bp;
while((bp=nbrecvp(c)) != nil)
free(bp);
chanfree(c);
}
int
cpdata(Serial *ser, Serialport *port, uchar *out, uchar *in, int sz)
{
int i, ncp, ntotcp, pksz;
pksz = ser->maxrtrans;
ntotcp = 0;
for(i = 0; i < sz; i+= pksz){
ftuseinhdr(port, in + i);
if(sz - i > pksz)
ncp = pksz - ser->inhdrsz;
else
ncp = sz - i - ser->inhdrsz;
memmove(out, in + i + ser->inhdrsz, ncp);
out += ncp;
ntotcp += ncp;
}
return ntotcp;
}
static void
epreader(void *u)
{
int dfd, rcount, cl;
char err[40];
Areader *a;
Channel *c;
Packser *pk;
Serial *ser;
Serialport *p;
threadsetname("epreader proc");
a = u;
p = a->p;
ser = p->s;
c = a->c;
free(a);
qlock(ser);
dfd = p->epin->dfd;
qunlock(ser);
pk = nil;
do {
if (pk == nil)
pk = emallocz(sizeof(Packser), 1);
rcount = read(dfd, pk->b, sizeof pk->b);
if(serialdebug > 5)
dsprint(2, "%d %#ux%#ux ", rcount, p->data[0],
p->data[1]);
if(rcount < 0)
break;
if(rcount == 0)
continue;
if(rcount >= ser->inhdrsz){
rcount = cpdata(ser, p, pk->b, pk->b, rcount);
if(rcount != 0){
pk->nb = rcount;
cl = sendp(c, pk);
if(cl < 0){
/*
* if it was a time-out, I don't want
* to give back an error.
*/
rcount = 0;
break;
}
}else
free(pk);
pk = nil;
}
} while(rcount >= 0 || (rcount < 0 && strstr(err, "timed out") != nil));
if(rcount < 0)
fprint(2, "%s: error reading %s: %r\n", argv0, p->fs.name);
free(pk);
nbsendp(c, nil);
if(p->w4data != nil)
chanclose(p->w4data);
if(p->gotdata != nil)
chanclose(p->gotdata);
devctl(ser->dev, "detach");
closedev(ser->dev);
usbfsdel(&p->fs);
}
static void
statusreader(void *u)
{
Areader *a;
Channel *c;
Packser *pk;
Serialport *p;
Serial *ser;
int cl;
p = u;
ser = p->s;
threadsetname("statusreader thread");
/* big buffering, fewer bytes lost */
c = chancreate(sizeof(Packser *), 128);
a = emallocz(sizeof(Areader), 1);
a->p = p;
a->c = c;
incref(ser->dev);
proccreate(epreader, a, 16*1024);
while((pk = recvp(c)) != nil){
memmove(p->data, pk->b, pk->nb);
p->ndata = pk->nb;
free(pk);
dsprint(2, "serial: status reader %d \n", p->ndata);
/* consume it all */
while(p->ndata != 0){
dsprint(2, "serial: status reader to consume: %d\n",
p->ndata);
cl = recvul(p->w4data);
if(cl < 0)
break;
cl = sendul(p->gotdata, 1);
if(cl < 0)
break;
}
}
shutdownchan(c);
devctl(ser->dev, "detach");
closedev(ser->dev);
usbfsdel(&p->fs);
}
static int
ftreset(Serial *ser)
{
Serialport *p;
int i;
p = ser->p;
for(i = 0; i < Maxifc; i++)
if(!p[i].isjtag && p[i].s != nil)
ftdiwrite(&p[i], FTRESETCTLVAL, 0, FTRESET);
return 0;
}
static int
ftinit(Serialport *p)
{
Serial *ser;
ser = p->s;
incref(ser->dev);
threadcreate(statusreader, p, 8*1024);
return 0;
}
static int
ftsetbreak(Serialport *p, int val)
{
return ftdiwrite(p, (val != 0? FTSETBREAK: 0), 0, FTSETDATA);
}
static int
ftclearpipes(Serialport *p)
{
/* maybe can be done in one... */
ftdiwrite(p, FTRESETCTLVALPURGETX, 0, FTRESET);
ftdiwrite(p, FTRESETCTLVALPURGERX, 0, FTRESET);
return 0;
}
static int
setctlline(Serialport *p, uchar val)
{
return ftdiwrite(p, val | (val << 8), 0, FTSETMODEMCTRL);
}
static void
updatectlst(Serialport *p, int val)
{
if(p->rts)
p->ctlstate |= val;
else
p->ctlstate &= ~val;
}
static int
setctl(Serialport *p)
{
int res;
Serial *ser;
ser = p->s;
if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did){
fprint(2, "serial: cannot set lines for this device\n");
updatectlst(p, CtlRTS|CtlDTR);
p->rts = p->dtr = 1;
return -1;
}
/* NB: you can not set DTR and RTS with one control message */
updatectlst(p, CtlRTS);
res = setctlline(p, (CtlRTS<<8)|p->ctlstate);
if(res < 0)
return res;
updatectlst(p, CtlDTR);
res = setctlline(p, (CtlDTR<<8)|p->ctlstate);
if(res < 0)
return res;
return 0;
}
static int
ftsendlines(Serialport *p)
{
int res;
dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate);
res = setctl(p);
dsprint(2, "serial: sendlines res: %d\n", res);
return 0;
}
static int
ftseteps(Serialport *p)
{
char *s;
Serial *ser;
ser = p->s;
s = smprint("maxpkt %d", ser->maxrtrans);
devctl(p->epin, s);
free(s);
s = smprint("maxpkt %d", ser->maxwtrans);
devctl(p->epout, s);
free(s);
return 0;
}
Serialops ftops = {
.init = ftinit,
.seteps = ftseteps,
.setparam = ftsetparam,
.clearpipes = ftclearpipes,
.reset = ftreset,
.sendlines = ftsendlines,
.modemctl = ftmodemctl,
.setbreak = ftsetbreak,
.wait4data = wait4data,
.wait4write = wait4write,
};
|