linux 网络设备DM9000驱动分析

合集下载

DM9000网卡驱动分析

DM9000网卡驱动分析

DM9000网卡驱动分析#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 <asm/delay.h>#include <asm/irq.h>#include <asm/io.h>#if defined(CONFIG_ARCH_S3C2410)#include <mach/regs-mem.h>#endif#include "dm9000.h"/* Board/System/Debug information/definition ---------------- *///在EEPROM或PHY地址寄存器中要选择内部PHY,那么7-6位强制为01#define DM9000_PHY 0x40 /* PHY address 0x01 */#define CARDNAME "dm9000"#define DRV_VERSION "1.31"/** Transmit timeout, default 5 seconds.*///传输超时时间设定,当传输超时时调用函数dm9000_timeout(struct net_device *dev) static int watchdog = 5000;module_param(watchdog, int, 0400);//在驱动程序加载时可以重新设定watchdog MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");/* DM9000 register address locking.** The DM9000 uses an address register to control where data written* to the data register goes. This means that the address register* must be preserved over interrupts or similar calls.** During interrupt and other critical calls, a spinlock is used to* protect the system, but the calls themselves save the address* in the address register in case they are interrupting another* access to the device.** For general accesses a lock is provided so that calls which are* allowed to sleep are serialised so that the address register does* not need to be saved. This lock also serves to serialise access* to the EEPROM and PHY access registers which are shared between* these two devices.*//* The driver supports the original DM9000E, and now the two newer* devices, DM9000A and DM9000B.*/enum dm9000_type {TYPE_DM9000E, /* original DM9000 */TYPE_DM9000A,TYPE_DM9000B};/* Structure/enum declaration ------------------------------- */typedef struct board_info {//cmd脚决定了数据数据口还是地址索引void __iomem *io_addr; /* Register I/O base address */void __iomem *io_data; /* Data I/O address */u16 irq; /* IRQ */u16 tx_pkt_cnt;//当前待传输的数据包的数量,最多两个//第二个数据包长度存于此处,第二个数据包写入网卡SRAM中后要释放skb u16 queue_pkt_len;u16 queue_start_addr;u16 dbug_cnt;u8 io_mode; /* 0:word, 2:byte */u8 phy_addr;u8 imr_all;//在probe()函数中有db->flags = pdata->flags;unsigned int flags;unsigned int in_suspend :1;int debug_level;enum dm9000_type type;void (*inblk)(void __iomem *port, void *data, int length);void (*outblk)(void __iomem *port, void *data, int length);void (*dumpblk)(void __iomem *port, int length);struct device *dev; /* parent device */struct resource *addr_res; /* resources found */struct resource *data_res;//物理地址struct resource *addr_req; /* resources requested */struct resource *data_req;//I/O映射后的虚拟地址struct resource *irq_res;struct mutex addr_lock; /* phy and eeprom access lock *///在probe函数中初始化,处理函数dm9000_poll_work,链路连接状态改变 struct delayed_work phy_poll;struct net_device *ndev;spinlock_t lock;struct mii_if_info mii;u32 msg_enable;//网络入口信息} board_info_t;/* debug code */#define dm9000_dbg(db, lev, msg...) do { \if ((lev) < CONFIG_DM9000_DEBUGLEVEL && \(lev) < db->debug_level) { \dev_dbg(db->dev, msg); \} \} while (0)static inline board_info_t *to_dm9000_board(struct net_device *dev){//存取私有数据指针专用函数return netdev_priv(dev);}/* DM9000 network board routine ---------------------------- */static voiddm9000_reset(board_info_t * db){dev_dbg(db->dev, "resetting device\n");//NCR(00H):网络控制寄存器/* RESET device */writeb(DM9000_NCR, db->io_addr);udelay(200);writeb(NCR_RST, db->io_data);udelay(200);}/** Read a byte from I/O port*/static u8ior(board_info_t * db, int reg){//对网卡寄存器进行操作要先写该寄存器的偏移地址writeb(reg, db->io_addr);return readb(db->io_data);}/** Write a byte to I/O port*/static voidiow(board_info_t * db, int reg, int value){writeb(reg, db->io_addr);writeb(value, db->io_data);}/* routines for sending block to chip */static void dm9000_outblk_8bit(void __iomem *reg, void *data, int count) {writesb(reg, data, count);}static void dm9000_outblk_16bit(void __iomem *reg, void *data, int count) {writesw(reg, data, (count+1) >> 1);}static void dm9000_outblk_32bit(void __iomem *reg, void *data, int count) {writesl(reg, data, (count+3) >> 2);}/* input block from chip to memory */static void dm9000_inblk_8bit(void __iomem *reg, void *data, int count) {readsb(reg, data, count);}static void dm9000_inblk_16bit(void __iomem *reg, void *data, int count) {readsw(reg, data, (count+1) >> 1);}static void dm9000_inblk_32bit(void __iomem *reg, void *data, int count) {readsl(reg, data, (count+3) >> 2);}/* dump block from chip to null *///读出没用的数据,为了改变网卡SRAM FIFO的数据指针,static void dm9000_dumpblk_8bit(void __iomem *reg, int count){int i;int tmp;for (i = 0; i < count; i++)tmp = readb(reg);}static void dm9000_dumpblk_16bit(void __iomem *reg, int count){int i;int tmp;count = (count + 1) >> 1;for (i = 0; i < count; i++)tmp = readw(reg);}static void dm9000_dumpblk_32bit(void __iomem *reg, int count){int i;int tmp;count = (count + 3) >> 2;for (i = 0; i < count; i++)tmp = readl(reg);}/* dm9000_set_io** select the specified set of io routines to use with the* device*///设定I/O线宽static void dm9000_set_io(struct board_info *db, int byte_width) {/* use the size of the data resource to work out what IO* routines we want to use*/switch (byte_width) {case 1:db->dumpblk = dm9000_dumpblk_8bit;db->outblk = dm9000_outblk_8bit;db->inblk = dm9000_inblk_8bit;break;case 3:dev_dbg(db->dev, ": 3 byte IO, falling back to 16bit\n");case 2:db->dumpblk = dm9000_dumpblk_16bit;db->outblk = dm9000_outblk_16bit;db->inblk = dm9000_inblk_16bit;break;case 4:default:db->dumpblk = dm9000_dumpblk_32bit;db->outblk = dm9000_outblk_32bit;db->inblk = dm9000_inblk_32bit;break;}}/*延时调度。

DM9000驱动移植详解及问题点

DM9000驱动移植详解及问题点

OK6410、2.6.36内核移植,dm9000 驱动移植,详细!分类:嵌入式学习Linux学习2012-04-27 00:54 3004人阅读评论(7) 收藏举报interfaceccompressionresourcesstructtable还是先来吐槽:本来我是在上一个星期的周末已经把Linux2.6.34.11 的驱动已经成功的移植到,OK6410 的开发板上的,并且能够启动主机上的NFS 根文件系统,可是我在周一的时候,开始学习LCD 的驱动程序,在修改内核文件的时候,有几处错误修改,将原来自己做的2.6.34.11 的内核源码搞的乱七八糟的,在这里还是自己在修改内核的时候没有提注重注释,并且没有记录下来自己的操作步骤,以至于我没办法,恢复2.6.34 的内核,所以也就只能重新先开始最基础的内核移植了。

这次我选择的是2.6.36.2 的内核,谁知到一开始移植就出现一大堆问题。

在这里我不得不说,飞凌开发人员对内核修改的代码,管理真的是太扯了,自己在注销任何一个设备是没有一点点注释,就把这个设备原有的线性地址分配给其它设备了,让我让我们这些菜鸟干看着一大堆的报错信息顶个什么用,真的是伤不起。

好了不乱扯了,现在开始记录。

我的开发环境是:VMware Ubuntu 10.10 。

OK6410 A版256M+2G 的开发板。

主机系统:XP。

Uboot:飞凌提供的Uboot。

