epca.c
来自「linux 内核源代码」· C语言 代码 · 共 2,328 行 · 第 1/5 页
C
2,328 行
ch->m_dcd = 0x80; ch->m_dsr = 0x20; ch->m_cts = 0x10; ch->m_ri = 0x40; ch->m_dtr = 0x01; break; case PCXE: case PCXEVE: case PCXI: case PC64XE: ch->m_rts = 0x02; ch->m_dcd = 0x08; ch->m_dsr = 0x10; ch->m_cts = 0x20; ch->m_ri = 0x40; ch->m_dtr = 0x80; break; } if (boards[crd].altpin) { ch->dsr = ch->m_dcd; ch->dcd = ch->m_dsr; ch->digiext.digi_flags |= DIGI_ALTPIN; } else { ch->dcd = ch->m_dcd; ch->dsr = ch->m_dsr; } ch->boardnum = crd; ch->channelnum = i; ch->magic = EPCA_MAGIC; ch->tty = NULL; if (shrinkmem) { fepcmd(ch, SETBUFFER, 32, 0, 0, 0); shrinkmem = 0; } tseg = readw(&bc->tseg); rseg = readw(&bc->rseg); switch (bd->type) { case PCIXEM: case PCIXRJ: case PCIXR: /* Cover all the 2MEG cards */ ch->txptr = memaddr + ((tseg << 4) & 0x1fffff); ch->rxptr = memaddr + ((rseg << 4) & 0x1fffff); ch->txwin = FEPWIN | (tseg >> 11); ch->rxwin = FEPWIN | (rseg >> 11); break; case PCXEM: case EISAXEM: /* Cover all the 32K windowed cards */ /* Mask equal to window size - 1 */ ch->txptr = memaddr + ((tseg << 4) & 0x7fff); ch->rxptr = memaddr + ((rseg << 4) & 0x7fff); ch->txwin = FEPWIN | (tseg >> 11); ch->rxwin = FEPWIN | (rseg >> 11); break; case PCXEVE: case PCXE: ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4) & 0x1fff); ch->txwin = FEPWIN | ((tseg - bd->memory_seg) >> 9); ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4) & 0x1fff); ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >>9 ); break; case PCXI: case PC64XE: ch->txptr = memaddr + ((tseg - bd->memory_seg) << 4); ch->rxptr = memaddr + ((rseg - bd->memory_seg) << 4); ch->txwin = ch->rxwin = 0; break; } ch->txbufhead = 0; ch->txbufsize = readw(&bc->tmax) + 1; ch->rxbufhead = 0; ch->rxbufsize = readw(&bc->rmax) + 1; lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2); /* Set transmitter low water mark */ fepcmd(ch, STXLWATER, lowwater, 0, 10, 0); /* Set receiver low water mark */ fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0); /* Set receiver high water mark */ fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0); writew(100, &bc->edelay); writeb(1, &bc->idata); ch->startc = readb(&bc->startc); ch->stopc = readb(&bc->stopc); ch->startca = readb(&bc->startca); ch->stopca = readb(&bc->stopca); ch->fepcflag = 0; ch->fepiflag = 0; ch->fepoflag = 0; ch->fepstartc = 0; ch->fepstopc = 0; ch->fepstartca = 0; ch->fepstopca = 0; ch->close_delay = 50; ch->count = 0; ch->blocked_open = 0; init_waitqueue_head(&ch->open_wait); init_waitqueue_head(&ch->close_wait); spin_unlock_irqrestore(&epca_lock, flags); } printk(KERN_INFO "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); memwinoff(bd, 0);}static void epcapoll(unsigned long ignored){ unsigned long flags; int crd; volatile unsigned int head, tail; struct channel *ch; struct board_info *bd; /* * This routine is called upon every timer interrupt. Even though the * Digi series cards are capable of generating interrupts this method * of non-looping polling is more efficient. This routine checks for * card generated events (Such as receive data, are transmit buffer * empty) and acts on those events. */ for (crd = 0; crd < num_cards; crd++) { bd = &boards[crd]; ch = card_ptr[crd]; if ((bd->status == DISABLED) || digi_poller_inhibited) continue; /* * assertmemoff is not needed here; indeed it is an empty * subroutine. It is being kept because future boards may need * this as well as some legacy boards. */ spin_lock_irqsave(&epca_lock, flags); assertmemoff(ch); globalwinon(ch); /* * In this case head and tail actually refer to the event queue * not the transmit or receive queue. */ head = readw(&ch->mailbox->ein); tail = readw(&ch->mailbox->eout); /* If head isn't equal to tail we have an event */ if (head != tail) doevent(crd); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); } /* End for each card */ mod_timer(&epca_timer, jiffies + (HZ / 25));}static void doevent(int crd){ void __iomem *eventbuf; struct channel *ch, *chan0; static struct tty_struct *tty; struct board_info *bd; struct board_chan __iomem *bc; unsigned int tail, head; int event, channel; int mstat, lstat; /* * This subroutine is called by epcapoll when an event is detected * in the event queue. This routine responds to those events. */ bd = &boards[crd]; chan0 = card_ptr[crd]; epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range"); assertgwinon(chan0); while ((tail = readw(&chan0->mailbox->eout)) != (head = readw(&chan0->mailbox->ein))) { /* Begin while something in event queue */ assertgwinon(chan0); eventbuf = bd->re_map_membase + tail + ISTART; /* Get the channel the event occurred on */ channel = readb(eventbuf); /* Get the actual event code that occurred */ event = readb(eventbuf + 1); /* * The two assignments below get the current modem status * (mstat) and the previous modem status (lstat). These are * useful becuase an event could signal a change in modem * signals itself. */ mstat = readb(eventbuf + 2); lstat = readb(eventbuf + 3); ch = chan0 + channel; if ((unsigned)channel >= bd->numports || !ch) { if (channel >= bd->numports) ch = chan0; bc = ch->brdchan; goto next; } if ((bc = ch->brdchan) == NULL) goto next; if (event & DATA_IND) { /* Begin DATA_IND */ receive_data(ch); assertgwinon(ch); } /* End DATA_IND */ /* else *//* Fix for DCD transition missed bug */ if (event & MODEMCHG_IND) { /* A modem signal change has been indicated */ ch->imodem = mstat; if (ch->asyncflags & ASYNC_CHECK_CD) { if (mstat & ch->dcd) /* We are now receiving dcd */ wake_up_interruptible(&ch->open_wait); else pc_sched_event(ch, EPCA_EVENT_HANGUP); /* No dcd; hangup */ } } tty = ch->tty; if (tty) { if (event & BREAK_IND) { /* A break has been indicated */ tty_insert_flip_char(tty, 0, TTY_BREAK); tty_schedule_flip(tty); } else if (event & LOWTX_IND) { if (ch->statusflags & LOWWAIT) { ch->statusflags &= ~LOWWAIT; tty_wakeup(tty); } } else if (event & EMPTYTX_IND) { /* This event is generated by setup_empty_event */ ch->statusflags &= ~TXBUSY; if (ch->statusflags & EMPTYWAIT) { ch->statusflags &= ~EMPTYWAIT; tty_wakeup(tty); } } } next: globalwinon(ch); BUG_ON(!bc); writew(1, &bc->idata); writew((tail + 4) & (IMAX - ISTART - 4), &chan0->mailbox->eout); globalwinon(chan0); } /* End while something in event queue */}static void fepcmd(struct channel *ch, int cmd, int word_or_byte, int byte2, int ncmds, int bytecmd){ unchar __iomem *memaddr; unsigned int head, cmdTail, cmdStart, cmdMax; long count; int n; /* This is the routine in which commands may be passed to the card. */ if (ch->board->status == DISABLED) return; assertgwinon(ch); /* Remember head (As well as max) is just an offset not a base addr */ head = readw(&ch->mailbox->cin); /* cmdStart is a base address */ cmdStart = readw(&ch->mailbox->cstart); /* * We do the addition below because we do not want a max pointer * relative to cmdStart. We want a max pointer that points at the * physical end of the command queue. */ cmdMax = (cmdStart + 4 + readw(&ch->mailbox->cmax)); memaddr = ch->board->re_map_membase; if (head >= (cmdMax - cmdStart) || (head & 03)) { printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n", __LINE__, cmd, head); printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n", __LINE__, cmdMax, cmdStart); return; } if (bytecmd) { writeb(cmd, memaddr + head + cmdStart + 0); writeb(ch->channelnum, memaddr + head + cmdStart + 1); /* Below word_or_byte is bits to set */ writeb(word_or_byte, memaddr + head + cmdStart + 2); /* Below byte2 is bits to reset */ writeb(byte2, memaddr + head + cmdStart + 3); } else { writeb(cmd, memaddr + head + cmdStart + 0); writeb(ch->channelnum, memaddr + head + cmdStart + 1); writeb(word_or_byte, memaddr + head + cmdStart + 2); } head = (head + 4) & (cmdMax - cmdStart - 4); writew(head, &ch->mailbox->cin); count = FEPTIMEOUT; for (;;) { count--; if (count == 0) { printk(KERN_ERR "<Error> - Fep not responding in fepcmd()\n"); return; } head = readw(&ch->mailbox->cin); cmdTail = readw(&ch->mailbox->cout); n = (head - cmdTail) & (cmdMax - cmdStart - 4); /* * Basically this will break when the FEP acknowledges the * command by incrementing cmdTail (Making it equal to head). */ if (n <= ncmds * (sizeof(short) * 4)) break; }}/* * Digi products use fields in their channels structures that are very similar * to the c_cflag and c_iflag fields typically found in UNIX termios * structures. The below three routines allow mappings between these hardware * "flags" and their respective Linux flags. */static unsigned termios2digi_h(struct channel *ch, unsigned cflag){ unsigned res = 0; if (cflag & CRTSCTS) { ch->digiext.digi_flags |= (RTSPACE | CTSPACE); res |= ((ch->m_cts) | (ch->m_rts)); } if (ch->digiext.digi_flags & RTSPACE) res |= ch->m_rts; if (ch->digiext.digi_flags & DTRPACE) res |= ch->m_dtr; if (ch->digiext.digi_flags & CTSPACE) res |= ch->m_cts; if (ch->digiext.digi_flags & DSRPACE) res |= ch->dsr; if (ch->digiext.digi_flags & DCDPACE) res |= ch->dcd; if (res & (ch->m_rts)) ch->digiext.digi_flags |= RTSPACE; if (res & (ch->m_cts)) ch->digiext.digi_flags |= CTSPACE; return res;}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_c(struct channel *ch, unsigned cflag){ unsigned res = 0; if (cflag & CBAUDEX) { ch->digiext.digi_flags |= DIGI_FAST; /* * HUPCL bit is used by FEP to indicate fast baud table is to * be used. */ res |= FEP_HUPCL; } else ch->digiext.digi_flags &= ~DIGI_FAST; /* * CBAUD has bit position 0x1000 set these days to indicate Linux * baud rate remap. Digi hardware can't handle the bit assignment. * (We use a different bit assignment for high speed.). Clear this * bit out. */ res |= cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE); /* * This gets a little confusing. The Digi cards have their own * representation of c_cflags controling baud rate. For the most part * this is identical to the Linux implementation. However; Digi * supports one rate (76800) that Linux doesn't. This means that the * c_cflag entry that would normally mean 76800 for Digi actually means * 115200 under Linux. Without the below mapping, a stty 115200 would * only drive the board at 76800. Since the rate 230400 is also found * after 76800, the same problem afflicts us when we choose a rate of * 230400. Without the below modificiation stty 230400 would actually * give us 115200. * * There are two additional differences. The Linux value for CLOCAL * (0x800; 0004000) has no meaning to the Digi hardware. Also in later * releases of Linux; the CBAUD define has CBAUDEX (0x1000; 0010000) * ored into it (CBAUD = 0x100f as opposed to 0xf). CBAUDEX should be * checked for a screened out prior to termios2digi_c returning. Since * CLOCAL isn't used by the board this can be ignored as long as the * returned value is used only by Digi hardware. */ if (cflag & CBAUDEX) { /* * The below code is trying to guarantee that only baud rates * 115200 and 230400 are remapped. We use exclusive or because * the various baud rates share common bit positions and * therefore can't be tested for easily. */ if ((!((cflag & 0x7) ^ (B115200 & ~CBAUDEX))) || (!((cflag & 0x7) ^ (B230400 & ~CBAUDEX)))) res += 1; } return res;}/* Caller must hold the locks */static void epcaparam(struct tty_struct *tty, struct channel *ch){ unsigned int cmdHead; struct ktermios *ts; struct board_chan __iomem *bc; unsigned mval, hflow, cflag, iflag; bc = ch->brdchan; epcaassert(bc !=0, "bc out of range"); assertgwinon(ch); ts = tty->termios; if ((ts->c_cflag & CBAUD) == 0) { /* Begin CBAUD detected */ cmdHead = readw(&bc->rin); writew(cmdHead, &bc->rout); cmdHead = readw(&bc->tin); /* Changing baud in mid-stream transmission can be wonderful */ /* * Flush current transmit buffer by setting cmdTail pointer * (tout) to cmdHead pointer (tin). Hopefully the transmit * buffer is empty. */ fepcmd(ch, STOUT, (unsigned) cmdHead, 0, 0, 0); mval = 0; } else { /* Begin CBAUD not detected */ /* * c_cflags have changed but that change had nothing to do with * BAUD. Propagate the change to the card. */ cflag = termios2digi_c(ch, ts->c_cflag); if (cflag != ch->fepcflag) { ch->fepcflag = cflag;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?