s3c2440 uboot 移植 (三)支持DM9000网卡

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 过程结束。

网卡驱动移植就写到这边

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值