参考内核:飞凌提供的Forlinx 的2.6.36.2 内核操作步骤以下./ 均代表你的内核根目录1、修改./Makefile191 ARCH ?=arm // 指定cpu类型,arm后面不要有空格,要不然编译是会提醒ARCH 不能为一个目录192 CROSS_COMPILE ?=/usr/local/arm/4.2.2-eabi/usr/bin/arm-linux- // 指定交叉编译器的路径,按照你自己的进行指定路径2、先来说说nand flash 的驱动涉及到的文件:MTD 通用nand flash 驱动程序位置:./drivers/mtd/nand/.nand_base.cNAND Flash 的platform 设备信息: ./drivers/mtd/nand/s3c_nand.c有了上面的依赖驱动依赖程序、接下来修改./arch/arm/mach-s3c64xx/mach-smdk6410.c 1) nandflash 驱动,修改方法加载头文件[cpp]view plaincopyprint?1.#include <linux/mtd/mtd.h>2.#include <linux/mtd/partitions.h>3.#include <plat/nand.h> //这些头文件放在./arch/arm/plat-samsung/include/ 下面添加nand 结构体[cpp]view plaincopyprint?1.// add by acanoe first2.extern void s3c64xx_reserve_bootmem(void); //add by acanoe3.4.5.struct mtd_partition ok6410_nand_part[] = {6. {7. .name = "Bootloader",8. .offset = 0,9. .size = (1 * SZ_1M),10. .mask_flags = MTD_CAP_NANDFLASH,11. },12. {13. .name = "Kernel",14. .offset = (1 * SZ_1M),15. .size = (5*SZ_1M) ,16. .mask_flags = MTD_CAP_NANDFLASH,17. },18. {19. .name = "User",20. .offset = (6 * SZ_1M),21. .size = (120*SZ_1M) ,22. },23. {24. .name = "File System",25. .offset = MTDPART_OFS_APPEND,26. .size = MTDPART_SIZ_FULL,27. }28.};29.30.31.static struct s3c2410_nand_set ok6410_nand_sets[] = {32. [0] = {33. .name = "nand",34. .nr_chips = 1,35. .nr_partitions = ARRAY_SIZE(ok6410_nand_part),36. .partitions = ok6410_nand_part,37. },38.};39.40.41.static struct s3c2410_platform_nand ok6410_nand_info = {42. .tacls = 25,43. .twrph0 = 55,44. .twrph1 = 40,45. .nr_sets = ARRAY_SIZE(ok6410_nand_sets),46. .sets = ok6410_nand_sets,47.};48.//add by acanoe first修改 smdk6410_devices[] __initdata = {对照这个结构体将那些进行修改,注意 by acanoe 的语句为修改重点。

DM9000网卡驱动

DM9000网卡驱动

DM9000网卡驱动笔记网卡芯片为DM9000,ARM开发板用的是S3C6410,PC机装的是RED HAT ENTERPRISE LINUX 5.5.网卡接入soc上并没有接入到专门的驱动ic上,而是接入到了SROM的控制器上,所以需要配置的寄存器就需要配置SROM的寄存器。

S3C6410支持6页的SROM,DM9000的片选连接到了SROM控制的bank1,地址线连接的是addr2,也就是以后地址线上发出去的数据,第2位为0,数据为地址,第2位为1,数据为数据(这比较难理解)。

