1 /* 2 * Windows serial driver 3 * 4 * to do: 5 * scan the registry for serial ports? 6 */ 7 8 #define Unknown win_Unknown 9 #include <windows.h> 10 #undef Unknown 11 #undef Sleep 12 #include "dat.h" 13 #include "fns.h" 14 #include "error.h" 15 #include <sys/types.h> 16 #include <sys/stat.h> 17 #include <fcntl.h> 18 #include <stdio.h> 19 #include <lm.h> 20 #include <direct.h> 21 22 // local fcts 23 static void openport(int); 24 static void wrctl(int, char*); 25 static long rdstat(int, void*, long, ulong ); 26 27 enum 28 { 29 Devchar = 't', 30 31 Ndataqid = 1, 32 Nctlqid, 33 Nstatqid, 34 Nqid = 3, /* number of QIDs */ 35 36 Maxctl = 128, 37 38 // in/out buffer sizes for comm port (NT requires an even number) 39 // set it to x* the max styx message rounded up to the 40 // nearest 4 byte value 41 CommBufSize = ((((8192+128)*2)+3) & ~3) 42 }; 43 44 /* 45 * Macros to manage QIDs 46 */ 47 #define NETTYPE(x) ((x)&0x0F) 48 #define NETID(x) ((x)>>4) 49 #define NETQID(i,t) (((i)<<4)|(t)) 50 51 static Dirtab *eiadir; 52 static int ndir; 53 54 typedef struct Eia Eia; 55 struct Eia { 56 Ref r; 57 HANDLE comfh; //handle to open port 58 int restore; //flag to restore prev. states 59 DCB dcb; //win32 device control block used for restore 60 int id; //index to host port name in sysdev 61 }; 62 63 // the same timeouts are used for all ports 64 // currently there is no Inferno interface to 65 // change the timeouts. 66 static COMMTIMEOUTS timeouts; 67 68 // std win32 serial port names are COM1..COM4 69 // however there can be more and they can be 70 // named anything. we should be more flexible 71 // pehaps adding a ctl command to allow you to 72 // access any win32 comm port 73 static char* sysdev[] = { 74 "COM1:", 75 "COM2:", 76 "COM3:", 77 "COM4:", 78 "COM5:", 79 "COM6:", 80 "COM7:", 81 "COM8:", 82 NULL 83 }; 84 85 static Eia *eia; 86 87 typedef struct OptTable OptTable; 88 struct OptTable { 89 char *str; 90 DWORD flag; 91 }; 92 93 #define BAD ((DWORD)-1) 94 95 // valid bit-per-byte sizes 96 static OptTable size[] = { 97 {"5", 5}, 98 {"6", 6}, 99 {"7", 7}, 100 {"8", 8}, 101 {NULL, BAD} 102 }; 103 104 // valid stop bits 105 static OptTable stopbits[] = { 106 {"1", ONESTOPBIT}, 107 {"1.5", ONE5STOPBITS}, 108 {"2", TWOSTOPBITS}, 109 {NULL, BAD} 110 }; 111 112 // valid parity settings 113 static OptTable parity[] = { 114 {"o", ODDPARITY}, 115 {"e", EVENPARITY}, 116 {"s", SPACEPARITY}, 117 {"m", MARKPARITY}, 118 {"n", NOPARITY}, 119 {NULL, NOPARITY} 120 }; 121 122 123 static char * 124 ftos(OptTable *tbl, DWORD flag) 125 { 126 while(tbl->str && tbl->flag != flag) 127 tbl++; 128 if(tbl->str == 0) 129 return "unknown"; 130 return tbl->str; 131 } 132 133 static DWORD 134 stof(OptTable *tbl, char *str) 135 { 136 while(tbl->str && strcmp(tbl->str, str) != 0) 137 tbl++; 138 return tbl->flag; 139 } 140 141 static void 142 eiainit(void) 143 { 144 int i,x; 145 byte ports; //bitmask of active host ports 146 int nports; //number of active host ports 147 int max; //number of highest port 148 Dirtab *dp; 149 150 // setup the timeouts; choose carefully 151 timeouts.ReadIntervalTimeout = 2; 152 timeouts.ReadTotalTimeoutMultiplier = 0; 153 timeouts.ReadTotalTimeoutConstant = 200; 154 timeouts.WriteTotalTimeoutMultiplier = 0; 155 timeouts.WriteTotalTimeoutConstant = 400; 156 157 // check to see which ports exist by trying to open them 158 // keep results in a bitmask 159 ports = nports = max = 0; 160 for(i=0; (sysdev[i] != NULL) && (i<8); i++) { 161 HANDLE comfh = CreateFile(sysdev[i], 0, 0, NULL, /* no security attrs */ 162 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 163 164 if(comfh != INVALID_HANDLE_VALUE) { 165 ports |= 1<<i; 166 CloseHandle(comfh); 167 nports++; 168 max = i; 169 } 170 } 171 172 if(nports == 0) 173 return; //no ports 174 175 // allocate directory table and eia structure 176 // for each active port. 177 ndir = Nqid*nports+1; 178 dp = eiadir = malloc(ndir*sizeof(Dirtab)); 179 if(dp == 0) 180 panic("eiainit"); 181 eia = malloc(nports*sizeof(Eia)); 182 if(eia == 0) { 183 free(dp); 184 panic("eiainit"); 185 } 186 187 // fill in the directory table and initialize 188 // the eia structure. skip inactive ports. 189 sprint(dp->name, "."); 190 dp->qid.path = 0; 191 dp->qid.type = QTDIR; 192 dp->perm = DMDIR|0555; 193 dp++; 194 x = 0; // index in eia[] 195 for(i = 0; i <= max; i++) { 196 if( (ports & (1<<i)) == 0) 197 continue; //port 'i' is not active 198 sprint(dp->name, "eia%d", i); 199 dp->qid.path = NETQID(x, Ndataqid); 200 dp->perm = 0660; 201 dp++; 202 sprint(dp->name, "eia%dctl", i); 203 dp->qid.path = NETQID(x, Nctlqid); 204 dp->perm = 0660; 205 dp++; 206 sprint(dp->name, "eia%dstatus", i); 207 dp->qid.path = NETQID(x, Nstatqid); 208 dp->perm = 0660; 209 dp++; 210 // init the eia structure 211 eia[x].restore = 0; 212 eia[x].id = i; 213 x++; 214 } 215 } 216 217 static Chan* 218 eiaattach(char *spec) 219 { 220 if(eiadir == nil) 221 error(Enodev); 222 223 return devattach(Devchar, spec); 224 } 225 226 static Walkqid* 227 eiawalk(Chan *c, Chan *nc, char **name, int nname) 228 { 229 return devwalk(c, nc, name, nname, eiadir, ndir, devgen); 230 } 231 232 static int 233 eiastat(Chan *c, uchar *db, int n) 234 { 235 return devstat(c, db, n, eiadir, ndir, devgen); 236 } 237 238 static Chan* 239 eiaopen(Chan *c, int mode) 240 { 241 int port = NETID(c->qid.path); 242 243 c = devopen(c, mode, eiadir, ndir, devgen); 244 245 switch(NETTYPE(c->qid.path)) { 246 case Nctlqid: 247 case Ndataqid: 248 case Nstatqid: 249 if(incref(&eia[port].r) != 1) 250 break; 251 if(waserror()) { 252 decref(&eia[port].r); 253 nexterror(); 254 } 255 openport(port); 256 poperror(); 257 break; 258 } 259 return c; 260 } 261 262 static void 263 eiaclose(Chan *c) 264 { 265 int port = NETID(c->qid.path); 266 267 if((c->flag & COPEN) == 0) 268 return; 269 270 switch(NETTYPE(c->qid.path)) { 271 case Nctlqid: 272 case Ndataqid: 273 case Nstatqid: 274 if(decref(&eia[port].r) == 0) { 275 osenter(); 276 CloseHandle(eia[port].comfh); 277 osleave(); 278 } 279 break; 280 } 281 282 } 283 284 static long 285 eiaread(Chan *c, void *buf, long n, vlong offset) 286 { 287 DWORD cnt; 288 int port = NETID(c->qid.path); 289 BOOL good; 290 291 if(c->qid.type & QTDIR) 292 return devdirread(c, buf, n, eiadir, ndir, devgen); 293 294 switch(NETTYPE(c->qid.path)) { 295 case Ndataqid: 296 cnt = 0; 297 // if ReadFile timeouts and cnt==0 then just re-read 298 // this will give osleave() a chance to detect an 299 // interruption (i.e. killprog) 300 while(cnt==0) { 301 osenter(); 302 good = ReadFile(eia[port].comfh, buf, n, &cnt, NULL); 303 SleepEx(0,FALSE); //allow another thread access to port 304 osleave(); 305 if(!good) 306 oserror(); 307 } 308 return cnt; 309 case Nctlqid: 310 return readnum(offset, buf, n, eia[port].id, NUMSIZE); 311 case Nstatqid: 312 return rdstat(port, buf, n, offset); 313 } 314 315 return 0; 316 } 317 318 static long 319 eiawrite(Chan *c, void *buf, long n, vlong offset) 320 { 321 DWORD cnt; 322 char cmd[Maxctl]; 323 int port = NETID(c->qid.path); 324 BOOL good; 325 uchar *data; 326 327 if(c->qid.type & QTDIR) 328 error(Eperm); 329 330 switch(NETTYPE(c->qid.path)) { 331 case Ndataqid: 332 cnt = 0; 333 data = (uchar*)buf; 334 // if WriteFile times out (i.e. return true; cnt<n) then 335 // allow osleave() to check for an interrupt otherwise try 336 // to send the unsent data. 337 while(n>0) { 338 osenter(); 339 good = WriteFile(eia[port].comfh, data, n, &cnt, NULL); 340 osleave(); 341 if(!good) 342 oserror(); 343 data += cnt; 344 n -= cnt; 345 } 346 return (data-(uchar*)buf); 347 case Nctlqid: 348 if(n >= sizeof(cmd)) 349 n = sizeof(cmd)-1; 350 memmove(cmd, buf, n); 351 cmd[n] = 0; 352 wrctl(port, cmd); 353 return n; 354 } 355 return 0; 356 } 357 358 static int 359 eiawstat(Chan *c, uchar *dp, int n) 360 { 361 Dir d; 362 int i; 363 364 if(!iseve()) 365 error(Eperm); 366 if(c->qid.type & QTDIR) 367 error(Eperm); 368 if(NETTYPE(c->qid.path) == Nstatqid) 369 error(Eperm); 370 371 n = convM2D(dp, n, &d, nil); 372 i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid; 373 if(d.mode != ~0UL) 374 eiadir[i+1].perm = d.mode&0666; 375 return n; 376 } 377 378 Dev eiadevtab = { 379 Devchar, 380 "eia", 381 382 eiainit, 383 eiaattach, 384 eiawalk, 385 eiastat, 386 eiaopen, 387 devcreate, 388 eiaclose, 389 eiaread, 390 devbread, 391 eiawrite, 392 devbwrite, 393 devremove, 394 eiawstat 395 }; 396 397 398 // 399 // local functions 400 // 401 402 /* 403 * open the indicated comm port and then set 404 * the default settings for the port. 405 */ 406 static void 407 openport(int port) 408 { 409 Eia* p = &eia[port]; 410 411 // open the port 412 p->comfh = CreateFile(sysdev[p->id], 413 GENERIC_READ|GENERIC_WRITE, //open underlying port for rd/wr 414 0, //comm port can't be shared 415 NULL, //no security attrs 416 OPEN_EXISTING, //a must for comm port 417 FILE_ATTRIBUTE_NORMAL, //nonoverlapped io 418 NULL); //another must for comm port 419 420 if(p->comfh == INVALID_HANDLE_VALUE) 421 oserror(); 422 if(waserror()){ 423 CloseHandle(p->comfh); 424 p->comfh = INVALID_HANDLE_VALUE; 425 nexterror(); 426 } 427 428 // setup in/out buffers (NT requires an even number) 429 if(!SetupComm(p->comfh, CommBufSize, CommBufSize)) 430 oserror(); 431 432 // either use existing settings or set defaults 433 if(!p->restore) { 434 // set default settings 435 if(!GetCommState(p->comfh, &p->dcb)) 436 oserror(); 437 p->dcb.BaudRate = 9600; 438 p->dcb.ByteSize = 8; 439 p->dcb.fParity = 0; 440 p->dcb.Parity = NOPARITY; 441 p->dcb.StopBits = ONESTOPBIT; 442 p->dcb.fInX = 0; //default to xoff 443 p->dcb.fOutX = 0; 444 p->dcb.fAbortOnError = 1; //read/write abort on err 445 } 446 447 // set state and timeouts 448 if(!SetCommState(p->comfh, &p->dcb) || 449 !SetCommTimeouts(p->comfh, &timeouts)) 450 oserror(); 451 poperror(); 452 } 453 454 /* 455 * Obtain status information on the com port. 456 */ 457 static long 458 rdstat(int port, void *buf, long n, ulong offset) 459 { 460 HANDLE comfh = eia[port].comfh; 461 char str[Maxctl]; 462 char *s; 463 DCB dcb; 464 DWORD modemstatus; 465 DWORD porterr; 466 COMSTAT portstat; 467 int frame, overrun, i; 468 469 // valid line control ids 470 static enum { 471 L_CTS, L_DSR, L_RING, L_DCD, L_DTR, L_RTS, L_MAX 472 }; 473 int status[L_MAX]; 474 475 // line control strings (should match above id's) 476 static char* lines[] = { 477 "cts", "dsr", "ring", "dcd", "dtr", "rts", NULL 478 }; 479 480 481 // get any error conditions; also clears error flag 482 // and enables io 483 if(!ClearCommError(comfh, &porterr, &portstat)) 484 oserror(); 485 486 // get comm port state 487 if(!GetCommState(comfh, &dcb)) 488 oserror(); 489 490 // get modem line information 491 if(!GetCommModemStatus(comfh, &modemstatus)) 492 oserror(); 493 494 // now set our local flags 495 status[L_CTS] = MS_CTS_ON & modemstatus; 496 status[L_DSR] = MS_DSR_ON & modemstatus; 497 status[L_RING] = MS_RING_ON & modemstatus; 498 status[L_DCD] = MS_RLSD_ON & modemstatus; 499 status[L_DTR] = FALSE; //?? cand this work: dcb.fDtrControl; 500 status[L_RTS] = FALSE; //?? dcb.fRtsControl; 501 frame = porterr & CE_FRAME; 502 overrun = porterr & CE_OVERRUN; 503 504 /* TO DO: mimic native eia driver's first line */ 505 506 s = seprint(str, str+sizeof(str), "opens %d ferr %d oerr %d baud %d", 507 eia[port].r.ref-1, 508 frame, 509 overrun, 510 dcb.BaudRate); 511 512 // add line settings 513 for(i=0; i < L_MAX; i++) 514 if(status[i]) 515 s = seprint(s, str+sizeof(str), " %s", lines[i]); 516 seprint(s, str+sizeof(str), "\n"); 517 return readstr(offset, buf, n, str); 518 } 519 520 // 521 // write on ctl file. modify the settings for 522 // the underlying port. 523 // 524 static void 525 wrctl(int port, char *cmd) 526 { 527 DCB dcb; 528 int nf, n, i; 529 char *f[16]; 530 HANDLE comfh = eia[port].comfh; 531 DWORD flag, opt; 532 BOOL rslt; 533 int chg; 534 535 // get the current settings for the port 536 if(!GetCommState(comfh, &dcb)) 537 oserror(); 538 539 chg = 0; 540 nf = tokenize(cmd, f, nelem(f)); 541 for(i = 0; i < nf; i++){ 542 if(strcmp(f[i], "break") == 0){ 543 if(!SetCommBreak(comfh)) 544 oserror(); 545 SleepEx((DWORD)300, FALSE); 546 if(!ClearCommBreak(comfh)) 547 oserror(); 548 continue; 549 } 550 551 n = atoi(f[i]+1); 552 switch(*f[i]) { 553 case 'B': 554 case 'b': // set the baud rate 555 if(n < 110) 556 error(Ebadarg); 557 dcb.BaudRate = n; 558 chg = 1; 559 break; 560 case 'C': 561 case 'c': 562 /* dcd */ 563 break; 564 case 'D': 565 case 'd': // set DTR 566 opt = n ? SETDTR : CLRDTR; 567 if(!EscapeCommFunction(comfh, opt)) 568 oserror(); 569 break; 570 case 'E': 571 case 'e': 572 /* dsr */ 573 break; 574 case 'F': 575 case 'f': // flush any untransmitted data 576 if(!PurgeComm(comfh, PURGE_TXCLEAR)) 577 oserror(); 578 break; 579 case 'H': 580 case 'h': 581 /* hangup */ 582 /* TO DO: close handle */ 583 break; 584 case 'I': 585 case 'i': 586 /* fifo: nothing to do */ 587 break; 588 case 'K': 589 case 'k': 590 /* send a break */ 591 if(!SetCommBreak(comfh)) 592 oserror(); 593 SleepEx((DWORD)300, FALSE); 594 if(!ClearCommBreak(comfh)) 595 oserror(); 596 break; 597 case 'L': 598 case 'l': // set bits per byte 599 flag = stof(size, f[0]+1); 600 if(flag == BAD) 601 error(Ebadarg); 602 dcb.ByteSize = (BYTE)flag; 603 chg = 1; 604 break; 605 case 'M': 606 case 'm': // set CTS (modem control) 607 dcb.fOutxCtsFlow = (n!=0); 608 chg = 1; 609 break; 610 case 'N': 611 case 'n': 612 /* don't block on output */ 613 break; 614 case 'P': 615 case 'p': // set parity -- even or odd 616 flag = stof(parity, f[0]+1); 617 if(flag==BAD) 618 error(Ebadarg); 619 dcb.Parity = (BYTE)flag; 620 chg = 1; 621 break; 622 case 'Q': 623 case 'q': 624 /* set i/o queue limits */ 625 break; 626 case 'R': 627 case 'r': // set RTS 628 opt = n ? SETRTS : CLRRTS; 629 if(!EscapeCommFunction(comfh, opt)) 630 oserror(); 631 break; 632 case 'S': 633 case 's': // set stop bits -- valid: 1 or 2 (win32 allows 1.5??) 634 flag = stof(stopbits, f[0]+1); 635 if(flag==BAD) 636 error(Ebadarg); 637 dcb.StopBits = flag; 638 chg = 1; 639 break; 640 case 'T': 641 case 't': 642 break; 643 case 'W': 644 case 'w': 645 /* set uart timer */ 646 break; 647 case 'X': 648 case 'x': // xon/xoff 649 opt = n ? SETXON : SETXOFF; 650 if(!EscapeCommFunction(comfh, opt)) 651 oserror(); 652 break; 653 default: 654 /* ignore */ 655 break; 656 } 657 } 658 659 if(!chg) 660 return; 661 // make the changes on the underlying port, but flush 662 // outgoing chars down the port before 663 osenter(); 664 rslt = FlushFileBuffers(comfh); 665 if(rslt) 666 rslt = SetCommState(comfh, &dcb); 667 osleave(); 668 if(!rslt) 669 oserror(); 670 eia[port].restore = 1; 671 eia[port].dcb = dcb; 672 } 673