pc linux ( 192.168.0.200 )
board linux ( 192.168.0.201 )
ifconfig sudo ifconfig eth0 192.168.0.xxx
测试网络连接 对ping可以连通
开发板上的跳线 S2 拨到 SDBOOT 那边,通过 SD 卡启动
启动之后进入 uboot 选项,修改环境变量
[FriendlyLEG-TINY210]# printenvbaudrate=115200bootdelay=3ethact=dm9000ethaddr=00:01:02:03:04:05gatewayip=192.168.0.1ipaddr=192.168.0.201netmask=255.255.255.0serverip=192.168.0.200stderr=serialstdin=serialstdout=serial[FriendlyLEG-TINY210]# setenv ipaddr 192.168.0.201[FriendlyLEG-TINY210]# setenv serverip 192.168.0.200[FriendlyLEG-TINY210]# saveenvSaving Environment to NAND...Erasing Nand...Erasing at 0x40000 -- 100% complete.Writing to Nand... done[FriendlyLEG-TINY210]#
重启开发板,验证是否设置成功? 看到 is alive 表示成功
FriendlyLEG-TINY210# ping 192.168.0.200
dm9000 i/o: 0x88001000, id: 0x90000a46 DM9000: running in 16 bit mode
MAC: 00:01:02:03:04:05
operating at 100M full duplex mode
Using dm9000 device
host 192.168.0.200 is alive
FriendlyLEG-TINY210#
Install tftpd and related packages.
$ sudo apt-get install xinetd tftpd tftp
Create /etc/xinetd.d/tftp and put this entry:
service tftp{ protocol = udp port = 69 socket_type = dgram wait = yes user = nobody server = /usr/sbin/in.tftpd server_args = /tftpboot disable = no}
Make /tftpboot directory
$ sudo mkdir /tftpboot
$ sudo chmod -R 777 /tftpboot
$ sudo chown -R nobody /tftpboot
Start tftpd through xinetd
$ sudo /etc/init.d/xinetd restart
test tftp in localhost
$ cp something /tftpboot
$ tftp localhost$ get something Received 4315348 bytes in 0.4 seconds$ tftp> quit$ ls something
test tftp in board $ tftp 21000000 zImage
# setenv bootargs root=/dev/mtdblock4 console=ttySAC0,115200 init=/linuxrc lcd=S70# setenv ipaddr 192.168.0.201# setenv serverip 192.168.0.200# saveenv# printenv
# tftp 21000000 uImage# bootm 21000000
arch/arm/mach-s5pv210/mach-mini210.c
drivers/net/dm9000.c
drivers/net/dm9000.h include/linux/dm9000.h
.config
make menuconfig 的配置选项保存在这个文件中
CONFIG_DM9000 = m
include/generated/autoconf.h
make 执行时会通过脚本分析 .config 文件,生成 autoconf.h 参与内核编译
#define CONFIG_DM9000 1
driver/net/makefile
obj-$(CONFIG_DM9000) = dm9000.o
obj-m := dm9000.o
KDIR := /home/limingth/tiny210/src/linux-2.6.35.7
modules: make -C $(KDIR) SUBDIRS=$(PWD) $@ ls -l dm9000.ko
clean: -rm .o.ko
install tftpd
sudo apt-get install tftpd
install inetd
sudo apt-get install openbsd-inetd
server dir /srv/tftp
sudo vi /etc/inetd.conf
#:BOOT: TFTP service is provided primarily for booting. Most sites# run this only on machines acting as "boot servers."#tftp dgram udp wait nobody /usr/sbin/tcpd /usr/sbin/in.tftpd /srv/tftptftp dgram udp wait nobody /usr/sbin/tcpd /usr/sbin/in.tftpd /home/limingth/tiny210
sudo /etc/init.d/openbsd-inetd restart
download Image
limingth@ubuntu:~$ tftp 127.0.0.1
tftp> get zImage2
tftp> quit
change u-boot bootargs
操作步骤
FriendlyLEG-TINY210# setenv bootargs root=/dev/mtdblock4 console=ttySAC0,115200 init=/linuxrc lcd=S70 FriendlyLEG-TINY210# printenv baudrate=115200 bootargs=root=/dev/mtdblock4 console=ttySAC0,115200 init=/linuxrc lcd=S70 bootdelay=3 ethact=dm9000 ethaddr=08:0a:0a:0a:0a:0a gatewayip=192.168.0.1 ip=192.168.0.201 ipaddr=192.168.0.201 netmask=255.255.255.0 serverip=192.168.0.200 stderr=serial stdin=serial stdout=serial
Environment size: 315/16380 bytes
* 启动命令
tftp 21000000 uImage bootm 21000000
按以下方法修改内核以适应uboot,详参见liukun321的文章
http://blog.csdn.net/liukun321/article/details/7383669, 忽略此步骤将会出现加载kernel时卡死在 Uncompressing Linux… done, booting the kernel….
arch/arm/mach-s5pv210/include/mach/memory.h 文件26,27行内容, 将Maximum of 256MiB in one bank的限制改为Maximum of 512MiB in one bank 作如下修改:#defineSECTION_SIZE_BITS 29#defineNODE_MEM_SIZE_BITS 29
limingth@ubuntu:~/tiny210/src$ ./mkimage Usage: ./mkimage -l image -l ==> list image header information ./mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image -A ==> set architecture to 'arch' -O ==> set operating system to 'os' -T ==> set image type to 'type' -C ==> set compression type 'comp' -a ==> set load address to 'addr' (hex) -e ==> set entry point to 'ep' (hex) -n ==> set image name to 'name' -d ==> use image data from 'datafile' -x ==> set XIP (execute in place) ./mkimage [-D dtc_options] -f fit-image.its fit-image ./mkimage -V ==> print version information and exit limingth@ubuntu:~/tiny210/src$ ./mkimage -A arm -O linux -T kernel -C none -a 21000000 -e 21000040 -n "linux-2.6.28" -d zImage2 myuImageImage Name: linux-2.6.28Created: Wed Aug 29 13:32:34 2012Image Type: ARM Linux Kernel Image (uncompressed)Data Size: 4288476 Bytes = 4187.96 kB = 4.09 MBLoad Address: 21000000Entry Point: 21000040
/include/generated/mach-types.h
./arch/arm/include/asm/mach-types.h
limingth@ubuntu:~/tiny210/src/linux-2.6.35.7$ vi arch/arm/include/asm/mach-types.h limingth@ubuntu:~/tiny210/src/linux-2.6.35.7$ vi include/generated/mach-types.h in line 2950 #define MACH_TYPE_MINI210 3466
#include <linux/module.h>#include <linux/ioport.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/init.h>#include <linux/skbuff.h>#include <linux/spinlock.h>#include <linux/crc32.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <linux/dm9000.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/irq.h>#include <linux/slab.h>#include <asm/delay.h>#include <asm/irq.h>#include <asm/io.h>#include "dm9000.h"MODULE_AUTHOR("AKAE");MODULE_DESCRIPTION("Sample DM9000 driver");MODULE_LICENSE("Dual BSD/GPL");#define DEVICE_NAME "dm9000"/*board_info结构体,用来保存芯片相关的一些私有信息*/typedef struct board_info { void __iomem *io_addr; /*映射到Linux内存空间的地址口虚拟地址*/ void __iomem *io_data; /*映射到Linux内存空间的数据口虚拟地址*/ u16 irq; /*IRQ*/ u16 tx_pkt_cnt; /*待发送数据包数量,最多只能有两个*/ u16 queue_pkt_len; /*待发送数据包长度*/ u8 imr_all; /*用于给DM9000_IMR赋值的一个变量*/ struct resource *addr_res; /*地址口地址*/ struct resource *data_res; /*数据口地址*/ struct resource *irq_res; /*IRQ*/ struct mutex addr_lock; /*互斥信号两*/ spinlock_t lock; /*自旋锁*/} board_info_t;/*Read a byte from I/O port*/static u8 ior(board_info_t * db, int reg){ writeb(reg, db->io_addr); return readb(db->io_data);}/*Write a byte to I/O port*/static void iow(board_info_t * db, int reg, int value){ writeb(reg, db->io_addr); /*将寄存器地址写入映射进内存的i\o内存空间,通过地址找到相应的寄存器*/ writeb(value, db->io_data); /*将数据写入寄存器*/}/*调用时机:当网卡有数据需要发送的时候,该函数被调用*//*第二个包的发送将在dm9000_tx_done中实现,这是因为当第一个数据发送完之后会产生一个中断,则会调用dm9000_tx_done对应的函数*/static int dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev){ unsigned long flags; board_info_t *db = netdev_priv(dev); /*获得自旋锁并禁止中断,中断状态保存在flags中*/ spin_lock_irqsave(&db->lock, flags); /*将io_addr写入寄存器MWCMD(写数据到DM9000内的发送SRAM中),进行这个操作后,向io_data写入的数据会传输到dm9000内部TX_SRAM中*/ writeb(DM9000_MWCMD, db->io_addr); writesw(db->io_data, skb->data, (skb->len+1) >> 1); /*待发送的数据包数量(tx_pkt_cnt)加1*/ db->tx_pkt_cnt++; /* TX control: First packet immediately send, second packet queue */ if (db->tx_pkt_cnt == 1) { iow(db, DM9000_TXPLL, skb->len); iow(db, DM9000_TXPLH, skb->len >> 8); iow(db, DM9000_TCR, TCR_TXREQ); } else { /* Second packet */ db->queue_pkt_len = skb->len; } /*释放自旋锁,并恢复中断之前的状态*/ spin_unlock_irqrestore(&db->lock, flags); /*每个数据包写入网卡SRAM后都要释放skb*/ dev_kfree_skb(skb); return NETDEV_TX_OK;}static void dm9000_tx_done(struct net_device *dev, board_info_t *db){ /*读取dm9000寄存器NSR(NetworkStatus Register)获取发送的状态,存在变量tx_status中*/ int tx_status = ior(db, DM9000_NSR); /*若有数据包发送完成,则进入*/ if (tx_status & (NSR_TX2END | NSR_TX1END)) { /*待发送的数据包数量(tx_pkt_cnt)减1*/ db->tx_pkt_cnt--; /*已发送的数据包数量(stats.tx_packets)加1*/ dev->stats.tx_packets++; /*检查变量tx_pkt_cnt是否大于0(如果大于0,表明还有数据包要发送),则再次发送*/ if (db->tx_pkt_cnt > 0) { /*将要发送的数据包的长度写入DM9000的两个发送数据包长度寄存器中*/ iow(db, DM9000_TXPLL, db->queue_pkt_len); iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8); /*提出发送请求,发送完成后该位自动清零*/ iow(db, DM9000_TCR, TCR_TXREQ); } }}/*该结构体封装了dm9000接收的数据包的头信息*/ struct dm9000_rxhdr { u8 RxPktReady; u8 RxStatus; __le16 RxLen;} __attribute__((__packed__));/*告诉编译器取消结构在编译过程中的优化*/static void dm9000_rx(struct net_device *dev){ board_info_t *db = netdev_priv(dev); struct dm9000_rxhdr rxhdr; struct sk_buff *skb; u8 rxbyte, *rdptr; int RxLen; /*接收数据包的长度*/ /* Check packet ready or not */ do { /* 读取寄存器MRCMDX,MRCMDX寄存器地址保存在了db->io_addr中,下面要读取MRCMDX寄存器的值只需要读取db->io_data即可*/ ior(db, DM9000_MRCMDX); /*读取MRCMDX寄存器的值,rxbyte为8位,因为MRCMDX寄存器存储的值就是8位*/ rxbyte = readb(db->io_data); /*DM9000_PKT_RDY定义为0x01,而rxbyte为我们读取的第一个字节,其值只能是0x00(表示还没接收)或0x01(表示已经接收)*/ if (!(rxbyte & DM9000_PKT_RDY)) return; /*将MRCMD寄存器地址写入db->io_addr*/ writeb(DM9000_MRCMD, db->io_addr); /*一次性从MRCMD寄存器(即RX_SRAM)中读入四个字节的内容到rxhdr变量,*/ readsw(db->io_data, &rxhdr, (sizeof(rxhdr)+1) >> 1); /*获取接收数据包的长度*/ RxLen = le16_to_cpu(rxhdr.RxLen); skb = dev_alloc_skb(RxLen + 4); skb_reserve(skb, 2); rdptr = (u8 *) skb_put(skb, RxLen - 4); /*读取数据放入rdptr所在位置,rdptr位置为skb_tail,所以这句话就是读取RX_SRAM内容到skb*/ readsw(db->io_data, rdptr, (RxLen+1) >> 1); dev->stats.rx_bytes += RxLen; /*函数eth_type_trans用于从以太网数据包中提取网络协议内容,并把它放入skb结构的相应位置*/ skb->protocol = eth_type_trans(skb, dev); /*调用netif_rx将数据交给协议栈*/ netif_rx(skb); } while (rxbyte & DM9000_PKT_RDY);}/*网卡有三种类型的中断:新报文到达中断,报文发送完成中断,出错中断*/static irqreturn_t dm9000_interrupt(int irq, void *dev_id){ struct net_device *dev = dev_id; board_info_t *db = netdev_priv(dev); int int_status; unsigned long flags; u8 reg_save; /*获得自旋锁并禁止中断,中断状态保存在flags中*/ spin_lock_irqsave(&db->lock, flags); /*保存中断前的状态*/ reg_save = readb(db->io_addr); /* 禁用DM9000中断*/ iow(db, DM9000_IMR, IMR_PAR); /*中断状态寄存器,当一个中断到来时,该寄存器存放着中断类型。DM9000中断处理函数通过读取该寄存器,得到目前中断信息*/ /*读取该中断状态寄存器之后,还需要将读取结果存放回该寄存器,也就是需要清除中断状态,否则将无法再次响应中断*/ int_status = ior(db, DM9000_ISR); iow(db, DM9000_ISR, int_status); /*检测中断状态寄存器,如果是由于收到数据而触发的中断,显然调用dm9000_rx()把数据取走,传递给上层*/ if (int_status & ISR_PRS) dm9000_rx(dev); /*检测中断状态寄存器,如果是由于发送完了数据而触发的中断,则调用dm9000_tx_done()函数*/ if (int_status & ISR_PTS) dm9000_tx_done(dev, db); /*重新使能DM9000各中断功能*/ iow(db, DM9000_IMR, db->imr_all); /*恢复中断前的状态*/ writeb(reg_save, db->io_addr); /*自旋锁解锁*/ spin_unlock_irqrestore(&db->lock, flags); return IRQ_HANDLED;}/*当使用命令ifconfig eth0 up时,网卡被打开,执行这一函数,向内核注册中断,复位并初始化dm9000,检查MII接口,使能传输等*/static int dm9000_open(struct net_device *dev){ /*返回board_info_t的地址*/ board_info_t *db = netdev_priv(dev); /*IRQF_TRIGGER_MASK为中断触发方式,定义在Interrupt.h中*/ unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK; /*设置为共享中断*/ irqflags |= IRQF_SHARED; /*申请中断并注册中断服务程序*/ request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev); /*复位dm9000*/ iow(db, DM9000_NCR, 1); do { udelay(100); } while (ior(db, DM9000_NCR) & 0x1); /*初始化DM9000*/ /*DM9000的GPIO0默认为输出做POWER_DOWN功能,默认值为1,表明要使PHY层POWER_DOWN,即不启用PHY, 若希望启用PHY,则驱动程序需要通过写“0”将PWER_DOWN信号清零*/ iow(db, DM9000_GPR, 0); /* Enable PHY */ /*使能数据包接收中断,使能数据包传输中断*/ db->imr_all = IMR_PAR | IMR_PTM | IMR_PRM; iow(db, DM9000_IMR, db->imr_all); /*设置DM9000的RCR接收控制寄存器的第0位为1,接收使能*/ iow(db, DM9000_RCR, RCR_RXEN); return 0;}/*驱动支持的网卡设备操作函数*/static const struct net_device_ops dm9000_netdev_ops = { .ndo_open = dm9000_open, .ndo_start_xmit = dm9000_start_xmit,};/*内核加载驱动后,自动执行驱动的probe函数,进行资源的探测和申请资源*/static int __devinit dm9000_probe(struct platform_device *pdev){ struct board_info *db; /*将获得的资源信息存放在这个结构体里*/ struct net_device *ndev; /*定义一个网络设备*/ int iosize; int i; u32 id_val; /*分配ndev,使用alloc_etherdev()函数分配一个网络设备的结构体,原型在include/linux/etherdevice.h */ ndev = alloc_etherdev(sizeof(struct board_info)); /*通过SET_NETDEV_DEV()将platform_device与net_device接洽关联起来*/ SET_NETDEV_DEV(ndev, &pdev->dev); /*下面都是设置board_info结构体,将获得的资源信息存放在这个结构体里*/ /*struct board_info *db和struct net_device *ndev都是局部变量。但是又需要board_info和net_device二者建立一一对应关系,而 board_info 是一个自定义结构,通过netdev_priv(ndev)就把board_info放入net_device里,建立了联系。创建net_device 时已经为 board_info 留了空间:ndev = alloc_etherdev(sizeof(struct board_info));*/ db = netdev_priv(ndev);/*将网络设备结构与自定义设备结构建立关联,获取net_device结构的私有成员保存到struct board_info *db中*/ spin_lock_init(&db->lock); /*初始化自旋锁*/ mutex_init(&db->addr_lock); /*初始化互斥信号量*/ db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /*获取DM9000地址口的资源地址*/ db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); /*获取DM9000数据口的资源地址*/ db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); /*获取DM9000中断资源*/ iosize = resource_size(db->addr_res); /*将地址口映射到linux内存空间的虚拟地址*/ db->io_addr = ioremap(db->addr_res->start, iosize); iosize = resource_size(db->data_res); /*将数据口映射到linux内存空间的虚拟地址*/ db->io_data = ioremap(db->data_res->start, iosize);/*****************************设置结构体board_info结束***************************************************/ /*填充net_device结构体*/ ndev->base_addr = (unsigned long)db->io_addr; /*设置网络设备地址*/ ndev->irq = db->irq_res->start; /*设置网络设备中断资源地址*/ /*获取DM9000的ID号,需要多次获得以免失败*/ for (i = 0; i < 8; i++) { id_val = ior(db, DM9000_VIDL); id_val |= (u32)ior(db, DM9000_VIDH) << 8; id_val |= (u32)ior(db, DM9000_PIDL) << 16; id_val |= (u32)ior(db, DM9000_PIDH) << 24; if (id_val == DM9000_ID) break; } /*借助ether_setup()函数来部分初始化ndev。因为对以太网设备来讲,很多操作与属性是固定的,内核可以帮助完成*/ ether_setup(ndev); /*驱动支持的网卡设备操作函数*/ ndev->netdev_ops = &dm9000_netdev_ops; /*注册ndev网络设备*/ register_netdev(ndev); return 0;}static struct platform_driver dm9000_driver = { .driver = { .name = "dm9000", /*驱动的名字*/ .owner = THIS_MODULE, }, .probe = dm9000_probe, /*重要的函数:资源探测函数*/};/*第一步:加载网卡驱动首先要被执行的*/static int __init dm9000_init(void){ printk(KERN_INFO "%s Ethernet Driver\n", DEVICE_NAME); return platform_driver_register(&dm9000_driver); /*调用函数platform_driver_register()函数注册驱动*/}static void __exit dm9000_cleanup(void){ platform_driver_unregister(&dm9000_driver);}module_init(dm9000_init);module_exit(dm9000_cleanup);
联系客服