1 /* 2 * Driver for POSIX serial ports 3 */ 4 #include "dat.h" 5 #include "fns.h" 6 #include "error.h" 7 #undef _POSIX_C_SOURCE /* for deveia-bsd.c */ 8 #include <sys/stat.h> 9 #include <termios.h> 10 11 enum 12 { 13 Devchar = 't', 14 15 Ndataqid = 1, 16 Nctlqid, 17 Nstatqid, 18 Nqid = 3, /* number of QIDs */ 19 20 CTLS= 023, 21 CTLQ= 021, 22 23 Maxctl = 128, 24 Maxfield = 32 25 }; 26 27 /* 28 * Macros to manage QIDs 29 */ 30 #define NETTYPE(x) ((x)&0x0F) 31 #define NETID(x) ((x)>>4) 32 #define NETQID(i,t) (((i)<<4)|(t)) 33 34 static Dirtab *eiadir; 35 static int ndir; 36 37 static char Devname[] = "eia"; 38 39 typedef struct Eia Eia; 40 struct Eia { 41 Ref r; 42 int fd; 43 int overrun; 44 int frame; 45 int restore; /* flag to restore prev. states */ 46 struct termios ts; 47 int dtr; 48 int rts; 49 int cts; 50 }; 51 52 static Eia *eia; 53 54 struct tcdef_t { 55 int val; 56 tcflag_t flag; 57 }; 58 59 struct flagmap { 60 char* s; 61 tcflag_t flag; 62 }; 63 64 static struct tcdef_t bps[]; 65 66 static struct tcdef_t size[] = { 67 {5, CS5}, 68 {6, CS6}, 69 {7, CS7}, 70 {8, CS8}, 71 {-1, -1} 72 }; 73 74 static char * 75 ftos(char *buf, struct tcdef_t *tbl, tcflag_t flag) 76 { 77 for(; tbl->val >= 0; tbl++) 78 if(tbl->flag == flag){ 79 sprint(buf, "%d", tbl->val); 80 return buf; 81 } 82 return "unknown"; 83 } 84 85 static tcflag_t 86 stof(struct tcdef_t *tbl, int val) 87 { 88 for(; tbl->val >= 0 && tbl->val != val; tbl++) 89 {} 90 return tbl->flag; 91 } 92 93 static char * 94 rdxtra(int port, struct termios *ts, char *str); /* non-POSIX extensions */ 95 96 static long 97 rdstat(int port, void *buf, long n, ulong offset) 98 { 99 int fd = eia[port].fd; 100 struct termios ts; 101 char str[Maxctl]; 102 char sbuf[20]; 103 char *s; 104 105 if(tcgetattr(fd, &ts) < 0) 106 oserror(); 107 108 s = str; 109 s += sprint(s, "opens %d ferr %d oerr %d baud %s", 110 eia[port].r.ref-1, eia[port].frame, eia[port].overrun, 111 ftos(sbuf, bps, (tcflag_t)cfgetospeed(&ts))); 112 s = rdxtra(port, &ts, s); 113 sprint(s, "\n"); 114 115 return readstr(offset, buf, n, str); 116 } 117 118 static char * 119 wrxtra(int port, struct termios *ts, char *cmd); /* non-POSIX extensions */ 120 121 static void 122 wrctl(int port, char *cmd) 123 { 124 struct termios ts; 125 char *xerr; 126 int r, nf, n, i; 127 char *f[Maxfield]; 128 int fd = eia[port].fd; 129 tcflag_t flag; 130 131 if(tcgetattr(fd, &ts) < 0) { 132 Error: 133 oserror(); 134 } 135 136 nf = tokenize(cmd, f, nelem(f)); 137 for(i = 0; i < nf; i++){ 138 if(strncmp(f[i], "break", 5) == 0){ 139 tcsendbreak(fd, 0); 140 continue; 141 } 142 n = atoi(f[i]+1); 143 switch(*f[i]) { 144 case 'F': 145 case 'f': 146 if(tcflush(fd, TCOFLUSH) < 0) 147 goto Error; 148 break; 149 case 'K': 150 case 'k': 151 if(tcsendbreak(fd, 0) < 0) 152 ; /* ignore it */ 153 break; 154 case 'H': 155 case 'h': 156 cfsetospeed(&ts, B0); 157 break; 158 case 'B': 159 case 'b': 160 flag = stof(bps, n); 161 if((int)flag == -1) 162 error(Ebadarg); 163 cfsetispeed(&ts, (speed_t)flag); 164 cfsetospeed(&ts, (speed_t)flag); 165 break; 166 case 'L': 167 case 'l': 168 flag = stof(size, n); 169 if((int)flag == -1) 170 error(Ebadarg); 171 ts.c_cflag &= ~CSIZE; 172 ts.c_cflag |= flag; 173 break; 174 case 'S': 175 case 's': 176 if(n == 1) 177 ts.c_cflag &= ~CSTOPB; 178 else if(n ==2) 179 ts.c_cflag |= CSTOPB; 180 else 181 error(Ebadarg); 182 break; 183 case 'P': 184 case 'p': 185 if(*(f[i]+1) == 'o') 186 ts.c_cflag |= PARENB|PARODD; 187 else if(*(f[i]+1) == 'e') { 188 ts.c_cflag |= PARENB; 189 ts.c_cflag &= ~PARODD; 190 } 191 else 192 ts.c_cflag &= ~PARENB; 193 break; 194 case 'X': 195 case 'x': 196 if(n == 0) 197 ts.c_iflag &= ~(IXON|IXOFF); 198 else 199 ts.c_iflag |= (IXON|IXOFF); 200 break; 201 case 'i': 202 case 'I': 203 /* enable fifo; ignore */ 204 break; 205 default: 206 if((xerr = wrxtra(port, &ts, f[i])) != nil) 207 error(xerr); 208 } 209 } 210 211 osenter(); 212 r = tcsetattr(fd, TCSADRAIN, &ts); 213 osleave(); 214 if(r < 0) 215 goto Error; 216 eia[port].restore = 1; 217 eia[port].ts = ts; 218 } 219 220 static void 221 eiainit(void) 222 { 223 int i, nports; 224 Dirtab *dp; 225 struct stat sb; 226 227 #ifdef buildsysdev 228 buildsysdev(); 229 #endif 230 231 /* check to see which ports exist by trying to stat them */ 232 nports = 0; 233 for (i=0; i < nelem(sysdev); i++) { 234 if(stat(sysdev[i], &sb) < 0) 235 break; 236 237 nports++; 238 } 239 240 if (!nports) 241 return; 242 243 ndir = Nqid*nports+1; 244 dp = eiadir = malloc(ndir*sizeof(Dirtab)); 245 if(dp == 0) 246 panic("eiainit"); 247 strcpy(dp->name, "."); 248 dp->qid.path = 0; 249 dp->qid.type = QTDIR; 250 dp->perm = DMDIR|0555; 251 dp++; 252 eia = malloc(nports*sizeof(Eia)); 253 if(eia == 0) 254 panic("eiainit"); 255 for(i = 0; i < nports; i++) { 256 sprint(dp->name, "%s%d", Devname, i); 257 dp->qid.path = NETQID(i, Ndataqid); 258 dp->perm = 0660; 259 dp++; 260 sprint(dp->name, "%s%dctl", Devname, i); 261 dp->qid.path = NETQID(i, Nctlqid); 262 dp->perm = 0660; 263 dp++; 264 sprint(dp->name, "%s%dstatus", Devname, i); 265 dp->qid.path = NETQID(i, Nstatqid); 266 dp->perm = 0660; 267 dp++; 268 eia[i].frame = eia[i].overrun = 0; 269 eia[i].restore = eia[i].dtr = eia[i].rts = eia[i].cts = 0; 270 } 271 } 272 273 static Chan* 274 eiaattach(char *spec) 275 { 276 if(eiadir == nil) 277 error(Enodev); 278 279 return devattach(Devchar, spec); 280 } 281 282 Walkqid* 283 eiawalk(Chan *c, Chan *nc, char **name, int nname) 284 { 285 return devwalk(c, nc, name, nname, eiadir, ndir, devgen); 286 } 287 288 int 289 eiastat(Chan *c, uchar *db, int n) 290 { 291 return devstat(c, db, n, eiadir, ndir, devgen); 292 } 293 294 static void 295 resxtra(int port, struct termios *ts); /* non-POSIX extensions */ 296 297 static Chan* 298 eiaopen(Chan *c, int mode) 299 { 300 int port = NETID(c->qid.path); 301 struct termios ts; 302 int r; 303 304 c = devopen(c, mode, eiadir, ndir, devgen); 305 306 switch(NETTYPE(c->qid.path)) { 307 case Nctlqid: 308 case Ndataqid: 309 case Nstatqid: 310 if(incref(&eia[port].r) != 1) 311 break; 312 313 osenter(); 314 eia[port].fd = open(sysdev[port], O_RDWR); 315 osleave(); 316 if(eia[port].fd < 0) 317 oserror(); 318 319 /* make port settings sane */ 320 if(tcgetattr(eia[port].fd, &ts) < 0) 321 oserror(); 322 ts.c_iflag = ts.c_oflag = ts.c_lflag = 0; 323 if(eia[port].restore) 324 ts = eia[port].ts; 325 else { 326 cfsetispeed(&ts, B9600); 327 cfsetospeed(&ts, B9600); 328 ts.c_iflag |= IGNPAR; 329 ts.c_cflag &= ~CSIZE; 330 ts.c_cflag |= CS8|CREAD; 331 ts.c_cflag &= ~(PARENB|PARODD); 332 ts.c_cc[VMIN] = 1; 333 ts.c_cc[VTIME] = 0; 334 } 335 osenter(); 336 r = tcsetattr(eia[port].fd, TCSANOW, &ts); 337 osleave(); 338 if(r < 0) 339 oserror(); 340 341 if(eia[port].restore) 342 resxtra(port, &ts); 343 break; 344 } 345 return c; 346 } 347 348 static void 349 eiaclose(Chan *c) 350 { 351 int port = NETID(c->qid.path); 352 353 if((c->flag & COPEN) == 0) 354 return; 355 356 switch(NETTYPE(c->qid.path)) { 357 case Nctlqid: 358 case Ndataqid: 359 case Nstatqid: 360 if(decref(&eia[port].r) != 0) 361 break; 362 if(eia[port].fd >= 0) { 363 osenter(); 364 close(eia[port].fd); 365 osleave(); 366 } 367 break; 368 } 369 370 } 371 372 static long 373 eiaread(Chan *c, void *buf, long n, vlong offset) 374 { 375 ssize_t cnt; 376 int port = NETID(c->qid.path); 377 378 if(c->qid.type & QTDIR) 379 return devdirread(c, buf, n, eiadir, ndir, devgen); 380 381 switch(NETTYPE(c->qid.path)) { 382 case Ndataqid: 383 osenter(); 384 cnt = read(eia[port].fd, buf, n); 385 osleave(); 386 if(cnt == -1) 387 oserror(); 388 return cnt; 389 case Nctlqid: 390 return readnum(offset, buf, n, port, NUMSIZE); 391 case Nstatqid: 392 return rdstat(port, buf, n, offset); 393 } 394 395 return 0; 396 } 397 398 static long 399 eiawrite(Chan *c, void *buf, long n, vlong offset) 400 { 401 ssize_t cnt; 402 char cmd[Maxctl]; 403 int port = NETID(c->qid.path); 404 405 USED(offset); 406 407 if(c->qid.type & QTDIR) 408 error(Eperm); 409 410 switch(NETTYPE(c->qid.path)) { 411 case Ndataqid: 412 osenter(); 413 cnt = write(eia[port].fd, buf, n); 414 osleave(); 415 if(cnt == -1) 416 oserror(); 417 return cnt; 418 case Nctlqid: 419 if(n >= (long)sizeof(cmd)) 420 n = sizeof(cmd)-1; 421 memmove(cmd, buf, n); 422 cmd[n] = 0; 423 wrctl(port, cmd); 424 return n; 425 } 426 return 0; 427 } 428 429 int 430 eiawstat(Chan *c, uchar *dp, int n) 431 { 432 Dir d; 433 int i; 434 435 if(strcmp(up->env->user, eve) != 0) 436 error(Eperm); 437 if(c->qid.type & QTDIR) 438 error(Eperm); 439 440 n = convM2D(dp, n, &d, nil); 441 i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid; 442 eiadir[i+1].perm = d.mode&0666; 443 return n; 444 } 445 446 Dev eiadevtab = { 447 Devchar, 448 Devname, 449 450 eiainit, 451 eiaattach, 452 eiawalk, 453 eiastat, 454 eiaopen, 455 devcreate, 456 eiaclose, 457 eiaread, 458 devbread, 459 eiawrite, 460 devbwrite, 461 devremove, 462 eiawstat 463 }; 464