/********************************************************************************* (c) 1999 by Computone Corporation************************************************************************************ PACKAGE: Linux tty Device Driver for IntelliPort family of multiport* serial I/O controllers.** DESCRIPTION: High-level interface code for the device driver. Uses the* Extremely Low Level Interface Support (i2ellis.c). Provides an* interface to the standard loadware, to support drivers or* application code. (This is included source code, not a separate* compilation module.)********************************************************************************///------------------------------------------------------------------------------// Note on Strategy:// Once the board has been initialized, it will interrupt us when:// 1) It has something in the fifo for us to read (incoming data, flow control// packets, or whatever).// 2) It has stripped whatever we have sent last time in the FIFO (and// consequently is ready for more).//// Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This// worsens performance considerably, but is done so that a great many channels// might use only a little memory.//------------------------------------------------------------------------------ //------------------------------------------------------------------------------// Revision History://// 0.00 - 4/16/91 --- First Draft// 0.01 - 4/29/91 --- 1st beta release// 0.02 - 6/14/91 --- Changes to allow small model compilation// 0.03 - 6/17/91 MAG Break reporting protected from interrupts routines with// in-line asm added for moving data to/from ring buffers,// replacing a variety of methods used previously.// 0.04 - 6/21/91 MAG Initial flow-control packets not queued until// i2_enable_interrupts time. Former versions would enqueue// them at i2_init_channel time, before we knew how many// channels were supposed to exist!// 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now;// supports new 16-bit protocol and expandable boards.// - 10/24/91 MAG Most changes in place and stable.// 0.06 - 2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no// argument.// 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt// level (mostly responses to specific commands.)// 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet// 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE// turning on the interrupt.// 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check// some incoming.//// 1.1 - 12/25/96 AKM Linux version.// - 10/09/98 DMC Revised Linux version.//------------------------------------------------------------------------------ //************//* Includes *//************ #include <linux/sched.h>#include "i2lib.h" //***********************//* Function Prototypes *//***********************static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int);static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int );static void i2StripFifo(i2eBordStrPtr);static void i2StuffFifoBypass(i2eBordStrPtr);static void i2StuffFifoFlow(i2eBordStrPtr);static void i2StuffFifoInline(i2eBordStrPtr);static int i2RetryFlushOutput(i2ChanStrPtr); // Not a documented part of the library routines (careful...) but the Diagnostic// i2diag.c finds them useful to help the throughput in certain limited// single-threaded operations.static void iiSendPendingMail(i2eBordStrPtr);static void serviceOutgoingFifo(i2eBordStrPtr); // Functions defined in ip2.c as part of interrupt handlingstatic void do_input(struct work_struct *);static void do_status(struct work_struct *); //***************//* Debug Data *//***************#ifdef DEBUG_FIFO unsigned char DBGBuf[0x4000];unsigned short I = 0; static voidWriteDBGBuf(char *s, unsigned char *src, unsigned short n ) { char *p = src; // XXX: We need a spin lock here if we ever use this again while (*s) { // copy label DBGBuf[I] = *s++; I = I++ & 0x3fff; } while (n--) { // copy data DBGBuf[I] = *p++; I = I++ & 0x3fff; }} static voidfatality(i2eBordStrPtr pB ){ int i; for (i=0;i<sizeof(DBGBuf);i++) { if ((i%16) == 0) printk("\n%4x:",i); printk("%02x ",DBGBuf[i]); } printk("\n"); for (i=0;i<sizeof(DBGBuf);i++) { if ((i%16) == 0) printk("\n%4x:",i); if (DBGBuf[i] >= ' ' && DBGBuf[i] <= '~') { printk(" %c ",DBGBuf[i]); } else { printk(" . "); } } printk("\n"); printk("Last index %x\n",I);}#endif /* DEBUG_FIFO */ //********//* Code *//******** static inline inti2Validate ( i2ChanStrPtr pCh ){ //ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity, // (CHANNEL_MAGIC | CHANNEL_SUPPORT)); return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT)) == (CHANNEL_MAGIC | CHANNEL_SUPPORT));} static void iiSendPendingMail_t(unsigned long data){ i2eBordStrPtr pB = (i2eBordStrPtr)data; iiSendPendingMail(pB);} //******************************************************************************// Function: iiSendPendingMail(pB)// Parameters: Pointer to a board structure// Returns: Nothing//// Description:// If any outgoing mail bits are set and there is outgoing mailbox is empty,// send the mail and clear the bits.//******************************************************************************static voidiiSendPendingMail(i2eBordStrPtr pB){ if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) ) { if (iiTrySendMail(pB, pB->i2eOutMailWaiting)) { /* If we were already waiting for fifo to empty, * or just sent MB_OUT_STUFFED, then we are * still waiting for it to empty, until we should * receive an MB_IN_STRIPPED from the board. */ pB->i2eWaitingForEmptyFifo |= (pB->i2eOutMailWaiting & MB_OUT_STUFFED); pB->i2eOutMailWaiting = 0; pB->SendPendingRetry = 0; } else {/* The only time we hit this area is when "iiTrySendMail" has failed. That only occurs when the outbound mailbox is still busy with the last message. We take a short breather to let the board catch up with itself and then try again. 16 Retries is the limit - then we got a borked board. /\/\|=mhw=|\/\/ */ if( ++pB->SendPendingRetry < 16 ) { setup_timer(&pB->SendPendingTimer, iiSendPendingMail_t, (unsigned long)pB); mod_timer(&pB->SendPendingTimer, jiffies + 1); } else { printk( KERN_ERR "IP2: iiSendPendingMail unable to queue outbound mail\n" ); } } }} //******************************************************************************// Function: i2InitChannels(pB, nChannels, pCh)// Parameters: Pointer to Ellis Board structure// Number of channels to initialize// Pointer to first element in an array of channel structures// Returns: Success or failure//// Description://// This function patches pointers, back-pointers, and initializes all the// elements in the channel structure array.//// This should be run after the board structure is initialized, through having// loaded the standard loadware (otherwise it complains).//// In any case, it must be done before any serious work begins initializing the// irq's or sending commands...////******************************************************************************static inti2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh){ int index, stuffIndex; i2ChanStrPtr *ppCh; if (pB->i2eValid != I2E_MAGIC) { COMPLETE(pB, I2EE_BADMAGIC); } if (pB->i2eState != II_STATE_STDLOADED) { COMPLETE(pB, I2EE_BADSTATE); } LOCK_INIT(&pB->read_fifo_spinlock); LOCK_INIT(&pB->write_fifo_spinlock); LOCK_INIT(&pB->Dbuf_spinlock); LOCK_INIT(&pB->Bbuf_spinlock); LOCK_INIT(&pB->Fbuf_spinlock); // NO LOCK needed yet - this is init pB->i2eChannelPtr = pCh; pB->i2eChannelCnt = nChannels; pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0; pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0; pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0; pB->SendPendingRetry = 0; memset ( pCh, 0, sizeof (i2ChanStr) * nChannels ); for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf); nChannels && index < ABS_MOST_PORTS; index++) { if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) { continue; } LOCK_INIT(&pCh->Ibuf_spinlock); LOCK_INIT(&pCh->Obuf_spinlock); LOCK_INIT(&pCh->Cbuf_spinlock); LOCK_INIT(&pCh->Pbuf_spinlock); // NO LOCK needed yet - this is init // Set up validity flag according to support level if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) { pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT; } else { pCh->validity = CHANNEL_MAGIC; } pCh->pMyBord = pB; /* Back-pointer */ // Prepare an outgoing flow-control packet to send as soon as the chance // occurs. if ( pCh->validity & CHANNEL_SUPPORT ) { pCh->infl.hd.i2sChannel = index; pCh->infl.hd.i2sCount = 5; pCh->infl.hd.i2sType = PTYPE_BYPASS; pCh->infl.fcmd = 37; pCh->infl.asof = 0; pCh->infl.room = IBUF_SIZE - 1; pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full // The following is similar to calling i2QueueNeeds, except that this // is done in longhand, since we are setting up initial conditions on // many channels at once. pCh->channelNeeds = NEED_FLOW; // Since starting from scratch pCh->sinceLastFlow = 0; // No bytes received since last flow // control packet was queued stuffIndex++; *ppCh++ = pCh; // List this channel as needing // initial flow control packet sent } // Don't allow anything to be sent until the status packets come in from // the board. pCh->outfl.asof = 0; pCh->outfl.room = 0; // Initialize all the ring buffers pCh->Ibuf_stuff = pCh->Ibuf_strip = 0; pCh->Obuf_stuff = pCh->Obuf_strip = 0; pCh->Cbuf_stuff = pCh->Cbuf_strip = 0; memset( &pCh->icount, 0, sizeof (struct async_icount) ); pCh->hotKeyIn = HOT_CLEAR; pCh->channelOptions = 0; pCh->bookMarks = 0; init_waitqueue_head(&pCh->pBookmarkWait); init_waitqueue_head(&pCh->open_wait); init_waitqueue_head(&pCh->close_wait); init_waitqueue_head(&pCh->delta_msr_wait); // Set base and divisor so default custom rate is 9600 pCh->BaudBase = 921600; // MAX for ST654, changed after we get pCh->BaudDivisor = 96; // the boxids (UART types) later pCh->dataSetIn = 0; pCh->dataSetOut = 0; pCh->wopen = 0; pCh->throttled = 0; pCh->speed = CBR_9600; pCh->flags = 0; pCh->ClosingDelay = 5*HZ/10; pCh->ClosingWaitTime = 30*HZ; // Initialize task queue objects INIT_WORK(&pCh->tqueue_input, do_input); INIT_WORK(&pCh->tqueue_status, do_status); #ifdef IP2DEBUG_TRACE pCh->trace = ip2trace;#endif ++pCh; --nChannels; } // No need to check for wrap here; this is initialization. pB->i2Fbuf_stuff = stuffIndex; COMPLETE(pB, I2EE_GOOD); } //******************************************************************************// Function: i2DeQueueNeeds(pB, type)// Parameters: Pointer to a board structure// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW// Returns: // Pointer to a channel structure//// Description: Returns pointer struct of next channel that needs service of// the type specified. Otherwise returns a NULL reference.////******************************************************************************static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr pB, int type){ unsigned short queueIndex; unsigned long flags; i2ChanStrPtr pCh = NULL; switch(type) { case NEED_INLINE: WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip) { queueIndex = pB->i2Dbuf_strip; pCh = pB->i2Dbuf[queueIndex]; queueIndex++; if (queueIndex >= CH_QUEUE_SIZE) { queueIndex = 0; } pB->i2Dbuf_strip = queueIndex; pCh->channelNeeds &= ~NEED_INLINE; } WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); break; case NEED_BYPASS: WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip) { queueIndex = pB->i2Bbuf_strip; pCh = pB->i2Bbuf[queueIndex]; queueIndex++; if (queueIndex >= CH_QUEUE_SIZE) { queueIndex = 0; } pB->i2Bbuf_strip = queueIndex; pCh->channelNeeds &= ~NEED_BYPASS; } WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); break; case NEED_FLOW: WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip) { queueIndex = pB->i2Fbuf_strip; pCh = pB->i2Fbuf[queueIndex]; queueIndex++; if (queueIndex >= CH_QUEUE_SIZE) { queueIndex = 0; } pB->i2Fbuf_strip = queueIndex; pCh->channelNeeds &= ~NEED_FLOW; } WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); break; default: printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type); break; } return pCh;} //******************************************************************************// Function: i2QueueNeeds(pB, pCh, type)// Parameters: Pointer to a board structure// Pointer to a channel structure// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW// Returns: Nothing//// Description:// For each type of need selected, if the given channel is not already in the// queue, adds it, and sets the flag indicating it is in the queue.//******************************************************************************static voidi2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type){ unsigned short queueIndex; unsigned long flags; // We turn off all the interrupts during this brief process, since the // interrupt-level code might want to put things on the queue as well. switch (type) { case NEED_INLINE: WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); if ( !(pCh->channelNeeds & NEED_INLINE) ) { pCh->channelNeeds |= NEED_INLINE; queueIndex = pB->i2Dbuf_stuff; pB->i2Dbuf[queueIndex++] = pCh; if (queueIndex >= CH_QUEUE_SIZE) queueIndex = 0; pB->i2Dbuf_stuff = queueIndex; } WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); break; case NEED_BYPASS: WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS)) { pCh->channelNeeds |= NEED_BYPASS; queueIndex = pB->i2Bbuf_stuff; pB->i2Bbuf[queueIndex++] = pCh; if (queueIndex >= CH_QUEUE_SIZE) queueIndex = 0; pB->i2Bbuf_stuff = queueIndex; } WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); break; case NEED_FLOW: WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW)) { pCh->channelNeeds |= NEED_FLOW; queueIndex = pB->i2Fbuf_stuff; pB->i2Fbuf[queueIndex++] = pCh; if (queueIndex >= CH_QUEUE_SIZE) queueIndex = 0; pB->i2Fbuf_stuff = queueIndex; } WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); break; case NEED_CREDIT: pCh->channelNeeds |= NEED_CREDIT;Lines 1–500 of 2207. Load more
/******************************************************************************* * * (c) 1999 by Computone Corporation * ******************************************************************************** * * * PACKAGE: Linux tty Device Driver for IntelliPort family of multiport * serial I/O controllers. * * DESCRIPTION: High-level interface code for the device driver. Uses the * Extremely Low Level Interface Support (i2ellis.c). Provides an * interface to the standard loadware, to support drivers or * application code. (This is included source code, not a separate * compilation module.) * *******************************************************************************/ //------------------------------------------------------------------------------ // Note on Strategy: // Once the board has been initialized, it will interrupt us when: // 1) It has something in the fifo for us to read (incoming data, flow control // packets, or whatever). // 2) It has stripped whatever we have sent last time in the FIFO (and // consequently is ready for more). // // Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This // worsens performance considerably, but is done so that a great many channels // might use only a little memory. //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Revision History: // // 0.00 - 4/16/91 --- First Draft // 0.01 - 4/29/91 --- 1st beta release // 0.02 - 6/14/91 --- Changes to allow small model compilation // 0.03 - 6/17/91 MAG Break reporting protected from interrupts routines with // in-line asm added for moving data to/from ring buffers, // replacing a variety of methods used previously. // 0.04 - 6/21/91 MAG Initial flow-control packets not queued until // i2_enable_interrupts time. Former versions would enqueue // them at i2_init_channel time, before we knew how many // channels were supposed to exist! // 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now; // supports new 16-bit protocol and expandable boards. // - 10/24/91 MAG Most changes in place and stable. // 0.06 - 2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no // argument. // 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt // level (mostly responses to specific commands.) // 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet // 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE // turning on the interrupt. // 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check // some incoming. // // 1.1 - 12/25/96 AKM Linux version. // - 10/09/98 DMC Revised Linux version. //------------------------------------------------------------------------------ //************ //* Includes * //************ #include <linux/sched.h> #include "i2lib.h" //*********************** //* Function Prototypes * //*********************** static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int); static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int ); static void i2StripFifo(i2eBordStrPtr); static void i2StuffFifoBypass(i2eBordStrPtr); static void i2StuffFifoFlow(i2eBordStrPtr); static void i2StuffFifoInline(i2eBordStrPtr); static int i2RetryFlushOutput(i2ChanStrPtr); // Not a documented part of the library routines (careful...) but the Diagnostic // i2diag.c finds them useful to help the throughput in certain limited // single-threaded operations. static void iiSendPendingMail(i2eBordStrPtr); static void serviceOutgoingFifo(i2eBordStrPtr); // Functions defined in ip2.c as part of interrupt handling static void do_input(struct work_struct *); static void do_status(struct work_struct *); //*************** //* Debug Data * //*************** #ifdef DEBUG_FIFO unsigned char DBGBuf[0x4000]; unsigned short I = 0; static void WriteDBGBuf(char *s, unsigned char *src, unsigned short n ) { char *p = src; // XXX: We need a spin lock here if we ever use this again while (*s) { // copy label DBGBuf[I] = *s++; I = I++ & 0x3fff; } while (n--) { // copy data DBGBuf[I] = *p++; I = I++ & 0x3fff; } } static void fatality(i2eBordStrPtr pB ) { int i; for (i=0;i<sizeof(DBGBuf);i++) { if ((i%16) == 0) printk("\n%4x:",i); printk("%02x ",DBGBuf[i]); } printk("\n"); for (i=0;i<sizeof(DBGBuf);i++) { if ((i%16) == 0) printk("\n%4x:",i); if (DBGBuf[i] >= ' ' && DBGBuf[i] <= '~') { printk(" %c ",DBGBuf[i]); } else { printk(" . "); } } printk("\n"); printk("Last index %x\n",I); } #endif /* DEBUG_FIFO */ //******** //* Code * //******** static inline int i2Validate ( i2ChanStrPtr pCh ) { //ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity, // (CHANNEL_MAGIC | CHANNEL_SUPPORT)); return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT)) == (CHANNEL_MAGIC | CHANNEL_SUPPORT)); } static void iiSendPendingMail_t(unsigned long data) { i2eBordStrPtr pB = (i2eBordStrPtr)data; iiSendPendingMail(pB); } //****************************************************************************** // Function: iiSendPendingMail(pB) // Parameters: Pointer to a board structure // Returns: Nothing // // Description: // If any outgoing mail bits are set and there is outgoing mailbox is empty, // send the mail and clear the bits. //****************************************************************************** static void iiSendPendingMail(i2eBordStrPtr pB) { if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) ) { if (iiTrySendMail(pB, pB->i2eOutMailWaiting)) { /* If we were already waiting for fifo to empty, * or just sent MB_OUT_STUFFED, then we are * still waiting for it to empty, until we should * receive an MB_IN_STRIPPED from the board. */ pB->i2eWaitingForEmptyFifo |= (pB->i2eOutMailWaiting & MB_OUT_STUFFED); pB->i2eOutMailWaiting = 0; pB->SendPendingRetry = 0; } else { /* The only time we hit this area is when "iiTrySendMail" has failed. That only occurs when the outbound mailbox is still busy with the last message. We take a short breather to let the board catch up with itself and then try again. 16 Retries is the limit - then we got a borked board. /\/\|=mhw=|\/\/ */ if( ++pB->SendPendingRetry < 16 ) { setup_timer(&pB->SendPendingTimer, iiSendPendingMail_t, (unsigned long)pB); mod_timer(&pB->SendPendingTimer, jiffies + 1); } else { printk( KERN_ERR "IP2: iiSendPendingMail unable to queue outbound mail\n" ); } } } } //****************************************************************************** // Function: i2InitChannels(pB, nChannels, pCh) // Parameters: Pointer to Ellis Board structure // Number of channels to initialize // Pointer to first element in an array of channel structures // Returns: Success or failure // // Description: // // This function patches pointers, back-pointers, and initializes all the // elements in the channel structure array. // // This should be run after the board structure is initialized, through having // loaded the standard loadware (otherwise it complains). // // In any case, it must be done before any serious work begins initializing the // irq's or sending commands... // //****************************************************************************** static int i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh) { int index, stuffIndex; i2ChanStrPtr *ppCh; if (pB->i2eValid != I2E_MAGIC) { COMPLETE(pB, I2EE_BADMAGIC); } if (pB->i2eState != II_STATE_STDLOADED) { COMPLETE(pB, I2EE_BADSTATE); } LOCK_INIT(&pB->read_fifo_spinlock); LOCK_INIT(&pB->write_fifo_spinlock); LOCK_INIT(&pB->Dbuf_spinlock); LOCK_INIT(&pB->Bbuf_spinlock); LOCK_INIT(&pB->Fbuf_spinlock); // NO LOCK needed yet - this is init pB->i2eChannelPtr = pCh; pB->i2eChannelCnt = nChannels; pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0; pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0; pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0; pB->SendPendingRetry = 0; memset ( pCh, 0, sizeof (i2ChanStr) * nChannels ); for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf); nChannels && index < ABS_MOST_PORTS; index++) { if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) { continue; } LOCK_INIT(&pCh->Ibuf_spinlock); LOCK_INIT(&pCh->Obuf_spinlock); LOCK_INIT(&pCh->Cbuf_spinlock); LOCK_INIT(&pCh->Pbuf_spinlock); // NO LOCK needed yet - this is init // Set up validity flag according to support level if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) { pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT; } else { pCh->validity = CHANNEL_MAGIC; } pCh->pMyBord = pB; /* Back-pointer */ // Prepare an outgoing flow-control packet to send as soon as the chance // occurs. if ( pCh->validity & CHANNEL_SUPPORT ) { pCh->infl.hd.i2sChannel = index; pCh->infl.hd.i2sCount = 5; pCh->infl.hd.i2sType = PTYPE_BYPASS; pCh->infl.fcmd = 37; pCh->infl.asof = 0; pCh->infl.room = IBUF_SIZE - 1; pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full // The following is similar to calling i2QueueNeeds, except that this // is done in longhand, since we are setting up initial conditions on // many channels at once. pCh->channelNeeds = NEED_FLOW; // Since starting from scratch pCh->sinceLastFlow = 0; // No bytes received since last flow // control packet was queued stuffIndex++; *ppCh++ = pCh; // List this channel as needing // initial flow control packet sent } // Don't allow anything to be sent until the status packets come in from // the board. pCh->outfl.asof = 0; pCh->outfl.room = 0; // Initialize all the ring buffers pCh->Ibuf_stuff = pCh->Ibuf_strip = 0; pCh->Obuf_stuff = pCh->Obuf_strip = 0; pCh->Cbuf_stuff = pCh->Cbuf_strip = 0; memset( &pCh->icount, 0, sizeof (struct async_icount) ); pCh->hotKeyIn = HOT_CLEAR; pCh->channelOptions = 0; pCh->bookMarks = 0; init_waitqueue_head(&pCh->pBookmarkWait); init_waitqueue_head(&pCh->open_wait); init_waitqueue_head(&pCh->close_wait); init_waitqueue_head(&pCh->delta_msr_wait); // Set base and divisor so default custom rate is 9600 pCh->BaudBase = 921600; // MAX for ST654, changed after we get pCh->BaudDivisor = 96; // the boxids (UART types) later pCh->dataSetIn = 0; pCh->dataSetOut = 0; pCh->wopen = 0; pCh->throttled = 0; pCh->speed = CBR_9600; pCh->flags = 0; pCh->ClosingDelay = 5*HZ/10; pCh->ClosingWaitTime = 30*HZ; // Initialize task queue objects INIT_WORK(&pCh->tqueue_input, do_input); INIT_WORK(&pCh->tqueue_status, do_status); #ifdef IP2DEBUG_TRACE pCh->trace = ip2trace; #endif ++pCh; --nChannels; } // No need to check for wrap here; this is initialization. pB->i2Fbuf_stuff = stuffIndex; COMPLETE(pB, I2EE_GOOD); } //****************************************************************************** // Function: i2DeQueueNeeds(pB, type) // Parameters: Pointer to a board structure // type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW // Returns: // Pointer to a channel structure // // Description: Returns pointer struct of next channel that needs service of // the type specified. Otherwise returns a NULL reference. // //****************************************************************************** static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr pB, int type) { unsigned short queueIndex; unsigned long flags; i2ChanStrPtr pCh = NULL; switch(type) { case NEED_INLINE: WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip) { queueIndex = pB->i2Dbuf_strip; pCh = pB->i2Dbuf[queueIndex]; queueIndex++; if (queueIndex >= CH_QUEUE_SIZE) { queueIndex = 0; } pB->i2Dbuf_strip = queueIndex; pCh->channelNeeds &= ~NEED_INLINE; } WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); break; case NEED_BYPASS: WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip) { queueIndex = pB->i2Bbuf_strip; pCh = pB->i2Bbuf[queueIndex]; queueIndex++; if (queueIndex >= CH_QUEUE_SIZE) { queueIndex = 0; } pB->i2Bbuf_strip = queueIndex; pCh->channelNeeds &= ~NEED_BYPASS; } WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); break; case NEED_FLOW: WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip) { queueIndex = pB->i2Fbuf_strip; pCh = pB->i2Fbuf[queueIndex]; queueIndex++; if (queueIndex >= CH_QUEUE_SIZE) { queueIndex = 0; } pB->i2Fbuf_strip = queueIndex; pCh->channelNeeds &= ~NEED_FLOW; } WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); break; default: printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type); break; } return pCh; } //****************************************************************************** // Function: i2QueueNeeds(pB, pCh, type) // Parameters: Pointer to a board structure // Pointer to a channel structure // type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW // Returns: Nothing // // Description: // For each type of need selected, if the given channel is not already in the // queue, adds it, and sets the flag indicating it is in the queue. //****************************************************************************** static void i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) { unsigned short queueIndex; unsigned long flags; // We turn off all the interrupts during this brief process, since the // interrupt-level code might want to put things on the queue as well. switch (type) { case NEED_INLINE: WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); if ( !(pCh->channelNeeds & NEED_INLINE) ) { pCh->channelNeeds |= NEED_INLINE; queueIndex = pB->i2Dbuf_stuff; pB->i2Dbuf[queueIndex++] = pCh; if (queueIndex >= CH_QUEUE_SIZE) queueIndex = 0; pB->i2Dbuf_stuff = queueIndex; } WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); break; case NEED_BYPASS: WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS)) { pCh->channelNeeds |= NEED_BYPASS; queueIndex = pB->i2Bbuf_stuff; pB->i2Bbuf[queueIndex++] = pCh; if (queueIndex >= CH_QUEUE_SIZE) queueIndex = 0; pB->i2Bbuf_stuff = queueIndex; } WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); break; case NEED_FLOW: WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW)) { pCh->channelNeeds |= NEED_FLOW; queueIndex = pB->i2Fbuf_stuff; pB->i2Fbuf[queueIndex++] = pCh; if (queueIndex >= CH_QUEUE_SIZE) queueIndex = 0; pB->i2Fbuf_stuff = queueIndex; } WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); break; case NEED_CREDIT: pCh->channelNeeds |= NEED_CREDIT;