根据这样的地址数据的读写,操作DM9000中的寄存器,配置好网卡芯片,arm端封装一个arp请求包,在PC机上用tcpdump命令看能不能接收到如果能接受,网卡配置就算比较成功了附上源码:#include "up.h"#define INDEX (0x18000000)#define DATA (0x18000004)int (*printf)(char *, ...) = 0x57e11d4c;void (*udelay)(int ) = 0x57e00a98;struct ethhdr{unsigned char dest[6];unsigned char source[6];unsigned short type;};struct arphdr{unsigned short hw_type;unsigned short pro_type;unsigned char hw_len;unsigned char pro_len;unsigned short op;unsigned char src_mac[6];unsigned char src_ip[4];unsigned char dest_mac[6];unsigned char dest_ip[4];};void write_dm9000(unsigned char reg, unsigned short val); unsigned short read_dm9000(unsigned char reg);void init_dm9000();void send_data(unsigned char *buff, int len);int create_arp(unsigned char *buff);void *memset(void *s, int c, int len);unsigned short htons(unsigned short);int main(){/*set XM0bank1*/SROM_BW = (1 << 4);SROM_BC1 = (4 << 4) | (5 << 16);unsigned char buff[1024] = {0};int len = create_arp(buff);init_dm9000();send_data(buff, len);return 0;}void write_dm9000(unsigned char reg, unsigned short val) {*(unsigned char *)INDEX = reg;*(unsigned short *)DATA = val;}unsigned short read_dm9000(unsigned char reg) {*(unsigned char *)INDEX = reg;unsigned char c = *(unsigned char *)DATA;return c;}void init_dm9000(){//application notes//step 1: p22write_dm9000(0x1f, 0x00);//step 2:write_dm9000(0x00, 0x01);udelay(10);write_dm9000(0x00, 0x00);//step 3:write_dm9000(0x00, (1 << 3));//step 4:write_dm9000(0xff, 0xff);//step 5://step 6:write_dm9000(0x10, 0x11);write_dm9000(0x11, 0x22);write_dm9000(0x12, 0x33);write_dm9000(0x13, 0x44);write_dm9000(0x14, 0x55);write_dm9000(0x15, 0x66);//step 7://step 8:write_dm9000(0x01, (1 << 2) | (1 << 3) | (1 << 5));write_dm9000(0xfe, 0x3f);printf("3\n");//step 9://step 10:write_dm9000(0xff, 0xff);//step 11:write_dm9000(0x05, 0x01);//step 12:}void send_data(unsigned char *buff, int len){//p28://step 1:unsigned char c = read_dm9000(0xfe);if(c >> 7)printf("8 bit mode\n");elseprintf("16 bit mode\n");//step 2*(unsigned char*)INDEX = 0xf8;int tmp = (len + 1) >> 1;int i = 0;for(i=0; i<tmp; i++){*(unsigned short *)DATA = ((unsigned short*)buff)[i];}//step 3write_dm9000(0xfd, (len >> 8) & 0xff);write_dm9000(0xfc, len & 0xff);//step 4write_dm9000(0x02, 0x01);}int create_arp(unsigned char *buff){int len = 0;struct ethhdr *eth = (struct ethhdr *)buff;eth->dest[0] = 0xff;eth->dest[1] = 0xff;eth->dest[2] = 0xff;eth->dest[3] = 0xff;eth->dest[4] = 0xff;eth->dest[5] = 0xff;eth->source[0] = 0x11;eth->source[1] = 0x22;eth->source[2] = 0x33;eth->source[3] = 0x44;eth->source[4] = 0x55;eth->source[5] = 0x66;eth->type = htons(0x0806);struct arphdr *arp = (struct arphdr *)(buff + sizeof(struct ethhdr));arp->hw_type = htons(1);arp->pro_type = htons(0x0800); arp->hw_len = 6;arp->pro_len = 4;arp->op = htons(1);arp->src_mac[0] = 0x11;arp->src_mac[1] = 0x22;arp->src_mac[2] = 0x33;arp->src_mac[3] = 0x44;arp->src_mac[4] = 0x55;arp->src_mac[5] = 0x66;arp->src_ip[0] = 192;arp->src_ip[1] = 168;arp->src_ip[2] = 1;arp->src_ip[3] = 2;arp->dest_mac[0] = 0xff;arp->dest_mac[1] = 0xff;arp->dest_mac[2] = 0xff;arp->dest_mac[3] = 0xff;arp->dest_mac[4] = 0xff;arp->dest_mac[5] = 0xff;arp->dest_ip[0] = 192;arp->dest_ip[1] = 168;arp->dest_ip[2] = 1;arp->dest_ip[3] = 1;len = sizeof(struct ethhdr) + sizeof(struct arphdr);return len;}void *memset(void *s, int c, int len){int i = 0;for(i=0; i<len; i++)((unsigned char *)s)[i] = c;}unsigned short htons(unsigned short data){unsigned short ret = 0;ret = ((data >> 8) & 0xff) | ((data << 8) & 0xff00);return ret;。

成功移植U-BOOT之---- DM9000网卡驱动

成功移植U-BOOT之---- DM9000网卡驱动
保存退出
5、修改完成,开始编译
make TQ2440_config
make CROSS_COMPILE=arm-linux-
把编译后在顶层目录生成的 u-boot.bin 通过 SAMBA 拷出来,通过 JTAG 或 JLINK 烧到开发 板!
-------- -------------- -------
…… /*修改环境变量*/ #define CONFIG_ETHADDDR 00:01:02:03:04:05 #define CONFIG_NETMASK 255.255.255.0 #define CONFIG_IPADDR 192.168.0.61 #define CONFIG_SERVERIP 192.168.0.60
case 2: printf("10M case 4: printf("100M half duplex ");
break; case 8:
printf("100M full duplex "); break; default: printf("unknown: %d ", lnk); break; } p rintf("mo d e \n");
#endif
return 0; }
重新编译,再烧写,问题解除。
今天成功的在天嵌 TQ2440 的板子上完成了 U-BOOT -DM9000 网卡驱动的移植!
实验使用的是国嵌实验 4-2-4 修改过的源码(u-boot-basic.tar.gz) 废话不说,记笔记:
mkdir 4-2-4 cp u-boot-basic.tar.gz 4-2-4/ cd 4-2-4 tar zxvf u-boot-basic.tar.gz

dm9000

dm9000

1、先看芯片手册,弄懂dm9000基本工作原理和寄存器含义之后,再看代码2、看代码时用source insight,可以快速查看定义,代码的大致流程如下,其中标红的函数再仔细看看。

