⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lan91c111.c

📁 lwip tcp/ip 协议栈 adsp BF533 DSP 移植 用 visual dsp++ 编译
💻 C
📖 第 1 页 / 共 4 页
字号:

   // Final clock bit (tristate)
   bits[clk_idx++] = 0;

   // Save the current bank
   oldBank = _inpw(BSR_REG);
   // Select bank 3
   _outpw(BSR_REG, 3);

   // Get the current MII register value
   mii_reg = _inpw(MII_REG);

   // Turn off all MII Interface bits
   mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);

   // Clock all cycles
   for (i = 0; i < sizeof bits; ++i)
   {
      // Clock Low - output data
      _outpw(MII_REG, mii_reg | bits[i] );
      // Clock Hi - input data
      _outpw(MII_REG, mii_reg | bits[i] | MII_MCLK);
   }

   // Return to idle state
   // Set clock to low, data to low, and output tristated
   _outpw(MII_REG, mii_reg);

   // Restore original bank select
   _outpw(BSR_REG, oldBank );
   return;
}

/******************************************************************************
 * Reads a word through the MII interface off the PHY
 *****************************************************************************/
static unsigned short LAN91C111_read_phy_register(unsigned char phyreg)
{
   volatile unsigned char bits[64];
   volatile unsigned char bmask;
   volatile unsigned short oldBank, mii_reg, phydata, wmask;
   volatile int i, clk_idx = 0;
   volatile int input_idx;

   // Prepare bit stream for PHY
   // 32 consecutive ones on MDO to establish sync
   for (i = 0; i < 32; ++i)
      bits[clk_idx++] = MII_MDOE | MII_MDO;

   // Start code <01>
   bits[clk_idx++] = MII_MDOE;
   bits[clk_idx++] = MII_MDOE | MII_MDO;
   // Read command <10>
   bits[clk_idx++] = MII_MDOE | MII_MDO;
   bits[clk_idx++] = MII_MDOE;
   // internal PHY address = <00000>
   bits[clk_idx++] = MII_MDOE;
   bits[clk_idx++] = MII_MDOE;
   bits[clk_idx++] = MII_MDOE;
   bits[clk_idx++] = MII_MDOE;
   bits[clk_idx++] = MII_MDOE;

   // Output the phy register number, msb first
   bmask = 0x10;
   for (i = 0; i < 5; ++i)
   {
      if (phyreg & bmask)
         bits[clk_idx++] = MII_MDOE | MII_MDO;
      else
         bits[clk_idx++] = MII_MDOE;
      // Shift to next lowest bit
      bmask >>= 1;
   }

   // Tristate and turnaround (2 bit times)
   bits[clk_idx++] = 0;

   // Input starts at this bit time
   input_idx = clk_idx;

   // Will input 16 bits
   for (i = 0; i < 16; ++i)
      bits[clk_idx++] = 0;

   // Final clock bit
   bits[clk_idx++] = 0;

   // Save the current bank
   oldBank = _inpw(BSR_REG);
   // Select bank 3
   _outpw(BSR_REG, 3);

   // Get the current MII register value
   mii_reg = _inpw(MII_REG);

   // Turn off all MII Interface bits
   mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);

   // Clock all 64 cycles
   for (i = 0; i < clk_idx /*sizeof bits*/; ++i)
   {
      // Clock Low - output data
      _outpw(MII_REG, mii_reg | bits[i]);
      // Clock Hi - input data
      _outpw(MII_REG, mii_reg | bits[i] | MII_MCLK);
      bits[i] |= _inpw(MII_REG) & MII_MDI;
   }

   // Return to idle state
   // Set clock to low, data to low, and output tristated
   _outpw(MII_REG, mii_reg);

   // Restore original bank select
   _outpw(BSR_REG, oldBank);

   // Recover input data
   phydata = 0;
   for (i = 0; i < 16; ++i)
   {
      phydata <<= 1;
      if (bits[input_idx++] & MII_MDI)
         phydata |= 0x0001;
   }
   return (phydata);
}

/******************************************************************************
 * IRQ Sub Function: SMC_RCV for the LAN91C111
 *
 * Purpose:
 *  There is a packet waiting to be read from 91C111
 *
 * Actions:
 *  - Read the status
 *  - If an error, record it
 *  - otherwise, read in the packet
 *
 *****************************************************************************/
