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

📄 rtl8019.c

📁 RT-Thread是发展中的下一代微内核嵌入式实时操作系统
💻 C
字号:
/*
 * File      : rtl8019.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://openlab.rt-thread.com/license/LICENSE
 *
 * Change Logs:
 * Date           Author       Notes
 * 2006-10-10     Qiu Yi       The init version
 * 2008-07-20     Bernard      Modified to RT-Thread Device interface
 */
#include <rtthread.h>

#include <rtl8019.h>
#include <netif/ethernetif.h>

#define RTL8019_DEBUG(...)	\
	do { \
	rt_kprintf("[%s:%d]", __FUNCTION__, __LINE__); \
	rt_kprintf(__VA_ARGS__);rt_kprintf("\n"); \
	} while(0)

struct net_device_stats
{
	unsigned long rx_packets;	/* total packets received	*/
	unsigned long tx_packets;	/* total packets transmitted	*/
	unsigned long rx_errors;	/* receive bad packets */
	unsigned long tx_errors;	/* transmit bad packets */
};

#define MAX_ADDR_LEN	6			/* Largest hardware address length */
struct rtl8019_device
{
	/* inherit from ethernet device */
	struct eth_device parent;

	/* interface address info. */
	unsigned char dev_addr[MAX_ADDR_LEN];	/* hw address	*/

	struct net_device_stats stats;
};
struct rtl8019_device rtl8019_device;

#define ETH_ALEN		6		/* Octets in one ethernet addr	 */
#define ETH_HLEN		14		/* Total octets in header.	 */
#define ETH_ZLEN		60		/* Min. octets in frame sans FCS */
#define ETH_DATA_LEN	1500	/* Max. octets in payload	 */
#define ETH_FRAME_LEN	1514	/* Max. octets in frame sans FCS */

static void ne_copyin(rt_uint16_t count, rt_uint8_t *buf);
static void ne_copyout(rt_uint16_t count, rt_uint8_t *buf);
static void ne_discard(rt_uint16_t count);

static rt_err_t rtl8019_init(rt_device_t dev);
static rt_err_t rtl8019_open(rt_device_t dev);
static rt_err_t rtl8019_close(rt_device_t dev);
static rt_err_t rtl8019_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
static rt_err_t rtl8019_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
static rt_err_t rtl8019_control(rt_device_t dev, rt_uint8_t cmd, void *args);
static void rtl8019_isr(int irqno);

/*
 * Read the specified number of bytes from the device DMA port into
 * the supplied buffer.
 */
static void ne_copyin(rt_uint16_t count, rt_uint8_t *buf)
{
	while(count--) *buf++ = NE_DMA;
}

/*
 * Write the specified number of bytes from the device DMA port into
 * the supplied buffer.
 */
static void ne_copyout(rt_uint16_t count, rt_uint8_t *buf)
{
	while(count--) NE_DMA = *buf++;
}

/*
 * Pull the specified number of bytes from the device DMA port,
 * and throw them away.
 */
static void ne_discard(rt_uint16_t count)
{
	rt_uint8_t value;
	while(count--) value = NE_DMA;
}

/*
 * RT-Thread Device Interface
 */
rt_err_t rtl8019_init(rt_device_t dev)
{
	/* config in PAGE0 */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_STOP;

	NE_RBCR0 = 0x0;
	NE_RBCR1 = 0x0;

	NE_TPSR		= 0x40;
	NE_PSTART	= 0x4c;
	NE_BNRY		= 0x4c;
	NE_PSTOP	= 0x80;

	NE_ISR = 0xff;
	NE_RCR = 0xcc;

	NE_TCR = 0xe0;
#if DMA16
	NE_DCR = 0xc9;
#else
	NE_DCR = 0xc8;
#endif

	NE_IMR = ISR_OVW | ISR_TXE | ISR_PTX | ISR_PRX;
	/* clear all interrupts */
	NE_ISR = 0xff;

	/* config in PAGE1 */
	NE_CR	= CMD_PAGE1 | CMD_NODMA | CMD_STOP;
	NE_CURR = 0x4c;

	/* set MAC address */
	NE_PAR0 = rtl8019_device.dev_addr[0];
	NE_PAR1 = rtl8019_device.dev_addr[1];
	NE_PAR2 = rtl8019_device.dev_addr[2];
	NE_PAR3 = rtl8019_device.dev_addr[3];
	NE_PAR4 = rtl8019_device.dev_addr[4];
	NE_PAR5 = rtl8019_device.dev_addr[5];

	/* get mac addr */
	rtl8019_device.dev_addr[0] = NE_PAR0;
	rtl8019_device.dev_addr[1] = NE_PAR1;
	rtl8019_device.dev_addr[2] = NE_PAR2;
	rtl8019_device.dev_addr[3] = NE_PAR3;
	rtl8019_device.dev_addr[4] = NE_PAR4;
	rtl8019_device.dev_addr[5] = NE_PAR5;

	/* clear multicast filters (reject all multicast) */
	NE_MAR0 = 0x0;
	NE_MAR1 = 0x0;
	NE_MAR2 = 0x0;
	NE_MAR3 = 0x0;
	NE_MAR4 = 0x0;
	NE_MAR5 = 0x0;
	NE_MAR6 = 0x0;
	NE_MAR7 = 0x0;

	rt_hw_interrupt_install(INT_NE, rtl8019_isr, RT_NULL);
	rt_hw_interrupt_umask(INT_NE);

	NE_CR	= CMD_PAGE1 | CMD_NODMA | CMD_RUN;

	return RT_EOK;
}

rt_err_t rtl8019_open(rt_device_t dev)
{
	return RT_EOK;
}

rt_err_t rtl8019_close(rt_device_t dev)
{
	/* mask all interrupts */
	NE_IMR = 0;

	/* clear ISR */
	NE_ISR = 0xff;

	/* stop nic */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_STOP;
	return RT_EOK;
}

struct pbuf* rtl8019_rx(rt_device_t eth)
{
	rt_ubase_t	level;
	struct pbuf *p = RT_NULL;
	rt_uint8_t	curr, bnry;
	rt_uint16_t packet_length, len;
	rt_uint8_t	pld_header[18], *payload;

	/* lock interrupt */
	level = rt_hw_interrupt_disable();

	NE_CR = CMD_PAGE1 | CMD_NODMA | CMD_STOP;
	curr  = NE_CURR;
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_STOP;
	bnry  = NE_BNRY;

	if(bnry > 0x7f) bnry = 0x4c;
	packet_length = 0;

	/* receive packet */
	if (curr != bnry)
	{
		/* clear ISR status */
		NE_ISR   = ISR_RDC;
		NE_RBCR1 = 0x0f;
		NE_CR    = CMD_PAGE0 | CMD_SEND | CMD_RUN;

		NE_RSAR0 = 0;
		NE_RSAR1 = bnry;

		/* get the first 18 bytes from nic */
		ne_copyin(18, pld_header);

		/* store real length, set len to packet length - header */
		/* ethernet packet header */
		packet_length = ((unsigned int) pld_header[2] | (pld_header[3] << 8 ));

		/* allocate pbuf from lwip mem pool */
		p = pbuf_alloc(PBUF_LINK, packet_length, PBUF_POOL);
		if(p != RT_NULL)
		{
			struct pbuf *q;

			/* We iterate over the pbuf chain until we have read the entire
	      	 * packet into the pbuf.
			 * This assumes a minimum pbuf size of 14 ... a good assumption
			 */
			rt_memcpy(p->payload, pld_header + 4, 14);

			for(q = p; q != RT_NULL; q= q->next)
			{
				/* Read enough bytes to fill this pbuf in the chain. The
				avaliable data in the pbuf is given by the q->len variable. */

				payload = q->payload;
				len = q->len;

				if (q == p)
				{
					/* First 14 bytes are already there, skip them */
					payload += 14;
					len -=14;
				}
				ne_copyin(len,payload);
			}
		}
		else
		{
			rt_kprintf("no pbuf\n");
			/* no more PBUF resource, Discard packet in buffer. */
			ne_discard(packet_length-14);
  		}
		++rtl8019_device.stats.rx_packets;

		/* RTL8019_DEBUG("received %d size network packet, total %d network packets\n",
				packet_length, rtl8019_device.stats.rx_packets); */

		bnry = pld_header[1];
		if(bnry < 0x4c) bnry = 0x7f;

		NE_BNRY = bnry;
	}
	else
	{
		NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_STOP;

		NE_ISR = ISR_PRX;		/* clear interrupt */

		/* open nic for next packet */
		NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	}

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

	return p;
}

rt_err_t rtl8019_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
{
	rt_ubase_t	level;
	rt_uint8_t	curr, bnry;
	rt_uint16_t packet_length;
	rt_uint8_t	pld_header[18]; /* temp storage for ethernet headers */

	/* lock interrupt */
	level = rt_hw_interrupt_disable();

	NE_CR = CMD_PAGE1 | CMD_NODMA | CMD_STOP;
	curr  = NE_CURR;
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_STOP;
	bnry  = NE_BNRY;

	if(bnry > 0x7f) bnry = 0x4c;
	packet_length = 0;

	/* receive packet */
	if (curr != bnry)
	{
		/* clear ISR status */
		NE_ISR   = ISR_RDC;
		NE_RBCR1 = 0x0f;
		NE_CR    = CMD_PAGE0 | CMD_SEND | CMD_RUN;

		NE_RSAR0 = 0;
		NE_RSAR1 = bnry;

		/* get the first 18 bytes from nic */
		ne_copyin(18, pld_header);

		/* store real length, set len to packet length - header */
		/* ethernet packet header */
		packet_length = ((unsigned int) pld_header[2] | (pld_header[3] << 8 ));

		if (packet_length > size )
		{
			/* no more PBUF resource, Discard packet in buffer. */
			ne_discard(packet_length-14);
			return -RT_ERROR;
		}

		// This assumes a minimum pbuf size of 14 ... a good assumption
		rt_memcpy(buffer, pld_header + 4, 14);
		ne_copyin(packet_length - 14, buffer + 14);
		++rtl8019_device.stats.rx_packets;

		/* RTL8019_DEBUG("received %d size network packet, total %d network packets\n",
				packet_length, rtl8019_device.stats.rx_packets); */

		bnry = pld_header[1];
		if(bnry < 0x4c) bnry = 0x7f;

		NE_BNRY = bnry;
	}
	else
	{
		NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_STOP;

		NE_ISR = ISR_PRX;		/* clear interrupt */

		/* open nic for next packet */
		NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	}

	/* enable interrupt */
	rt_hw_interrupt_enable(level);

	return packet_length;
}

rt_err_t rtl8019_tx(rt_device_t eth, struct pbuf* p)
{
	rt_uint8_t isr;
	rt_uint32_t count;
	rt_ubase_t level;
	struct pbuf* q;

	level = rt_hw_interrupt_disable();

	/* set up to transfer the packet contents to the NIC RAM. */

	/* count: add pading to the size, if size < 64 */
	count = p->tot_len < 64? 64 : p->tot_len;

	/* close receive interrupt */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	isr = NE_IMR;
	isr &= ~ISR_PRX;
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	NE_IMR = isr;

	NE_ISR = ISR_RDC;

    /* set the length of send */
	NE_RBCR0 = count & 0xff;
	NE_RBCR1 = (count >> 8) & 0xff;

    /* the address on NIC to store */
	NE_RSAR0 = 0;
	NE_RSAR1 = 0x40;

	/* start to transmit */
	NE_CR = CMD_PAGE0 | CMD_WRITE | CMD_RUN;

	for (q = p; q != NULL; q = q->next)
		ne_copyout(q->len, q->payload);

	/* padding */
	if (p->tot_len < 64)
	{
		rt_uint32_t size = p->tot_len;

		/* write padding */
		while (size++ < count)
		{
			NE_DMA = 0;
		}
	}

	/* wait for remote dma to complete */
	while((rt_uint8_t)(NE_ISR & ISR_RDC) == 0 );

	/* clear RDC */
	NE_ISR = ISR_RDC;

	/* issue the transmit command.(start local dma) */
 	NE_TPSR = 0x40;
	NE_TBCR0 = count & 0xff;
	NE_TBCR1 = (count >> 8) & 0xff;

	/* start transmission */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_XMIT | CMD_RUN;

	/* reopen receive interrupt */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	isr = NE_IMR;
	isr |= ISR_PRX;
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	NE_IMR = isr;

	rt_hw_interrupt_enable(level);

	return RT_EOK;
}

rt_err_t rtl8019_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
{
	rt_uint8_t isr;
	rt_uint32_t count;
	rt_ubase_t level;

	rt_kprintf("rtl8019 write buffer, size:%d\n", size);
	level = rt_hw_interrupt_disable();

	/* set up to transfer the packet contents to the NIC RAM. */

	/* count: add pading to the size, if size < 64 */
	count = size < 64? 64 : size;

	/* close receive interrupt */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	isr = NE_IMR;
	isr &= ~ISR_PRX;
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	NE_IMR = isr;

	NE_ISR = ISR_RDC;

    /* set the length of send */
	NE_RBCR0 = count & 0xff;
	NE_RBCR1 = (count >> 8) & 0xff;

    /* the address on NIC to store */
	NE_RSAR0 = 0;
	NE_RSAR1 = 0x40;

	/* start to transmit */
	NE_CR = CMD_PAGE0 | CMD_WRITE | CMD_RUN;

	ne_copyout(size, (rt_uint8_t*)buffer);
	if (size < 64)
	{
		/* write padding */
		while (size++ < count)
		{
			NE_DMA = 0;
		}
	}

	/* wait for remote dma to complete */
	while((rt_uint8_t)(NE_ISR & ISR_RDC) == 0 );

	/* clear RDC */
	NE_ISR = ISR_RDC;

	/* issue the transmit command.(start local dma) */
 	NE_TPSR = 0x40;
	NE_TBCR0 = count & 0xff;
	NE_TBCR1 = (count >> 8) & 0xff;

	/* start transmission */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_XMIT | CMD_RUN;

	/* reopen receive interrupt */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	isr = NE_IMR;
	isr |= ISR_PRX;
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;
	NE_IMR = isr;

	rt_hw_interrupt_enable(level);

	return RT_EOK;
}

rt_err_t rtl8019_control(rt_device_t dev, rt_uint8_t cmd, void *args)
{
	switch(cmd)
	{
		case NIOCTL_GADDR:
    		/* get mac address */
    		if(args) rt_memcpy(args, rtl8019_device.dev_addr, 6);
			else return -RT_ERROR;
			break;

		default :
			break;
	}

	return RT_EOK;
}

/**
 * This function will register realtek 8019 ethernet interface
 */
int rtl8019_device_register(char* name)
{
	rt_err_t result;

	/* init rt-thread device interface */
	rtl8019_device.parent.parent.init		= rtl8019_init;
	rtl8019_device.parent.parent.open		= rtl8019_open;
	rtl8019_device.parent.parent.close		= rtl8019_close;
	rtl8019_device.parent.parent.read		= rtl8019_read;
	rtl8019_device.parent.parent.write		= rtl8019_write;
	rtl8019_device.parent.parent.control	= rtl8019_control;
	rtl8019_device.parent.eth_rx			= rtl8019_rx;
	rtl8019_device.parent.eth_tx			= rtl8019_tx;

	/* set MAC address */
	rtl8019_device.dev_addr[0] = 0x0;
	rtl8019_device.dev_addr[1] = 0x1;
	rtl8019_device.dev_addr[2] = 0x2;
	rtl8019_device.dev_addr[3] = 0x3;
	rtl8019_device.dev_addr[4] = 0x4;
	rtl8019_device.dev_addr[5] = 0x5;

	/* init rtl8019 device as ethernet interface */
	result = eth_device_init(&(rtl8019_device.parent), (char*)name);
	RT_ASSERT(result == RT_EOK);

	return result;
}

/*
 * The realtek 8019 ethernet interface interrupt service rountine
 */
static void rtl8019_isr(int irqno)
{
	rt_uint8_t isr, curr;

	/* close nic */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_STOP;

	isr = NE_ISR;

	//RTL8019_DEBUG("rtl8019 ISR coming\n");

	/* if ram overflow interrupt */
	if (isr & ISR_OVW )
	{
		RTL8019_DEBUG("overflow");

		/* clear interrupt */
		NE_ISR = ISR_OVW;
	}

	/* if Tx error, NIC abort tx due to excessive collisions */
	if (isr & ISR_TXE)
	{
		RTL8019_DEBUG("Tx error");

		/* clear interrupt */
		NE_ISR = ISR_TXE;

		rtl8019_device.stats.tx_errors ++;
	}

	/* if Rx error , reset BNRY pointer to CURR (use SEND PACKET mode) */
	if (isr & ISR_RXE)
	{
		RTL8019_DEBUG("Rx error");

		/* clear interrupt */
		NE_ISR = ISR_RXE;

		NE_CR = CMD_PAGE1 | CMD_NODMA | CMD_STOP;
		curr = NE_CURR;
		NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_STOP;
		NE_BNRY = curr;

		rtl8019_device.stats.rx_errors ++;
	}

	/* no errors */
	if (isr & ISR_PRX)
	{
		//RTL8019_DEBUG("Receive a packet, add a net task job");

		NE_ISR = ISR_PRX;		/* clear interrupt */

		/* notify device ready to ethernet task */
		eth_device_ready((struct rt_device*)&rtl8019_device);
	}

	/* Transfer complete, do nothing here */
	if( isr & ISR_PTX)
	{
		//RTL8019_DEBUG("Transfer complete");

		/* clear interrupt */
		NE_ISR = ISR_PTX;
		rtl8019_device.stats.tx_packets++;
	}

	NE_CR  = CMD_PAGE0 | CMD_NODMA | CMD_STOP;

	/* clear interrupt */
	NE_ISR = 0xff;

	/* open nic for next packet */
	NE_CR = CMD_PAGE0 | CMD_NODMA | CMD_RUN;

//	RTL8019_DEBUG("Open rtl8019 ISR again");
}

⌨️ 快捷键说明

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