3、先把关于VxWorks驱动介绍的PPT做好,介绍大体框架,具体的程序不用着急着一步到位dm9000start()cpuForDM9000Init() →配置CPU,使用EINT7SYS_INT_CONNECT (pDrvCtrl, dm9000Int, (int)pDrvCtrl, &result); →注册中断处理函数dm9000IntintEnable (pDrvCtrl->ilevel); →使能该设备的中断/* Activate DM9000 */ →配置dm9000的0x05和0xff寄存器DM9000_OUT_CHAR( 0x05, DM9000_REG05 ); /* RX enable */DM9000_OUT_CHAR( 0xff, DM9000_REGFF );/* Enable TX/RX interrupt mask */dm9000Stop →→ dmfe_stop_dm9000( pDrvCtrl );→ cpuForDm9000disable();配置CPU,设置EINT7掩码→ SYS_INT_DISCONNECT (pDrvCtrl, dm9000Int, &result);注销中断函数dm9000Int→中断处理函数(详细分析。

)接收→netJobAdd ((FUNCPTR)dm9000HandleRcvInt, (int)pDrvCtrl, 0,0,0,0);→dm9000HandleRcvInt→dm9000Recv(详细分析。

vxworks网络数据结构MBlock)→dmfe_packet_receive发送→DM9000_OUT_CHAR( 0xfc, pDrvCtrl->queue_pkt_len & 0xff );→DM9000_OUT_CHAR( 0xfd, (pDrvCtrl->queue_pkt_len >> 8) & 0xff ); dm9000Send→intLock ()→dmfe_start_xmit( &skb, pDrvCtrl )→intUnLock ()dm9000PollStart →启动dm9000轮询模式dm9000PollRcv →轮询方式接收数据→ dmfe_packet_receive(详细分析。

移植调试dm9000网卡驱动(U-Boot)

移植调试dm9000网卡驱动(U-Boot)

这样就到了 DM9000A 的驱动部分代码。 DM9000A 的接收数据函数: eth_rx(void) { u8 rxbyte, *rdptr = (u8 *) NetRxPackets[0]; u16 RxStatus, RxLen = 0; u32 tmplen, i;
// DM9000A 接收缓冲区中的数据格中格式是 4 字节的包头+真正的数据. // 其中 4 字节包头分别位为: rxbyte, status, count_low, count_hight // 说明如下: // rxbyte: 0-没有接收到数据, 1-有新的数据包 // status: 状态 // count_low, count_hight: 接收到的数据长度
/*******************************************************/ 包含两部分内容:配置说明和调试。
1 配置说明 Uboot 可以使用 RS232 和主机进行通讯,但其最大的波特率不过是 115200,1 秒最多只能 传送 11,520byte 的数据,如果说是传输 1.5M 的内核文件,计算下来将需要 133 秒,2 分钟的 时间,如果是传送 32M 的根文件系统文件,那么传输时间将是非常惊人的! 为了提高效率我们选用以太网传输,Uboot 本身也提供了网络的支持。 本系统中使用的网络芯片是 DM9000A,Uboot 的 driver 目录中包含了该芯片的驱动,文件 名为 dm9000x.c 和 dm9000x.h。要加入驱动的支持,必须在 config 文件中增加对 DM9000A
在这里#define CONFIG_DM9000_BASE 的定义最为重要。不同的板子只要修改这个参数即可。友善之臂的 SBC2440V4 上的 DM9000 在 BANK4 上所以定义为“0x20000300”。有 的板子是在 BANK1 上,就为“0x08000300”。

U-BOOT_DM9000驱动完全注释

U-BOOT_DM9000驱动完全注释

int eth_init(bd_t * bd);
//DM9000 网卡初始化
int eth_send(volatile void *, int);
//将来自上层的数据包发送到媒介上
int eth_rx(void);
//接收数据包并且发送到上层去
void eth_halt(void);
//关闭网卡
static int dm9000_probe(void);
{
u16 phy_reg4 = 0x01e1, phy_reg0 = 0x1000;
if (!(media_mode & DM9000_AUTO)) {
switch (media_mode) { case DM9000_10MHD:
//10M 半双工
phy_reg4 = 0x21;
//设置双工、半双工
return -1;
}
}
//*======================================================
==========
//函数名称:s e t_P HY_mod e
//函数功能:设置 PHY 芯片的操作模式 。若设置的是自动选择,则直接配置,若不是则可以选择
//
/* #define CONFIG_DM9000_DEBUG */ #define DM9000_DBG(fmt,args...) //*====================================================== ========== //DM9000 芯片的 PHY 层模式 //*====================================================== ========== enum DM9000_PHY_mode { DM9000_10MHD = 0, DM9000_100MHD =

DM9000调试血泪总结http...

DM9000调试血泪总结http...

DM9000调试血泪总结http...因为需要使用DM9000,因此就有了这条血泪之路。

话说那是一个月前,板子刚回来,感觉难度不大,因为此前搞过F4板子上的网络,对LWIP的移植和使用相对比较熟悉,所以花了3天的时间就把DM9000的驱动写好了,其实就是FSMC的配置,然后就是内部寄存器的读写,这个不难,驱动写完以后就是LWIP的移植,花了2个小时左右就移植成功了,成功以后开ping,ping成功,兴奋啊!但是在做webserver的时候发现网速很慢,而且很容易就会挂掉的!因此做了一次稳定性测试,就是连续的ping好几个小时,但是发现ping到0.5-2个小时的时候就会挂掉!!心碎啊,没办法,度娘,找各种DM9000在STM32上的程序,发现就那么几个,大多是就与RTT的DM9000例程修改的,就是在RTT例程的基础上修改一下IO口而已。

而RTT对LWIP做了修改,而且移植方法和常用的不同,因此就排除了RTT的例程。

但是将RTT的例程做了小修改,在我的板子上可以跑下去,这样可以确定板子硬件有没有问题!经过测试ping很稳定,时间基本小于1ms的。

这说明什么?说明我的代码有问题啊,初步怀疑是接收和发送函数有问题,接着又是各种度娘啊,还是没有什么用啊,当时那个心情啊!本以为很容易的一件事结果拖了好几个星期了!!心急啊,但是又有什么办法啊,面对庞大的UCOSII和LWIP协议栈,顿时慌了手脚啊,当时感觉那个无助啊,就像被抛弃到了一个荒岛一样!前两天再看其他参考例程的时候突然灵光一现,发现虽然其他代码都使用了DM9000的中断,但是却没有一个在DM9000的中断中接收数据,而是在中断中发送一个信号量(参考的代码都是带系统的),其他的任务接收该信号量,做任务同步,数据的接收在专门的接收任务中完成!!而我的数据接收是直接放在了DM9000的中断服务函数中,在进入中断的时候是要关闭中断的,这样有新的数据来时就会塞进MD9000的RX SRAM中,很容易导致overflow!接着就是死掉!昨天把数据的接收放到了一个任务中,中断里面也只是发送一个信号量,这样关中断时间就大大减小了,经过测试,ping好几个小时都没有挂掉,ping的时间基本也小于等于1ms。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

说明1:本文分析基于内核源码版本为linux-2.6.31说明2:本文在理解了linux中总线、设备和驱动模型的基础上加以分析代码虽然Linux驱动程序应该是和具体的硬件平台分离的,但是为了更好的理解DM9000的驱动程序,这里还是结合一下Mini2440开发板,这样也可以更好的体会如何实现驱动和平台分离。

本文分成以下几个部分:一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系。

二、两个重要的结构体介绍:sk_buff和net_device三、具体代码分析一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系Mini2440开发板上DM9000与S3C2440的连接关系如下:其中片选信号AEN使用了nGCS4,所以网卡的内存区域在BANK4,也就是从地址0x20000000开始。

DM9000的TXD[2:0]作为 strap pin在电路图中是空接的,所以IO base是300H。

中断使用了EINT7。

这些内容在Mach文件中有如下体现:点击(此处)折叠或打开1.01.#define S3C2410_CS4 (0x20000000)2.02.#define MACH_MINI2440_DM9K_BASE (S3C2410_CS4 + 0x300)3.03.static struct resource mini2440_dm9k_resource[] __initdata ={4.04.[0]={5.05..start = MACH_MINI2440_DM9K_BASE,6.06..end= MACH_MINI2440_DM9K_BASE + 3,7.07..flags = IORESOURCE_MEM8.08.},9.09.[1]={10.10..start = MACH_MINI2440_DM9K_BASE + 4,11.11..end= MACH_MINI2440_DM9K_BASE + 7,12.12..flags = IORESOURCE_MEM13.13.},14.14.[2]={15.15..start = IRQ_EINT7,16.16..end= IRQ_EINT7,17.17..flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,18.18.}19.19.};另外在Mach文件中还定义了DM9000平台设备,设备名称为“dm9000”,设备资源就是上面定义的IO和中断资源。

代码清单如下:点击(此处)折叠或打开1.01.static struct dm9000_plat_data mini2440_dm9k_pdata __initdata ={2.02..flags =(DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),3.03.};4.04.5.05.static struct platform_device mini2440_device_eth __initdata ={ ="dm9000",7.07..id =-1,8.08..num_resources = ARRAY_SIZE(mini2440_dm9k_resource),9.09..resource = mini2440_dm9k_resource,10.10..dev ={11.11..platform_data =&mini2440_dm9k_pdata,12.12.},13.13.};这个DM9000平台设备作为众多平台设备中的一个在扳子初始化的时候就被添加到了总线上。

代码清单如下:点击(此处)折叠或打开1.01.MACHINE_START(MINI2440,"MINI2440")2.02./* Maintainer: Michel Pollet <buserror@>*/3.03..phys_io = S3C2410_PA_UART,4.04..io_pg_offst =(((u32)S3C24XX_VA_UART)>> 18)& 0xfffc,5.05..boot_params = S3C2410_SDRAM_PA + 0x100,6.06..map_io = mini2440_map_io,7.07..init_machine = mini2440_init,/*初始化函数*/8.08..init_irq = s3c24xx_init_irq,9.09..timer =&s3c24xx_timer,10.10.MACHINE_END点击(此处)折叠或打开1.01.static void __init mini2440_init(void)2.02.{3.03....4.04....5.05. platform_add_devices(mini2440_devices,ARRAY_SIZE(mini2440_devices));6.06.7.07....8.08....9.09.}点击(此处)折叠或打开1.01.static struct platform_device *mini2440_devices[] __initdata ={2.02.&s3c_device_usb,3.03.&s3c_device_wdt,4.04./*&s3c_device_adc,*//* ADC doesn't like living with touchscreen !*/5.05.&s3c_device_i2c0,6.06.&s3c_device_rtc,7.07.&s3c_device_usbgadget,8.08.&mini2440_device_eth,/*dm9000是众多平台设备中的一个*/9.09.&mini2440_led1,10.10.&mini2440_led2,11.11.&mini2440_led3,12.12.&mini2440_led4,13.13.&mini2440_button_device,14.14.&s3c_device_nand,15.15.&s3c_device_sdi,16.16.&s3c_device_iis,17.17.&mini2440_audio,18.18./*&s3c_device_timer[0],*//* buzzer pwm, no API for it */19.19./* remaining devices are optional */20.20.};二、两个重要的结构体简单介绍:sk_buff和net_device*sk_buff如果把网络传输看成是运送货物的话,那么sk_buff就是这个“货物”了,所有经手这个货物的人都要干点什么事儿,要么加个包装,要么印个戳儿等等。

收货的时候就要拆掉这些包装,得到我们需要的货物(payload data)。

没有货物你还运输什么呢?由此可见sk_buff的重要性了。

关于sk_buff的详细介绍和几个操作它的函数,参考:“linux内核 sk_buff的结构分析”/Linux/2011-07/39163.htm,写得非常明白了。

赞一个~*net_device又是一个庞大的结构体。

好吧,我承认我从来就没有看全过这个结构体。

它在内核中就是指代了一个网络设备。

驱动程序需要在探测的时候分配并初始化这个结构体,然后使用register_netdev来注册它,这样就可以把操作硬件的函数与内核挂接在一起。

三、具体代码的分析在顺序分析之前先看三个结构体变量和一个自定义的结构体。

* dm9000_driver变量。

是platform_driver结构体变量,其中包含了重要的:驱动的名字(用来match)和几个重要操作函数。

点击(此处)折叠或打开1.01.static struct platform_driver dm9000_driver ={2.02..driver ={ ="dm9000",4.04..owner = THIS_MODULE,5.05.},6.06..probe = dm9000_probe,7.07..remove = __devexit_p(dm9000_drv_remove),8.08..suspend = dm9000_drv_suspend,9.09..resume= dm9000_drv_resume,10.10.};* dm9000_netdev_ops变量。

是net_device_ops结构体变量,其中定义了操作net_device的重要函数,我们在驱动程序中根据需要的操作要填充这些函数。

代码清单如下:点击(此处)折叠或打开1.01.static const struct net_device_ops dm9000_netdev_ops ={2.02..ndo_open = dm9000_open,3.03..ndo_stop = dm9000_stop,4.04..ndo_start_xmit = dm9000_start_xmit,5.05..ndo_tx_timeout = dm9000_timeout,6.06..ndo_set_multicast_list = dm9000_hash_table,7.07..ndo_do_ioctl = dm9000_ioctl,8.08..ndo_change_mtu = eth_change_mtu,9.09..ndo_validate_addr = eth_validate_addr,10.10..ndo_set_mac_address = eth_mac_addr,11.11.#ifdef CONFIG_NET_POLL_CONTROLLER12.12..ndo_poll_controller = dm9000_poll_controller,13.13.#endif14.14.};* dm9000_ethtool_ops变量。

是ethtool_ops结构体变量,为了支持ethtool,其中的函数主要是用于查询和设置网卡参数(当然也有的驱动程序可能不支持ethtool)。

代码清单如下:点击(此处)折叠或打开1.01.static const struct ethtool_ops dm9000_ethtool_ops ={2.02..get_drvinfo = dm9000_get_drvinfo,3.03..get_settings = dm9000_get_settings,4.04..set_settings = dm9000_set_settings,5.05..get_msglevel = dm9000_get_msglevel,6.06..set_msglevel = dm9000_set_msglevel,7.07..nway_reset = dm9000_nway_reset,8.08..get_link = dm9000_get_link,9.09..get_eeprom_len = dm9000_get_eeprom_len,10.10..get_eeprom = dm9000_get_eeprom,11.11..set_eeprom = dm9000_set_eeprom,12.12.};* board_info结构体。

相关文档
最新文档