static void LAN91C111_rcv(ADI_ETHER_LAN91C111_DATA *dev)
{
   volatile int     i, packet_number,chip_id;
   volatile unsigned short    status;
   volatile unsigned short    packet_length;
#ifndef USE_16_BIT
    unsigned int  volatile *data;
#else
    unsigned short volatile *data;
#endif
	unsigned short *elnth;

   // bank 2 already opened by main IRQ entry

   packet_number = _inpw(FIFO_REG);

   if ( packet_number & RXFIFO_REMPTY )
   {
      // we got called , but nothing was on the FIFO
      // so, just leave
      dma_relinquish();
      LAN91C111_enable_int( (SMC_INTERRUPT_MASK) );
      return;
   }

   //  start reading from the start of the packet
   _outpw(PTR_REG, PTR_READ | PTR_RCV | PTR_AUTOINC);

   // First two words are status and packet_length
   status          = _inpw(DATA_REG);
   packet_length   = _inpw(DATA_REG);

   // store for debug
   dev->LAN_state.rpsize = packet_length;

   // max 2k packets over Ethernet
   packet_length &= 0x07ff;  /* mask off top bits */

   if ( !(status & RS_ERRORS ) )
   {
      ADI_ETHER_BUFFER *tmp_q_ele;

#if LAN91C111_LOG_NET_ERRORS
      if ( status & RS_MULTICAST ) dev->Stats->cEMAC_RX_CNT_MULTI++;
#endif

      // collect the packet
      // first two words already read: status and packet size
	  if((status & 0x1000))
	      packet_length -= 5;   //## used to be 4
	  else
	  {
		  // Get the chip id and fix the hardware anomaly rev-A
		  _outpw(BSR_REG,3);
		  chip_id = _inpw(REV_REG);
		 if( ((chip_id & 0x0F00) == 9) && ((chip_id & 0xF000) == 0) ) 
			 packet_length -= 4;
		 else
			 packet_length -= 6;
		 _outpw(BSR_REG, 2);
	  }

     
      tmp_q_ele = dev->m_RxEnqueuedHead;

      if (tmp_q_ele != NULL) {
		  elnth = (unsigned short *)tmp_q_ele->Data;
		  *elnth = (packet_length + 4);	// add on 4 bytes for the CRC (even if we dont store it)
		  tmp_q_ele->ProcessedElementCount = (*elnth+2+tmp_q_ele->ElementWidth-1)/tmp_q_ele->ElementWidth;
      }

      if ((tmp_q_ele)&&(tmp_q_ele->ProcessedElementCount <= tmp_q_ele->ElementCount))
      {
         //store packet length
         // assign pointer
         data = (void *)( ((char *)tmp_q_ele->Data) +2);
      }
      else
      {
#if LAN91C111_LOG_NET_ERRORS
		 if (tmp_q_ele) {
         	dev->Stats->cEMAC_RX_CNT_LONG++;
		 } else {
         	dev->Stats->cEMAC_RX_CNT_LOST++;
		 }
#endif
         // drop packet
         //  good or bad, delete this packet */
   	while ( _inpw(MMU_CMD_REG ) & MC_BUSY );
         _outpw(MMU_CMD_REG, MC_RELEASE);
		 dma_relinquish();
	 LAN91C111_enable_int( (SMC_INTERRUPT_MASK) );

         return;
      }
      // dma is already grabbed in the interrupt handler.
	  dma_initiate_transfer((unsigned long)DATA_REG,(unsigned long)data,packet_length >> 1,DMA_DIR_RX); 
#if LAN91C111_LOG_NET_ERRORS
      dev->Stats->cEMAC_RX_CNT_OK++;
#endif
   }
   else
   {
	   dma_relinquish();
	   LAN91C111_enable_int( (SMC_INTERRUPT_MASK) );
#if LAN91C111_LOG_NET_ERRORS
      //cptr->stats.rx_errors++;
      if ( status & RS_ALGNERR )  dev->Stats->cEMAC_RX_CNT_ALIGN++; 
      if ( status & (RS_TOOSHORT|RS_TOOLONG)) dev->Stats->cEMAC_RX_CNT_LONG++;
      if ( status & RS_BADCRC)    dev->Stats->cEMAC_RX_CNT_FCS++;
#endif
   while ( _inpw(MMU_CMD_REG ) & MC_BUSY );

   //  good or bad, delete this packet */
   _outpw(MMU_CMD_REG, MC_RELEASE);
   }

   return;
}

/******************************************************************************
 * TX IRQ Error Handler Function for SMSC91C111
 *
 * Purpose:  Handle the transmit error message.
 *  This is only called when an TX error occured because of
 *  the AUTO_RELEASE mode.
 *
 * Actions:
 *  - Save pointer and packet no
 *  - Get the packet no from the top of the queue
 *  - check if it's valid ( if not, is this an error??? )
 *  - read the status word
 *  - record the error
 *  - ( resend?  Not really, since we don't want old packets around )
 *  - Restore saved values
 *
 *****************************************************************************/
static void LAN91C111_tx(ADI_ETHER_LAN91C111_DATA *dev)
{
   volatile unsigned char saved_packet;
   volatile unsigned char packet_no;
   volatile unsigned short tx_status;

   // bank 2 already opened by main IRQ entry
   saved_packet = _inp(PN_REG);
   packet_no = _inpw(FIFO_REG);
   packet_no &= 0x7F;

   // If the TX FIFO is empty then nothing to do
   if ( packet_no & TXFIFO_TEMPTY )
      return;

   // select this as the packet to read from
   _outp(PN_REG, packet_no);

   // read the first word (status word) from this packet
   _outpw(PTR_REG, PTR_AUTOINC|PTR_READ);

   // read tx status
   tx_status = _inpw(DATA_REG );

#if LAN91C111_LOG_NET_ERRORS
   dev->Stats->cEMAC_TX_CNT_ABORT++;
   if ( tx_status & TS_LOSTCAR ) dev->Stats->cEMAC_TX_CNT_CRS++;
   if ( tx_status & TS_LATCOL  ) dev->Stats->cEMAC_TX_CNT_LATE++;
#endif

   // re-enable transmit
   _outpw(BSR_REG, 0);
   _outpw(TCR_REG, _inpw(TCR_REG) | TCR_TXENA);

   // kill the packet
   _outpw(BSR_REG, 2);
   _outpw(MMU_CMD_REG, MC_FREEPKT);

   // one less packet waiting for me
   dev->LAN_state.packets_waiting--;

   // Don't change Packet Number Reg until busy bit is cleared
   while ( _inpw(MMU_CMD_REG) & MC_BUSY );

   _outp(PN_REG, saved_packet);

   return;
}

/******************************************************************************
 * IRQ Sub Function: smc_phy_interrupt for the LAN91C111
 *
 * Purpose:
 *  Handle interrupts relating to PHY register 18.
 *
 * Actions:
 *  Log last Phy18 Interrupt Source
 *
 *****************************************************************************/
static void LAN91C111_phy_interrupt(ADI_ETHER_LAN91C111_DATA *dev )
{
   volatile unsigned short phy18;

   while (1)
   {
      // Read PHY Register 18, Status Output
      phy18 = LAN91C111_read_phy_register(PHY_INT_REG);

      // Exit if no more changes
      if (phy18 == dev->LAN_state.lastPhy18)
         break;

      // Update the last phy 18 variable
      dev->LAN_state.lastPhy18 = phy18;
   } // end while
}
/******************************************************************************
 * LANC91C111: LAN91C111_hardware_send_packet
 *
 * Purpose:
 *  This sends the actual packet to the SMC9xxx chip.
 *
 *  - First, see if a pending packet is available.
 *      this should NOT be called if there is none
 *  - Find the packet number that the chip allocated
 *  - Point the data pointers at it in memory
 *  - Set the length word in the chip's memory
 *  - Dump the packet to chip memory
 *  - Check if a last byte is needed ( odd length packet )
 *      if so, set the control flag right
 *  - Tell the LAn 91C111 to send it
 *  - Enable the transmit interrupt, so I know if it failed
 *  - Free the packet data if I actually sent it.
 *
 *****************************************************************************/
static unsigned char LAN91C111_hardware_send_packet(ADI_ETHER_LAN91C111_DATA* dev)
{
   volatile int     i;
   volatile unsigned char    packet_no;
   volatile unsigned short   length;
#ifndef USE_16_BIT
    unsigned int  volatile  *buf;
#else
    unsigned short volatile *buf;
#endif
   unsigned short	 *elnth;
   unsigned short	 lnth_first;
   ADI_ETHER_BUFFER  *bf = dev->m_TxEnqueuedHead;

   // get pointer
#ifndef USE_16_BIT
   buf = (unsigned int *)(((char *)bf->Data)+2); // first 2 bytes has length
#else
   buf = (unsigned short *)(((char *)bf->Data)+2);
#endif

   // the length is held in the first two bytes of the 'frame', the ElementCount says the number of elements in the first buffer
   
   // get length
   elnth = (unsigned short *)bf->Data;
   length = *elnth;
   lnth_first = bf->ElementCount*bf->ElementWidth - 2; 

   length = ETH_ZLEN < length ? length : ETH_ZLEN;
   lnth_first = ETH_ZLEN < lnth_first ? lnth_first : ETH_ZLEN;

   // If I get here, I _know_ there is a packet slot waiting for me
   packet_no = _inp(AR_REG);

   // or isn't there?  BAD CHIP
   if ( packet_no & AR_FAILED ) return LAN91C111_TX_ERROR;

   // we have a packet address, so tell the card to use it
   _outp(PN_REG, packet_no);

   // point to the beginning of the packet
   _outpw(PTR_REG, PTR_AUTOINC);

   // send the packet length ( +6 for status, length and ctl byte )
   //   and the status word ( set to zeros ) */

#ifndef USE_16_BIT
   _outpd(DATA_REG, (length+6) << 16);
#else
   _outpw(DATA_REG, 0);
   _outpw(DATA_REG, (length+6));
#endif

   if(dev->Cache)
   {
	FlushArea((char*)buf,((char*)buf + lnth_first));
   }
   dma_initiate_transfer((unsigned long)buf,DATA_REG,lnth_first >> 1,DMA_DIR_TX); 
   // transfer second buffer
   if(lnth_first < length)
   {
       buf = bf->PayLoad;
       length = length - lnth_first;
       dma_initiate_transfer((unsigned long)buf,DATA_REG,length >> 1,DMA_DIR_TX); 

   }

   return LAN91C111_TX_SUCCESS;
}
/******************************************************************************
 * LANC91C111: LAN91C111_dma_send_packet()
 *
 * Purpose:
 *    Attempt to allocate memory for a packet, if chip-memory is not
 *    available, then tell the card to generate an interrupt when it
 *    is available.
 *****************************************************************************/
static unsigned char LAN91C111_dma_send_packet(ADI_ETHER_LAN91C111_DATA *dev)
{
   volatile unsigned short    length;
   volatile unsigned short    numPages;
   volatile unsigned short    time_out;
   volatile unsigned short    status;
   volatile unsigned short    retry_count=0;
   unsigned short	 *elnth;

   // the length is held in the first two bytes of the 'frame', the ElementCount says the number of elements in the first buffer
   
   // get length
   elnth = (unsigned short *)dev->m_TxEnqueuedHead->Data;
   length = *elnth;

   // set length
   length = ETH_ZLEN < length ? length : ETH_ZLEN;

   // clear allocation flag
   dev->LAN_state.alloc_success = 0;

   /*
    * The MMU wants the number of pages to be the number of 256 bytes
    * 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
    *
    * Pkt size for allocating is data length +6 (for additional status
    * words, length and ctl!)
    *
    * If odd size then last byte is included in this header.
    */
   numPages = ((length & 0xfffe) + 6);
   numPages >>= 8; // Divide by 256

   if (numPages > 7 )
   {
      // way too big packet, this is an error
      // free packet
      return LAN91C111_TX_ERROR;
   }
   // either way, a packet is waiting now
   dev->LAN_state.packets_waiting++;

   // now, try to allocate the memory */
   _outpw(BSR_REG, 2);
   _outpw(MMU_CMD_REG, MC_ALLOC | numPages);
   /*
    * Performance Hack
    *
    * wait a short amount of time.. if I can allocate a packet now, I set
    * the flag now.  Otherwise, I enable an interrupt and wait for one to be
    * available.
    *
    */
TX_RETRY:
   time_out = 200; // number of iterations
   do
   {
      status = _inp(INT_REG);
      if ( status & IM_ALLOC_INT )
      {
         /* acknowledge the interrupt */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -