/************************************************************* * File: lib/termio.c * Purpose: Part of C runtime library * Author: Phil Bunce (pjb@carmel.com) * Revision History: * 970304 Start of revision history * 970826 Created addDevice. Changed devinit to use addDevice. * 980616 Removed devinit. * 980618 Added ioctl call to get baudlist from driver. */ #include #include #include #include #include #include fFunc *clkinit_ptr; void *_clkinfo; DevEntry DevTable[DEV_MAX]; File _file[OPEN_MAX] = { {0,1},{0,1},{0,1} }; struct TermEntry { char *name; Func *func; }; int tvi920(),vt100(); int p2681(); struct TermEntry TermTable[] = { {"tvi920",tvi920}, {"vt100",vt100}, {0}}; int *curlst; /* list of files open in the current context */ int re_ether; /************************************************************* * _write(fd,buf,n) write n bytes from buf to fd */ _write(fd,buf,n) int fd,n; char *buf; { int i; DevEntry *p; char *t; if (fd == ETHERFD) return(0); p = &DevTable[_file[fd].dev]; for (i=0;it.c_oflag&ONLCR && buf[i] == '\n') _chwrite(p,'\r'); if (p->t.c_lflag&ECHOE && buf[i] == p->t.c_cc[VERASE]) { for (t="\b \b";*t;t++) _chwrite(p,*t); } else _chwrite(p,buf[i]); } return(i); } /************************************************************* * _chwrite(p,ch) * write one character */ _chwrite(p,ch) DevEntry *p; char ch; { while (p->txoff) reschedule("write txoff"); while (!(*p->handler)(OP_TXRDY,p->sio,p->chan)) reschedule("write txrdy"); (*p->handler)(OP_TX,p->sio,p->chan,ch); } /************************************************************* * scandevs() * scan devices for input */ scandevs() { int i,c,n; DevEntry *p; for (i=0;DevTable[i].rxq && DevTable[i].pollin;i++) { p = &DevTable[i]; if ((*p->handler)(OP_RXRDY,p->sio,p->chan)) { c = (*p->handler)(OP_RX,p->sio,p->chan); if (p->t.c_iflag&ISTRIP) c &= 0x7f; n = Qinquiry(p->rxq,Q_SPACE); if (c == p->t.c_cc[VINTR] && p->intr) longjmp((void *)p->intr,1); else if (p->t.c_iflag&IXON && p->t.c_iflag&IXANY && p->txoff) p->txoff = 0; else if (p->t.c_iflag&IXON && c == p->t.c_cc[V_STOP]) p->txoff = 1; else if (p->t.c_iflag&IXON && c == p->t.c_cc[V_START]) p->txoff = 0; else if (n > 0) { Qput(p->rxq,c); if (n < 10 && p->t.c_iflag&IXOFF && !p->rxoff) { p->rxoff = 1; _chwrite(p,CNTRL('S')); } } } } } #ifdef BSO_TASKING #pragma nooptim #endif /************************************************************* * _ioctl(fd,op,argp) perform control operation on fd */ _ioctl(fd,op,argp) int fd,op,*argp; { DevEntry *p; struct termio *t; int i; if (fd == ETHERFD) return(0); switch (op) { case TXRDY : p = &DevTable[_file[fd].dev]; if ((*p->handler)(OP_TXRDY,p->sio,p->chan,0)) return(1); break; case TCGETA : p = &DevTable[_file[fd].dev]; t = (struct termio *) argp; *t = p->t; break; case TCSETAF : /* after flush of input queue */ p = &DevTable[_file[fd].dev]; while (! Qempty(p->rxq)) Qget(p->rxq); case TCSETAW : /* after write */ /* no txq, so no delay needed */ p = &DevTable[_file[fd].dev]; if (p->t.c_cflag != ((struct termio *)argp)->c_cflag) { if ((*p->handler)(OP_BAUD,p->sio,p->chan, CBAUD&((struct termio *)argp)->c_cflag)) return(-1); } p->t = *((struct termio *) argp); break; case FIONREAD : p = &DevTable[_file[fd].dev]; scandevs(); *argp = Qinquiry(p->rxq,Q_USED); break; case SETINTR : p = &DevTable[_file[fd].dev]; p->intr = (jmp_buf *)argp; break; case GETINTR : p = &DevTable[_file[fd].dev]; *argp = (unsigned long)p->intr; break; case SETICEE : /* ICANON & ECHOE */ p = &DevTable[_file[fd].dev]; p->t.c_lflag = (ICANON|ECHOE); break; case SETSANE : p = &DevTable[_file[fd].dev]; p->t.c_iflag |= (ISTRIP|ICRNL|IXON); p->t.c_lflag = (ICANON|ECHO|ECHOE); /* required */ p->t.c_oflag = (ONLCR); p->t.c_cc[VINTR] = CNTRL('c'); p->t.c_cc[VEOL] = '\n'; p->t.c_cc[VEOL2] = CNTRL('c'); p->t.c_cc[VERASE] = CNTRL('h'); p->t.c_cc[V_STOP] = CNTRL('s'); p->t.c_cc[V_START] = CNTRL('q'); break; case SETNCNE : p = &DevTable[_file[fd].dev]; if (argp != 0) { t = (struct termio *) argp; *t = p->t; } p->t.c_lflag &= ~(ICANON|ECHO|ECHOE); p->t.c_cc[4] = 1; break; case CBREAK : p = &DevTable[_file[fd].dev]; if (argp != 0) { t = (struct termio *) argp; *t = p->t; } p->t.c_lflag &= ~(ICANON|ECHO); p->t.c_cc[4] = 1; break; case GETTERM : p = &DevTable[_file[fd].dev]; *argp = 0; if (p->tfunc == 0) return(-1); strcpy((char *)argp,p->tname); break; case SETTERM : p = &DevTable[_file[fd].dev]; for (i=0;TermTable[i].name;i++) { if (strequ(argp,TermTable[i].name)) break; } if (TermTable[i].name == 0) return(-1); p->tname = TermTable[i].name; p->tfunc = TermTable[i].func; break; case TERMTYPE : if (TermTable[fd].name == 0) return(-1); strcpy((char *)argp,TermTable[fd].name); return(fd+1); case DEVENTRY : p = &DevTable[_file[fd].dev]; *argp = (int)p; break; case BAUDRATES : p = &DevTable[_file[fd].dev]; *argp = (*p->handler)(OP_BAUDRATES,p->sio,p->chan,0); break; default: return(-1); } return(0); } #ifdef BSO_TASKING #pragma optim #endif /************************************************************* * reschedule(p) * gets called when there's nothing better to do */ reschedule(p) char *p; { scandevs(); } /************************************************************* * ttctl(fd,op,a1,a2) perform terminal specific operation */ ttctl(fd,op,a1,a2) int fd,op,a1,a2; { DevEntry *p; int r; p = &DevTable[_file[fd].dev]; if (p->tfunc == 0) return(-1); r = (*p->tfunc)(fd,op,a1,a2); return(r); } /************************************************************* * _open(fname,mode,perms) * return fd for fname. mode and perms are ignored. */ _open(fname,mode,perms) char *fname; int mode,perms; { int i,c,dev,len; char tmp[80]; #ifdef ETHERNET if (strequ(fname,"ethernet")) return ether_open(getMonEnv("etheraddr")); #endif for (i=0;i= 'a' && c <= 'z') dev = c - 'a'; if (c >= 'A' && c <= 'Z') dev = c - 'A'; if (c >= '0' && c <= '9') dev = c - '0'; if (dev > DEV_MAX) { /* device number too large */ errno = ENOENT; return(-1); } if (DevTable[dev].rxq == 0) { /* device not initialized */ errno = ENOENT; return(-1); } _file[i].valid = 1; _file[i].dev = dev; ioctl(i,SETSANE); if (curlst) *curlst |= (1<rxq,Q_USED)) == 0) reschedule("read Qempty"); if (used < 10 && p->rxoff) { p->rxoff = 0; _chwrite(p,CNTRL('Q')); } ch = Qget(p->rxq); if (p->t.c_iflag&ICRNL && ch == '\r') ch = '\n'; if (p->t.c_lflag&ICANON) { if (p->t.c_cc[VERASE] != 0xff && ch == p->t.c_cc[VERASE]) { if (i > 0) { i--; if (p->t.c_lflag&ECHO) write(fd,&ch,1); } } else { if (p->t.c_lflag&ECHO) write(fd,&ch,1); buf[i++] = ch; } if (ch == p->t.c_cc[VEOL]) break; if (ch == p->t.c_cc[VEOL2]) break; } else { if (p->t.c_lflag&ECHO) write(fd,&ch,1); buf[i++] = ch; break; } } return(i); } /************************************************************* * Func *_clkinit() */ Func *_clkinit() { if (!clkinit_ptr) return(0); return (* clkinit_ptr)(); } /************************************************************* * addDevice(devinfo,chan,handler,rxqsize,brate) */ addDevice(devinfo,chan,handler,rxqsize,brate) Addr devinfo; /* addr of struct containing device address etc */ int chan; /* channel#. Used for DUARTS */ Func *handler; /* addr of driver */ int rxqsize; /* size of receive buffer */ int brate; /* default baudrate */ { int i,r; DevEntry *p; /* find first available device */ for (i=0;DevTable[i].rxq && i < DEV_MAX;i++) ; if (i >= DEV_MAX) return; p = &DevTable[i]; p->txoff = 0; p->rxoff = 0; if (chan == 0) (*handler)(OP_INIT,devinfo,brate); p->qsize = rxqsize; p->rxq = Qcreate(p->qsize); if (p->rxq == 0) return(-1); r = (*handler)(OP_BAUD,devinfo,chan,brate); p->sio = devinfo; p->chan = chan; p->handler = handler; p->intr = 0; p->pollin = 1; p->tfunc = 0; p->t.c_iflag = (ISTRIP|ICRNL|IXON); p->t.c_oflag = (ONLCR); p->t.c_lflag = (ICANON|ECHO|ECHOE); p->t.c_cflag = brate; p->t.c_cc[VINTR] = CNTRL('c'); p->t.c_cc[VEOL] = '\n'; p->t.c_cc[VEOL2] = CNTRL('c'); p->t.c_cc[VERASE] = CNTRL('h'); p->t.c_cc[V_STOP] = CNTRL('s'); p->t.c_cc[V_START] = CNTRL('q'); if (r==0) _chwrite(p,CNTRL('Q')); }