epca.c
来自「linux 内核源代码」· C语言 代码 · 共 2,328 行 · 第 1/5 页
C
2,328 行
spin_lock_irqsave(&epca_lock, flags); tty->closing = 0; ch->event = 0; ch->tty = NULL; spin_unlock_irqrestore(&epca_lock, flags); if (ch->blocked_open) { if (ch->close_delay) msleep_interruptible(jiffies_to_msecs(ch->close_delay)); wake_up_interruptible(&ch->open_wait); } ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | ASYNC_CLOSING); wake_up_interruptible(&ch->close_wait); }}static void shutdown(struct channel *ch){ unsigned long flags; struct tty_struct *tty; struct board_chan __iomem *bc; if (!(ch->asyncflags & ASYNC_INITIALIZED)) return; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; /* * In order for an event to be generated on the receipt of data the * idata flag must be set. Since we are shutting down, this is not * necessary clear this flag. */ if (bc) writeb(0, &bc->idata); tty = ch->tty; /* If we're a modem control device and HUPCL is on, drop RTS & DTR. */ if (tty->termios->c_cflag & HUPCL) { ch->omodem &= ~(ch->m_rts | ch->m_dtr); fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1); } memoff(ch); /* * The channel has officialy been closed. The next time it is opened it * will have to reinitialized. Set a flag to indicate this. */ /* Prevent future Digi programmed interrupts from coming active */ ch->asyncflags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&epca_lock, flags);}static void pc_hangup(struct tty_struct *tty){ struct channel *ch; /* * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ if ((ch = verifyChannel(tty)) != NULL) { unsigned long flags; if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); shutdown(ch); spin_lock_irqsave(&epca_lock, flags); ch->tty = NULL; ch->event = 0; ch->count = 0; ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED); spin_unlock_irqrestore(&epca_lock, flags); wake_up_interruptible(&ch->open_wait); }}static int pc_write(struct tty_struct *tty, const unsigned char *buf, int bytesAvailable){ unsigned int head, tail; int dataLen; int size; int amountCopied; struct channel *ch; unsigned long flags; int remain; struct board_chan __iomem *bc; /* * pc_write is primarily called directly by the kernel routine * tty_write (Though it can also be called by put_char) found in * tty_io.c. pc_write is passed a line discipline buffer where the data * to be written out is stored. The line discipline implementation * itself is done at the kernel level and is not brought into the * driver. */ /* * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ if ((ch = verifyChannel(tty)) == NULL) return 0; /* Make a pointer to the channel data structure found on the board. */ bc = ch->brdchan; size = ch->txbufsize; amountCopied = 0; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); head = readw(&bc->tin) & (size - 1); tail = readw(&bc->tout); if (tail != readw(&bc->tout)) tail = readw(&bc->tout); tail &= (size - 1); if (head >= tail) { /* head has not wrapped */ /* * remain (much like dataLen above) represents the total amount * of space available on the card for data. Here dataLen * represents the space existing between the head pointer and * the end of buffer. This is important because a memcpy cannot * be told to automatically wrap around when it hits the buffer * end. */ dataLen = size - head; remain = size - (head - tail) - 1; } else { /* head has wrapped around */ remain = tail - head - 1; dataLen = remain; } /* * Check the space on the card. If we have more data than space; reduce * the amount of data to fit the space. */ bytesAvailable = min(remain, bytesAvailable); txwinon(ch); while (bytesAvailable > 0) { /* there is data to copy onto card */ /* * If head is not wrapped, the below will make sure the first * data copy fills to the end of card buffer. */ dataLen = min(bytesAvailable, dataLen); memcpy_toio(ch->txptr + head, buf, dataLen); buf += dataLen; head += dataLen; amountCopied += dataLen; bytesAvailable -= dataLen; if (head >= size) { head = 0; dataLen = tail; } } ch->statusflags |= TXBUSY; globalwinon(ch); writew(head, &bc->tin); if ((ch->statusflags & LOWWAIT) == 0) { ch->statusflags |= LOWWAIT; writeb(1, &bc->ilow); } memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); return amountCopied;}static void pc_put_char(struct tty_struct *tty, unsigned char c){ pc_write(tty, &c, 1);}static int pc_write_room(struct tty_struct *tty){ int remain; struct channel *ch; unsigned long flags; unsigned int head, tail; struct board_chan __iomem *bc; remain = 0; /* * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ if ((ch = verifyChannel(tty)) != NULL) { spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; head = readw(&bc->tin) & (ch->txbufsize - 1); tail = readw(&bc->tout); if (tail != readw(&bc->tout)) tail = readw(&bc->tout); /* Wrap tail if necessary */ tail &= (ch->txbufsize - 1); if ((remain = tail - head - 1) < 0 ) remain += ch->txbufsize; if (remain && (ch->statusflags & LOWWAIT) == 0) { ch->statusflags |= LOWWAIT; writeb(1, &bc->ilow); } memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); } /* Return how much room is left on card */ return remain;}static int pc_chars_in_buffer(struct tty_struct *tty){ int chars; unsigned int ctail, head, tail; int remain; unsigned long flags; struct channel *ch; struct board_chan __iomem *bc; /* * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ if ((ch = verifyChannel(tty)) == NULL) return 0; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; tail = readw(&bc->tout); head = readw(&bc->tin); ctail = readw(&ch->mailbox->cout); if (tail == head && readw(&ch->mailbox->cin) == ctail && readb(&bc->tbusy) == 0) chars = 0; else { /* Begin if some space on the card has been used */ head = readw(&bc->tin) & (ch->txbufsize - 1); tail &= (ch->txbufsize - 1); /* * The logic here is basically opposite of the above * pc_write_room here we are finding the amount of bytes in the * buffer filled. Not the amount of bytes empty. */ if ((remain = tail - head - 1) < 0 ) remain += ch->txbufsize; chars = (int)(ch->txbufsize - remain); /* * Make it possible to wakeup anything waiting for output in * tty_ioctl.c, etc. * * If not already set. Setup an event to indicate when the * transmit buffer empties. */ if (!(ch->statusflags & EMPTYWAIT)) setup_empty_event(tty,ch); } /* End if some space on the card has been used */ memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); /* Return number of characters residing on card. */ return chars;}static void pc_flush_buffer(struct tty_struct *tty){ unsigned int tail; unsigned long flags; struct channel *ch; struct board_chan __iomem *bc; /* * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ if ((ch = verifyChannel(tty)) == NULL) return; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; tail = readw(&bc->tout); /* Have FEP move tout pointer; effectively flushing transmit buffer */ fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wakeup(tty);}static void pc_flush_chars(struct tty_struct *tty){ struct channel *ch; /* * verifyChannel returns the channel from the tty struct if it is * valid. This serves as a sanity check. */ if ((ch = verifyChannel(tty)) != NULL) { unsigned long flags; spin_lock_irqsave(&epca_lock, flags); /* * If not already set and the transmitter is busy setup an * event to indicate when the transmit empties. */ if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT)) setup_empty_event(tty,ch); spin_unlock_irqrestore(&epca_lock, flags); }}static int block_til_ready(struct tty_struct *tty, struct file *filp, struct channel *ch){ DECLARE_WAITQUEUE(wait,current); int retval, do_clocal = 0; unsigned long flags; if (tty_hung_up_p(filp)) { if (ch->asyncflags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; return retval; } /* * If the device is in the middle of being closed, then block until * it's done, and then try again. */ if (ch->asyncflags & ASYNC_CLOSING) { interruptible_sleep_on(&ch->close_wait); if (ch->asyncflags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; } if (filp->f_flags & O_NONBLOCK) { /* * If non-blocking mode is set, then make the check up front * and then exit. */ ch->asyncflags |= ASYNC_NORMAL_ACTIVE; return 0; } if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; /* Block waiting for the carrier detect and the line to become free */ retval = 0; add_wait_queue(&ch->open_wait, &wait); spin_lock_irqsave(&epca_lock, flags); /* We dec count so that pc_close will know when to free things */ if (!tty_hung_up_p(filp)) ch->count--; ch->blocked_open++; while (1) { set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(ch->asyncflags & ASYNC_INITIALIZED)) { if (ch->asyncflags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } if (!(ch->asyncflags & ASYNC_CLOSING) && (do_clocal || (ch->imodem & ch->dcd))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } spin_unlock_irqrestore(&epca_lock, flags); /* * Allow someone else to be scheduled. We will occasionally go * through this loop until one of the above conditions change. * The below schedule call will allow other processes to enter * and prevent this loop from hogging the cpu. */ schedule(); spin_lock_irqsave(&epca_lock, flags); } __set_current_state(TASK_RUNNING); remove_wait_queue(&ch->open_wait, &wait); if (!tty_hung_up_p(filp)) ch->count++; ch->blocked_open--; spin_unlock_irqrestore(&epca_lock, flags); if (retval) return retval; ch->asyncflags |= ASYNC_NORMAL_ACTIVE; return 0;}static int pc_open(struct tty_struct *tty, struct file * filp){ struct channel *ch; unsigned long flags; int line, retval, boardnum; struct board_chan __iomem *bc; unsigned int head; line = tty->index; if (line < 0 || line >= nbdevs) return -ENODEV; ch = &digi_channels[line]; boardnum = ch->boardnum; /* Check status of board configured in system. */ /* * I check to see if the epca_setup routine detected an user error. It * might be better to put this in pc_init, but for the moment it goes * here. */ if (invalid_lilo_config) { if (setup_error_code & INVALID_BOARD_TYPE) printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n"); if (setup_error_code & INVALID_NUM_PORTS) printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n"); if (setup_error_code & INVALID_MEM_BASE) printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n"); if (setup_error_code & INVALID_PORT_BASE) printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n"); if (setup_error_code & INVALID_BOARD_STATUS) printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n"); if (setup_error_code & INVALID_ALTPIN) printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n"); tty->driver_data = NULL; /* Mark this device as 'down' */ return -ENODEV; } if (boardnum >= num_cards || boards[boardnum].status == DISABLED) { tty->driver_data = NULL; /* Mark this device as 'down' */ return(-ENODEV); } if ((bc = ch->brdchan) == 0) { tty->driver_data = NULL; return -ENODEV; } spin_lock_irqsave(&epca_lock, flags); /*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?