📄 i2lib.c
字号:
/********************************************************************************* (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(void *);static void do_status(void *);//***************//* Debug Data *//***************#ifdef DEBUG_FIFOunsigned 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));}//******************************************************************************// 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 inline 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 ) { init_timer( &(pB->SendPendingTimer) ); pB->SendPendingTimer.expires = jiffies + 1; pB->SendPendingTimer.function = (void*)(unsigned long)iiSendPendingMail; pB->SendPendingTimer.data = (unsigned long)pB; add_timer( &(pB->SendPendingTimer) ); } 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, pCh); INIT_WORK(&pCh->tqueue_status, do_status, pCh);#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.//******************************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -