halether.c
上传用户:qiulin1960
上传日期:2013-10-16
资源大小:2844k
文件大小:19k
源码类别:

Windows CE

开发平台:

Windows_Unix

  1. /******************************************************************************
  2.  *
  3.  * System On Chip(SOC)
  4.  *
  5.  * Copyright (c) 2002 Software Center, Samsung Electronics, Inc.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information of Samsung 
  9.  * Electronics, Inc("Confidential Information"). You Shall not disclose such 
  10.  * Confidential Information and shall use it only in accordance with the terms 
  11.  * of the license agreement you entered into Samsung.
  12.  *
  13.  *-----------------------------------------------------------------------------
  14.  *
  15.  * S3C2440 BSP
  16.  *
  17.  * halether.c :
  18.  *
  19.  * Platform specific code for debug ethernet services (debug messages,          
  20.  * kernel debugger, text shell (CESH)).  These functions are all called         
  21.  * from ethdbg.lib.  They are non-preemptible, and cannot make any system calls.
  22.  *
  23.  *
  24.  * @author zartoven@samsung.com (SOC, SWC, SAMSUNG Electronics)
  25.  *
  26.  * @date 2002/04/04
  27.  * 
  28.  * Log:
  29.  * 2002/04/04  Start(From ODO's BSP)
  30.  *      
  31.  ******************************************************************************
  32.  */
  33. #include <windows.h>
  34. #include <nkintr.h>
  35. #include <ethdbg.h>
  36. #include <halether.h>
  37. #include <kitl.h>
  38. #include "s2440.h"
  39. #include "drv_glob.h"
  40. #include "oalintr.h"
  41. #define pDriverGlobals ((PDRIVER_GLOBALS) DRIVER_GLOBALS_PHYSICAL_MEMORY_START)
  42. BOOL   CS8900DBG_Init(BYTE *iobase, DWORD membase, USHORT MacAddr[3]);
  43. BOOL   CS8900DBG_IsReceivedPacket(void);
  44. UINT16 CS8900DBG_GetFrame(BYTE *pbData, UINT16 *pwLength);
  45. UINT16 CS8900DBG_SendFrame( BYTE *pbData, DWORD dwLength );
  46. BOOL   CS8900DBG_ReInit(DWORD iobase, DWORD membase);
  47. BOOL   CS8900DBG_MulticastList(PUCHAR pucMulticastAddresses, DWORD dwNumAddresses);
  48. void   CS8900DBG_CurrentPacketFilter(DWORD dwFilter);
  49. void   CS8900DBG_EnableInts(void);
  50. void   CS8900DBG_DisableInts(void);
  51. DWORD  CS8900DBG_GetPendingInts(void);
  52. PVOID PCMCIA_Init(void);
  53. //
  54. // Function pointers to the support library functions of the currently installed debug ethernet controller.
  55. //
  56. PFN_EDBG_INIT             pfnEDbgInit;
  57. PFN_EDBG_ENABLE_INTS      pfnEDbgEnableInts;
  58. PFN_EDBG_DISABLE_INTS     pfnEDbgDisableInts;
  59. PFN_EDBG_GET_PENDING_INTS pfnEDbgGetPendingInts;
  60. PFN_EDBG_GET_FRAME        pfnEDbgGetFrame;
  61. PFN_EDBG_SEND_FRAME       pfnEDbgSendFrame;
  62. PFN_EDBG_READ_EEPROM      pfnEDbgReadEEPROM;
  63. PFN_EDBG_WRITE_EEPROM     pfnEDbgWriteEEPROM;
  64. PFN_EDBG_SET_OPTIONS      pfnEDbgSetOptions;
  65. #ifdef IMGSHAREETH
  66. PFN_EDBG_CURRENT_PACKET_FILTER  pfnCurrentPacketFilter;
  67. PFN_EDBG_MULTICAST_LIST pfnMulticastList;
  68. BOOL OEMEthCurrentPacketFilter(PDWORD pdwRequestedFilter);
  69. // Multicast list - Vmini assumes a maximum of 8 entries.
  70. //
  71. #define MAX_MULTICAST_LIST      8
  72. BOOL ucMultiAddr[MAX_MULTICAST_LIST][6];
  73. BOOL bNewFilter = FALSE; // User mode --> Kernel mode to set new filter.
  74. DWORD dwFilter; // The filter..
  75. BOOL bNewMulticast = FALSE; // User mode --> Kernel mode for new list
  76. DWORD dwNoOfEntry;
  77. /* ProcessVMiniSend()
  78.  *
  79.  * This routine drains the pending VMINI TX.
  80.  * 
  81.  */
  82. void ProcessVMiniSend(void)
  83. {
  84. PBYTE   pVMiniData;
  85.     DWORD   dwVMiniDataLength;
  86. ////////////////////////////////////////////////////////////////////////////
  87. // Handle the filter if we need to..
  88. //
  89. if (bNewFilter && pfnCurrentPacketFilter)
  90. {
  91. bNewFilter = FALSE;
  92. pfnCurrentPacketFilter(dwFilter);
  93. }
  94. //
  95.     //  Handle new multicast list..
  96.     //
  97.     if (bNewMulticast && pfnMulticastList)
  98.     {
  99.         bNewMulticast = FALSE;
  100.         pfnMulticastList((PUCHAR)ucMultiAddr, dwNoOfEntry);
  101.     }
  102.     ////////////////////////////////////////////////////////////////////////////
  103.     //  Consume all the client packets.
  104.     //
  105.     while (VBridgeKGetOneTxBuffer(&pVMiniData, &dwVMiniDataLength) == TRUE)
  106.     {
  107.         pfnEDbgSendFrame (pVMiniData, dwVMiniDataLength);
  108.         VBridgeKGetOneTxBufferComplete(pVMiniData);
  109.     } 
  110. } // ProcessVMiniSend()
  111. #endif //IMGSHAREETH
  112. /* OEMEthInit
  113.  *
  114.  *  Initialization routine
  115.  *
  116.  *  Return Value:
  117.  *    Return TRUE if init is successful, FALSE if error.
  118.  */
  119. BOOL
  120. OEMEthInit(EDBG_ADAPTER *pAdapter) 
  121. {
  122. PBYTE pBaseIOAddress;
  123. // Driver globals from the bootloader.
  124. //
  125. if (pDriverGlobals->eth.EbootMagicNum == EBOOT_MAGIC_NUM)
  126. {
  127. memcpy(pAdapter, &pDriverGlobals->eth.TargetAddr, sizeof(EDBG_ADAPTER));
  128. switch(pDriverGlobals->misc.EbootDevice)
  129. {
  130. case(DOWNLOAD_DEVICE_PCMCIA): // NE2000 CF card.
  131. pBaseIOAddress  = (PBYTE)PCMCIA_Init();
  132. if (pBaseIOAddress)
  133. {
  134. // Initialize the built-in Ethenet controller.
  135. //
  136. if (!NE2000Init((PBYTE)pBaseIOAddress, 1, pAdapter->Addr.wMAC))
  137. {
  138. EdbgOutputDebugString("ERROR: OEMEthInit: Failed to initialize Ethernet controller.rn");
  139. return(FALSE);
  140. }
  141. }
  142. pfnEDbgInit            = NE2000Init;
  143. pfnEDbgEnableInts      = NE2000EnableInts;
  144. pfnEDbgDisableInts     = NE2000DisableInts;
  145. pfnEDbgGetPendingInts  = NE2000GetPendingInts;
  146. pfnEDbgGetFrame        = NE2000GetFrame;
  147. pfnEDbgSendFrame       = NE2000SendFrame;
  148. pfnEDbgReadEEPROM      = NE2000ReadEEPROM;
  149. pfnEDbgWriteEEPROM     = NE2000WriteEEPROM;
  150. pfnEDbgSetOptions      = NE2000SetOptions;
  151. #ifdef IMGSHAREETH
  152. pfnCurrentPacketFilter = Ne2000CurrentPacketFilter;
  153. pfnMulticastList = NE2000MulticastList;
  154. #endif // IMGSHAREETH.
  155. break;
  156. case(DOWNLOAD_DEVICE_CS8900): // CS8900A.
  157. // Initialize the CS8900.
  158. //
  159. if (!CS8900DBG_Init((PBYTE)CS8900DBG_IOBASE, CS8900DBG_MEMBASE, pAdapter->Addr.wMAC))
  160. {
  161. EdbgOutputDebugString("ERROR: OEMEthInit: CS8900 initialization failed.rn");
  162. return(FALSE);
  163. }
  164. pfnEDbgInit = CS8900DBG_Init;
  165. pfnEDbgEnableInts      = CS8900DBG_EnableInts;
  166. pfnEDbgDisableInts     = CS8900DBG_DisableInts;
  167. pfnEDbgGetFrame = CS8900DBG_GetFrame;
  168. pfnEDbgSendFrame = CS8900DBG_SendFrame;
  169. pfnEDbgGetPendingInts  = CS8900DBG_GetPendingInts;
  170. #ifdef IMGSHAREETH
  171. pfnCurrentPacketFilter = CS8900DBG_CurrentPacketFilter;
  172. pfnMulticastList = CS8900DBG_MulticastList;
  173. #endif // IMGSHAREETH.
  174. break;
  175. default:
  176. EdbgOutputDebugString("ERROR: OEMInit: Unknown download NIC (0x%x).rn", pDriverGlobals->misc.EbootDevice);
  177. return(FALSE);
  178. }
  179. }
  180. else
  181. {
  182. // TODO - retrieve CS8900 MAC address from flash...
  183. // TODO - intialize the CS8900 from scratch...
  184. }
  185. EdbgOutputDebugString("::: OEMEthInit() IP Address : %srn", inet_ntoa(pAdapter->Addr.dwIP));
  186. EdbgOutputDebugString("::: OEMEthInit() Netmask    : %srn", inet_ntoa(pDriverGlobals->eth.SubnetMask));
  187. // Setup SYSINTR used for KITL NIC.
  188. //
  189. if (pDriverGlobals->misc.EbootDevice == DOWNLOAD_DEVICE_PCMCIA)
  190. pAdapter->SysIntrVal    = SYSINTR_PCMCIA_LEVEL;
  191. else
  192. pAdapter->SysIntrVal    = SYSINTR_ETHER;
  193. pAdapter->DHCPLeaseTime = DEFAULT_DHCP_LEASE;
  194.     pAdapter->EdbgFlags     = pDriverGlobals->eth.EdbgFlags;
  195. #ifdef IMGSHAREETH
  196.     VBridgeInit();
  197.     VBridgeKSetLocalMacAddress((char *)pAdapter->Addr.wMAC);
  198. #endif // IMGSHAREETH.
  199. return(TRUE);
  200. }
  201. /* OEMEthEnableInts
  202.  *
  203.  *  Turn on HW interrupts.  
  204.  */
  205. void
  206. OEMEthEnableInts()
  207. {
  208. if (pfnEDbgEnableInts)
  209. {
  210. pfnEDbgEnableInts();
  211. }
  212. }
  213. /* OEMEthDisableInts
  214.  *
  215.  *  Disable HW interrupts.
  216.  */
  217. void
  218. OEMEthDisableInts()
  219. {
  220. if (pfnEDbgDisableInts)
  221. {
  222. pfnEDbgDisableInts();
  223. }
  224. }
  225. /* OEMEthISR
  226.  *
  227.  *    ISR routine, called by EDBG IST when Ethernet controller interrupts. Also
  228.  *    called in polling mode, to check for received data.
  229.  *
  230.  * Return Value:
  231.  *    Return bitmask indicating which interrupts are pending.  Currently, the ones
  232.  *    that EDBG cares about are the following (others should be handled internally):
  233.  *       INTR_TYPE_RX   -- Receive interrupt. IST will call into GetFrame to read data.
  234.  */
  235. DWORD
  236. OEMEthISR()
  237. {
  238. #ifdef IMGSHAREETH
  239.     ProcessVMiniSend();
  240. #endif // IMGSHAREETH.
  241.     return (pfnEDbgGetPendingInts());
  242. }
  243. /* OEMEthGetFrame
  244.  *
  245.  *   Check to see if a frame has been received, and if so copy to buffer. An optimization
  246.  *   which may be performed in the Ethernet driver is to filter out all received broadcast
  247.  *   packets except for ARPs.  This is done in the SMC9000 driver.
  248.  *
  249.  * Return Value:
  250.  *    Return TRUE if frame has been received, FALSE if not.
  251.  */
  252. BOOL
  253. OEMEthGetFrame(
  254.     BYTE *pData,       // OUT - Receives frame data
  255.     UINT16 *pwLength)  // IN  - Length of Rx buffer
  256.                        // OUT - Number of bytes received
  257. {
  258. #ifdef IMGSHAREETH
  259. BOOL bStatus;
  260. BOOL bTaken;
  261. UINT16 wOriginalLength = *pwLength;
  262. ProcessVMiniSend();
  263. while (1)
  264. {
  265. *pwLength = wOriginalLength;
  266. bStatus  = pfnEDbgGetFrame(pData, pwLength);
  267. if (bStatus)
  268. {
  269. ////////////////////////////////////////////////////////////////////
  270. VBridgeKIndicateOneRxBuffer(pData, *pwLength, FALSE, &bTaken);
  271. if (!bTaken)
  272. return bStatus;
  273. }
  274. else
  275. break;
  276. }
  277.     return (FALSE);
  278. #else
  279.    return pfnEDbgGetFrame(pData, pwLength);
  280. #endif  // IMGSHAREETH.
  281.    
  282. }
  283. /* OEMEthSendFrame
  284.  *
  285.  *   Send Ethernet frame.  
  286.  *
  287.  *  Return Value:
  288.  *   TRUE if frame successfully sent, FALSE otherwise.
  289.  */
  290. BOOL
  291. OEMEthSendFrame(
  292.     BYTE *pData,     // IN - Data buffer
  293.     DWORD dwLength)  // IN - Length of buffer
  294. {
  295.     int retries = 0;
  296.     while (retries++ < 4) {
  297.         if (!pfnEDbgSendFrame(pData, dwLength))
  298. {
  299. #ifdef IMGSHAREETH
  300. ProcessVMiniSend();
  301. #endif //IMGSHAREETH
  302.             return TRUE;
  303. }
  304.         else
  305.             EdbgOutputDebugString("!OEMEthSendFrame failure, retry %un",retries);
  306.     }
  307.     return FALSE;
  308. }
  309. /* OEMEthQueryClientInfo
  310.  *
  311.  *    Return address information for default ethernet services, plus a buffer pool to use
  312.  *    for formatting and receiving EDBG packets (single buffer pool specified, divided in
  313.  *    two for Rx and Tx buffers).  By specifying a smaller window size, less memory can be
  314.  *    used (but the protocol will be less efficient...).  The amount of memory required per
  315.  *    client is (2*WindowSize*1500) bytes.
  316.  *
  317.  *    For Odo, we reserve 3 buffer pools worth of memory in the bib file, based on the IMGEBOOT
  318.  *    flag being set.
  319.  *
  320.  *  Return Value:
  321.  *    If client can be configured, return TRUE and fill in addressing and buffer info. Otherwise
  322.  *    return FALSE.  For Odo, configure clients based on the flags set by Eshell (received in the
  323.  *    JUMPIMG command by eboot, and placed in the uninitalized driver globals section).
  324.  */
  325. /* OEMEthQueryClientInfo
  326.  *
  327.  *    Return address information for default ethernet services, plus a buffer pool to use
  328.  *    for formatting and receiving EDBG packets (single buffer pool specified, divided in
  329.  *    two for Rx and Tx buffers).  By specifying a smaller window size, less memory can be
  330.  *    used (but the protocol will be less efficient...).  The amount of memory required per
  331.  *    client is (2*WindowSize*1500) bytes.
  332.  *
  333.  *    For Odo, we reserve 3 buffer pools worth of memory in the bib file, based on the IMGEBOOT
  334.  *    flag being set.
  335.  *
  336.  *  Return Value:
  337.  *    If client can be configured, return TRUE and fill in addressing and buffer info. Otherwise
  338.  *    return FALSE.  For Odo, configure clients based on the flags set by Eshell (received in the
  339.  *    JUMPIMG command by eboot, and placed in the uninitalized driver globals section).
  340.  */
  341. BOOL
  342. OEMEthQueryClientInfo(
  343.     UCHAR Service,         // IN - Service ID (one of EDBG_SVC defs from ethdbg.h).
  344.     EDBG_ADDR *pPeerAddr,  // OUT -Filled in with the peer Ether/IP address and UDP port number.
  345.     PUCHAR  pWindowSize,   // OUT -Filled in with the client window size.
  346.     PUCHAR *ppBufferPool)  // OUT -Filled in with the packet buffer pool address.
  347. {
  348.     
  349.     // We use the default window size (8) for all services
  350.     *pWindowSize = EDBG_WINDOW_SIZE;
  351.     
  352.     switch (Service)
  353.     {
  354.         // Check the flag in driver globals (set by eboot when it receives the JUMPIMG command)
  355.         case EDBG_SVC_DBGMSG:
  356.             if (! (pDriverGlobals->eth.etherFlags & EDBG_FL_DBGMSG)) {
  357.                 EdbgOutputDebugString("FAIL : EDBG_SVC_DBGMSG n");
  358.                 return FALSE;
  359.             }
  360.             memcpy(pPeerAddr, &pDriverGlobals->eth.DbgHostAddr,sizeof(EDBG_ADDR));
  361.             *ppBufferPool = (UCHAR *)EDBG_PHYSICAL_MEMORY_START;
  362.             EdbgOutputDebugString("Querying to ethernet for debug messages, host: %s, port: %d rnn",
  363.                                     inet_ntoa(pDriverGlobals->eth.DbgHostAddr.dwIP),
  364.                                     ntohs(pDriverGlobals->eth.DbgHostAddr.wPort));
  365.             break;
  366.         case EDBG_SVC_PPSH:
  367.             if (! (pDriverGlobals->eth.etherFlags & EDBG_FL_PPSH)) {
  368.                 EdbgOutputDebugString("FAIL : EDBG_SVC_PPSH n");
  369.                 return FALSE;
  370.             }
  371.             memcpy(pPeerAddr, &pDriverGlobals->eth.PpshHostAddr,sizeof(EDBG_ADDR));
  372.             *ppBufferPool = (UCHAR *)EDBG_PHYSICAL_MEMORY_START + EDBG_DFLT_BUFFER_POOL_SIZE;
  373.             EdbgOutputDebugString("Querying ethernet for PPSH, host: %s, port: %d rnn",
  374.                                     inet_ntoa(pDriverGlobals->eth.PpshHostAddr.dwIP),
  375.                                     ntohs(pDriverGlobals->eth.PpshHostAddr.wPort));  
  376.             break;
  377.         case EDBG_SVC_KDBG:
  378.             if (! (pDriverGlobals->eth.etherFlags & EDBG_FL_KDBG)) {
  379.                 EdbgOutputDebugString("FAIL : EDBG_SVC_KDBG n");
  380.                 return FALSE;
  381.             }
  382.             memcpy(pPeerAddr, &pDriverGlobals->eth.KdbgHostAddr,sizeof(EDBG_ADDR));    
  383.             *ppBufferPool = (UCHAR *)EDBG_PHYSICAL_MEMORY_START + 2*EDBG_DFLT_BUFFER_POOL_SIZE;
  384. EdbgOutputDebugString("Querying ethernet for KDBG, host: %s, port: %d rnn",
  385.                                     inet_ntoa(pDriverGlobals->eth.KdbgHostAddr.dwIP),
  386.                                     ntohs(pDriverGlobals->eth.KdbgHostAddr.wPort));
  387.             break;            
  388.         default:
  389.             return FALSE;
  390.     }
  391.     return TRUE;
  392. }
  393. /* OEMEthGetSecs
  394.  *
  395.  *  Return a count of seconds from some arbitrary time (the absolute value is not important,
  396.  *  so long as it increments appropriately).
  397.  */
  398. DWORD 
  399. OEMEthGetSecs( void ) 
  400. {
  401.     SYSTEMTIME st;
  402. DWORD ti;
  403.     OEMGetRealTime( &st );
  404. ti = ((60UL * (60UL * (24UL * (31UL * st.wMonth + st.wDay) + st.wHour) + st.wMinute)) + st.wSecond);
  405. return ti;
  406. }
  407. #ifdef IMGSHAREETH
  408. //
  409. //  These functions are only needed if vmini is in.
  410. //  i.e. if IMGSHAREETH is set.
  411. //
  412. ////////////////////////////////////////////////////////////////////////////////
  413. //  OEMEthSetFilter()
  414. //
  415. //  Description:
  416. //      
  417. //      This function is used by VMINI to inform the underlying ethernet
  418. //      library on the filtering mode it requires.
  419. //
  420. //  Arguments:
  421. //
  422. //      pdwRequestedFilter  :: The requested filter.
  423. //                             Identical constants have been added in 
  424. //                             that mimics NDIS_PACKET_TYPE_XXX constants.
  425. //
  426. //  Return Value:
  427. //
  428. //      TRUE    :: if we can set the filter.
  429. //      FALSE   :: otherwise..
  430. //
  431. //
  432. //  Note:
  433. //      
  434. //      As a minimum to get vmini to work, we need to support
  435. //      PACKET_TYPE_DIRECTED 
  436. //      PACKET_TYPE_BROADCAST
  437. //
  438. //  
  439. BOOL
  440. OEMEthCurrentPacketFilter(PDWORD pdwRequestedFilter)
  441. {
  442.     
  443.     //EdbgOutputDebugString(
  444.         //"OEMEthCurrentPacketFilter set to [0x%x]rn",
  445.         //*pdwRequestedFilter);   
  446.     
  447.     //
  448.     //  Note that we can't do it immediately here, since we are called 
  449.     //  by user mode code.
  450.     //  So what we do is to set the flag here for the kernel mode code
  451.     //  to pick up.
  452.     //
  453.     
  454.     dwFilter   = *pdwRequestedFilter;
  455.     bNewFilter = TRUE;
  456.     return TRUE;
  457. }   //  OEMEthCurrentPacketFilter()
  458. ////////////////////////////////////////////////////////////////////////////////
  459. //  OEMEthMulticastList()
  460. //
  461. //  Description:
  462. //
  463. //      This function is used by VMINI to inform the underlying ethernet 
  464. //      library on multicast addresses that winsock app is interested in.
  465. //      
  466. //  Arguments:
  467. //
  468. //      pucMulticastAddressList :: Pointer to an array of multicast addresses.
  469. //      dwNoOfAddresses         :: Number of addresses passed in to us.
  470. //
  471. //  Return Value:
  472. //      
  473. //      TRUE    ::  if we can set the underlying edbg ethernet libary to start
  474. //                      filtering on these multicast addresses.
  475. //
  476. //      FALSE   ::  otherwise.
  477. //
  478. BOOL
  479. OEMEthMulticastList(PUCHAR  pucMulticastAddressList, DWORD  dwNoOfAddresses)
  480. {
  481.     //
  482.     //  This platform does not support multicast yet..
  483.     //
  484.     DWORD   i;
  485.     BOOL    bReturnValue;
  486.     EdbgOutputDebugString(
  487.         "OEMEthMulticastList():: No of Entries [%d]rn",
  488.             dwNoOfAddresses);   
  489.     //
  490.     //  We can check how many entries an adapter that are attached to 
  491.     //  can support.
  492.     //  To make things simple, we just assume we can support minimum that
  493.     //  vmini thinks we support (i.e. 8).
  494.     //
  495.     if (dwNoOfAddresses > 8)
  496.     {
  497.         //
  498.         //  This should never happen, since VMINI is known to support
  499.         //  8 entries only... 
  500.         //
  501.         
  502.         EdbgOutputDebugString(
  503.             "Multicast list requeste [%d] > 8 !!!rn",
  504.             dwNoOfAddresses);
  505.         return FALSE;
  506.     }
  507.     //
  508.     //   8 entries, 6 bytes each..
  509.     //
  510.     memset(
  511.         ucMultiAddr,
  512.         0x00,
  513.         8 * 6);     
  514.     //
  515.     //  6 bytes per entry..
  516.     //
  517.     memcpy(
  518.         ucMultiAddr,
  519.         pucMulticastAddressList,
  520.         dwNoOfAddresses * 6);
  521.     for (i = 0 ; i < dwNoOfAddresses ; i++)
  522.     {
  523.         EdbgOutputDebugString(
  524.             "[%d] : %x - %x - %x - %x - %x - %xrn",
  525.             i,
  526.             pucMulticastAddressList[6*i+0],
  527.             pucMulticastAddressList[6*i+1],
  528.             pucMulticastAddressList[6*i+2],
  529.             pucMulticastAddressList[6*i+3],
  530.             pucMulticastAddressList[6*i+4],
  531.             pucMulticastAddressList[6*i+5]);
  532.     }
  533.     //
  534.     //  We are in KernelIOCTL call, some h/w requires the 
  535.     //  setting of the MULTICAST addresses in interrupt service routine.
  536.     //  So we will do it later, and mark it so the ISR will know about it..
  537.     //  Note:
  538.     //  Order is important here.    Don't set bNewMulticast to true until
  539.     //  all the entries and the dwNoOfEntry are properly set up.
  540.     //  This is because we can be switched out in the middle of 
  541.     /// KernelIOCTL!!
  542.     //
  543.     
  544.     dwNoOfEntry   = dwNoOfAddresses;
  545.     bNewMulticast = TRUE;       
  546.     bReturnValue  = TRUE;
  547.     
  548.     EdbgOutputDebugString(
  549.         "OEMEthMulticastList returning [%d]rn",
  550.         bReturnValue);
  551.     return bReturnValue;
  552.     
  553. }   //  OEMEthMulticastList()
  554. #endif