uboot 起来以后,想要引导linux,方便调试,最好需要把网卡功能调通,然后直接通过tftp download kernel到ram里,再去引导系统,网上DM9000在2440上面的修改很多,我也忘记在哪抄的代码了,在这边也记录一下
在include\configs\smdk2410.h文件中,把cs8900的配置去掉,加上DM9000的配置,其中0x20000000这个地址,网上也有写其他地址,看了下网卡DM9000和2440的连接,其实这个地址里面只有较高的一个地址位决定数据还是cmd的片选位,所以网上的其他地址在这个范围内,也是可以使用的
/*#define CONFIG_CS8900
#define CONFIG_CS8900_BASE 0x19000300
#define CONFIG_CS8900_BUS16 */
#define CONFIG_ETHADDR 00:01:02:03:04:05
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_DM9000_BASE 0x20000000
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE +4)
#define CONFIG_DM9000_USE_16BIT 1
/*#define CONFIG_DM9000_DEBUG 8*/
CONFIG_DM9000_DEBUG可以把网卡工作的一些信息打印出来,有助于调试,在include\env_default.h文件中static char default_environment[] 数组中加入网卡mac地址的设定:
#ifdef CONFIG_ETHADDR
"ethaddr=" __stringify(CONFIG_ETHADDR) "\0"
#endif
在board\samsung\smdk2410\smdk2410.c文件中,
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_CS8900
rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);
#endif
return rc;
}
需要加入
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);
#endif进行网卡的初始化,然后drivers\net\dm9000x.c文件中,dm9000_init函数,需要把如下代码注释掉,
#if 0
i = 0;
while (!(dm9000_phy_read(1) & 0x20)) { /* autonegation complete bit */
udelay(1000);
i++;
if (i == 10000) {
printf("could not establish link\n");
return 0;
}
}
#endif
dm9000_halt 函数中的内容注释掉:
static void dm9000_halt(struct eth_device *netdev)
{
#if 0
DM9000_DBG("%s\n", __func__);
/* RESET devie */
dm9000_phy_write(0, 0x8000); /* PHY RESET */
DM9000_iow(DM9000_GPR, 0x01); /* Power-Down PHY */
DM9000_iow(DM9000_IMR, 0x80); /* Disable all interrupt */
DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */
#endif
}
这样修改以后,重新编译uboot,应该网卡就可以起来了,可以看到能ping通pc:
然后设置好Tftpd32程序,pc端作为tftp server:
其中192.168.1.2为pc端ip地址,download目录下面放的是uImage,用来下载到2440的ram中,在2440的console上面执行
tftpboot uImage
可以看到kernel加载成功,其中加载地址为0x31000000,不知道为什么这版uboot中没有tftp命令,只有tftpboot 命令,tftpboot只能把uboot 加载到指定地址,在环境变量中设置loadaddr 为0x31000000为加载地址。
修改完驱动以后,如果ping不通pc,这个时候用wireshark抓包发现arp包和icmp包都正常发送,但是pc就是没有反应,一定要检查pc的防火墙有没有关闭,我就是在这边遇到一个坑,检查了好久,才想起来可能是防火墙的问题。
由于之前ping不通,所以把ping程序的流程看了下,也记录下吧,程序中 ping程序的执行流程:
在cmd/net.c文件中,执行do_ping
static int do_ping(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
if (argc < 2)
return CMD_RET_USAGE;
net_ping_ip = string_to_ip(argv[1]);
if (net_ping_ip.s_addr == 0)
return CMD_RET_USAGE;
if (net_loop(PING) < 0) {
printf("ping failed; host %s is not alive\n", argv[1]);
return CMD_RET_FAILURE;
}
printf("host %s is alive\n", argv[1]);
return CMD_RET_SUCCESS;
}
接着执行net_loop,执行其中的ping_start:
void ping_start(void)
{
printf("Using %s device\n", eth_get_name());
net_set_timeout_handler(10000UL, ping_timeout_handler);
ping_send();
}
在ping_send中可以看到,虽然构建了icmp数据包,但其实是先要发送arp_request,确保链路上相互对方有mac地址,uboot里面程序做的比较简单,一般不执行命令在网络上是不会交互数据包的,没有中断,过程基本都由轮询来实现:
static int ping_send(void)
{
uchar *pkt;
int eth_hdr_size;
/* XXX always send arp request */
debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &net_ping_ip);
net_arp_wait_packet_ip = net_ping_ip;
eth_hdr_size = net_set_ether(net_tx_packet, net_null_ethaddr, PROT_IP);
pkt = (uchar *)net_tx_packet + eth_hdr_size;
set_icmp_header(pkt, net_ping_ip);
/* size of the waiting packet */
arp_wait_tx_packet_size = eth_hdr_size + IP_ICMP_HDR_SIZE;
/* and do the ARP request */
arp_wait_try = 1;
arp_wait_timer_start = get_timer(0);
arp_request();
return 1; /* waiting */
}
发送完arp_request以后,在net_loop里面接着往下走,调用int eth_rx(void),其实调用的是DM9000x.c中的
static int dm9000_rx(struct eth_device *netdev)接收函数,在该函数中又调用到
net_process_received_packet函数,在net_process_received_packet中根据收到的数据包头,应该是arp reply包,调用
switch (eth_proto) {
case PROT_ARP:
arp_receive(et, ip, len);
break;
在arp_receive函数中,检测到时arp_reply包,由于之前在发arp request包的时候,在ping_send函数中设置了net_arp_wait_packet_ip,同时在arp_request函数中设置了net_arp_wait_reply_ip,所以在这里就match了arp reply
包,调用net_send_packet(net_tx_packet, arp_wait_tx_packet_size);把之前造好的icmp request数据包发送出去
case ARPOP_REPLY: /* arp reply */
/* are we waiting for a reply */
if (!net_arp_wait_packet_ip.s_addr)
break;
#ifdef CONFIG_KEEP_SERVERADDR
if (net_server_ip.s_addr == net_arp_wait_packet_ip.s_addr) {
char buf[20];
sprintf(buf, "%pM", &arp->ar_sha);
setenv("serveraddr", buf);
}
#endif
reply_ip_addr = net_read_ip(&arp->ar_spa);
/* matched waiting packet's address */
if (reply_ip_addr.s_addr == net_arp_wait_reply_ip.s_addr) {
debug_cond(DEBUG_DEV_PKT,
"Got ARP REPLY, set eth addr (%pM)\n",
arp->ar_data);
/* save address for later use */
if (arp_wait_packet_ethaddr != NULL)
memcpy(arp_wait_packet_ethaddr,
&arp->ar_sha, ARP_HLEN);
net_get_arp_handler()((uchar *)arp, 0, reply_ip_addr,
0, len);
/* set the mac address in the waiting packet's header
and transmit it */
memcpy(((struct ethernet_hdr *)net_tx_packet)->et_dest,
&arp->ar_sha, ARP_HLEN);
net_send_packet(net_tx_packet, arp_wait_tx_packet_size);
/* no arp request pending now */
net_arp_wait_packet_ip.s_addr = 0;
arp_wait_tx_packet_size = 0;
arp_wait_packet_ethaddr = NULL;
}
return;
然后在net_loop继续循环,调用int eth_rx(void)
在static int dm9000_rx中接着调用net_process_received_packet(uchar *in_packet, int len):
if (ip->ip_p == IPPROTO_ICMP) {
receive_icmp(ip, len, src_ip, et);
return;
检测到是ping receive包:
static void receive_icmp(struct ip_udp_hdr *ip, int len,
struct in_addr src_ip, struct ethernet_hdr *et)
{
struct icmp_hdr *icmph = (struct icmp_hdr *)&ip->udp_src;
switch (icmph->type) {
case ICMP_REDIRECT:
if (icmph->code != ICMP_REDIR_HOST)
return;
printf(" ICMP Host Redirect to %pI4 ",
&icmph->un.gateway);
break;
default:
#if defined(CONFIG_CMD_PING)
ping_receive(et, ip, len);
#endif
void ping_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len)
{
struct icmp_hdr *icmph = (struct icmp_hdr *)&ip->udp_src;
struct in_addr src_ip;
int eth_hdr_size;
switch (icmph->type) {
case ICMP_ECHO_REPLY:
src_ip = net_read_ip((void *)&ip->ip_src);
if (src_ip.s_addr == net_ping_ip.s_addr)
net_set_state(NETLOOP_SUCCESS);
return;
收到icmp reply包,然后设置NETLOOP_SUCCESS状态,退出net_loop,打印host #### is alive ,整个ping 过程结束。
网卡驱动移植就写到这边