📄 epca.c
字号:
}static inline void pcxi_txwinon(struct channel *ch){ outb_p(FEPMEM, (int)ch->board->port);}static inline void pcxi_memoff(struct channel *ch){ outb_p(0, (int)ch->board->port);}static inline void pcxi_assertgwinon(struct channel *ch){ epcaassert(inb((int)ch->board->port) & FEPMEM, "Global memory off");}static inline void pcxi_assertmemoff(struct channel *ch){ epcaassert(!(inb((int)ch->board->port) & FEPMEM), "Memory on");}/* ---------------------------------------------------------------------- Not all of the cards need specific memory windowing routines. Some cards (Such as PCI) needs no windowing routines at all. We provide these do nothing routines so that the same code base can be used. The driver will ALWAYS call a windowing routine if it thinks it needs to; regardless of the card. However, dependent on the card the routine may or may not do anything.---------------------------------------------------------------------------*/static inline void dummy_memwinon(struct board_info *b, unsigned int win){}static inline void dummy_memwinoff(struct board_info *b, unsigned int win){}static inline void dummy_globalwinon(struct channel *ch){}static inline void dummy_rxwinon(struct channel *ch){}static inline void dummy_txwinon(struct channel *ch){}static inline void dummy_memoff(struct channel *ch){}static inline void dummy_assertgwinon(struct channel *ch){}static inline void dummy_assertmemoff(struct channel *ch){}/* ----------------- Begin verifyChannel function ----------------------- */static inline struct channel *verifyChannel(register struct tty_struct *tty){ /* Begin verifyChannel */ /* -------------------------------------------------------------------- This routine basically provides a sanity check. It insures that the channel returned is within the proper range of addresses as well as properly initialized. If some bogus info gets passed in through tty->driver_data this should catch it. --------------------------------------------------------------------- */ if (tty) { /* Begin if tty */ register struct channel *ch = (struct channel *)tty->driver_data; if ((ch >= &digi_channels[0]) && (ch < &digi_channels[nbdevs])) { if (ch->magic == EPCA_MAGIC) return ch; } } /* End if tty */ /* Else return a NULL for invalid */ return NULL;} /* End verifyChannel *//* ------------------ Begin pc_sched_event ------------------------- */static inline void pc_sched_event(struct channel *ch, int event){ /* Begin pc_sched_event */ /* ---------------------------------------------------------------------- We call this to schedule interrupt processing on some event. The kernel sees our request and calls the related routine in OUR driver. -------------------------------------------------------------------------*/ ch->event |= 1 << event; queue_task(&ch->tqueue, &tq_scheduler);} /* End pc_sched_event *//* ------------------ Begin epca_error ------------------------- */static void epca_error(int line, char *msg){ /* Begin epca_error */ printk(KERN_ERR "epca_error (Digi): line = %d %s\n",line,msg); return;} /* End epca_error *//* ------------------ Begin pc_close ------------------------- */static void pc_close(struct tty_struct * tty, struct file * filp){ /* Begin pc_close */ struct channel *ch; unsigned long flags; if (tty->driver.subtype == SERIAL_TYPE_INFO) { return; } /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if ch != NULL */ save_flags(flags); cli(); if (tty_hung_up_p(filp)) { restore_flags(flags); return; } /* Check to see if the channel is open more than once */ if (ch->count-- > 1) { /* Begin channel is open more than once */ /* ------------------------------------------------------------- Return without doing anything. Someone might still be using the channel. ---------------------------------------------------------------- */ restore_flags(flags); return; } /* End channel is open more than once */ /* Port open only once go ahead with shutdown & reset */ if (ch->count < 0) { ch->count = 0; } /* --------------------------------------------------------------- Let the rest of the driver know the channel is being closed. This becomes important if an open is attempted before close is finished. ------------------------------------------------------------------ */ ch->asyncflags |= ASYNC_CLOSING; /* ------------------------------------------------------------- Save the termios structure, since this port may have separate termios for callout and dialin. --------------------------------------------------------------- */ if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) ch->normal_termios = *tty->termios; if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) ch->callout_termios = *tty->termios; tty->closing = 1; if (ch->asyncflags & ASYNC_INITIALIZED) { /* Setup an event to indicate when the transmit buffer empties */ setup_empty_event(tty, ch); tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ } if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); shutdown(ch); tty->closing = 0; ch->event = 0; ch->tty = NULL; if (ch->blocked_open) { /* Begin if blocked_open */ if (ch->close_delay) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(ch->close_delay); } wake_up_interruptible(&ch->open_wait); } /* End if blocked_open */ ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | ASYNC_CALLOUT_ACTIVE | ASYNC_CLOSING); wake_up_interruptible(&ch->close_wait);#ifdef MODULE MOD_DEC_USE_COUNT;#endif restore_flags(flags); } /* End if ch != NULL */} /* End pc_close */ /* ------------------ Begin shutdown ------------------------- */static void shutdown(struct channel *ch){ /* Begin shutdown */ unsigned long flags; struct tty_struct *tty; volatile struct board_chan *bc; if (!(ch->asyncflags & ASYNC_INITIALIZED)) return; save_flags(flags); cli(); 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) bc->idata = 0; 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; restore_flags(flags);} /* End shutdown *//* ------------------ Begin pc_hangup ------------------------- */static void pc_hangup(struct tty_struct *tty){ /* Begin pc_hangup */ 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) { /* Begin if ch != NULL */ unsigned long flags; save_flags(flags); cli(); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); shutdown(ch);#ifdef MODULE if (ch->count) MOD_DEC_USE_COUNT;#endif /* MODULE */ ch->tty = NULL; ch->event = 0; ch->count = 0; restore_flags(flags); ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | ASYNC_CALLOUT_ACTIVE); wake_up_interruptible(&ch->open_wait); } /* End if ch != NULL */} /* End pc_hangup *//* ------------------ Begin pc_write ------------------------- */static int pc_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int bytesAvailable){ /* Begin pc_write */ register unsigned int head, tail; register int dataLen; register int size; register int amountCopied; struct channel *ch; unsigned long flags; int remain; volatile struct board_chan *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. ------------------------------------------------------------------- */ /* Stop users from hurting themselves on control minor */ if (tty->driver.subtype == SERIAL_TYPE_INFO) { return (0) ; } /* --------------------------------------------------------- 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; if (from_user) { /* Begin from_user */ save_flags(flags); cli(); globalwinon(ch); /* ----------------------------------------------------------------- Anding against size will wrap the pointer back to its begining position if it is necessary. This will only work if size is a power of 2 which should always be the case. Size is determined by the cards on board FEP/OS. -------------------------------------------------------------------- */ /* head refers to the next empty location in which data may be stored */ head = bc->tin & (size - 1); /* tail refers to the next data byte to be transmitted */ tail = bc->tout; /* Consider changing this to a do statement to make sure */ if (tail != bc->tout) tail = bc->tout; /* ------------------------------------------------------------------ Anding against size will wrap the pointer back to its begining position if it is necessary. This will only work if size is a power of 2 which should always be the case. Size is determined by the cards on board FEP/OS. --------------------------------------------------------------------- */ tail &= (size - 1); /* ----------------------------------------------------------------- Two situations can affect how space in the transmit buffer is calculated. You can have a situation where the transmit in pointer (tin) head has wrapped around and actually has a lower address than the transmit out pointer (tout) tail; or the transmit in pointer (tin) head will not be wrapped around yet, and have a higher address than the transmit out pointer (tout) tail. Obviously space available in the transmit buffer is calculated differently for each case. Example 1: Consider a 10 byte buffer where head is a pointer to the next empty location in the buffer and tail is a pointer to the next byte to transmit. In this example head will not have wrapped around and therefore head > tail.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -