ref: 3866717cbb020199d58171c1c0cdd7382a74ee82
dir: /emu/port/deveia-posix.c/
/*
* Driver for POSIX serial ports
*/
#include "dat.h"
#include "fns.h"
#include "error.h"
#undef _POSIX_C_SOURCE /* for deveia-bsd.c */
#include <sys/stat.h>
#include <termios.h>
enum
{
Devchar = 't',
Ndataqid = 1,
Nctlqid,
Nstatqid,
Nqid = 3, /* number of QIDs */
CTLS= 023,
CTLQ= 021,
Maxctl = 128,
Maxfield = 32
};
/*
* Macros to manage QIDs
*/
#define NETTYPE(x) ((x)&0x0F)
#define NETID(x) ((x)>>4)
#define NETQID(i,t) (((i)<<4)|(t))
static Dirtab *eiadir;
static int ndir;
static char Devname[] = "eia";
typedef struct Eia Eia;
struct Eia {
Ref r;
int fd;
int overrun;
int frame;
int restore; /* flag to restore prev. states */
struct termios ts;
int dtr;
int rts;
int cts;
};
static Eia *eia;
struct tcdef_t {
int val;
tcflag_t flag;
};
struct flagmap {
char* s;
tcflag_t flag;
};
static struct tcdef_t bps[];
static struct tcdef_t size[] = {
{5, CS5},
{6, CS6},
{7, CS7},
{8, CS8},
{-1, -1}
};
static char *
ftos(char *buf, struct tcdef_t *tbl, tcflag_t flag)
{
for(; tbl->val >= 0; tbl++)
if(tbl->flag == flag){
sprint(buf, "%d", tbl->val);
return buf;
}
return "unknown";
}
static tcflag_t
stof(struct tcdef_t *tbl, int val)
{
for(; tbl->val >= 0 && tbl->val != val; tbl++)
{}
return tbl->flag;
}
static char *
rdxtra(int port, struct termios *ts, char *str); /* non-POSIX extensions */
static long
rdstat(int port, void *buf, long n, ulong offset)
{
int fd = eia[port].fd;
struct termios ts;
char str[Maxctl];
char sbuf[20];
char *s;
if(tcgetattr(fd, &ts) < 0)
oserror();
s = str;
s += sprint(s, "opens %d ferr %d oerr %d baud %s",
eia[port].r.ref-1, eia[port].frame, eia[port].overrun,
ftos(sbuf, bps, (tcflag_t)cfgetospeed(&ts)));
s = rdxtra(port, &ts, s);
sprint(s, "\n");
return readstr(offset, buf, n, str);
}
static char *
wrxtra(int port, struct termios *ts, char *cmd); /* non-POSIX extensions */
static void
wrctl(int port, char *cmd)
{
struct termios ts;
char *xerr;
int r, nf, n, i;
char *f[Maxfield];
int fd = eia[port].fd;
tcflag_t flag;
if(tcgetattr(fd, &ts) < 0) {
Error:
oserror();
}
nf = tokenize(cmd, f, nelem(f));
for(i = 0; i < nf; i++){
if(strncmp(f[i], "break", 5) == 0){
tcsendbreak(fd, 0);
continue;
}
n = atoi(f[i]+1);
switch(*f[i]) {
case 'F':
case 'f':
if(tcflush(fd, TCOFLUSH) < 0)
goto Error;
break;
case 'K':
case 'k':
if(tcsendbreak(fd, 0) < 0)
; /* ignore it */
break;
case 'H':
case 'h':
cfsetospeed(&ts, B0);
break;
case 'B':
case 'b':
flag = stof(bps, n);
if((int)flag == -1)
error(Ebadarg);
cfsetispeed(&ts, (speed_t)flag);
cfsetospeed(&ts, (speed_t)flag);
break;
case 'L':
case 'l':
flag = stof(size, n);
if((int)flag == -1)
error(Ebadarg);
ts.c_cflag &= ~CSIZE;
ts.c_cflag |= flag;
break;
case 'S':
case 's':
if(n == 1)
ts.c_cflag &= ~CSTOPB;
else if(n ==2)
ts.c_cflag |= CSTOPB;
else
error(Ebadarg);
break;
case 'P':
case 'p':
if(*(f[i]+1) == 'o')
ts.c_cflag |= PARENB|PARODD;
else if(*(f[i]+1) == 'e') {
ts.c_cflag |= PARENB;
ts.c_cflag &= ~PARODD;
}
else
ts.c_cflag &= ~PARENB;
break;
case 'X':
case 'x':
if(n == 0)
ts.c_iflag &= ~(IXON|IXOFF);
else
ts.c_iflag |= (IXON|IXOFF);
break;
case 'i':
case 'I':
/* enable fifo; ignore */
break;
default:
if((xerr = wrxtra(port, &ts, f[i])) != nil)
error(xerr);
}
}
osenter();
r = tcsetattr(fd, TCSADRAIN, &ts);
osleave();
if(r < 0)
goto Error;
eia[port].restore = 1;
eia[port].ts = ts;
}
static void
eiainit(void)
{
int i, nports;
Dirtab *dp;
struct stat sb;
#ifdef buildsysdev
buildsysdev();
#endif
/* check to see which ports exist by trying to stat them */
nports = 0;
for (i=0; i < nelem(sysdev); i++) {
if(stat(sysdev[i], &sb) < 0)
break;
nports++;
}
if (!nports)
return;
ndir = Nqid*nports+1;
dp = eiadir = malloc(ndir*sizeof(Dirtab));
if(dp == 0)
panic("eiainit");
strcpy(dp->name, ".");
dp->qid.path = 0;
dp->qid.type = QTDIR;
dp->perm = DMDIR|0555;
dp++;
eia = malloc(nports*sizeof(Eia));
if(eia == 0)
panic("eiainit");
for(i = 0; i < nports; i++) {
sprint(dp->name, "%s%d", Devname, i);
dp->qid.path = NETQID(i, Ndataqid);
dp->perm = 0660;
dp++;
sprint(dp->name, "%s%dctl", Devname, i);
dp->qid.path = NETQID(i, Nctlqid);
dp->perm = 0660;
dp++;
sprint(dp->name, "%s%dstatus", Devname, i);
dp->qid.path = NETQID(i, Nstatqid);
dp->perm = 0660;
dp++;
eia[i].frame = eia[i].overrun = 0;
eia[i].restore = eia[i].dtr = eia[i].rts = eia[i].cts = 0;
}
}
static Chan*
eiaattach(char *spec)
{
if(eiadir == nil)
error(Enodev);
return devattach(Devchar, spec);
}
Walkqid*
eiawalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, eiadir, ndir, devgen);
}
int
eiastat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, eiadir, ndir, devgen);
}
static void
resxtra(int port, struct termios *ts); /* non-POSIX extensions */
static Chan*
eiaopen(Chan *c, int mode)
{
int port = NETID(c->qid.path);
struct termios ts;
int r;
c = devopen(c, mode, eiadir, ndir, devgen);
switch(NETTYPE(c->qid.path)) {
case Nctlqid:
case Ndataqid:
case Nstatqid:
if(incref(&eia[port].r) != 1)
break;
osenter();
eia[port].fd = open(sysdev[port], O_RDWR);
osleave();
if(eia[port].fd < 0)
oserror();
/* make port settings sane */
if(tcgetattr(eia[port].fd, &ts) < 0)
oserror();
ts.c_iflag = ts.c_oflag = ts.c_lflag = 0;
if(eia[port].restore)
ts = eia[port].ts;
else {
cfsetispeed(&ts, B9600);
cfsetospeed(&ts, B9600);
ts.c_iflag |= IGNPAR;
ts.c_cflag &= ~CSIZE;
ts.c_cflag |= CS8|CREAD;
ts.c_cflag &= ~(PARENB|PARODD);
ts.c_cc[VMIN] = 1;
ts.c_cc[VTIME] = 0;
}
osenter();
r = tcsetattr(eia[port].fd, TCSANOW, &ts);
osleave();
if(r < 0)
oserror();
if(eia[port].restore)
resxtra(port, &ts);
break;
}
return c;
}
static void
eiaclose(Chan *c)
{
int port = NETID(c->qid.path);
if((c->flag & COPEN) == 0)
return;
switch(NETTYPE(c->qid.path)) {
case Nctlqid:
case Ndataqid:
case Nstatqid:
if(decref(&eia[port].r) != 0)
break;
if(eia[port].fd >= 0) {
osenter();
close(eia[port].fd);
osleave();
}
break;
}
}
static long
eiaread(Chan *c, void *buf, long n, vlong offset)
{
ssize_t cnt;
int port = NETID(c->qid.path);
if(c->qid.type & QTDIR)
return devdirread(c, buf, n, eiadir, ndir, devgen);
switch(NETTYPE(c->qid.path)) {
case Ndataqid:
osenter();
cnt = read(eia[port].fd, buf, n);
osleave();
if(cnt == -1)
oserror();
return cnt;
case Nctlqid:
return readnum(offset, buf, n, port, NUMSIZE);
case Nstatqid:
return rdstat(port, buf, n, offset);
}
return 0;
}
static long
eiawrite(Chan *c, void *buf, long n, vlong offset)
{
ssize_t cnt;
char cmd[Maxctl];
int port = NETID(c->qid.path);
USED(offset);
if(c->qid.type & QTDIR)
error(Eperm);
switch(NETTYPE(c->qid.path)) {
case Ndataqid:
osenter();
cnt = write(eia[port].fd, buf, n);
osleave();
if(cnt == -1)
oserror();
return cnt;
case Nctlqid:
if(n >= (long)sizeof(cmd))
n = sizeof(cmd)-1;
memmove(cmd, buf, n);
cmd[n] = 0;
wrctl(port, cmd);
return n;
}
return 0;
}
int
eiawstat(Chan *c, uchar *dp, int n)
{
Dir d;
int i;
if(strcmp(up->env->user, eve) != 0)
error(Eperm);
if(c->qid.type & QTDIR)
error(Eperm);
n = convM2D(dp, n, &d, nil);
i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid;
eiadir[i+1].perm = d.mode&0666;
return n;
}
Dev eiadevtab = {
Devchar,
Devname,
eiainit,
eiaattach,
eiawalk,
eiastat,
eiaopen,
devcreate,
eiaclose,
eiaread,
devbread,
eiawrite,
devbwrite,
devremove,
eiawstat
};