📄 pcxx.c
字号:
}static unsigned termios2digi_i(struct channel *ch, unsigned iflag){ unsigned res = iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|IXON|IXANY|IXOFF); if(ch->digiext.digi_flags & DIGI_AIXON) res |= IAIXON; return res;}static unsigned termios2digi_h(struct channel *ch, unsigned cflag){ unsigned res = 0; if(cflag & CRTSCTS) { ch->digiext.digi_flags |= (RTSPACE|CTSPACE); res |= (CTS | RTS); } if(ch->digiext.digi_flags & RTSPACE) res |= RTS; if(ch->digiext.digi_flags & DTRPACE) res |= DTR; if(ch->digiext.digi_flags & CTSPACE) res |= CTS; if(ch->digiext.digi_flags & DSRPACE) res |= ch->dsr; if(ch->digiext.digi_flags & DCDPACE) res |= ch->dcd; if (res & RTS) ch->digiext.digi_flags |= RTSPACE; if (res & CTS) ch->digiext.digi_flags |= CTSPACE; return res;}static void pcxxparam(struct tty_struct *tty, struct channel *ch){ volatile struct board_chan *bc; unsigned int head; unsigned mval, hflow, cflag, iflag; struct termios *ts; bc = ch->brdchan; assertgwinon(ch); ts = tty->termios; if((ts->c_cflag & CBAUD) == 0) { head = bc->rin; bc->rout = head; head = bc->tin; fepcmd(ch, STOUT, (unsigned) head, 0, 0, 0); mval = 0; } else { cflag = termios2digi_c(ch, ts->c_cflag); if(cflag != ch->fepcflag) { ch->fepcflag = cflag; fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0); } if(cflag & CLOCAL) ch->asyncflags &= ~ASYNC_CHECK_CD; else { ch->asyncflags |= ASYNC_CHECK_CD; } mval = DTR | RTS; } iflag = termios2digi_i(ch, ts->c_iflag); if(iflag != ch->fepiflag) { ch->fepiflag = iflag; fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0); } bc->mint = ch->dcd; if((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD)) if(ch->digiext.digi_flags & DIGI_FORCEDCD) bc->mint = 0; ch->imodem = bc->mstat; hflow = termios2digi_h(ch, ts->c_cflag); if(hflow != ch->hflow) { ch->hflow = hflow; fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1); } /* mval ^= ch->modemfake & (mval ^ ch->modem); */ if(ch->omodem != mval) { ch->omodem = mval; fepcmd(ch, SETMODEM, mval, RTS|DTR, 0, 1); } if(ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) { ch->fepstartc = ch->startc; ch->fepstopc = ch->stopc; fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); } if(ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) { ch->fepstartca = ch->startca; ch->fepstopca = ch->stopca; fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); }}static void receive_data(struct channel *ch){ volatile struct board_chan *bc; struct tty_struct *tty; unsigned int tail, head, wrapmask; int n; int piece; struct termios *ts=0; unchar *rptr; int rc; int wrapgap; globalwinon(ch); if (ch->statusflags & RXSTOPPED) return; tty = ch->tty; if(tty) ts = tty->termios; bc = ch->brdchan; if(!bc) { printk("bc is NULL in receive_data!\n"); return; } wrapmask = ch->rxbufsize - 1; head = bc->rin; head &= wrapmask; tail = bc->rout & wrapmask; n = (head-tail) & wrapmask; if(n == 0) return; /* * If CREAD bit is off or device not open, set TX tail to head */ if(!tty || !ts || !(ts->c_cflag & CREAD)) { bc->rout = head; return; } if(tty->flip.count == TTY_FLIPBUF_SIZE) { /* printk("tty->flip.count = TTY_FLIPBUF_SIZE\n"); */ return; } if(bc->orun) { bc->orun = 0; printk("overrun! DigiBoard device minor=%d\n",MINOR(tty->device)); } rxwinon(ch); rptr = tty->flip.char_buf_ptr; rc = tty->flip.count; while(n > 0) { wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail; piece = (wrapgap < n) ? wrapgap : n; /* * Make sure we don't overflow the buffer */ if ((rc + piece) > TTY_FLIPBUF_SIZE) piece = TTY_FLIPBUF_SIZE - rc; if (piece == 0) break; memcpy(rptr, ch->rxptr + tail, piece); rptr += piece; rc += piece; tail = (tail + piece) & wrapmask; n -= piece; } tty->flip.count = rc; tty->flip.char_buf_ptr = rptr; globalwinon(ch); bc->rout = tail; /* Must be called with global data */ tty_schedule_flip(ch->tty); return;}static int pcxe_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ int error; struct channel *ch = (struct channel *) tty->driver_data; volatile struct board_chan *bc; int retval; unsigned int mflag, mstat; unsigned char startc, stopc; unsigned long flags; digiflow_t dflow; if(ch) bc = ch->brdchan; else { printk("ch is NULL in pcxe_ioctl!\n"); return(-EINVAL); } save_flags(flags); switch(cmd) { case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if(retval) return retval; setup_empty_event(tty,ch); tty_wait_until_sent(tty, 0); if(!arg) digi_send_break(ch, HZ/4); /* 1/4 second */ return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if(retval) return retval; setup_empty_event(tty,ch); tty_wait_until_sent(tty, 0); digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4); return 0; case TIOCGSOFTCAR: return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned int *) arg); case TIOCSSOFTCAR: { unsigned int value; error = get_user( value, (unsigned int *) arg); if (error) return error; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (value ? CLOCAL : 0)); } return 0; case TIOCMODG: case TIOCMGET: mflag = 0; cli(); globalwinon(ch); mstat = bc->mstat; memoff(ch); restore_flags(flags); if(mstat & DTR) mflag |= TIOCM_DTR; if(mstat & RTS) mflag |= TIOCM_RTS; if(mstat & CTS) mflag |= TIOCM_CTS; if(mstat & ch->dsr) mflag |= TIOCM_DSR; if(mstat & RI) mflag |= TIOCM_RI; if(mstat & ch->dcd) mflag |= TIOCM_CD; error = put_user(mflag, (unsigned int *) arg); if(error) return error; break; case TIOCMBIS: case TIOCMBIC: case TIOCMODS: case TIOCMSET: error = get_user(mstat, (unsigned int *) arg); if(error) return error; mflag = 0; if(mstat & TIOCM_DTR) mflag |= DTR; if(mstat & TIOCM_RTS) mflag |= RTS; switch(cmd) { case TIOCMODS: case TIOCMSET: ch->modemfake = DTR|RTS; ch->modem = mflag; break; case TIOCMBIS: ch->modemfake |= mflag; ch->modem |= mflag; break; case TIOCMBIC: ch->modemfake &= ~mflag; ch->modem &= ~mflag; break; } cli(); globalwinon(ch); pcxxparam(tty,ch); memoff(ch); restore_flags(flags); break; case TIOCSDTR: cli(); ch->omodem |= DTR; globalwinon(ch); fepcmd(ch, SETMODEM, DTR, 0, 10, 1); memoff(ch); restore_flags(flags); break; case TIOCCDTR: ch->omodem &= ~DTR; cli(); globalwinon(ch); fepcmd(ch, SETMODEM, 0, DTR, 10, 1); memoff(ch); restore_flags(flags); break; case DIGI_GETA: if((error=verify_area(VERIFY_WRITE, (char*)arg, sizeof(digi_t)))) return(error); copy_to_user((char*)arg, &ch->digiext, sizeof(digi_t)); break; case DIGI_SETAW: case DIGI_SETAF: if(cmd == DIGI_SETAW) { setup_empty_event(tty,ch); tty_wait_until_sent(tty, 0); } else { if(tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); } /* Fall Thru */ case DIGI_SETA: if((error=verify_area(VERIFY_READ, (char*)arg,sizeof(digi_t)))) return(error); copy_from_user(&ch->digiext, (char*)arg, sizeof(digi_t));#ifdef DEBUG_IOCTL printk("ioctl(DIGI_SETA): flags = %x\n", ch->digiext.digi_flags);#endif if(ch->digiext.digi_flags & DIGI_ALTPIN) { ch->dcd = DSR; ch->dsr = CD; } else { ch->dcd = CD; ch->dsr = DSR; } cli(); globalwinon(ch); pcxxparam(tty,ch); memoff(ch); restore_flags(flags); break; case DIGI_GETFLOW: case DIGI_GETAFLOW: cli(); globalwinon(ch); if(cmd == DIGI_GETFLOW) { dflow.startc = bc->startc; dflow.stopc = bc->stopc; } else { dflow.startc = bc->startca; dflow.stopc = bc->stopca; } memoff(ch); restore_flags(flags); if((error=verify_area(VERIFY_WRITE, (char*)arg,sizeof(dflow)))) return(error); copy_to_user((char*)arg, &dflow, sizeof(dflow)); break; case DIGI_SETAFLOW: case DIGI_SETFLOW: if(cmd == DIGI_SETFLOW) { startc = ch->startc; stopc = ch->stopc; } else { startc = ch->startca; stopc = ch->stopca; } if((error=verify_area(VERIFY_READ, (char*)arg,sizeof(dflow)))) return(error); copy_from_user(&dflow, (char*)arg, sizeof(dflow)); if(dflow.startc != startc || dflow.stopc != stopc) { cli(); globalwinon(ch); if(cmd == DIGI_SETFLOW) { ch->fepstartc = ch->startc = dflow.startc; ch->fepstopc = ch->stopc = dflow.stopc; fepcmd(ch,SONOFFC,ch->fepstartc,ch->fepstopc,0, 1); } else { ch->fepstartca = ch->startca = dflow.startc; ch->fepstopca = ch->stopca = dflow.stopc; fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); } if(ch->statusflags & TXSTOPPED) pcxe_start(tty); memoff(ch); restore_flags(flags); } break; default: return -ENOIOCTLCMD; } return 0;}static void pcxe_set_termios(struct tty_struct *tty, struct termios *old_termios){ struct channel *info; if ((info=chan(tty))!=NULL) { unsigned long flags; save_flags(flags); cli(); globalwinon(info); pcxxparam(tty,info); memoff(info); if ((old_termios->c_cflag & CRTSCTS) && ((tty->termios->c_cflag & CRTSCTS) == 0)) tty->hw_stopped = 0; if(!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) wake_up_interruptible(&info->open_wait); restore_flags(flags); }}static void do_pcxe_bh(void){ run_task_queue(&tq_pcxx);}static void do_softint(void *private_){ struct channel *info = (struct channel *) private_; if(info && info->magic == PCXX_MAGIC) { struct tty_struct *tty = info->tty; if (tty && tty->driver_data) { if(test_and_clear_bit(PCXE_EVENT_HANGUP, &info->event)) { tty_hangup(tty); wake_up_interruptible(&info->open_wait); info->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); } } }}static void pcxe_stop(struct tty_struct *tty){ struct channel *info; if ((info=chan(tty))!=NULL) { unsigned long flags; save_flags(flags); cli(); if ((info->statusflags & TXSTOPPED) == 0) { globalwinon(info); fepcmd(info, PAUSETX, 0, 0, 0, 0); info->statusflags |= TXSTOPPED; memoff(info); } restore_flags(flags); }}static void pcxe_throttle(struct tty_struct * tty){ struct channel *info; if ((info=chan(tty))!=NULL) { unsigned long flags; save_flags(flags); cli(); if ((info->statusflags & RXSTOPPED) == 0) { globalwinon(info); fepcmd(info, PAUSERX, 0, 0, 0, 0); info->statusflags |= RXSTOPPED; memoff(info); } restore_flags(flags); }}static void pcxe_unthrottle(struct tty_struct *tty){ struct channel *info; if ((info=chan(tty)) != NULL) { unsigned long flags; /* Just in case output was resumed because of a change in Digi-flow */ save_flags(flags); cli(); if(info->statusflags & RXSTOPPED) { volatile struct board_chan *bc; globalwinon(info); bc = info->brdchan; fepcmd(info, RESUMERX, 0, 0, 0, 0); info->statusflags &= ~RXSTOPPED; memoff(info); } restore_flags(flags); }}static void pcxe_start(struct tty_struct *tty){ struct channel *info; if ((info=chan(tty))!=NULL) { unsigned long flags; save_flags(flags); cli(); /* Just in case output was resumed because of a change in Digi-flow */ if(info->statusflags & TXSTOPPED) { volatile struct board_chan *bc; globalwinon(info); bc = info->brdchan; if(info->statusflags & LOWWAIT) bc->ilow = 1; fepcmd(info, RESUMETX, 0, 0, 0, 0); info->statusflags &= ~TXSTOPPED; memoff(info); } restore_flags(flags); }}void digi_send_break(struct channel *ch, int msec){ unsigned long flags; save_flags(flags); cli(); globalwinon(ch); /* * Maybe I should send an infinite break here, schedule() for * msec amount of time, and then stop the break. This way, * the user can't screw up the FEP by causing digi_send_break() * to be called (i.e. via an ioctl()) more than once in msec amount * of time. Try this for now... */ fepcmd(ch, SENDBREAK, msec, 0, 10, 0); memoff(ch); restore_flags(flags);}static void setup_empty_event(struct tty_struct *tty, struct channel *ch){ volatile struct board_chan *bc; unsigned long flags; save_flags(flags); cli(); globalwinon(ch); ch->statusflags |= EMPTYWAIT; bc = ch->brdchan; bc->iempty = 1; memoff(ch); restore_flags(flags);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -