📄 lan91c111.c
字号:
/*
* File: lan91c11.c
* Purpose: Device driver for the SMSC LAN91C111
*
* Notes:
*
*/
#include "src/include/dbug.h"
#include "src/uif/net/net.h"
#include "src/include/dev/lan91c111.h"
/********************************************************************/
static int lan91c111_reset (NIF *);
static void lan91c111_start (NIF *);
static void lan91c111_stop (NIF *);
static int lan91c111_send (NIF *, HWA_ADDR_P, HWA_ADDR_P, uint16, NBUF*);
static void lan91c111_receive (void *, NIF *);
static uint16 lan91c111_read_phy_reg(uint8, uint8);
static void lan91c111_write_phy_reg(uint8, uint8, uint16);
static void lan91c111_phy_configure(void);
static void lan91c111_phy_shutdown(void);
/********************************************************************/
int
lan91c111_init (NIF *nif, int vector)
{
nif_init(nif, "lan91c111");
nif->vector = vector;
nif->hwa_size = 6;
nif->mtu = 1500;
nif->reset = lan91c111_reset;
nif->start = lan91c111_start;
nif->stop = lan91c111_stop;
nif->send = lan91c111_send;
nif->receive = (void *)&lan91c111_receive;
nif->rx_alloc = nbuf_rx_allocate;
nif->tx_alloc = nbuf_tx_allocate;
nif->rx_free = nbuf_rx_release;
nif->tx_free = nbuf_tx_release;
return TRUE;
}
/********************************************************************/
static int
lan91c111_reset (NIF *nif)
{
int i;
(void)nif;
/* This resets the registers mostly to defaults, but doesn't
affect EEPROM. That seems unnecessary */
SMC_SELECT_BANK(0);
LAN91C111_RCR = LAN91C111_RCR_SOFTRST;
/* Setup the Configuration Register */
/* This is necessary because the CONFIG_REG is not affected */
/* by a soft reset */
SMC_SELECT_BANK(1);
LAN91C111_CFGR |= LAN91C111_CFGR_DEFAULT;
SMC_SELECT_BANK(0);
/* this should pause enough for the chip to be happy */
for( i=0 ; i < 10000; i++);
/* Disable transmit and receive functionality */
LAN91C111_RCR = LAN91C111_RCR_CLEAR;
LAN91C111_TCR = LAN91C111_TCR_CLEAR;
/* set the control register to automatically
release successfully transmitted packets, to make the best
use out of our limited memory */
SMC_SELECT_BANK(1);
LAN91C111_CTRLR |= LAN91C111_CTRLR_AUTO_RELEASE;
/* Reset the MMU */
SMC_SELECT_BANK(2);
LAN91C111_MMCR = LAN91C111_MMCR_RESET;
while (LAN91C111_MMCR & LAN91C111_MMCR_BUSY);
/* Disable all interrupts */
LAN91C111_IMR = 0;
return TRUE;
}
/********************************************************************/
static void
lan91c111_start (NIF *nif)
{
/* Configure the Phy */
lan91c111_phy_configure();
/* now, enable interrupts */
SMC_SELECT_BANK(2);
LAN91C111_IMR = (LAN91C111_IMR_RCV_INT | LAN91C111_IMR_TX_INT);
SMC_SELECT_BANK(0);
/* see the header file for options in TCR/RCR DEFAULT*/
/* enable transmit and recieve */
LAN91C111_RCR = LAN91C111_RCR_DEFAULT;
LAN91C111_TCR = LAN91C111_TCR_DEFAULT;
/* Set Hardware Address */
SMC_SELECT_BANK(1);
LAN91C111_IAR01 = (uint16)(((nif->hwa[0])<<8) | nif->hwa[1]);
LAN91C111_IAR23 = (uint16)(((nif->hwa[2])<<8) | nif->hwa[3]);
LAN91C111_IAR45 = (uint16)(((nif->hwa[4])<<8) | nif->hwa[5]);
}
/********************************************************************/
static void
lan91c111_stop (NIF *nif)
{
(void)nif;
SMC_SELECT_BANK(0);
/* Disable transmit and receive functionality */
LAN91C111_RCR = LAN91C111_RCR_CLEAR;
LAN91C111_TCR = LAN91C111_TCR_CLEAR;
/* Shutdown Phy */
lan91c111_phy_shutdown();
}
/********************************************************************/
static int
lan91c111_send (NIF *nif,
HWA_ADDR_P eth_dest,
HWA_ADDR_P eth_src,
uint16 eth_type,
NBUF *pNbuf)
{
/*
* This routine transmits one frame. This routine only accepts
* 6-byte Ethernet addresses.
*/
uint32 mod_data_length, index, success, timeout, j;
uint16 numPages, isr;
/*
* The format of an Ethernet frame looks like:
*
* 00 01 02 03 04 05
* +--+--+--+--+--+--+
* | | | | | | | destination ethernet address
* +--+--+--+--+--+--+
* 06 07 08 09 0A 0B
* +--+--+--+--+--+--+
* | | | | | | | source ethernet address
* +--+--+--+--+--+--+
* 0C 0D
* +--+--+
* | | | type (IP = 0x0800)
* +--+--+
* 0E 0F ...
* +--+--+--+--+--+--+
* | | | | | | | data (min of 46 octets, max of 1500)
* +--+--+--+--+--+--+
*
* +--+--+--+--+
* | | | | | 32-bit CRC (generated by hardware)
* +--+--+--+--+
*
* NOTE: The NS8390 takes care of pre-amble, and is programmed
* to generate the 32-bit CRC.
*
* NOTE: The minimum size of data transmitted in an ethernet
* frame is 46 octets (bytes).
*/
/*
* Check for valid length of data.
*/
if ((pNbuf->length > nif->mtu) || (pNbuf->length <= 0))
return 0;
/*
* Modify Data Length if necessary
*/
mod_data_length = pNbuf->length;
/*
* Align data length to 16-bits
*/
if (mod_data_length & 0x0001)
++mod_data_length;
/*
* Verify data length >= minimum Ethernet packet size
*/
if (mod_data_length < ETH_MIN_SIZE)
mod_data_length = ETH_MIN_SIZE;
/*
* During the course of loading a frame into the FIFO, we
* can not have interrupts occur, especially those from the
* receiver!
*/
board_irq_disable();
/*
* Allocate memory for TX
*/
SMC_SELECT_BANK(2);
numPages = (uint16)(mod_data_length >> 8);
if (numPages > 7 ) /* Too big */
return FALSE;
LAN91C111_MMCR = (uint16)(LAN91C111_MMCR_ALLOC | (numPages <<8));
while (LAN91C111_MMCR & LAN91C111_MMCR_BUSY)
;
/*
* Wait for allocation to complete
*/
timeout = 100;
do {
isr = LAN91C111_ISR;
if (isr & LAN91C111_ISR_ALLOC_INT)
{
// allocation complete
LAN91C111_ISR = (isr & 0x00FF) | LAN91C111_ISR_ALLOC_INT;
break;
}
/* delay some time */
for( j=0 ; j<100 ; j++);
} while ( -- timeout );
if (timeout < 1)
{
printf("SMSC MMU memory allocation timeout");
return FALSE;
}
/*
* Copy the TX packet number into the Packet Number Register
*/
LAN91C111_PNR = (uint16)((LAN91C111_ARR & 0x3F)<<8);
/*
* Check to see if FIFO is emtpy
*/
if(LAN91C111_PTR & LAN91C111_PTR_NOT_EMPTY)
{
return FALSE;
}
/*
* Write the Pointer Register
*/
LAN91C111_PTR = LAN91C111_PTR_AUTOINC;
/*
* Write status word to Data area
*/
LAN91C111_DATA = 0x0000;
/*
* Write Byte count
*/
LAN91C111_DATA = (uint16)UBLBSWTICH(mod_data_length + 20);
/*
* Write destination ethernet address
*/
LAN91C111_DATA = *((uint16 *)ð_dest[0]);
LAN91C111_DATA = *((uint16 *)ð_dest[2]);
LAN91C111_DATA = *((uint16 *)ð_dest[4]);
/*
* Write source ethernet address
*/
LAN91C111_DATA = *((uint16 *)ð_src[0]);
LAN91C111_DATA = *((uint16 *)ð_src[2]);
LAN91C111_DATA = *((uint16 *)ð_src[4]);
/*
* Write type
*/
LAN91C111_DATA = eth_type;
/*
* Write data
*/
for (index = 0; (index + 1) < pNbuf->length; index += 2)
{
LAN91C111_DATA = *((uint16*)&pNbuf->data[ETH_HDR_SIZE +index]);
}
/*
* Write odd byte and pad with an extra byte to maintain 16-bit alignment
*/
if(pNbuf->length & 0x0001)
{
LAN91C111_DATA = (uint16)(pNbuf->data[ETH_HDR_SIZE + pNbuf->length - 1] << 8);
++pNbuf->length;
}
/*
* Pad with 0x00's if original size was less than ETH_MIN_SIZE
*/
for(index=pNbuf->length; index < ETH_MIN_SIZE; index += 2)
LAN91C111_DATA = 0;
/*
* Write control byte - already aligned to always be even
*/
LAN91C111_DATA = 0x0010;
/*
* Issus "ENQUEUE PACKET NUMBER TO TX FIFO
*/
LAN91C111_MMCR = LAN91C111_MMCR_ENQUEUE;
while (LAN91C111_MMCR & LAN91C111_MMCR_BUSY)
;
/*
* Free the network buffer
*/
nif->tx_free(pNbuf);
/*
* Allow interrupts again
*/
board_irq_enable();
return TRUE;
}
/********************************************************************/
static void
lan91c111_receive (void *not_used, NIF *nif)
{
uint16 pktnum;
uint16 pktlength;
uint16 status;
uint16 lastword;
uint32 index;
eth_frame_hdr * ethframe;
NBUF * pNbuf;
(void)not_used;
/* I should already be in BANK 2 */
pktnum = LAN91C111_RXFIFOR;
if ( pktnum & LAN91C111_RXFIFOR_REMPTY )
{
/* we got called , but nothing was on the FIFO */
/* don't need to restore anything */
return;
}
/* Check to see if FIFO is emtpy */
if(LAN91C111_PTR & LAN91C111_PTR_NOT_EMPTY)
{
printf("FIFO is NOT EMPTY\n");
return;
}
/* Write Addr Ptr */
/* Write Address Pointer Reg */
LAN91C111_PTR = (LAN91C111_PTR_AUTOINC | LAN91C111_PTR_RCV |LAN91C111_PTR_READ);
/* Read Word 0 from RAM */
/* First two words are status and packet_length */
status = LAN91C111_DATA;
pktlength = LAN91C111_DATA;
pktlength = (uint16)(UBLBSWTICH(pktlength) & 0x07FF);
/* Status work ok? */
if(status & RS_ERRORS)
{
printf("error in receive status word: %04X\n",status);
}
/* check if multicast*/
if((status & RS_MULTICAST) | (status & RS_BRODCAST))
{
/* Issue "Remove and Release" command */
while (LAN91C111_MMCR & LAN91C111_MMCR_BUSY);
LAN91C111_MMCR = LAN91C111_MMCR_RELEASE;
return;
}
/* Obtain system RAM buffer for the frame */
if ((pNbuf = nif->rx_alloc()) == NULL)
{
printf("error, cannot obtain system RAM buffer for the frame\n");
/* Issue "Remove and Release" command */
while (LAN91C111_MMCR & LAN91C111_MMCR_BUSY);
LAN91C111_MMCR = LAN91C111_MMCR_RELEASE;
return;
}
ethframe = (eth_frame_hdr *)&pNbuf->data[ETH_HDR_OFFSET];
/* Copy Data Per upper Layer Specs */
/* get destination */
*(uint16*)ðframe->dest[0] = LAN91C111_DATA;
*(uint16*)ðframe->dest[2] = LAN91C111_DATA;
*(uint16*)ðframe->dest[4] = LAN91C111_DATA;
/* get source */
*(uint16*)ðframe->src[0] = LAN91C111_DATA;
*(uint16*)ðframe->src[2] = LAN91C111_DATA;
*(uint16*)ðframe->src[4] = LAN91C111_DATA;
/* get frame type */
ethframe->type = LAN91C111_DATA;
/* check if we can handle this type */
if(!nif_protocol_exist(nif, ethframe->type))
{
/* Issue "Remove and Release" command */
printf("can't handle this frame\n");
while (LAN91C111_MMCR & LAN91C111_MMCR_BUSY);
LAN91C111_MMCR = LAN91C111_MMCR_RELEASE;
nif->rx_free(pNbuf);
return;
}
/* Get length of data in frame */
/* 6 for des address */
/* 6 for src address */
/* 2 for type */
/* 6 for SMSC frame (status word, byte count, control byte and odd byte) */
pNbuf->length = pktlength - (6+6+2+6);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -