📄 epca.c
字号:
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; MOD_INC_USE_COUNT; if (schedule_task(&ch->tqueue) == 0) MOD_DEC_USE_COUNT;} /* 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); MOD_DEC_USE_COUNT; 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); if (ch->count) MOD_DEC_USE_COUNT; 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 beginning 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 beginning 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. 0 1 2 3 4 5 6 7 8 9 tail head The above diagram shows that buffer locations 2,3,4,5 and 6 have data to be transmitted, while head points at the next empty location. To calculate how much space is available first we have to determine if the head pointer (tin) has wrapped. To do this compare the head pointer to the tail pointer, If head is equal or greater than tail; then it has not wrapped; and the space may be calculated by subtracting tail from head and then subtracting that value from the buffers size. A one is subtracted from the new value to indicate how much space is available between the head pointer and end of buffer; as well as the space between the beginning of the buffer and the tail. If the head is not greater or equal to the tail this indicates that the head has wrapped around to the beginning of the buffer. To calculate the space available in this case simply subtract head from tail. This new value minus one represents the space available betwwen the head and tail pointers. In this example head (7) is greater than tail (2) and therefore has not wrapped around. We find the space by first subtracting tail from head (7-2=5). We then subtract this value from the buffer size of ten and subtract one (10-5-1=4). The space remaining is 4 bytes. Example 2: 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 wrapped around and therefore head < tail. 0 1 2 3 4 5 6 7 8 9 head tail The above diagram shows that buffer locations 7,8,9,0 and 1 have data to be transmitted, while head points at the next empty location. To find the space available we compare head to tail. If head is not equal to, or greater than tail this indicates that head has wrapped around. In this case head (2) is not equal to, or greater than tail (7) and therefore has already wrapped around. To calculate the available space between the two pointers we subtract head from tail (7-2=5). We then subtract one from this new value (5-1=4). We have 5 bytes empty remaining in the buffer. Unlike the previous example these five bytes are located between the head and tail pointers. ----------------------------------------------------------------------- */ dataLen = (head >= tail) ? (size - (head - tail) - 1) : (tail - head - 1); /* ---------------------------------------------------------------------- In this case bytesAvailable has been passed into pc_write and represents the amount of data that needs to be written. dataLen represents the amount of space available on the card. Whichever
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -