一个Bootloader没有tftp的支持,那么移植内核实在是痛苦的事。因为你要不断的用串口烧写内核到SDRAM里,时间少则2分多钟,多则4分多钟,错了还要重烧!在实现这个驱动前,我用U-Boot就是这么痛苦。 在移植完Linux2.6.22.2的AX88796的驱动之后,我决心一定要把AX88796移植到U-Boot上,一劳永逸。借助移植Linux下的AX88796的经验,我首先看懂了/drivers下的ne2000.c的驱动,再参考了一些RTL8019的移植记录(都是NE2000兼容网卡),在痛苦了三四天后,驱动成功,ping和tftp成功,高兴得差点跳起来!以下介绍驱动的移植和U-Boot下网卡驱动移植、编写的一般方法。 U-Boot下网卡驱动框架: U-Boot的/drivers文件夹包含了许多U-Boot可能用到的驱动,其中包括:nand flash和网卡。(如果你有修改过nand flash驱动,你一定来过这。)这些驱动基本上都是最底层的硬件驱动。网卡驱动也不例外,它并不包含协议层,只实现网卡初始化、读写、停止等等功能,所以移植起来比较容易。 U-Boot网卡驱动的接口函数由以下四个函数组成:
int eth_init(bd_t *bd):完成网卡初始化的过程:热复位、相应寄存器的赋值、设置MAC地址等等 void eth_halt() :停止网卡运行 int eth_rx() :接收网络数据 int eth_send(volatile void *packet, int length) :发送数据
U-Boot在进行网络操作时,调用的就是这四个函数。所以移植时,只要集中精力在这四个函数,使它们对网卡的操作是正确的,移植就成功了。知道以上的知识,再学习一些网卡和网络的知识,看看成功的网卡驱动,要是以后出现U-boot不支持的网卡,也可以自己写驱动了!
-------------------------------------------------------------------------------- U-Boot下AX88796网卡移植过程 U-Boot下的AX88796网卡(NE2000寄存器兼容)驱动是使用NE2000的驱动,用到的文件是在/drivers文件夹下的8390.h、ne2000.c和ne2000.h。
首先说明一个关键问题,U-Boot的NE2000驱动是为8位总线写的,而博创的2410-S实验箱的AX88796的硬件连接适合16位总线的网卡驱动。所以必须将NE2000驱动该写成16位总线的驱动。(也许你会想:我也可以把2410的总线宽度改成8位,来使用8位的驱动。但是请你注意看看AX88796的数据手册的第51页和实验箱原理图的网卡部分,你就会知道:即使你用8位的驱动,你也必须使用16位 的总线,而且数据会处理更加麻烦。)还有就是没有修改过的驱动有BUG,没有修正是无法正常使用的。 --------------------------------------------------------------------------------
(1)修改ne2000.c ...... #define DEBUG 0
#if DEBUG & 1 #define DEBUG_FUNCTION() do { printf('%s ', __FUNCTION__); } while (0) #define DEBUG_LINE() do { printf('%d ', __LINE__); } while (0) #else #define DEBUG_FUNCTION() do {} while(0) #define DEBUG_LINE() do {} while(0) #endif
#include 'ne2000.h' //将8390的头文件上移到此,因为前面就要用到 #include '8390.h'
#if DEBUG & 1 #define PRINTK(args...) printf(args) #else #define PRINTK(args...) #endif
static dp83902a_priv_data_t nic; /* just one instance of the card supported */
//添加从U-Boot的参数区读取MAC地址的函数 static int ne2000_read_mac_addr(unsigned char * enaddr) { int ii; char *s, *e;
s = getenv ('ethaddr'); if (s == NULL){ return -1; } else{ for(ii = 0; ii < 12; ii+=2) { enaddr[ii] =enaddr[ii+1]= s ? simple_strtoul (s, &e, 16) : 0; if (s){ s = (*e) ? e + 1 : e; } } } return 0; } ......
static void dp83902a_start(unsigned char * enaddr) { dp83902a_priv_data_t *dp = &nic; cyg_uint8 *base = dp->base; int i;
DEBUG_FUNCTION();
DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_STOP); /* Brutal */ DP_OUT(base, DP_DCR, 0x49); //将网卡的总线宽度改为16位 DP_OUT(base, DP_RBCH, 0); /* Remote byte count */
......
DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_STOP); DP_OUT(base, DP_TCR, DP_TCR_NORMAL); /* Normal transmit operations */ DP_OUT(base, DP_RCR, DP_RCR_AB); /* Accept broadcast, no errors, no multicast */ DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); dp->running = true; }
......
static void dp83902a_send(unsigned short *data, int total_len, unsigned long key) { ......
DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ { /* Dummy read. The manual sez something slightly different, */ /* but the code is extended a bit to do what Hitachi's monitor */ /* does (i.e., also read data). */ /* //屏蔽无用的语句 cyg_uint16 tmp; int len = 1;
DP_OUT(base, DP_RSAL, 0x100-len); DP_OUT(base, DP_RSAH, (start_page-1) & 0xff); DP_OUT(base, DP_RBCL, len); DP_OUT(base, DP_RBCH, 0); DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_RDMA | DP_CR_START); DP_IN_DATA(dp->data, tmp); */ }
......
#if DEBUG & 4 printf(' sg buf %08lx len %08x ', (unsigned long) data, len); dx = 0; #endif while (len > 1) { #if DEBUG & 4 printf(' %04x', *data); if (0 == (++dx % 16)) printf(' '); #endif DP_OUT_DATA(dp->data, *data++); len-=2; } #if DEBUG & 4 if (len==1) printf(' %04x', (*data)&0xff); printf(' '); #endif if (len==1) {DP_OUT_DATA(dp->data, (*data++)&0xff); total_len++; }
if (total_len < pkt_len) { #if DEBUG & 4 printf(' + %d bytes of padding ', pkt_len - total_len); #endif /* Padding to 802.3 length was required */ for (i = total_len; i < pkt_len;) { i+=2; DP_OUT_DATA(dp->data, 0); } }
...... }
/* This function is called when a packet has been received. It's job is to prepare to unload the packet from the hardware. Once the length of the packet is known, the upper layer of the driver can be told. When the upper layer is ready to unload the packet, the internal function 'dp83902a_recv' will be called to actually fetch it from the hardware. */ static void dp83902a_RxEvent(void) { struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; cyg_uint8 *base = dp->base; unsigned char rsr; unsigned short rcv_hdr[2]; int i, len, pkt, cur; ...... DP_OUT(base, DP_RBCL, 4); DP_OUT(base, DP_RBCH, 0); DP_OUT(base, DP_RSAL, 0); DP_OUT(base, DP_RSAH, pkt); if (dp->rx_next == pkt) { if (cur == dp->rx_buf_start) DP_OUT(base, DP_BNDRY, dp->rx_buf_end-1); else DP_OUT(base, DP_BNDRY, cur-1); /* Update pointer */ return; } dp->rx_next = pkt; DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START); #ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA CYGACC_CALL_IF_DELAY_US(10); #endif
for (i = 0; i < sizeof(rcv_hdr);) { DP_IN_DATA(dp->data, rcv_hdr[i++]); }
#if DEBUG & 5 printf('rx hdr %04x %04x ', rcv_hdr[0], rcv_hdr[1]); #endif len = rcv_hdr[1] - 4; uboot_push_packet_len(len); if (((rcv_hdr[0] >>8)&0xff) == dp->rx_buf_start) DP_OUT(base, DP_BNDRY, dp->rx_buf_end-1); else DP_OUT(base, DP_BNDRY, ((rcv_hdr[0] >>8)&0xff)-1); /* Update pointer */ } }
/* This function is called as a result of the 'eth_drv_recv()' call above. It's job is to actually fetch data for a packet from the hardware once memory buffers have been allocated for the packet. Note that the buffers may come in pieces, using a scatter-gather list. This allows for more efficient processing in the upper layers of the stack. */ static void dp83902a_recv(unsigned short *data, int len) { ......
/* Read incoming packet data */ DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); DP_OUT(base, DP_RBCL, len & 0xFF); DP_OUT(base, DP_RBCH, (len >> 8)& 0xFF); DP_OUT(base, DP_RSAL, 4); /* Past header */ DP_OUT(base, DP_RSAH, dp->rx_next); DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START); #ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA CYGACC_CALL_IF_DELAY_US(10); #endif
saved = false; for (i = 0; i < 1; i++) { if (data) { mlen = len; #if DEBUG & 4 printf(' sg buf %08lx len %08x ', (unsigned long) data, mlen); dx = 0; #endif while (0 < mlen) { /* Saved byte from previous loop? */ if (saved) { *data++ = saved_char; mlen--; saved = false; continue; }
{ cyg_uint16 tmp; DP_IN_DATA(dp->data, tmp); #if DEBUG & 4 printf(' %04x', tmp); if (0 == (++dx % 16)) printf(' '); #endif *data++ = tmp; mlen-=2; if (mlen==1) { DP_IN_DATA(dp->data, tmp); tmp = tmp & 0xff; #if DEBUG & 4 printf(' %04x', tmp); #endif *data++ = tmp; mlen--; } } } #if DEBUG & 4 printf(' '); #endif } } }
...... //添加自定义的AX88796硬件信息,在这里定义了如果网卡的MAC地址的前三个为 //0x08, 0x08, 0x08,那就是AX88796。如果你要修改MAC地址 , //最好前三个要和这三个一样,不然驱动会认不到网卡。 static hw_info_t hw_info[] = { ...... { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 }, { /* AX88796 */ 0x0ff0, 0x08, 0x08, 0x08, 0 }, { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65, ...... };
......
static void pcnet_reset_8390(void) { int i, r;
PRINTK('nic base is %lx ', nic_base);
#if 1 n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD); PRINTK('cmd (at %lx) is %x ', nic_base+ E8390_CMD, n2k_inb(E8390_CMD)); n2k_outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, E8390_CMD); PRINTK('cmd (at %lx) is %x ', nic_base+ E8390_CMD, n2k_inb(E8390_CMD)); n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD); PRINTK('cmd (at %lx) is %x ', nic_base+ E8390_CMD, n2k_inb(E8390_CMD)); #endif n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD);
n2k_outb(n2k_inb(PCNET_RESET), PCNET_RESET); //低级错误,严重的BUG,没有修改无法实现网卡的热复位,晕死。
for (i = 0; i < 100; i++) { if ((r = (n2k_inb(EN0_ISR) & ENISR_RESET)) != 0) break; PRINTK('got %x in reset ', r); my_udelay(100); } n2k_outb(0xff, EN0_ISR); /* Ack intr. */
if (i == 100) printf('pcnet_reset_8390() did not complete. '); } /* pcnet_reset_8390 */
static hw_info_t * get_prom(void ) { unsigned char prom[32]; char ethaddr[20]; //tekkaman int i, j, tekkaman; //tekkaman unsigned char ne_defethaddr[]={0x08,0x08,0x08,0x08,0x12,0x27,0};//tekkaman
......
pcnet_reset_8390();
mdelay(10);
for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) n2k_outb(program_seq.value, program_seq.offset);
tekkaman=ne2000_read_mac_addr(prom); if (tekkaman) { printf('ethaddr in nand is not found ,loading ne_defethaddr:'); for (i = 0; i < 12; i++) { prom = ne_defethaddr[i/2]; printf(' %02x', prom); } } prom[28] = prom[30] = 0x57;
PRINTK(' '); for (i = 0; i < NR_INFO; i++) { if ((prom[0] == hw_info.a0) && (prom[2] == hw_info.a1) && (prom[4] == hw_info.a2)) { PRINTK('matched board %d ', i); break; } } if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) { for (j = 0; j < 6; j++) dev_addr[j] = prom[j<<1]; PRINTK('on exit i is %d/%ld ', i, NR_INFO); PRINTK('MAC address is %02x:%02x:%02x:%02x:%02x:%02x ', dev_addr[0],dev_addr[1],dev_addr[2],dev_addr[3],dev_addr[4],dev_addr[5]); return (i < NR_INFO) ? hw_info+i : &default_info; if (tekkaman) { sprintf (ethaddr, '%02X:%02X:%02X:%02X:%02X:%02X', dev_addr[0], dev_addr[1], dev_addr[2], dev_addr[3], dev_addr[4], dev_addr[5]) ; printf('Set environment from HW MAC addr = '%s' ', ethaddr); setenv ('ethaddr', ethaddr); } } return NULL; }
/* U-boot specific routines */
#define NB 5
static unsigned short *pbuf = NULL; static int plen[NB]; static int nrx = 0;
static int pkey = -1;
void uboot_push_packet_len(int len) { PRINTK('pushed len = %d, nrx = %d ', len, nrx); if (len>=2000) { printf('NE2000: packet too big '); return; } if (nrx >= NB) { printf('losing packets in rx '); return; } plen[nrx] = len; dp83902a_recv(&pbuf[nrx*1000], len); nrx++; }
void uboot_push_tx_done(int key, int val) { PRINTK('pushed key = %d ', key); pkey = key; }
int eth_init(bd_t *bd) { static hw_info_t * r; // char ethaddr[20];
PRINTK('### eth_init ');
if (!pbuf) { pbuf = malloc(NB*1000); if (!pbuf) { printf('Cannot allocate rx buffers '); return -1; } }
#ifdef CONFIG_DRIVER_NE2000_CCR { volatile unsigned char *p = (volatile unsigned char *) CONFIG_DRIVER_NE2000_CCR;
PRINTK('CCR before is %x ', *p); *p = CONFIG_DRIVER_NE2000_VAL; PRINTK('CCR after is %x ', *p); } #endif
nic_base = CONFIG_DRIVER_NE2000_BASE; nic.base = (cyg_uint8 *) CONFIG_DRIVER_NE2000_BASE;
r = get_prom(); if (!r) return -1; /*//屏蔽无用的语句 sprintf (ethaddr, '%02X:%02X:%02X:%02X:%02X:%02X', dev_addr[0], dev_addr[1], dev_addr[2], dev_addr[3], dev_addr[4], dev_addr[5]) ; PRINTK('Set environment from HW MAC addr = '%s' ', ethaddr); setenv ('ethaddr', ethaddr); */
#define DP_DATA 0x10 nic.data = (unsigned short *) (nic.base + DP_DATA); nic.tx_buf1 = 0x40; nic.tx_buf2 = 0x46; nic.rx_buf_start = 0x4C; nic.rx_buf_end = 0x80; dp83902a_start(dev_addr); if (dp83902a_init() == false) return -1; return 0; }
void eth_halt() {
PRINTK('### eth_halt ');
dp83902a_stop(); }
int eth_rx() { int j, tmo; volatile uchar * inpkt PRINTK('### eth_rx ');
tmo = get_timer (0) + TOUT * CFG_HZ; while(1) { dp83902a_poll(); if (nrx > 0) { for(j=0; j inpkt = (uchar *) &pbuf[j*1000];
NetReceive(inpkt, plen[j]);//这句的作用就是将接收到的数据
//送到MAC层以上的协议层 } nrx = 0; return 1; } if (get_timer (0) >= tmo) { printf('timeout during rx '); return 0; } } return 0; }
int eth_send(volatile void *packet, int length) { int tmo;
PRINTK('### eth_send ');
pkey = -1;
dp83902a_send((unsigned short *) packet, length, 666); tmo = get_timer (0) + TOUT * CFG_HZ; while(1) { dp83902a_poll(); if (pkey != -1) { PRINTK('Packet sucesfully sent '); return 0; } if (get_timer (0) >= tmo) { printf('transmission error (timoeut) '); return 0; }
} return 0; }
#endif
ne2000.c修改完毕
--------------------------------------------------------------------------------
(2)修改ne2000.h NE2000驱动的低级错误 将第45行: at http://sources.redhat.com/ecos/ecos-license/ */
改为: at http://sources.redhat.com/ecos/ecos-license/
就是去掉“*/” , 真是TNND汗死!!!!
......
#define DP_IN(_b_, _o_, _d_) (_d_) = *( (volatile unsigned char *) ((_b_)+(_o_))) #define DP_OUT(_b_, _o_, _d_) *( (volatile unsigned char *) ((_b_)+(_o_))) = ((unsigned char) (_d_))
#define DP_IN_DATA(_b_, _d_) (_d_) = *( (volatile unsigned short *) ((_b_))) #define DP_OUT_DATA(_b_, _d_) *( (volatile unsigned short *) ((_b_))) = ((unsigned short) (_d_))
/* here is all the data */
#define cyg_uint8 unsigned char #define cyg_uint16 unsigned short #define bool int
#define false 0 #define true 1
#define CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA 1 #define CYGACC_CALL_IF_DELAY_US(X) my_udelay(X)
typedef struct dp83902a_priv_data { cyg_uint8* base; cyg_uint16* data; cyg_uint8* reset; int tx_next; /* First free Tx page */ int tx_int; /* Expecting interrupt from this buffer */ int rx_next; /* First free Rx page */ int tx1, tx2; /* Page numbers for Tx buffers */ unsigned long tx1_key, tx2_key; /* Used to ack when packet sent */ int tx1_len, tx2_len; bool tx_started, running, hardwired_esa; cyg_uint8 esa[6]; void* plf_priv;
/* Buffer allocation */ int tx_buf1, tx_buf2; int rx_buf_start, rx_buf_end; } dp83902a_priv_data_t;
......
--------------------------------------------------------------------------------
(3)修改8390.h
...... /* * Only generate indirect loads given a machine that needs them. * - removed AMIGA_PCMCIA from this list, handled as ISA io now */
#define n2k_inb(port) (*((volatile unsigned char *)(port+CONFIG_DRIVER_NE2000_BASE))) #define n2k_outb(val,port) (*((volatile unsigned char *)(port+CONFIG_DRIVER_NE2000_BASE)) = ((unsigned char) val))
#define EI_SHIFT(x) (x) ......
--------------------------------------------------------------------------------
网卡驱动程序的修改到此结束,以下是总线参数的修改:
修改/board/tekkaman/tekkaman2410/lowlevel_init.S文件(参数都是参考 刘淼 的书):
#define B1_BWSCON (DW16) /*@tekkaman*/ #define B2_BWSCON (DW16 + UBLB) #define B3_BWSCON (DW16) /*@tekkaman*/ ......
#define B2_Tacs 0x3 /* 4clk tekkaman*/ #define B2_Tcos 0x3 /* 4clk tekkaman*/ #define B2_Tacc 0x7 /* 14clk */ #define B2_Tcoh 0x3 /* 4clk tekkaman*/ #define B2_Tah 0x3 /* 4clk tekkaman*/ #define B2_Tacp 0x3 /* 6clk tekkaman*/ #define B2_PMC 0x0 /* normal */
......
-------------------------------------------------------------------------------- 在/include/configs/tekkaman2410.h文件中加上AX88796网卡驱动的信息: /* * Hardware drivers */ //#define CONFIG_DRIVER_CS8900 1 /* we have a CS8900 on-board */ //#define CS8900_BASE 0x19000300 //#define CS8900_BUS16 1 /* the Linux driver does accesses as shorts */ #define CONFIG_DRIVER_NE2000 1 #define CONFIG_DRIVER_NE2000_BASE (0x10000000+0x200)
--------------------------------------------------------------------------------
U-Boot下的AX88796移植结束了,我有复查过,应该在编译网卡驱动的时候连警告都不会有,所以如果您遇到了问题,在检查是否按照上面步骤移植后,还不能解决,可以联系我,QQ:78027228。不过我建议:最好是在看过我介绍的资料后再来移植,出了问题你自己就可以解决了。下一步的目标是U-Boot和Linux下的LCD驱动。以下附上我启动时和运行的输出信息(请注意U-Boot环境变量的设定):
U-Boot 1.2.0 (Sep 25 2007 - 14:59:27)
U-Boot code: 33F80000 -> 33F98BC0 BSS: -> 33F9D3C0 DRAM: 64 MB NAND: 64 MB In: serial Out: serial Err: serial Hit any key to stop autoboot: 0 [Tekkaman2410]# printenv bootdelay=3 baudrate=115200 ethaddr=08:08:08:08:12:27 netmask=255.255.255.0 bootfile=zImage.img loadaddr=0x30008000 bootargs=root=/dev/nfs rw nfsroot=192.168.1.22:/home/tekkaman/working/rootfs ip=192.168.1.2:192.168.1.22::255.255.255.0 console=ttySAC0,115200 init=/linuxrc mem=64M bootcmd=tftp;bootm ipaddr=192.168.1.2 serverip=192.168.1.22 stdin=serial stdout=serial stderr=serial
Environment size: 382/65532 bytes
U-Boot 1.2.0 (Sep 25 2007 - 14:59:27)
U-Boot code: 33F80000 -> 33F98BC0 BSS: -> 33F9D3C0 DRAM: 64 MB NAND: 64 MB In: serial Out: serial Err: serial Hit any key to stop autoboot: 0 ### eth_init matched board 31 AX88796 - tekkmana ESA: 08:08:08:08:12:27 TFTP from server 192.168.1.22; our IP address is 192.168.1.2 Filename 'zImage.img'. Load address: 0x30008000 Loading: ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ############# done Bytes transferred = 1727136 (1a5aa0 hex) ## Booting image at 30008000 ... Image Name: tekkamanninja Created: 2007-09-25 9:28:11 UTC Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 1727072 Bytes = 1.6 MB Load Address: 30008000 Entry Point: 30008040 Verifying Checksum ... OK XIP Kernel Image ... OK
Starting kernel ...
Uncompressing Linux................................................................................................................ done, booting the kernel. Linux version 2.6.22.2 (root@Tekkaman-Ninja) (gcc version 4.1.0) #5 Tue Sep 25 15:43:11 CST 2007 CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177 Machine: Tekkaman2410 Memory policy: ECC disabled, Data cache writeback CPU S3C2410A (id 0x32410002) S3C2410: core 202.800 MHz, memory 101.400 MHz, peripheral 50.700 MHz S3C24XX Clocks, (c) 2004 Simtec Electronics CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on CPU0: D VIVT write-back cache CPU0: I cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets CPU0: D cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets Built 1 zonelists. Total pages: 16256 Kernel command line: root=/dev/nfs rw nfsroot=192.168.1.22:/home/tekkaman/working/rootfs ip=192.168.1.2:192.168.1.22::255.255.255.0 console=ttySAC0,115200 init=/linuxrc mem=64M irq: clearing pending ext status 00000100 irq: clearing subpending status 00000002
|