if_eex.c
上传用户:luoyougen
上传日期:2008-05-12
资源大小:23136k
文件大小:62k
源码类别:

VxWorks

开发平台:

C/C++

  1. /* if_eex.c - Intel EtherExpress 16 network interface driver */
  2. /* Copyright 1989-1997 Wind River Systems, Inc. */
  3. #include "copyright_wrs.h"
  4. /*
  5. modification history
  6. --------------------
  7. 01k,15jul97,spm  added ARP request to SIOCSIFADDR ioctl handler
  8. 01j,07apr97,spm  corrected statistics counter for incoming packets
  9. 01i,17mar97,spm  upgraded to BSD 4.4 and corrected statistics counters.
  10. 01h,14jun95,hdn  removed function declarations defined in sysLib.h.
  11. 01g,01feb95,jdi  doc cleanup.
  12. 01f,17jan95,jag  only send connector select command if NOT auto-detect
  13. 01e,12nov94,tmk  removed some IMPORTed function decs and included unixLib.h
  14. 01d,20feb94,bcs  add attachment type selection, stub for auto-detect
  15. 01c,13dec93,bcs  rearranged initialization, fixed comments
  16. 01b,04dec93,bcs  made functional, added AL_LOC compile-time option
  17. 01a,28nov93,bcs  derived from if_ei.c (rev. 03c,15jan93)
  18. */
  19. /*
  20. DESCRIPTION
  21. This module implements the Intel EtherExpress 16 PC network interface card
  22. driver.  It is specific to that board as used in PC 386/486 hosts.
  23. This driver is written using the device's I/O registers exclusively.
  24. SIMPLIFYING ASSUMPTIONS
  25. This module assumes a little-endian host (80x86); thus, no endian
  26. adjustments are needed to manipulate the 82586 data structures (little-endian).
  27. The on-board memory is assumed to be sufficient; thus, no provision is made
  28. for additional buffering in system memory.
  29. The "frame descriptor" and "buffer descriptor" structures can be bound
  30. into permanent pairs by pointing each FD at a "chain" of one BD of MTU size.
  31. The 82586 receive algorithm fills exactly one BD for each FD; it looks to
  32. the NEXT FD in line for the next BD.
  33. The transmit and receive descriptor lists are permanently linked into
  34. circular queues partitioned into sublists designated by the EEX_LIST
  35. headers in the driver control structure.  Empty partitions have NULL pointer
  36. fields.  EL bits are set as needed to tell the 82586 where a partition ends.
  37. The lists are managed in strict FIFO fashion; thus the link fields are never
  38. modified, just ignored if a descriptor is at the end of a list partition.
  39. BOARD LAYOUT
  40. This device is soft-configured.  No jumpering diagram is required.
  41. EXTERNAL INTERFACE
  42. This driver provides the standard external interface with the following
  43. exceptions.  All initialization is performed within the attach routine and
  44. there is no separate initialization routine.  Therefore, in the global interface
  45. structure, the function pointer to the init() routine is NULL.
  46. There is one user-callable routine, eexattach().  For details on usage,
  47. see the manual entry for this routine.
  48. EXTERNAL SUPPORT REQUIREMENTS
  49. None.
  50. SYSTEM RESOURCE USAGE
  51.     - one mutual exclusion semaphore
  52.     - one interrupt vector
  53.     - one watchdog timer.
  54.     - 8 bytes in the initialized data section (data)
  55.     - 912 bytes in the uninitialized data section (bss)
  56. The data and bss sections are quoted for the MC68020 architecture and may vary
  57. for other architectures.  The code size (text) will vary widely between
  58. architectures, and is thus not quoted here.
  59. The device contains on-board buffer memory; no system memory is required
  60. for buffering.
  61. TUNING HINTS
  62. The only adjustable parameter is the number of TFDs to create in adapter buffer
  63. memory.  The total number of TFDs and RFDs is 21, given full-frame buffering
  64. and the sizes of the auxiliary structures.  eexattach() requires at least
  65. MIN_NUM_RFDS RFDs to exist.  More than ten TFDs is not sensible
  66. in typical circumstances.
  67. SEE ALSO: ifLib
  68. */
  69. #include "vxWorks.h"
  70. #include "wdLib.h"
  71. #include "iv.h"
  72. #include "vme.h"
  73. #include "net/mbuf.h"
  74. #include "net/protosw.h"
  75. #include "socket.h"
  76. #include "ioctl.h"
  77. #include "errno.h"
  78. #include "memLib.h"
  79. #include "intLib.h"
  80. #include "net/if.h"
  81. #include "net/route.h"
  82. #include "iosLib.h"
  83. #include "errnoLib.h"
  84. #include "cacheLib.h"
  85. #include "logLib.h"
  86. #include "netLib.h"
  87. #include "stdio.h"
  88. #include "stdlib.h"
  89. #include "sysLib.h"
  90. #ifdef  INET
  91. #include "netinet/in.h"
  92. #include "netinet/in_systm.h"
  93. #include "netinet/in_var.h"
  94. #include "netinet/ip.h"
  95. #include "netinet/if_ether.h"
  96. #endif  /* INET */
  97. #include "etherLib.h"
  98. #include "net/systm.h"
  99. #include "net/unixLib.h"
  100. #include "sys/times.h"
  101. #include "drv/netif/if_eex.h"
  102. #include "net/if_subr.h"
  103. /***** LOCAL DEFINITIONS *****/
  104. #undef EEX_DEBUG                             /* Compiles debug output */
  105. #undef EEX_DEBUG1  /* dhe 10/24/94 */
  106. #define MAX_UNITS       4                   /* maximum units to support */
  107. #define DEF_NUM_TFDS    8                   /* default number of TFDs */
  108. #define MIN_NUM_RFDS    4                   /* minimum number of RFDs */
  109. #define MAX_IRQ         15              /* Highest PC IRQ number */
  110. #define EEX_MEMSIZE     0x8000          /* Size of EtherExpress memory */
  111.                                         /* Production units are 32K */
  112. #define SCP_OFFSET      0xfff6          /* At top of 64K address space */
  113.                                         /* (aliased in a 32K board) */
  114. #define ISCP_OFFSET     0x0000          /* EtherExpress convention */
  115. #define SCB_OFFSET      0x0008          /* EtherExpress convention */
  116.                                         /* Supported by hardware feature */
  117. #define CFD_OFFSET      ( SCB_OFFSET + sizeof (SCB) )
  118. #define TFD_OFFSET      ( CFD_OFFSET + sizeof (CFD) )
  119. /* These should be in a common file but which one? */
  120. /* Ethernet attachment type selections */
  121. #define ATTACHMENT_DEFAULT      0       /* use card as configured */
  122. #define ATTACHMENT_AUI          1       /* AUI  (thick, DIX, DB-15) */
  123. #define ATTACHMENT_BNC          2       /* BNC  (thin, 10BASE-2) */
  124. #define ATTACHMENT_RJ45         3       /* RJ-45 (twisted pair, TPE, 10BASE-T)*/
  125. /* Typedefs for external structures that are not typedef'd in their .h files */
  126. typedef struct mbuf MBUF;
  127. typedef struct arpcom IDR;                  /* Interface Data Record wrapper */
  128. typedef struct ifnet IFNET;                 /* real Interface Data Record */
  129. typedef struct sockaddr SOCK;
  130. /* The definition of our linked list management structure */
  131. typedef struct eex_list                     /* EEX_LIST - 82586 queue head */
  132.     {
  133.     volatile EEX_SHORTLINK  head;       /* header of list */
  134.     volatile EEX_SHORTLINK  tail;       /* tail of list */
  135.     } EEX_LIST;
  136. /* Macro to follow TFRAME and RFRAME (TFD/RFD) lists */
  137. #define SUCC_FD(fd)     ( 
  138.                 sysOutWord (pDrvCtrl->port + RDPTR, (fd) + RF_LINK), 
  139.                 sysInWord (pDrvCtrl->port + DXREG) )
  140. /* The definition of the driver control structure */
  141. typedef struct drv_ctrl
  142.     {
  143.     IDR                 idr;        /* interface data record */
  144.     BOOL                attached;   /* indicates attach() called */
  145.     CFD                 *pCfd;      /* Scratch CFD to copy to board mem.*/
  146.     EEX_SHORTLINK       rfdBase;    /* RFD pool starts here */
  147.     EEX_SHORTLINK       lastFd;     /* last RFD dhe 10/26/94 */
  148.     volatile EEX_LIST   rxQueue;    /* receive queue */
  149.     volatile EEX_SHORTLINK freeRfd; /* first free receive frame */
  150.     volatile EEX_LIST   tfdQueue;   /* free transmit frame descriptors */
  151.     volatile EEX_LIST   txQueue;    /* to be sent queue */
  152.     volatile EEX_LIST   cblQueue;   /* actual chip transmit queue */
  153.     volatile EEX_LIST   cleanQueue; /* queue of TFDs to cleanup */
  154.     volatile BOOL       rcvHandling;/* flag, indicates netTask active */
  155.     volatile BOOL       txCleaning; /* flag, indicates netTask active */
  156.     volatile BOOL       txIdle;     /* flag, indicates idle transmitter */
  157.     int                 nTFDs;      /* how many TFDs to create */
  158.     int                 nRFDs;      /* how many RFDs to create */
  159.     int                 port;       /* base I/O port */
  160.     int                 ivec;       /* interrupt vector */
  161.     int                 intLevel;   /* interrupt number */
  162.     int                 irqCode;    /* encoded value written to IRQ port */
  163.     int                 attachment; /* connector type: BNC, AUI, or TPE */
  164.     UINT16              setup;      /* base, boot ROM, I/O, AUI/BNC, IRQ */
  165.     UINT16              tpeBit;     /* bit 0 = 1 && setup AUI bit = 0 for TPE */
  166.     MEM_SETUP           memSetup;   /* everything to set up memory mapping */
  167.     WDOG_ID             wid;        /* watchdog timer for transmit */
  168.     long                transLocks; /* count for transmit lockup failures */
  169.     } DRV_CTRL;
  170. #define DRV_CTRL_SIZ  sizeof(DRV_CTRL)
  171. /***** GLOBALS *****/
  172. /***** LOCALS *****/
  173. /* The array of driver control structs */
  174. LOCAL DRV_CTRL drvCtrl [MAX_UNITS];
  175. /* Encoded IRQ selection numbers for EtherExpress 16 IRQ Control port
  176.  * Entries correspond to PC IRQ numbers 0-15.  A zero entry means the
  177.  * corresponding IRQ cannot be used by the board; a non-zero entry is
  178.  * the value needed in the board's interrupt control register to program
  179.  * the board for the corresponding IRQ.
  180.  */
  181. LOCAL int irqTable [MAX_IRQ + 1] =
  182.     {
  183.     0, 0, 1, 2, 3, 4, 0, 0, 0, 1, 5, 6, 0, 0, 0, 0
  184.     };
  185. /* following are used only in netTask() context so can be common */
  186. LOCAL CFD       Cfd;                    /* expedient for code porting */
  187.                                         /* build commands here then copy */
  188.                                         /* to board memory */
  189. LOCAL char      buffer [FRAME_SIZE];    /* Copy frames through here */
  190. /* forward function declarations */
  191. static void eexReset (int unit);
  192. static int eexIoctl (IDR *pIDR, int cmd, caddr_t data);
  193. #ifdef BSD43_DRIVER
  194. static int eexOutput (IDR *pIDR, MBUF *pMbuf, SOCK *pDestAddr);
  195. static void eexTxStartup (int unit);
  196. #else
  197. static void eexTxStartup (DRV_CTRL *pDrvCtrl);
  198. #endif
  199. static void eexInt (DRV_CTRL *pDrvCtrl);
  200. static void eexTxCleanQ (DRV_CTRL *pDrvCtrl);
  201. static void eexHandleRecvInt (DRV_CTRL *pDrvCtrl);
  202. static STATUS eexReceive (DRV_CTRL *pDrvCtrl, EEX_SHORTLINK rfdOffset);
  203. static void eexDiag (int unit);
  204. static void eexConfig (int unit);
  205. static void eexIASetup (int unit);
  206. static void eexRxStartup (DRV_CTRL *pDrvCtrl);
  207. static void eexAction (int unit, UINT16 action);
  208. static STATUS eexCommand (DRV_CTRL *pDrvCtrl, UINT16 cmd);
  209. static void eexTxQPut (DRV_CTRL *pDrvCtrl, EEX_SHORTLINK tfdOffset);
  210. static void eexTxQFlush (DRV_CTRL *pDrvCtrl);
  211. static void eexRxQPut (DRV_CTRL *pDrvCtrl, EEX_SHORTLINK rfdOffset);
  212. static EEX_SHORTLINK eexRxQGet (DRV_CTRL *pDrvCtrl);
  213. static BOOL eexRxQFull (DRV_CTRL *pDrvCtrl);
  214. static void eexQInit (EEX_LIST *pHead);
  215. static EEX_SHORTLINK eexQGet (DRV_CTRL *pDrvCtrl, EEX_LIST *pQueue);
  216. static void eexQPut (DRV_CTRL *pDrvCtrl, EEX_LIST *pQueue, EEX_SHORTLINK pNode);
  217. static STATUS eexDeviceStart (int unit);
  218. static void eexWatchDog(int unit);
  219. static STATUS eexEnetAddrGet (DRV_CTRL *pDrvCtrl, char addr[]);
  220. static STATUS eex586Init (int unit);
  221. static void   eex586IntAck (int unit);
  222. static void   eex586IntEnable (int unit);
  223. static void   eex586IntDisable (int unit);
  224. static void   eex586SetReset (int unit);
  225. static void   eex586ClearReset (int unit);
  226. static void   eex586ChanAtn (DRV_CTRL *pDrvCtrl);
  227. static UINT16 eexReadEEPROM (DRV_CTRL *pDRvCtrl, UINT16 address);
  228. static void eexOutBitsEEPROM (int port, int count, UINT16 bits);
  229. static UINT16 eexInBitsEEPROM (int port, int count);
  230. static void   eexDelayEEPROM (void);
  231. static void DUMMY ()
  232. {}
  233. /*******************************************************************************
  234. *
  235. * eexattach - publish the `eex' network interface and initialize the driver and device
  236. *
  237. * The routine publishes the `eex' interface by filling in a network interface
  238. * record and adding this record to the system list.  This routine also
  239. * initializes the driver and the device to the operational state.
  240. *
  241. * RETURNS: OK or ERROR.
  242. *
  243. * SEE ALSO: ifLib
  244. */
  245. STATUS eexattach
  246.     (
  247.     int         unit,                   /* unit number */
  248.     int         port,                   /* base I/O address */
  249.     int         ivec,                   /* interrupt vector number */
  250.     int         ilevel,                 /* interrupt level */
  251.     int         nTfds,                  /* # of transmit frames (0=default) */
  252.     int         attachment              /* 0=default, 1=AUI, 2=BNC, 3=TPE */
  253.     )
  254.     {
  255.     DRV_CTRL    *pDrvCtrl;
  256.     long        size;                   /* temporary size holder */
  257.     /* Sanity check the unit number */
  258.     if (unit < 0 || unit >= MAX_UNITS)
  259.         return (ERROR);
  260.     /* Ensure single invocation per system life */
  261.     pDrvCtrl = & drvCtrl [unit];
  262. #ifdef EEX_DEBUG /* dhe 10/20/94 */
  263.     logMsg ("eex: eeattach: pDrvCtrl=%xn", pDrvCtrl, 0, 0, 0, 0, 0);
  264. #endif
  265.     if (pDrvCtrl->attached)
  266.         return (OK);
  267.     /* Check interrupt number for sanity and usability */
  268.     if (ilevel < 0 || ilevel > MAX_IRQ)
  269.         return (ERROR);
  270.     pDrvCtrl->intLevel = ilevel;
  271.     pDrvCtrl->irqCode = irqTable [ilevel];
  272.     if (pDrvCtrl->irqCode == 0)
  273.         return (ERROR);
  274.     /* Determine number of Tx and Rx descriptors to use */
  275.     pDrvCtrl->nTFDs = (nTfds != 0) ? nTfds : DEF_NUM_TFDS;
  276.     /* Determine which connector is desired */
  277.     switch (attachment)
  278.         {
  279.         case ATTACHMENT_DEFAULT:
  280.         case ATTACHMENT_AUI:
  281.         case ATTACHMENT_BNC:
  282.         case ATTACHMENT_RJ45:
  283.             pDrvCtrl->attachment = attachment;
  284.             break;
  285.         default:
  286.             return (ERROR);             /* unknown value */
  287.         }
  288.     /* Find and activate the adapter */
  289.     /* Publish the interface record */
  290. #ifdef BSD43_DRIVER
  291.     ether_attach    ( (IFNET *) & pDrvCtrl->idr, unit, "eex", (FUNCPTR) NULL,
  292.                      (FUNCPTR) eexIoctl, (FUNCPTR) eexOutput,
  293.                      (FUNCPTR) eexReset);
  294. #else
  295.     ether_attach    (
  296.                     (IFNET *) & pDrvCtrl->idr,
  297.                     unit,
  298.                     "eex",
  299.                     (FUNCPTR) NULL,
  300.                     (FUNCPTR) eexIoctl,
  301.                     (FUNCPTR) ether_output,
  302.                     (FUNCPTR) eexReset
  303.                     );
  304.     pDrvCtrl->idr.ac_if.if_start = (FUNCPTR)eexTxStartup;
  305. #endif
  306.     /* Calculate the total size of 82586 memory pool.
  307.      * Evaluate as a long so it can force nRFDs negative if way too many
  308.      * TFDs were asked for.
  309.      */
  310.     size =
  311.             sizeof (SCP) +
  312.             sizeof (ISCP) +
  313.             sizeof (SCB) +
  314.             sizeof (CFD) +                      /* synch'ed command frame */
  315.             (sizeof (TFRAME) * pDrvCtrl->nTFDs);   /* pool of transmit frames */
  316.     pDrvCtrl->nRFDs = ( (long)EEX_MEMSIZE - size ) / sizeof (RFRAME);
  317. #ifdef EEX_DEBUG /* dhe 10/20/94 */
  318.     logMsg ("eex: eeattach: nRFDS=%xn", pDrvCtrl->nRFDs, 0, 0, 0, 0, 0);
  319. #endif
  320.     if (pDrvCtrl->nRFDs < MIN_NUM_RFDS)
  321.         return (ERROR);
  322.     /* Save other passed-in parameters */
  323.     pDrvCtrl->port = port;                      /* base I/O address */
  324.     pDrvCtrl->ivec = ivec;                      /* interrupt vector */
  325.     pDrvCtrl->pCfd      = &Cfd;
  326.     /* Init the watchdog that will patrol for device lockups */
  327.     pDrvCtrl->transLocks = 0;
  328.     pDrvCtrl->wid = wdCreate ();                      /* create watchdog */
  329.     if (pDrvCtrl->wid == NULL)                        /* no resource */
  330.         goto error;
  331.     pDrvCtrl->rcvHandling   = FALSE;  /* netTask not running our receive job */
  332.     pDrvCtrl->txCleaning    = FALSE;  /* netTask not running our clean job */
  333.     pDrvCtrl->txIdle        = TRUE;         /* transmitter idle */
  334.     /* Perform device initialization */
  335.     if (eex586Init (unit) != OK)
  336.         goto error;
  337.     sysIntEnablePIC (ilevel);
  338.     /* Set our flag */
  339.     pDrvCtrl->attached = TRUE;
  340.     /* Set status flags in IDR */
  341.     pDrvCtrl->idr.ac_if.if_flags |= (IFF_UP | IFF_RUNNING | IFF_NOTRAILERS);
  342.     /* Successful return */
  343.     return (OK);
  344.     /***** Handle error cases *****/
  345.     error:
  346.         {
  347.         /* Release watchdog */
  348.         if (pDrvCtrl->wid != NULL)
  349.             wdDelete (pDrvCtrl->wid);
  350.         return (ERROR);
  351.         }
  352.     }
  353. /*******************************************************************************
  354. *
  355. * eexReset - reset the eex interface
  356. *
  357. * Mark interface as inactive and reset the chip.
  358. */
  359. static void eexReset
  360.     (
  361.     int unit
  362.     )
  363.     {
  364.     DRV_CTRL *pDrvCtrl;
  365.     pDrvCtrl = & drvCtrl [unit];
  366.     pDrvCtrl->idr.ac_if.if_flags = 0;
  367.     eex586IntDisable (unit);                    /* disable ints from the dev */
  368.     eex586SetReset (unit);                      /* put the 586 chip in reset */
  369.     }
  370. /*******************************************************************************
  371. *
  372. * eexIoctl - interface ioctl procedure
  373. *
  374. * Process an interface ioctl request.
  375. */
  376. static int eexIoctl
  377.     (
  378.     IDR     *pIDR,
  379.     int     cmd,
  380.     caddr_t data
  381.     )
  382.     {
  383.     int error;
  384. #ifdef EEX_DEBUG
  385.     printf ("eex: ioctl: pIDR=%x cmd=%x data=%xn", pIDR, cmd, data);
  386. #endif /* EEX_DEBUG */
  387.     error = 0;
  388.     switch (cmd)
  389.         {
  390.         case SIOCSIFADDR:
  391.             ((struct arpcom *)pIDR)->ac_ipaddr = IA_SIN (data)->sin_addr;
  392.             arpwhohas (pIDR, &IA_SIN (data)->sin_addr);
  393.             break;
  394.         case SIOCSIFFLAGS:
  395.             /* Flags are set outside this module. No additional work to do. */
  396.             break;
  397.         default:
  398.             error = EINVAL;
  399.         }
  400.     return (error);
  401.     }
  402. #ifdef BSD43_DRIVER
  403. /*******************************************************************************
  404. *
  405. * eexOutput - interface output routine.
  406. *
  407. * This is the entry point for packets arriving from protocols above.  This
  408. * routine merely calls our specific output routines that eventually lead
  409. * to a transmit to the network.
  410. *
  411. * RETURNS: 0 or appropriate errno
  412. */
  413. static int eexOutput
  414.     (
  415.     IDR *    pIDR,
  416.     MBUF *     pMbuf,
  417.     SOCK * pDestAddr
  418.     )
  419.     {
  420. #ifdef EEX_DEBUG
  421.     printf ("eex: output: pIDR=%x pMbuf=%x pDestAddr=%xn",
  422.             pIDR, pMbuf, pDestAddr);
  423. #endif /* EEX_DEBUG */
  424.     return (ether_output ( (IFNET *)pIDR, pMbuf, pDestAddr,
  425.             (FUNCPTR) eexTxStartup, pIDR));
  426.     }
  427. #endif
  428. /*******************************************************************************
  429. *
  430. * eexTxStartup - start output on the chip
  431. *
  432. * Looks for any action on the queue, and begins output if there is anything
  433. * there. This routine is called from several possible threads. Each will be
  434. * described below.
  435. *
  436. * The first, and most common thread, is when a user task requests the
  437. * transmission of data. Under BSD 4.3, this will cause eexOutput() to be 
  438. * called, which will cause ether_output() to be called, which will cause 
  439. * this routine to be called (usually). This routine will not be called if 
  440. * ether_output() finds that our interface output queue is full. In this case, 
  441. * the outgoing data will be thrown out. BSD 4.4 uses a slightly different
  442. * model in which the generic ether_output() routine is called directly, 
  443. * followed by a call to this routine.
  444. *
  445. * The second, and most obscure thread, is when the reception of certain
  446. * packets causes an immediate (attempted) response. For example, ICMP echo
  447. * packets (ping), and ICMP "no listener on that port" notifications. All
  448. * functions in this driver that handle the reception side are executed in the
  449. * context of netTask(). Always. So, in the case being discussed, netTask() will
  450. * receive these certain packets, cause IP to be stimulated, and cause the
  451. * generation of a response to be sent. We then find ourselves following the
  452. * thread explained in the second example, with the important distinction that
  453. * the context is that of netTask().
  454. *
  455. * The third thread occurs when this routine runs out of TFDs and returns. If
  456. * this occurs when our output queue is not empty, this routine would typically
  457. * not get called again until new output was requested. Even worse, if the
  458. * output queue was also full, this routine would never get called again and
  459. * we would have a lock state. It DOES happen. To guard against this, the
  460. * transmit clean-up handler detects the out-of-TFDs state and calls this
  461. * function. The clean-up handler also runs from netTask.
  462. *
  463. * Note that this function is ALWAYS called between an splnet() and an splx().
  464. * This is true because netTask(), and ether_output() take care of
  465. * this when calling this function. Therefore, no calls to these spl functions
  466. * are needed anywhere in this output thread.
  467. */
  468. #ifdef BSD43_DRIVER
  469. static void eexTxStartup
  470.     (
  471.     int unit
  472.     )
  473.     {
  474.     DRV_CTRL * pDrvCtrl;
  475.     pDrvCtrl = & drvCtrl [unit];
  476. #else
  477. static void eexTxStartup
  478.     (
  479.     DRV_CTRL *  pDrvCtrl
  480.     )
  481.     {
  482. #endif
  483.     MBUF * pMbuf;
  484.     int length;
  485.     EEX_SHORTLINK tfdOffset;
  486.     /*
  487.      * Loop until there are no more packets ready to send or we
  488.      * have insufficient resources left to send another one.
  489.      */
  490.     while (pDrvCtrl->idr.ac_if.if_snd.ifq_head)
  491.         {
  492.         IF_DEQUEUE (&pDrvCtrl->idr.ac_if.if_snd, pMbuf);  /* dequeue a packet */
  493.         copy_from_mbufs (buffer, pMbuf, length);
  494.         length = max (ETHERSMALL, length);
  495.         if ( (etherOutputHookRtn != NULL) &&
  496.             (* etherOutputHookRtn)
  497.             (&pDrvCtrl->idr, buffer, length))
  498.             continue;
  499.         /* get free tfd */
  500.         tfdOffset = eexQGet (pDrvCtrl, (EEX_LIST *)&pDrvCtrl->tfdQueue);
  501.         if (tfdOffset == NULL)
  502.             break;                              /* out of TFD's */
  503.         /* copy header and data to contiguous board memory areas */
  504.         sysOutWord (pDrvCtrl->port + WRPTR, tfdOffset + TF_BUFFER);
  505.         sysOutWordString (pDrvCtrl->port + DXREG, (short *)buffer,
  506.                           (length + 1) / 2);
  507. #ifndef EEX_AL_LOC
  508.         length -= EH_SIZE;              /* compensate for header not in data */
  509. #endif  /* EEX_AL_LOC */
  510.         sysOutWord (pDrvCtrl->port + WRPTR, tfdOffset + TF_ACT_COUNT);
  511.         sysOutWord (pDrvCtrl->port + DXREG,
  512.                     (length & ACT_COUNT_MASK) | TBD_S_EOF);
  513. #ifndef EEX_AL_LOC
  514.         /* Move length/ethertype up to where 82586 expects it following dest. */
  515.         sysOutWord (pDrvCtrl->port + RDPTR, tfdOffset + TF_OLDLENGTH);
  516.         length = sysInWord (pDrvCtrl->port + DXREG);
  517.         /* separate read and write because both pointers increment */
  518.         /* on any access to DXREG */
  519.         sysOutWord (pDrvCtrl->port + WRPTR, tfdOffset + TF_NEWLENGTH);
  520.         sysOutWord (pDrvCtrl->port + DXREG, length);
  521. #endif  /* EEX_AL_LOC */
  522. #ifndef BSD43_DRIVER    /* BSD 4.4 ether_output() doesn't bump statistic. */
  523.         pDrvCtrl->idr.ac_if.if_opackets++;
  524. #endif
  525.         eexTxQPut (pDrvCtrl, tfdOffset);
  526.         }
  527.     }
  528. /*******************************************************************************
  529. *
  530. * eexInt - entry point for handling interrupts from the 82586
  531. *
  532. * The interrupting events are acknowledged to the device, so that the device
  533. * will deassert its interrupt signal. The amount of work done here is kept
  534. * to a minimum; the bulk of the work is defered to the netTask. Several flags
  535. * are used here to synchronize with task level code and eliminate races.
  536. */
  537. static void eexInt
  538.     (
  539.     DRV_CTRL *pDrvCtrl
  540.     )
  541.     {
  542.     UINT16  event;
  543.     int unit;
  544.     unit = pDrvCtrl->idr.ac_if.if_unit;
  545.     sysBusIntAck (pDrvCtrl->intLevel);
  546.     event = sysInWord (pDrvCtrl->port + SCB_STATUS) & (SCB_S_CX | SCB_S_FR | SCB_S_CNA | SCB_S_RNR);
  547. #ifdef EEX_DEBUG
  548.     logMsg ("eex: interrupt: event=%xn", event, 0, 0, 0, 0, 0);
  549. #endif /* EEX_DEBUG */
  550.     eexCommand (pDrvCtrl, event);                        /* ack the events */
  551.     eex586IntAck (unit);                                /* ack 586 interrupt */
  552.     /* Handle transmitter interrupt */
  553.     if (event & SCB_S_CNA)                              /* reclaim tx tfds */
  554.         {
  555.         wdCancel(pDrvCtrl->wid);                /* canus delenda est */
  556.         if (pDrvCtrl->cleanQueue.head == NULL)      /* clean queue empty */
  557.             {
  558.             pDrvCtrl->cleanQueue.head = pDrvCtrl->cblQueue.head; /* new head */
  559.             pDrvCtrl->cleanQueue.tail = pDrvCtrl->cblQueue.tail; /* new tail */
  560.             }
  561.         else                                            /* concatenate queues */
  562.             {
  563.             pDrvCtrl->cleanQueue.tail = pDrvCtrl->cblQueue.tail;
  564.             }
  565.         if (!pDrvCtrl->txCleaning)                          /* not cleaning? */
  566.             {
  567.             pDrvCtrl->txCleaning = TRUE;                    /* set flag */
  568.             netJobAdd ( (FUNCPTR) eexTxCleanQ, (int) pDrvCtrl, 0, 0, 0, 0);
  569.                             /* defer cleanup */
  570.             }
  571.         if (pDrvCtrl->txQueue.head != NULL)             /* anything to flush? */
  572.             eexTxQFlush (pDrvCtrl);                      /* flush the tx q */
  573.         else
  574.             pDrvCtrl->txIdle = TRUE;                    /* transmitter idle */
  575.         }
  576.     /* Handle receiver interrupt */
  577.     if ( (event & SCB_S_FR) && !(pDrvCtrl->rcvHandling))
  578.         {
  579.         pDrvCtrl->rcvHandling = TRUE;
  580.         (void) netJobAdd ( (FUNCPTR) eexHandleRecvInt, (int) pDrvCtrl,0, 0, 0, 0);         /* netTask processes */
  581.         }
  582.          
  583.     /* dhe 10/20/94 could lose some packets here */
  584.     if ( (event & SCB_S_RNR) && !(pDrvCtrl->rcvHandling))
  585.         eexRxStartup(pDrvCtrl);
  586.     } 
  587. /*******************************************************************************
  588. *
  589. * eexTxCleanQ - checks errors in completed TFDs and moves TFDs to free queue
  590. *
  591. * This routine is executed by netTask. It "cleans" the TFDs on the clean-up
  592. * queue by checking each one for errors and then returning the TFD to the
  593. * "free TFD" queue. The startup routine is sometimes called here to eliminate
  594. * the lock-out case where the driver input queue is full but there are no
  595. * TFDs available.
  596. */
  597. static void eexTxCleanQ
  598.     (
  599.     DRV_CTRL *pDrvCtrl
  600.     )
  601.     {
  602.     EEX_SHORTLINK tfdOffset;
  603.     UINT16 status;
  604.     BOOL needTxStart;
  605.     int unit;
  606.     unit = pDrvCtrl->idr.ac_if.if_unit;
  607.     do
  608.         {
  609.         pDrvCtrl->txCleaning = TRUE;
  610.         if (pDrvCtrl->tfdQueue.head == NULL)              /* tfd queue empty */
  611.             needTxStart = TRUE;                           /* set flag */
  612.         else
  613.             needTxStart = FALSE;
  614.         /* process transmitted frames */
  615.         while (1)
  616.             {
  617.             /* Get TFD. No ints allowed while manipulating this queue. */
  618.             eex586IntDisable (unit);
  619.             tfdOffset = eexQGet (pDrvCtrl, (EEX_LIST *)&pDrvCtrl->cleanQueue);
  620.             eex586IntEnable (unit);
  621.             if (tfdOffset == NULL)
  622.                 break;
  623.             sysOutWord (pDrvCtrl->port + RDPTR, tfdOffset);
  624.             status = sysInWord (pDrvCtrl->port + DXREG);
  625.             pDrvCtrl->idr.ac_if.if_collisions +=         /* add any colls */
  626.                 (status & CFD_S_RETRY) ? 16 :  /* excessive colls */
  627.                 (status & CFD_S_COLL_MASK);    /* some colls */
  628.             if (!(status & CFD_S_OK))          /* packet not sent */
  629.                 {
  630.                 pDrvCtrl->idr.ac_if.if_oerrors++;        /* incr err count */
  631.                 pDrvCtrl->idr.ac_if.if_opackets--;       /* decr sent count */
  632.                 }
  633.             /* return to tfdQ */
  634.             eexQPut (pDrvCtrl, (EEX_LIST *)&pDrvCtrl->tfdQueue, tfdOffset);
  635.             }
  636.         if (needTxStart)                                  /* check flag */
  637. #ifdef BSD43_DRIVER
  638.             eexTxStartup (unit);
  639. #else
  640.             eexTxStartup (pDrvCtrl);
  641. #endif
  642.         pDrvCtrl->txCleaning = FALSE;
  643.         }
  644.     while (pDrvCtrl->cleanQueue.head != NULL);            /* check again */
  645.     }
  646. /*******************************************************************************
  647. *
  648. * eexHandleRecvInt - task level interrupt service for input packets
  649. *
  650. * This routine is called at task level indirectly by the interrupt
  651. * service routine to do any message received processing.
  652. */
  653. static void eexHandleRecvInt
  654.     (
  655.     DRV_CTRL *pDrvCtrl
  656.     )
  657.     {
  658.     EEX_SHORTLINK rfdOffset;
  659.     do
  660.         {
  661.         pDrvCtrl->rcvHandling = TRUE;             /* interlock with eexInt() */
  662.         while ( (rfdOffset = eexRxQGet (pDrvCtrl)) != NULL)
  663.             (void) eexReceive (pDrvCtrl, rfdOffset);
  664.         pDrvCtrl->rcvHandling = FALSE;            /* interlock with eexInt() */
  665.         }
  666.     while (eexRxQFull (pDrvCtrl));              /* make sure rx q still empty */
  667.     }
  668. /*******************************************************************************
  669. *
  670. * eexReceive - pass a received frame to the next layer up
  671. *
  672. * Strips the Ethernet header and passes the packet to the appropriate
  673. * protocol.
  674. * Is responsible for proper disposition of frame buffer in all cases.
  675. */
  676. static STATUS eexReceive
  677.     (
  678.     DRV_CTRL *pDrvCtrl,
  679.     EEX_SHORTLINK rfdOffset
  680.     )
  681.     {
  682.     struct ether_header *  pEh;
  683.     u_char *  pData;
  684.     int         len;
  685. #ifdef BSD43_DRIVER
  686.     UINT16      etherType;
  687. #endif
  688.     MBUF *  m = NULL;
  689.     UINT16      status;
  690.     /* Check packet for errors.  This should be completely unnecessary,
  691.      * but since Intel does not seem capable of explaining the exact
  692.      * functioning of the 'save bad frames' config bit, we will look for
  693.      * errors.
  694.      */
  695.     sysOutWord (pDrvCtrl->port + RDPTR, rfdOffset);
  696.     status = sysInWord (pDrvCtrl->port + DXREG);
  697.     if  (
  698.         ( status & ( RFD_S_OK | RFD_S_COMPLETE ) ) !=
  699.         ( RFD_S_OK | RFD_S_COMPLETE )
  700.         )
  701.         {
  702.         ++pDrvCtrl->idr.ac_if.if_ierrors;            /* bump error counter */
  703. #ifdef EEX_DEBUG1 /* dhe 10/20/94 */
  704.     logMsg ("case 1: status=%x,offset=%xn", status, rfdOffset, 0, 0, 0, 0);
  705. #endif
  706.         eexRxQPut (pDrvCtrl, rfdOffset);             /* free the RFD */
  707.         return (ERROR);
  708.         }
  709.     /* get frame length */
  710.     sysOutWord (pDrvCtrl->port + RDPTR, rfdOffset + RF_ACT_COUNT);
  711.     len = sysInWord (pDrvCtrl->port + DXREG) & ACT_COUNT_MASK;
  712. #ifndef EEX_AL_LOC
  713.     len += EH_SIZE;
  714. #endif  /* EEX_AL_LOC */
  715.     sysOutWord (pDrvCtrl->port + RDPTR, rfdOffset + RF_BUFFER);
  716.     sysInWordString (pDrvCtrl->port + DXREG, (short *) buffer, (len + 1) / 2);
  717.     pEh = (struct ether_header *)buffer;    /* get ptr to ethernet header */
  718.     /* Bump input packet counter. */
  719.     ++pDrvCtrl->idr.ac_if.if_ipackets;
  720.     /* Service input hook */
  721.     if ( (etherInputHookRtn != NULL) )
  722.         {
  723.         if  ( (* etherInputHookRtn) (&pDrvCtrl->idr, (char *)pEh, len) )
  724.             {
  725. #ifdef EEX_DEBUG1 /* dhe 10/20/94 */
  726.     logMsg ("case 2: status=%x, offset=%xn", status, rfdOffset, 0, 0, 0, 0);
  727. #endif
  728.             eexRxQPut (pDrvCtrl, rfdOffset);             /* free the RFD */
  729.             return (OK);
  730.             }
  731.         }
  732.     len -= EH_SIZE;
  733.     pData = (u_char *) buffer + EH_SIZE;
  734. #ifdef BSD43_DRIVER
  735.     etherType = ntohs (pEh->ether_type);
  736. #endif
  737.     m = copy_to_mbufs (pData, len, 0, &pDrvCtrl->idr);
  738.     if (m != NULL)
  739. #ifdef BSD43_DRIVER
  740.         do_protocol_with_type (etherType, m, &pDrvCtrl->idr, len);
  741. #else
  742.         do_protocol (pEh, m, &pDrvCtrl->idr, len);
  743. #endif
  744.     else
  745.         ++pDrvCtrl->idr.ac_if.if_ierrors;    /* bump statistic */
  746. #ifdef EEX_DEBUG1 /* dhe 10/20/94 */
  747.     logMsg ("case 3: status=%x, offset=%xn", status, rfdOffset, 0, 0, 0, 0);
  748. #endif
  749.     eexRxQPut (pDrvCtrl, rfdOffset);             /* free the RFD */
  750.     return (OK);
  751.     }
  752. /*******************************************************************************
  753. *
  754. * eexDeviceStart - reset and start the device
  755. *
  756. * This routine assumes interrupts from the device have been disabled, and
  757. * that the driver control structure has been initialized.
  758. */
  759. static STATUS eexDeviceStart
  760.     (
  761.     int unit                              /* physical unit number */
  762.     )
  763.     {
  764.     DRV_CTRL *pDrvCtrl;
  765.     /* Get pointers */
  766.     pDrvCtrl = & drvCtrl [unit];
  767.     /* Ensure the 82586 is in its reset state */
  768.     eex586SetReset (unit);
  769.     /* Initialize the SCP */
  770.     sysOutWord (pDrvCtrl->port + WRPTR, SCP_OFFSET);
  771.     sysOutWord (pDrvCtrl->port + DXREG, 0);     /* sysbus is 16-bit */
  772.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  773.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  774.     sysOutWord (pDrvCtrl->port + DXREG, ISCP_OFFSET);
  775.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  776.     /* Initialize the ISCP */
  777.     sysOutWord (pDrvCtrl->port + WRPTR, ISCP_OFFSET);
  778.     sysOutWord (pDrvCtrl->port + DXREG, 1);     /* Set ISCP busy bit */
  779.     sysOutWord (pDrvCtrl->port + DXREG, SCB_OFFSET);
  780.     sysOutWord (pDrvCtrl->port + DXREG, 0);     /* SCB is zero-based */
  781.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  782.     /* Initialize the SCB */
  783.     /* WRPTR is at the SCB following ISCP initialization */
  784.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  785.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  786.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  787.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  788.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  789.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  790.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  791.     sysOutWord (pDrvCtrl->port + DXREG, 0);
  792.     /* Tell the device to read the SCP */
  793.     eex586ClearReset (unit);
  794.     eex586ChanAtn (pDrvCtrl);
  795.     /*
  796.      * The device will now read our SCP and ISCP. It will clear the busy
  797.      * flag in the ISCP.
  798.      */
  799.     taskDelay (50);
  800.     sysOutWord (pDrvCtrl->port + RDPTR, ISCP_OFFSET);
  801.     if ( sysInByte (pDrvCtrl->port + DXREG) == 1 )
  802.         {
  803.         printf ("neex: device did not initializen");
  804.         return (ERROR);
  805.         }
  806.     return (OK);
  807.     }
  808. /*******************************************************************************
  809. *
  810. * eexDiag - format and issue a diagnostic command
  811. */
  812. static void eexDiag
  813.     (
  814.     int unit
  815.     )
  816.     {
  817.     DRV_CTRL *pDrvCtrl;
  818.     pDrvCtrl = & drvCtrl [unit];
  819.     bzero ( (char *)pDrvCtrl->pCfd, sizeof (CFD));      /* zero command frame */
  820.     eexAction (unit, CFD_C_DIAG);                /* run diagnostics */
  821.     if (!(pDrvCtrl->pCfd->cfdStatus & CFD_S_OK))
  822.         printErr ("eexDiag: i82586 diagnostics failed.n");
  823.     }
  824. /*******************************************************************************
  825. *
  826. * eexConfig - format and issue a config command
  827. */
  828. static void eexConfig
  829.     (
  830.     int unit
  831.     )
  832.     {
  833.     DRV_CTRL *pDrvCtrl;
  834.     pDrvCtrl = & drvCtrl [unit];
  835.     bzero ( (char *)pDrvCtrl->pCfd, sizeof (CFD));      /* zero command frame */
  836.     /* Recommended i82586 User's Manual configuration values. */
  837.     pDrvCtrl->pCfd->cfdConfig.byteCount         = 12;
  838.     pDrvCtrl->pCfd->cfdConfig.fifoLimit         = 0x08;
  839.     pDrvCtrl->pCfd->cfdConfig.srdy_saveBad      = 0x40;
  840. #ifdef  EEX_AL_LOC
  841.     pDrvCtrl->pCfd->cfdConfig.addrLen_loopback  = 0x2e;
  842. #else
  843.     pDrvCtrl->pCfd->cfdConfig.addrLen_loopback  = 0x26;
  844. #endif  /* EEX_AL_LOC */
  845.     pDrvCtrl->pCfd->cfdConfig.backoff           = 0x00;
  846.     pDrvCtrl->pCfd->cfdConfig.interframe        = 0x60;
  847.     pDrvCtrl->pCfd->cfdConfig.slotTimeLow       = 0x00;
  848.     pDrvCtrl->pCfd->cfdConfig.slotTimeHi_retry  = 0xf2;
  849.     pDrvCtrl->pCfd->cfdConfig.promiscuous       = 0x00; /* promiscuous off */
  850.     pDrvCtrl->pCfd->cfdConfig.carrier_collision = 0x00;
  851.     pDrvCtrl->pCfd->cfdConfig.minFrame          = 0x40;
  852.     eexAction (unit, CFD_C_CONFIG);          /* configure the chip */
  853.     }
  854. /*******************************************************************************
  855. *
  856. * eexIASetup - format and issue an interface address command
  857. */
  858. static void eexIASetup
  859.     (
  860.     int unit
  861.     )
  862.     {
  863.     DRV_CTRL *pDrvCtrl;
  864.     pDrvCtrl = & drvCtrl [unit];
  865.     bzero ( (char *)pDrvCtrl->pCfd, sizeof (CFD));      /* zero command frame */
  866.     bcopy   (
  867.             (char *)pDrvCtrl->idr.ac_enaddr,
  868.             (char *)pDrvCtrl->pCfd->cfdIASetup.ciAddress,
  869.             6
  870.             );
  871.     eexAction (unit, CFD_C_IASETUP);         /* set up the address */
  872.     }
  873. /*******************************************************************************
  874. *
  875. * eexRxStartup - start up the Receive Unit
  876. *
  877. * Starts up the Receive Unit.  Assumes that the receive structures are set up.
  878. */
  879. static void eexRxStartup
  880.     (
  881.     DRV_CTRL *pDrvCtrl
  882.     )
  883.     {
  884.     if (sysInWord (pDrvCtrl->port + SCB_STATUS) & SCB_S_RUREADY)
  885.         /* already running */
  886.         return;
  887.     /* zero the status and EL bit of the current queue tail dhe 10/26/94 */
  888.     sysOutWord(pDrvCtrl->port + WRPTR, pDrvCtrl->rxQueue.tail);
  889.     sysOutWord(pDrvCtrl->port + DXREG, 0);
  890.     sysOutWord(pDrvCtrl->port + DXREG, 0);
  891.     /* set up head and tail to point to original list  dhe 10/26/94 */
  892.     pDrvCtrl->rxQueue.head = pDrvCtrl->rfdBase;
  893.     pDrvCtrl->rxQueue.tail = pDrvCtrl->lastFd;
  894.     /* Point receive area to list of free frames */
  895.     sysOutWord (pDrvCtrl->port + SCB_RFA, pDrvCtrl->freeRfd);
  896.     
  897.     /*
  898.      * need to rebuild the rfd list here--copy code from initialization
  899.      * routine.  set queue.head and queue.tail.
  900.      */
  901.     eex586IntDisable (pDrvCtrl->idr.ac_if.if_unit);
  902.     if ( (sysInWord (pDrvCtrl->port + SCB_STATUS) & SCB_S_RUMASK)
  903.         != SCB_S_RUIDLE)
  904.         eexCommand (pDrvCtrl, SCB_C_RUABORT);            /* abort if not idle */
  905.     eexCommand (pDrvCtrl, SCB_C_RUSTART);              /* start receive unit */
  906.     eex586IntEnable (pDrvCtrl->idr.ac_if.if_unit);
  907.     }
  908. /*******************************************************************************
  909. *
  910. * eexAction - execute the specified action with the CFD pointed to in pDrvCtrl
  911. *
  912. * Do the command contained in the CFD synchronously, so that we know
  913. * it's complete upon return.  This routine assumes that interrupts from the
  914. * device have been disabled.
  915. */
  916. static void eexAction
  917.     (
  918.     int    unit,
  919.     UINT16 action
  920.     )
  921.     {
  922.     CFD      *pCfd;
  923.     DRV_CTRL *pDrvCtrl;
  924.     pDrvCtrl = & drvCtrl [unit];
  925.     pCfd = pDrvCtrl->pCfd;      /* Building command in scratch area */
  926.     while (1)                   /* wait for idle command unit */
  927.         {
  928.         if ( (sysInWord (pDrvCtrl->port + SCB_STATUS) & SCB_S_CUMASK)
  929.             == SCB_S_CUIDLE)
  930.             break;
  931.         }
  932.     { /* Prepare and issue the command to the device */
  933.     /* fill in CFD */
  934.     pCfd->cfdStatus  = 0;                       /* clear status */
  935.     pCfd->cfdCommand = CFD_C_EL | action;       /* fill in action */
  936.     /* and the SCB */
  937.     sysOutWord (pDrvCtrl->port + SCB_COMMAND,
  938.                         SCB_S_CX |              /* ack any events */
  939.                         SCB_S_FR |
  940.                         SCB_S_CNA |
  941.                         SCB_S_RNR |
  942.                         SCB_C_CUSTART);         /* start command unit */
  943.     /* Point SCB to command area in board memory */
  944.     sysOutWord (pDrvCtrl->port + SCB_CBL, CFD_OFFSET);
  945.     /* Copy CFD to board memory */
  946.     sysOutWord (pDrvCtrl->port + WRPTR, CFD_OFFSET);
  947.     sysOutWordString (pDrvCtrl->port + DXREG, (short *) pCfd, sizeof (CFD) / 2);
  948.     eex586ChanAtn (pDrvCtrl);               /* notify device of new command */
  949.     }
  950.     while (1)               /* wait for command acceptance and interrupt */
  951.         {
  952.         if ( (sysInWord (pDrvCtrl->port + SCB_COMMAND) == 0)
  953.             && (sysInWord (pDrvCtrl->port + SCB_STATUS) & SCB_S_CNA))
  954.             break;
  955.         }
  956.     /* Copy updated CFD back to system memory */
  957.     sysOutWord (pDrvCtrl->port + RDPTR, CFD_OFFSET);
  958.     sysInWordString (pDrvCtrl->port + DXREG, (short *) pCfd, sizeof (CFD) / 2);
  959.     /* Acknowledge the event to the device */
  960.     sysOutWord(pDrvCtrl->port + SCB_COMMAND, SCB_S_CX | SCB_S_CNA);
  961.     eex586ChanAtn (pDrvCtrl);
  962.     while (1)               /* wait for acknowledge acceptance */
  963.         {
  964.         if (sysInWord (pDrvCtrl->port + SCB_COMMAND) == 0)
  965.             break;
  966.         }
  967.     }
  968. /*******************************************************************************
  969. *
  970. * eexCommand - deliver a command to the 82586 via SCB
  971. *
  972. * This function causes the device to execute a command. It should be called
  973. * with interrupts from the device disabled. An error status is returned if
  974. * the command field does not return to zero, from a previous command, in a
  975. * reasonable amount of time.
  976. */
  977. static STATUS eexCommand
  978.     (
  979.     DRV_CTRL *pDrvCtrl,
  980.     UINT16    cmd
  981.     )
  982.     {
  983.     int loopy;
  984.     for (loopy = 0x8000; loopy--;)
  985.         {
  986.         /* wait for cmd zero */
  987.         if (sysInWord (pDrvCtrl->port + SCB_COMMAND) == 0)
  988.             break;
  989.         }
  990.     if (loopy > 0)
  991.         {
  992.         /* fill in command */
  993.         sysOutWord (pDrvCtrl->port + SCB_COMMAND, cmd);
  994.         eex586ChanAtn (pDrvCtrl);    /* channel attention */
  995.         return (OK);
  996.         }
  997.     else
  998.         {
  999.         logMsg("eex driver: command field frozenn", 0, 0, 0, 0, 0, 0);
  1000.         return (ERROR);
  1001.         }
  1002.     }
  1003. /*******************************************************************************
  1004. *
  1005. * eexTxQPut - place a transmit frame on the transmit queue
  1006. *
  1007. * The TFD has been filled in with the network pertinent data. This
  1008. * routine will enqueue the TFD for transmission and attempt to feed
  1009. * the queue to the device.
  1010. */
  1011. static void eexTxQPut
  1012.     (
  1013.     DRV_CTRL *pDrvCtrl,
  1014.     EEX_SHORTLINK tfdOffset
  1015.     )
  1016.     {
  1017.     int unit;
  1018.     unit = pDrvCtrl->idr.ac_if.if_unit;
  1019.     eex586IntDisable (pDrvCtrl->idr.ac_if.if_unit);  /* disable dev ints */
  1020.     if (pDrvCtrl->txQueue.head != NULL)
  1021.         {
  1022.         /* Clear EL bit on previous tail frame */
  1023.         sysOutWord (pDrvCtrl->port + WRPTR,
  1024.                     pDrvCtrl->txQueue.tail + TF_COMMAND);
  1025.         sysOutWord (pDrvCtrl->port + DXREG, CFD_C_XMIT);
  1026.         }
  1027.         
  1028.     /* enqueue the TFD */
  1029.     eexQPut (pDrvCtrl, (EEX_LIST *)&pDrvCtrl->txQueue, tfdOffset);
  1030.     sysOutWord (pDrvCtrl->port + WRPTR, pDrvCtrl->txQueue.tail + TF_COMMAND);
  1031.     sysOutWord (pDrvCtrl->port + DXREG, CFD_C_XMIT | CFD_C_EL);
  1032.     if (pDrvCtrl->txIdle)                             /* transmitter idle */
  1033.         eexTxQFlush (pDrvCtrl);                        /* flush txQueue */
  1034.     eex586IntEnable (pDrvCtrl->idr.ac_if.if_unit);  /* enable dev ints */
  1035.     }
  1036. /*******************************************************************************
  1037. *
  1038. * eexTxQFlush - make cmd unit of device start processing cmds
  1039. *
  1040. * This routine flushes the contents of the txQ to the cblQ and starts the
  1041. * device transmitting the cblQ. Called only if transmit queue is not empty.
  1042. * Sometimes called from interrupt handler.
  1043. */
  1044. static void eexTxQFlush
  1045.     (
  1046.     DRV_CTRL *pDrvCtrl
  1047.     )
  1048.     {
  1049.     extern int sysClkRateGet();     /* we call this */
  1050.     pDrvCtrl->cblQueue.head = pDrvCtrl->txQueue.head;   /* remember cbl head */
  1051.     pDrvCtrl->cblQueue.tail = pDrvCtrl->txQueue.tail;   /* remember cbl tail */
  1052.     eexQInit ( (EEX_LIST *)&pDrvCtrl->txQueue);         /* tx queue now empty */
  1053.     sysOutWord(pDrvCtrl->port + SCB_CBL, pDrvCtrl->cblQueue.head);
  1054.     pDrvCtrl->txIdle = FALSE;                   /* transmitter busy */
  1055.     /* start watchdog */
  1056.     wdStart (
  1057.             pDrvCtrl->wid,
  1058.             (int) (sysClkRateGet() >> 1),
  1059.             (FUNCPTR) eexWatchDog,
  1060.             pDrvCtrl->idr.ac_if.if_unit
  1061.             );
  1062.     /* start command unit */
  1063.     eexCommand (pDrvCtrl, SCB_C_CUSTART);
  1064.     }
  1065. /*******************************************************************************
  1066. *
  1067. * eexRxQPut - return a RFD to the receive queue for use by the device
  1068. */
  1069. static void eexRxQPut
  1070.     (
  1071.     DRV_CTRL *pDrvCtrl,
  1072.     EEX_SHORTLINK rfdOffset
  1073.     )
  1074.     {
  1075.     EEX_SHORTLINK rxTail;
  1076.     UINT16 status;
  1077.     EEX_SHORTLINK rbdOffset;    /* dhe 10/24/94 */
  1078.     sysOutWord (pDrvCtrl->port + WRPTR, rfdOffset);
  1079.     sysOutWord (pDrvCtrl->port + DXREG, 0);     /* clear status */
  1080.     sysOutWord (pDrvCtrl->port + DXREG, CFD_C_EL); /* new end of list */
  1081.     /* dhe 10/24/94 get the buffer pointer */
  1082.     sysOutWord(pDrvCtrl->port + RDPTR, rfdOffset + RB_OFFSET);
  1083.     rbdOffset = sysInWord(pDrvCtrl->port + DXREG);
  1084.     /* dhe 10/24/94 TEST TEST zero the actual count of the buffer */
  1085.     sysOutWord(pDrvCtrl->port + WRPTR, rbdOffset);
  1086.     sysOutWord(pDrvCtrl->port + DXREG, 0);
  1087.     rxTail = pDrvCtrl->rxQueue.tail;         /* remember tail */
  1088.     /* Put the RFD on the list */
  1089.     eexQPut (pDrvCtrl, (EEX_LIST *) & pDrvCtrl->rxQueue, rfdOffset);
  1090.     if (rxTail != NULL)
  1091.         {
  1092.         sysOutWord (pDrvCtrl->port + WRPTR, rxTail + RF_COMMAND);
  1093.         sysOutWord (pDrvCtrl->port + DXREG, 0); /* clear old tail EL */
  1094.         sysOutWord (pDrvCtrl->port + RDPTR, rxTail);
  1095.         status = sysInWord (pDrvCtrl->port + DXREG);
  1096.         if (status & (CFD_S_COMPLETE | CFD_S_BUSY))
  1097.             {
  1098.             pDrvCtrl->freeRfd = rfdOffset;       /* link questionable */
  1099.             }
  1100.         else if (!(sysInWord (pDrvCtrl->port + SCB_STATUS) & SCB_S_RUREADY))
  1101.             /* receiver dormant */
  1102.             {
  1103.             eexRxStartup (pDrvCtrl);             /* start receive unit */
  1104.             }
  1105.         }
  1106.     else
  1107.         {
  1108.         pDrvCtrl->freeRfd = rfdOffset;           /* first free RFD */
  1109.         }
  1110.     }
  1111. /*******************************************************************************
  1112. *
  1113. * eexRxQGet - get a successfully received frame from the receive queue
  1114. *
  1115. * RETURNS: ptr to valid RFD, or NULL if none available
  1116. */
  1117. static EEX_SHORTLINK eexRxQGet
  1118.     (
  1119.     DRV_CTRL *pDrvCtrl
  1120.     )
  1121.     {
  1122.     EEX_SHORTLINK rfdOffset = NULL;
  1123.     if (eexRxQFull (pDrvCtrl))
  1124.         rfdOffset = eexQGet (pDrvCtrl, (EEX_LIST *)&pDrvCtrl->rxQueue);
  1125.     return (rfdOffset);
  1126.     }
  1127. /*******************************************************************************
  1128. *
  1129. * eexRxQFull - boolean function to determine fullness of receive queue
  1130. *
  1131. * RETURNS: TRUE if completely received frame is available, FALSE otherwise.
  1132. */
  1133. static BOOL eexRxQFull
  1134.     (
  1135.     DRV_CTRL *pDrvCtrl
  1136.     )
  1137.     {
  1138.     return (
  1139.         (pDrvCtrl->rxQueue.head != NULL) &&
  1140.         (
  1141.         sysOutWord (pDrvCtrl->port + RDPTR, pDrvCtrl->rxQueue.head),
  1142.         sysInWord (pDrvCtrl->port + DXREG) & CFD_S_COMPLETE
  1143.         )
  1144.         );
  1145.     }
  1146. /*******************************************************************************
  1147. *
  1148. * eexQInit - initialize a singly linked node queue
  1149. */
  1150. static void eexQInit
  1151.     (
  1152.     EEX_LIST *pQueue
  1153.     )
  1154.     {
  1155.     pQueue->head = pQueue->tail = NULL;         /* init head & tail */
  1156.     }
  1157. /*******************************************************************************
  1158. *
  1159. * eexQGet - get a node from the head of a node queue
  1160. *
  1161. * RETURNS: ptr to useable node, or NULL ptr if none available
  1162. * Since we never alter links, we are taking the last node when head==tail.
  1163. */
  1164. static EEX_SHORTLINK eexQGet
  1165.     (
  1166.     DRV_CTRL *pDrvCtrl,
  1167.     EEX_LIST *pQueue
  1168.     )
  1169.     {
  1170.     EEX_SHORTLINK pNode;
  1171.     if ( (pNode = (EEX_SHORTLINK) pQueue->head) != NULL) /* if list not empty */
  1172.         {
  1173.         if (pQueue->head == pQueue->tail)
  1174.             pQueue->head =                              /* list now empty */
  1175.             pQueue->tail = NULL;
  1176.         else
  1177.             pQueue->head = SUCC_FD (pNode);             /* advance ptr */
  1178.         }
  1179.     return (pNode);
  1180.     }
  1181. /*******************************************************************************
  1182. *
  1183. * eexQPut - put a node on the tail of a node queue
  1184. */
  1185. static void eexQPut
  1186.     (
  1187.     DRV_CTRL *pDrvCtrl,
  1188.     EEX_LIST *pQueue,
  1189.     EEX_SHORTLINK pNode
  1190.     )
  1191.     {
  1192.     /* assert (SUCC_FD (pQueue->tail) == pNode); */
  1193.     if (pQueue->head == NULL)                   /* if list empty */
  1194.         pQueue->head = pNode;                   /* new element is head */
  1195.     pQueue->tail = pNode;                       /* update tail ptr */
  1196.     }
  1197. /*******************************************************************************
  1198. *
  1199. * eexWatchDog - if the watchdog timer fired off, we've hung during a transmit
  1200. *
  1201. * Check the scb command to verify and if so, reinit.
  1202. */
  1203. static void eexWatchDog
  1204.     (
  1205.     int unit        /* unit number */
  1206.     )
  1207.     {
  1208.     DRV_CTRL *pDrvCtrl;
  1209.     pDrvCtrl = & drvCtrl [unit];
  1210.     /* sanity check.
  1211.      * If the scb status indicates that CU (transmit) is active
  1212.      * It might make sense to loop through the cfd's to look for
  1213.      * a complete bit as a sanity check , but given that we are
  1214.      * here and that transmit was active, we will go ahead and do
  1215.      * a reset.
  1216.      */
  1217.     if (sysInWord (pDrvCtrl->port + SCB_STATUS) & SCB_S_CUACTIVE)
  1218.         {
  1219.         pDrvCtrl->transLocks++;                     /* local failure count */
  1220.         pDrvCtrl->idr.ac_if.if_oerrors++;           /* incr err count */
  1221.         pDrvCtrl->idr.ac_if.if_opackets--;          /* decr sent count */
  1222.         DUMMY(unit);
  1223.         }
  1224.     }
  1225. /*******************************************************************************
  1226. *
  1227. * eex586Init - Initialize board features
  1228. *
  1229. * Does pretty much everything to initialize the 82586 and its memory
  1230. * structures, either in this routine or in called routines.
  1231. */
  1232. static STATUS eex586Init
  1233.     (
  1234.     int unit
  1235.     )
  1236.     {
  1237.     DRV_CTRL *pDrvCtrl;
  1238.     int ix;
  1239.     int port;
  1240.     EEX_SHORTLINK currentFd;
  1241.     /**** dhe 10/26/94 moved to global for reassigning during restart
  1242.     EEX_SHORTLINK lastFd;
  1243.     ****/
  1244.     int connValue;
  1245.     pDrvCtrl = & drvCtrl [unit];
  1246.     port = pDrvCtrl->port;
  1247.     eex586IntDisable (unit);            /* disable device interrupts */
  1248.     /* extract setup information from the board's EEPROM */
  1249.     /* Remember that readEEPROM() sets the 82586 reset condition. */
  1250.     pDrvCtrl->setup = eexReadEEPROM (pDrvCtrl, EEX_EEPROM_SETUP);
  1251.     pDrvCtrl->tpeBit = eexReadEEPROM (pDrvCtrl, EEX_EEPROM_TPE_BIT);
  1252.     pDrvCtrl->memSetup.wordView.memPage =
  1253.                 eexReadEEPROM (pDrvCtrl, EEX_EEPROM_MEMPAGE);
  1254.     pDrvCtrl->memSetup.wordView.memDecode =
  1255.                 eexReadEEPROM (pDrvCtrl, EEX_EEPROM_MEMDECODE);
  1256.     if (pDrvCtrl->attachment == ATTACHMENT_DEFAULT)
  1257.         if ( (pDrvCtrl->memSetup.wordView.memPage & MEMPAGE_AUTODETECT) != 0)
  1258.             {
  1259.             /* Here we would figure out which connector is in use;
  1260.              * "auto-detect" relies on software to do the actual detection.
  1261.              */
  1262.             }
  1263.         else
  1264.             {
  1265.             if ( (pDrvCtrl->setup & SETUP_BNC) == 0)
  1266.                 pDrvCtrl->attachment = ATTACHMENT_AUI;
  1267.             else if ( (pDrvCtrl->tpeBit & TPE_BIT) == 0)
  1268.                 pDrvCtrl->attachment = ATTACHMENT_BNC;
  1269.             else
  1270.                 pDrvCtrl->attachment = ATTACHMENT_RJ45;
  1271.             }
  1272.     /* Tell the board hardware which connector we're using */
  1273.     connValue = sysInByte (port + ECR1) & ~(CONN_INTEGRITY | CONN_TRANSCEIVER);
  1274.     switch (pDrvCtrl->attachment)
  1275.         {
  1276.         case ATTACHMENT_AUI:
  1277.             break;
  1278.         case ATTACHMENT_BNC:
  1279.             connValue |= CONN_TRANSCEIVER;
  1280.             break;
  1281.         case ATTACHMENT_RJ45:
  1282.             connValue |= CONN_TRANSCEIVER | CONN_INTEGRITY;
  1283.             break;
  1284.         }
  1285.     /* 17jan95  jag
  1286.      * Only send the connector selection command if the board is
  1287.      * NOT in "auto-detect" mode.  (If the board is in
  1288.      * "auto-detect" mode, another command might disrupt the
  1289.      * auto-detected mode).
  1290.      */
  1291.     if ( (pDrvCtrl->memSetup.wordView.memPage & MEMPAGE_AUTODETECT) == 0)
  1292.         sysOutByte (port + ECR1, connValue);
  1293.     /* get our enet addr */
  1294.     if (eexEnetAddrGet (pDrvCtrl, (char *)pDrvCtrl->idr.ac_enaddr) == ERROR)
  1295.         {
  1296.         errnoSet (S_iosLib_INVALID_ETHERNET_ADDRESS);
  1297.         return (ERROR);
  1298.         }
  1299.     /* Connect the interrupt handler */
  1300.     if (intConnect ( (VOIDFUNCPTR *)INUM_TO_IVEC (pDrvCtrl->ivec),
  1301.                     eexInt, (int)pDrvCtrl) == ERROR)
  1302.         return (ERROR);
  1303.     /* Start the device */
  1304.     if ( eexDeviceStart (unit) == ERROR )
  1305.         return (ERROR);
  1306.     eexDiag (unit);                             /* run diagnostics */
  1307.     eexConfig (unit);                           /* configure chip */
  1308.     eexIASetup (unit);                          /* set up address */
  1309.     /* Carve up the shared memory region into the specific sections */
  1310.     pDrvCtrl->rfdBase =
  1311.             (TFD_OFFSET + (sizeof (TFRAME) * pDrvCtrl->nTFDs));
  1312.     /* create the free tfd queue; nTFDs is non-zero */
  1313.     for (ix = 0, currentFd = TFD_OFFSET;
  1314.          ix < pDrvCtrl->nTFDs;
  1315.          ix ++, pDrvCtrl->lastFd = currentFd, currentFd += sizeof (TFRAME))
  1316.         {
  1317.         /* Initialize the TFD to link to next TFD, point to adjacent TBD */
  1318.         sysOutWord (port + WRPTR, currentFd);
  1319.         sysOutWord (port + DXREG, 0);                           /* status */
  1320.         sysOutWord (port + DXREG, CFD_C_XMIT);                  /* command */
  1321.         sysOutWord (port + DXREG, currentFd + sizeof (TFRAME)); /* TFD link */
  1322. #ifdef  EEX_AL_LOC
  1323.         sysOutWord (port + DXREG, currentFd + sizeof (TFD));    /* TBD ptr */
  1324. #else
  1325.         sysOutWord (port + DXREG, currentFd + sizeof (TFD) +
  1326.                                   FRAME_SIZE);                  /* TBD ptr */
  1327.         sysOutWord (port + WRPTR, currentFd + sizeof (TFD) +
  1328.                                   FRAME_SIZE);          /* point to TBD */
  1329. #endif  /* EEX_AL_LOC */
  1330.         /* Initialize the TBD following the above TFD */
  1331.         sysOutWord (port + DXREG, 0);                   /* status */
  1332.         sysOutWord (port + DXREG, 0xffff);              /* next TBD */
  1333. #ifdef  EEX_AL_LOC
  1334.         sysOutWord (port + DXREG,                       /* buffer address */
  1335.                     currentFd + sizeof (TFD) + sizeof (TBD));
  1336. #else
  1337.         sysOutWord (port + DXREG,                       /* buffer address */
  1338.                     currentFd + sizeof (TFD) + EH_SIZE);
  1339. #endif  /* EEX_AL_LOC */
  1340.         sysOutWord (port + DXREG, 0);                   /* buffer add. hi */
  1341.         }
  1342.     /* Fix the link field of the last TFD to point to the first */
  1343.     sysOutWord (port + WRPTR, pDrvCtrl->lastFd + TF_LINK);
  1344.     sysOutWord (port + DXREG, TFD_OFFSET);
  1345.     pDrvCtrl->tfdQueue.head = TFD_OFFSET;       /* Initialize queues */
  1346.     pDrvCtrl->tfdQueue.tail = pDrvCtrl->lastFd;
  1347.     eexQInit ( (EEX_LIST *)&pDrvCtrl->txQueue);    /* to be sent queue */
  1348.     eexQInit ( (EEX_LIST *)&pDrvCtrl->cblQueue);   /* actively sending queue */
  1349.     eexQInit ( (EEX_LIST *)&pDrvCtrl->cleanQueue); /* queue of TFDs to clean */
  1350.     /* create all the receive frame descriptors */
  1351.     for (ix = 0, currentFd = pDrvCtrl->rfdBase;
  1352.          ix < pDrvCtrl->nRFDs;
  1353.          ix ++, pDrvCtrl->lastFd = currentFd, currentFd += sizeof (RFRAME))
  1354.         {
  1355.         /* Initialize the RFD to link to next RFD, point to following RBD */
  1356.         sysOutWord (port + WRPTR, currentFd);
  1357.         sysOutWord (port + DXREG, 0);                           /* status */
  1358.         sysOutWord (port + DXREG, 0);                           /* command */
  1359.         sysOutWord (port + DXREG, currentFd + sizeof (RFRAME)); /* RFD link */
  1360. #ifdef  EEX_AL_LOC
  1361.         sysOutWord (port + DXREG, currentFd + sizeof (RFD));    /* RBD ptr */
  1362. #else
  1363.         sysOutWord (port + DXREG, currentFd + sizeof (RFD) +
  1364.                                   FRAME_SIZE);                  /* RBD ptr */
  1365.         sysOutWord (port + WRPTR, currentFd + sizeof (RFD) +
  1366.                                   FRAME_SIZE);          /* point to RBD */
  1367. #endif  /* EEX_AL_LOC */
  1368.         /* Initialize the RBD following the above RFD */
  1369.         sysOutWord (port + DXREG, 0);                   /* act. count */
  1370.         sysOutWord (port + DXREG, 0xffff);              /* next RBD */
  1371. #ifdef  EEX_AL_LOC
  1372.         sysOutWord (port + DXREG,                       /* buffer address */
  1373.                     currentFd + sizeof (RFD) + sizeof (RBD));
  1374. #else
  1375.         sysOutWord (port + DXREG,                       /* buffer address */
  1376.                     currentFd + sizeof (RFD) + EH_SIZE);
  1377. #endif  /* EEX_AL_LOC */
  1378.         sysOutWord (port + DXREG, 0);                   /* buffer add. hi */
  1379.         sysOutWord (port + DXREG, RBD_M_EL + FRAME_SIZE); /* buffer size */
  1380.         }
  1381.     /* Fix the link field of the last RFD to point to the first */
  1382.     sysOutWord (port + WRPTR, pDrvCtrl->lastFd + RF_LINK);
  1383.     sysOutWord (port + DXREG, pDrvCtrl->rfdBase);
  1384.     pDrvCtrl->rxQueue.head = pDrvCtrl->rfdBase; /* Initialize queue */
  1385.     pDrvCtrl->rxQueue.tail = pDrvCtrl->lastFd;
  1386.     pDrvCtrl->freeRfd = pDrvCtrl->rxQueue.head;
  1387.     eex586IntAck (unit);                     /* clear any pended dev ints */
  1388.     eex586IntEnable (unit);                  /* enable interrupts at system */
  1389.     eexRxStartup (pDrvCtrl);                 /* start receive unit */
  1390.     return (OK);
  1391.     }
  1392. /*******************************************************************************
  1393. *
  1394. * eexEnetAddrGet - get OEM Ethernet address from EtherExpress on-board EEPROM
  1395. *
  1396. * The "OEM" (i.e. not necessarily assigned by Intel) Ethernet address for the
  1397. * board is contained in words 2, 3, and 4 of the on-board EEPROM.  Note that
  1398. * EEPROM addresses are WORD not byte addresses!
  1399. * The byte swapping arises from the address being stored in big-endian byte
  1400. * order, apparently in approximation of the transmitted order (but the bits
  1401. * would have to be reversed for true transmitted bit order).
  1402. */
  1403. static STATUS eexEnetAddrGet
  1404.     (
  1405.     DRV_CTRL *pDrvCtrl,
  1406.     char addr[]
  1407.     )
  1408.     {
  1409.     UINT16 eepromAddress;
  1410.     char *dest = addr;
  1411.     UINT16 t;
  1412.     for (eepromAddress = EEX_EEPROM_EA_HIGH;
  1413.          eepromAddress >= EEX_EEPROM_EA_LOW;
  1414.          eepromAddress--)
  1415.         {
  1416.         t = eexReadEEPROM (pDrvCtrl, eepromAddress);
  1417.         *dest++ = t >> 8;
  1418.         *dest++ = t & 0xff;
  1419.         }
  1420.     
  1421.     return (OK);
  1422.     }
  1423. /*******************************************************************************
  1424. *
  1425. * eex586IntAck - acknowledge EtherExpress 16 interrupt
  1426. *
  1427. * The board hardware conatains an interrupt latch which is cleared by clearing
  1428. * the interrupt enable bit.  We presume we want the interrupts enabled so we
  1429. * re-enable the interrupt.
  1430. */
  1431. static void eex586IntAck
  1432.     (
  1433.     int unit
  1434.     )
  1435.     {
  1436.     eex586IntDisable (unit);
  1437.     eex586IntEnable (unit);
  1438.     }
  1439. /*******************************************************************************
  1440. *
  1441. * eex586IntDisable - disable EtherExpress 16 interrupt
  1442. *
  1443. * Prevent the board's 82586 from interrupting the system.
  1444. * As noted in IntAck above, the board has an interrupt latch which is cleared
  1445. * "with the disable-enable interrupt bit".  We will assume that after the
  1446. * moment of disabling, pending interrupts will be latched and honored when
  1447. * the interrupt is re-enabled.  There is no evidence to support this
  1448. * assumption, and it should be tested some day.
  1449. */
  1450. static void eex586IntDisable
  1451.     (
  1452.     int unit
  1453.     )
  1454.     {
  1455.     DRV_CTRL *pDrvCtrl;
  1456.     pDrvCtrl = & drvCtrl [unit];
  1457.     sysOutByte (pDrvCtrl->port + SEL_IRQ, pDrvCtrl->irqCode);
  1458.     }
  1459. /*******************************************************************************
  1460. *
  1461. * eex586IntEnable - enable EtherExpress 16 interrupt
  1462. *
  1463. * Allow the board's 82586 to interrupt the system.
  1464. */
  1465. static void eex586IntEnable
  1466.     (
  1467.     int unit
  1468.     )
  1469.     {
  1470.     DRV_CTRL *pDrvCtrl;
  1471.     pDrvCtrl = & drvCtrl [unit];
  1472.     sysOutByte (pDrvCtrl->port + SEL_IRQ, pDrvCtrl->irqCode | IRQ_ENB);
  1473.     }
  1474. /*******************************************************************************
  1475. *
  1476. * eex586SetReset - reset the EtherExpress 16's  82586 chip
  1477. *
  1478. * Set chip into reset state (and hold it there until released elsewhere).
  1479. */
  1480. static void eex586SetReset
  1481.     (
  1482.     int unit
  1483.     )
  1484.     {
  1485.     DRV_CTRL *pDrvCtrl;
  1486.     pDrvCtrl = & drvCtrl [unit];
  1487.     sysOutByte (pDrvCtrl->port + EE_CTRL, RESET_82586);
  1488.     }
  1489. /*******************************************************************************
  1490. *
  1491. * eex586ClearReset - remove the EtherExpress 16's 82586 chip from reset state
  1492. *
  1493. * Clear the bit holding the chip's reset line enabled, allowing the chip
  1494. * to follow its power-on initialization sequence.
  1495. */
  1496. static void eex586ClearReset
  1497.     (
  1498.     int unit
  1499.     )
  1500.     {
  1501.     DRV_CTRL *pDrvCtrl;
  1502.     pDrvCtrl = & drvCtrl [unit];
  1503.     /* All other bits in this register should be zero when chip runs */
  1504.     sysOutByte (pDrvCtrl->port + EE_CTRL, 0);
  1505.     }
  1506. /*******************************************************************************
  1507. *
  1508. * eex586ChanAtn - assert Channel Attention to the EtherExpress 16's  82586 chip
  1509. *
  1510. * Channel Attention tells the 82586 to examine its System Control Block
  1511. * for commands and acknowledgements.
  1512. * This could really be a macro.
  1513. */
  1514. static void eex586ChanAtn
  1515.     (
  1516.     DRV_CTRL *pDrvCtrl
  1517.     )
  1518.     {
  1519.     /* The act of writing to the port does the job; data is irrelevant */
  1520.     sysOutByte (pDrvCtrl->port + CA_CTRL, 0);
  1521.     }
  1522. /*******************************************************************************
  1523. *
  1524. * eexReadEEPROM - read one word from the on-board EEPROM
  1525. *
  1526. * Note that a necessary side effect of accessing the EEPROM is to set the
  1527. * RESET_82586 bit, because the 82586's bus signals must be disabled.
  1528. * Note that EEPROM addresses are WORD not byte addresses!
  1529. */
  1530. static UINT16 eexReadEEPROM
  1531.     (
  1532.     DRV_CTRL *pDrvCtrl,
  1533.     UINT16 address
  1534.     )
  1535.     {
  1536.     int port;
  1537.     UCHAR eeCtrl;               /* maintains current port contents */
  1538.     UINT16 value;
  1539.     port = pDrvCtrl->port + EE_CTRL;
  1540.     eeCtrl = sysInByte (port) & (EEPROM_CHIPSEL | RESET_82586);
  1541.     sysOutByte (port, eeCtrl | EEPROM_CHIPSEL);
  1542.     eexOutBitsEEPROM (port, 3, EEPROM_OP_READ);
  1543.     eexOutBitsEEPROM (port, 6, address);
  1544.     value = eexInBitsEEPROM (port, 16);
  1545.     eeCtrl &= EEPROM_CHIPSEL;           /* be certain chip select is clear */
  1546.     sysOutByte (port, eeCtrl);          /* also the data port */
  1547.     eexDelayEEPROM();                   /* data setup time */
  1548.     sysOutByte (port, eeCtrl | EEPROM_CLOCK);   /* clock with CS low? */
  1549.     eexDelayEEPROM();
  1550.     sysOutByte (port, eeCtrl);
  1551.     eexDelayEEPROM();
  1552.     return (value);
  1553.     }
  1554. /*******************************************************************************
  1555. *
  1556. * eexOutBitsEEPROM - send a bit string to the on-board EEPROM
  1557. *
  1558. * Shifts the righmost 'count' bits out, LEFTMOST first.
  1559. */
  1560. static void eexOutBitsEEPROM
  1561.     (
  1562.     int port,
  1563.     int count,
  1564.     UINT16 bits
  1565.     )
  1566.     {
  1567.     UCHAR eeCtrl;               /* maintains current port contents */
  1568.     bits <<= 16 - count;        /* left-justify the bit string */
  1569.     eeCtrl = sysInByte (port) & (EEPROM_CHIPSEL | RESET_82586);
  1570.     while (count--)
  1571.         {
  1572.         if (bits & 0x8000)
  1573.             eeCtrl |= EEPROM_OUTPUT;
  1574.         else
  1575.             eeCtrl &= ~EEPROM_OUTPUT;
  1576.         sysOutByte (port, eeCtrl);
  1577.         eexDelayEEPROM();                       /* data setup time */
  1578.         sysOutByte (port, eeCtrl | EEPROM_CLOCK);
  1579.         eexDelayEEPROM();
  1580.         sysOutByte (port, eeCtrl);
  1581.         eexDelayEEPROM();
  1582.         bits <<= 1;
  1583.         }
  1584.     sysOutByte (port, eeCtrl & ~EEPROM_OUTPUT); /* clear output bit */
  1585.     }
  1586. /*******************************************************************************
  1587. *
  1588. * eexInBitsEEPROM - receive a bit string from the on-board EEPROM
  1589. *
  1590. */
  1591. static UINT16 eexInBitsEEPROM
  1592.     (
  1593.     int port,
  1594.     int count
  1595.     )
  1596.     {
  1597.     UCHAR eeCtrl;               /* maintains current port contents */
  1598.     UINT16 value;               /* build result a bit at a time */
  1599.     eeCtrl = sysInByte (port) & (EEPROM_CHIPSEL | RESET_82586);
  1600.     value = 0;
  1601.     while (count--)
  1602.         {
  1603.         value <<= 1;
  1604.         sysOutByte (port, eeCtrl | EEPROM_CLOCK);
  1605.         eexDelayEEPROM();
  1606.         if (sysInByte (port) & EEPROM_INPUT)
  1607.             value |= 1;
  1608.         sysOutByte (port, eeCtrl);
  1609.         eexDelayEEPROM();
  1610.         }
  1611.     return (value);
  1612.     }
  1613. /*******************************************************************************
  1614. *
  1615. * eexDelayEEPROM - delay for EEPROM to recognize clock change
  1616. *
  1617. * Supposedly 4.0 microseconds for the Hyundai EEPROM.
  1618. */
  1619. static void eexDelayEEPROM
  1620.     (
  1621.     )
  1622.     {
  1623.     taskDelay (1);              /* gross overkill! */
  1624.     }
  1625. /******************************************************************************/
  1626. /* END OF FILE */