2014-04-21 16:18:06 qiao_yihan 阅读数 1251
  • 网络设备驱动介绍-linux驱动开发第11部分

    本课程是linux驱动开发的第11个课程,主要内容是linux的网络驱动的介绍,首先讲述了网络设备驱动接口和之前讲的2种的不同,然后以一个虚拟网卡驱动源码学习了网卡驱动的框架,后分析了一个实际网卡DM9000的驱动细节。

    4314 人正在学习 去看看 朱有鹏

【linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析 

【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的资源和设备以及几个宏 

【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析 

【linux驱动分析】之dm9000驱动分析(四):net_device结构体 

【linux驱动分析】之dm9000驱动分析(五):另外几个重要的结构体 

【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现 

【linux驱动分析】之dm9000驱动分析(七):dm9000的卸载挂起和恢复以及打开和停止


硬件平台:友善之臂Tiny6410核心板 + DM9000EP
软件平台:linux-2.6.38
交叉编译器:Friendly ARM提供的arm-linux-gcc 4.5.1
一、源代码(mach-mini6410.c)
 1 /* Ethernet */
 2 #ifdef CONFIG_DM9000
 3 #define S3C64XX_PA_DM9000    (0x18000000)
 4 #define S3C64XX_SZ_DM9000    SZ_1M
 5 #define S3C64XX_VA_DM9000    S3C_ADDR(0x03b00300)
 6 
 7 static struct resource dm9000_resources[] = {
 8     [0] = {
 9         .start        = S3C64XX_PA_DM9000,
10         .end        = S3C64XX_PA_DM9000 + 3,
11         .flags        = IORESOURCE_MEM,
12     },
13     [1] = {
14         .start        = S3C64XX_PA_DM9000 + 4,
15         .end        = S3C64XX_PA_DM9000 + S3C64XX_SZ_DM9000 - 1,
16         .flags        = IORESOURCE_MEM,
17     },
18     [2] = {
19         .start        = IRQ_EINT(7),
20         .end        = IRQ_EINT(7),
21         .flags        = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,
22     },
23 };
24 
25 static struct dm9000_plat_data dm9000_setup = {
26     .flags            = DM9000_PLATF_16BITONLY | DM9000_PLATF_EXT_PHY,
27     .dev_addr        = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },
28 };
29 
30 static struct platform_device s3c_device_dm9000 = {
31     .name            = "dm9000",
32     .id                = 0,
33     .num_resources    = ARRAY_SIZE(dm9000_resources),
34     .resource        = dm9000_resources,
35     .dev            = {
36         .platform_data = &dm9000_setup,
37     }
38 };
39 
40 static int __init dm9000_set_mac(char *str) {
41     unsigned char addr[6];
42     unsigned int val;
43     int idx = 0;
44     char *p = str, *end;
45 
46     while (*p && idx < 6) {
47         val = simple_strtoul(p, &end, 16);
48         if (end <= p) {
49             /* convert failed */
50             break;
51         } else {
52             addr[idx++] = val;
53             p = end;
54             if (*p == ':'|| *p == '-') {
55                 p++;
56             } else {
57                 break;
58             }
59         }
60     }
61 
62     if (idx == 6) {
63         printk("Setup ethernet address to %pM\n", addr);
64         memcpy(dm9000_setup.param_addr, addr, 6);
65     }
66 
67     return 1;
68 }
69 
70 __setup("ethmac=", dm9000_set_mac);
71 #endif
72 
73 static struct map_desc mini6410_iodesc[] = {
74     {
75         /* LCD support */
76         .virtual    = (unsigned long)S3C_VA_LCD,
77         .pfn        = __phys_to_pfn(S3C_PA_FB),
78         .length     = SZ_16K,
79         .type       = MT_DEVICE,
80     },
81 #ifdef CONFIG_DM9000           /*这里的定义不知道是做什么用的*/
82     {
83         .virtual    = (u32)S3C64XX_VA_DM9000,
84         .pfn        = __phys_to_pfn(S3C64XX_PA_DM9000),
85         .length        = S3C64XX_SZ_DM9000,
86         .type        = MT_DEVICE,
87     },
88 #endif
89 };
DM9000的设备会在
platform_add_devices(mini6410_devices, ARRAY_SIZE(mini6410_devices));
里统一注册。

二、下面分析一下上面代码中红色的宏或者函数
1、ARRAY_SIZE
  #define  ARRAY_SIZE(arr)   (sizeof(arr) / sizeof( (arr)[0] )  +  __must_be_array(arr) )
它是定义在include/linux/kernel.h中的一个宏,用来计算数组中元素的个数。
__must_be_array是编译器相关的,用来防止传入的参数不是数组,比如说传入了指针,这样的话可能回编译不通过(猜测)。
2、simple_strtoul
/**
 * simple_strtoul - convert a string to an unsigned long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
simple_strtoul是定义在lib/vsprintf.c中的函数,它的作用是把一个字符串转换为unsigned long型的整数,并返回。
其中的endp参数存放解析后的字符串地址,base参数,是要转换的进制数。
vsprintf.c里还定义了其他好多字符串处理的函数,具体用到时去查。

3、__setup
它是定义在include/linux/init.h中的一个宏:
#define __setup(str, fn)                    \
    __setup_param(str, fn, fn, 0)

其中:str是关键字,fn是关联处理函数。__setup只是告诉内核在启动时输入串中含有str时,内核要去执行fn。Str必须以“=”符结束以使parse_args更方便解析。紧随“=”后的任何文本都会作为输入传给fn。

例如本例中的:__setup("ethmac=", dm9000_set_mac);
关于__setup的更多分析见《__setup宏的作用》

2014-04-14 11:24:43 u014619185 阅读数 773
  • 网络设备驱动介绍-linux驱动开发第11部分

    本课程是linux驱动开发的第11个课程,主要内容是linux的网络驱动的介绍,首先讲述了网络设备驱动接口和之前讲的2种的不同,然后以一个虚拟网卡驱动源码学习了网卡驱动的框架,后分析了一个实际网卡DM9000的驱动细节。

    4314 人正在学习 去看看 朱有鹏

  DM9000产品已经推出多年,驱动已经更新多个版本,以下为最新版本,解决掉线,丢包,以及PING不通的问题。有其他问题可随时探讨,QQ:1215483516  TEL:18589097498


   

/*
 *   dm9000.c: Version 1.2 03/18/2003   QQ:1215483516
 *
 *          A Davicom DM9000 ISA NIC fast Ethernet driver for Linux.
 * Copyright (C) 1997   Fly-core   Frank  dai
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 *   (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
 *
 * V0.11 06/20/2001REG_0A bit3=1, default enable BP with DA match
 * 06/22/2001 Support DM9801 progrmming
 * E3: R25 = ((R24 + NF) & 0x00ff) | 0xf000
 * E4: R25 = ((R24 + NF) & 0x00ff) | 0xc200
 *     R17 = (R17 & 0xfff0) | NF + 3
 * E5: R25 = ((R24 + NF - 3) & 0x00ff) | 0xc200
 *     R17 = (R17 & 0xfff0) | NF
 *
 * v1.00               modify by simon 2001.9.5
 *                         change for kernel 2.4.x
 *
 * v1.1   11/09/2001       fix force mode bug
 *
 * v1.2   03/18/2003       Weilun Huang <weilun_huang@davicom.com.tw>:
 * Fixed phy reset.
 * Added tx/rx 32 bit mode.
 * Cleaned up for kernel merge.
 *
 *        03/03/2004    Sascha Hauer <s.hauer@pengutronix.de>
 *                      Port to 2.6 kernel
 *
 *  24-Sep-2004   Ben Dooks <ben@simtec.co.uk>
 * Cleanup of code to remove ifdefs
 * Allowed platform device data to influence access width
 * Reformatting areas of code
 *
 *        17-Mar-2005   Sascha Hauer <s.hauer@pengutronix.de>
 *                      * removed 2.4 style module parameters
 *                      * removed removed unused stat counter and fixed
 *                        net_device_stats
 *                      * introduced tx_timeout function
 *                      * reworked locking
 *
 *  01-Jul-2005   Ben Dooks <ben@simtec.co.uk>
 * * fixed spinlock call without pointer
 * * ensure spinlock is initialised
 */


#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/dm9000.h>
#include <linux/delay.h>
#include <linux/platform_device.h>


#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/io.h>


//#include <asm/arch/regs-irq.h>
//#include <asm/arch/regs-mem.h>
//#include <asm/arch/regs-gpio.h>
#include <plat/map-base.h>
#include <plat/regs-serial.h>
#include <plat/regs-clock.h>
#include <plat/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/regs-mem.h>
#include <mach/regs-irq.h>
#include <asm/gpio.h>




#include "dm9000.h"




/* Board/System/Debug information/definition ---------------- */


#define DM9000_PHY 0x40/* PHY address 0x01 */
#define DM9000_CMD         0X04    //1:DATA,0:ADDR


#define CARDNAME "dm9000"
#define PFX CARDNAME ": "


#define DM9000_TIMER_WUT  jiffies+(HZ*2) /* timer wakeup time : 2 second */


#define DM9000_DEBUG 0


#if DM9000_DEBUG > 2
#define PRINTK3(args...)  printk(CARDNAME ": " args)
#else
#define PRINTK3(args...)  do { } while(0)
#endif


#if DM9000_DEBUG > 1
#define PRINTK2(args...)  printk(CARDNAME ": " args)
#else
#define PRINTK2(args...)  do { } while(0)
#endif


#if DM9000_DEBUG > 0
#define PRINTK1(args...)  printk(CARDNAME ": " args)
#define PRINTK(args...)   printk(CARDNAME ": " args)
#else
#define PRINTK1(args...)  do { } while(0)
#define PRINTK(args...)   printk(KERN_DEBUG args)
#endif


#ifdef CONFIG_BLACKFIN
#define readsb insb
#define readsw insw
#define readsl insl
#define writesb outsb
#define writesw outsw
#define writesl outsl
#define DM9000_IRQ_FLAGS (IRQF_SHARED | IRQF_TRIGGER_HIGH)
#else
#define DM9000_IRQ_FLAGS IRQF_SHARED
#endif


/*
 * Transmit timeout, default 5 seconds.
 */
static int watchdog = 5000;
module_param(watchdog, int, 0400);
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");


/* Structure/enum declaration ------------------------------- */
typedef struct board_info {


void __iomem *io_addr;/* Register I/O base address */
void __iomem *io_data;/* Data I/O address */
u16 irq; /* IRQ */


u16 tx_pkt_cnt;
u16 dbug_cnt;
u8 io_mode; /* 0:word, 2:byte */
u8 phy_addr;
u8 chip_ver;
u8 wait_reset;


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 resource*addr_res;   /* resources found */
struct resource *data_res;
struct resource*addr_req;   /* resources requested */
struct resource *data_req;
struct resource *irq_res;


struct timer_list timer;
unsigned char srom[128];
spinlock_t lock;


struct mii_if_info mii;
u32 msg_enable;
} board_info_t;


/* function declaration ------------------------------------- */
static int dm9000_probe(struct platform_device *);
static int dm9000_open(struct net_device *);
static int dm9000_start_xmit(struct sk_buff *, struct net_device *);
static int dm9000_stop(struct net_device *);




static void dm9000_timer(unsigned long);
static void dm9000_init_dm9000(struct net_device *);


static irqreturn_t dm9000_interrupt(int, void *);


static int dm9000_phy_read(struct net_device *dev, int phyaddr_unsused, int reg);
static void dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg,
  int value);
static u16 read_srom_word(board_info_t *, int);
static void dm9000_rx(struct net_device *);
static void dm9000_hash_table(struct net_device *);
static void dm9000_hash_table_impl(struct net_device *);


//#define DM9000_PROGRAM_EEPROM
#ifdef DM9000_PROGRAM_EEPROM
static void program_eeprom(board_info_t * db);
#endif
/* DM9000 network board routine ---------------------------- */


static void
dm9000_reset(board_info_t * db)
{
PRINTK1("****************************dm9000x: resetting,ioaddr=%x,iodata=%x\n",db->io_addr,db->io_data);


/* RESET device */
writeb(DM9000_NCR, db->io_addr);
udelay(200);
writeb(0x03, db->io_data);
udelay(200);

writeb(DM9000_NCR, db->io_addr);
udelay(200);
writeb(0x03, db->io_data);
udelay(200);
}


/*
 *   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);
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 */


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
 */


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
*/




PRINTK1("****************************dm9000x: dm9000_set_io***************************\n");


printk(KERN_INFO "dm9000_set_io byte_width %d \n", byte_width);


switch (byte_width) {
case 1:
db->dumpblk = dm9000_dumpblk_8bit;
db->outblk  = dm9000_outblk_8bit;
db->inblk   = dm9000_inblk_8bit;
break;


case 2:
db->dumpblk = dm9000_dumpblk_16bit;
db->outblk  = dm9000_outblk_16bit;
db->inblk   = dm9000_inblk_16bit;
break;


case 3:
printk(KERN_ERR PFX ": 3 byte IO, falling back to 16bit\n");
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;
}
}




/* Our watchdog timed out. Called by the networking layer */
static void dm9000_timeout(struct net_device *dev)
{
board_info_t *db = (board_info_t *) dev->priv;
u8 reg_save;
unsigned long flags;




PRINTK1("****************************dm9000x: dm9000_set_timeout  ***************************\n");


/* Save previous register address */
reg_save = readb(db->io_addr);
spin_lock_irqsave(&db->lock,flags);


netif_stop_queue(dev);
// dm9000_reset(db);
dm9000_init_dm9000(dev);
/* We can accept TX packets again */
dev->trans_start = jiffies;
netif_wake_queue(dev);


/* Restore previous register address */
writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock,flags);
}


#ifdef CONFIG_NET_POLL_CONTROLLER
/*
 *Used by netconsole
 */
static void dm9000_poll_controller(struct net_device *dev)
{
disable_irq(dev->irq);
dm9000_interrupt(dev->irq,dev);
enable_irq(dev->irq);
}
#endif


/* dm9000_release_board
 *
 * release a board, and any mapped resources
 */


static void
dm9000_release_board(struct platform_device *pdev, struct board_info *db)
{


PRINTK1("****************************dm9000x: dm9000_release board ***************************\n");


if (db->data_res == NULL) {
if (db->addr_res != NULL)
release_mem_region((unsigned long)db->io_addr, 4);
return;
}


/* unmap our resources */


iounmap(db->io_addr);
iounmap(db->io_data);


/* release the resources */


if (db->data_req != NULL) {
release_resource(db->data_req);
kfree(db->data_req);
}


if (db->addr_req != NULL) {
release_resource(db->addr_req);
kfree(db->addr_req);
}
}


#define res_size(_r) (((_r)->end - (_r)->start) + 1)


/*
 * Search DM9000 board, allocate space and register it
 */
static int
dm9000_probe(struct platform_device *pdev)
{
struct dm9000_plat_data *pdata = pdev->dev.platform_data;
struct board_info *db;/* Point a board information structure */
struct net_device *ndev;
unsigned long base;
int ret = 0;
int iosize;
int i;
u32 id_val;


#if 1
unsigned int tmp;
printk("%s: dm9000_probe, init GPIO/EINT.\n", CARDNAME);
//ncs1


// fusq 
#if 0
tmp = __raw_readl(S3C64XX_SROM_BW);
tmp &=~(0xF<<4);
tmp |= (1<<4);
__raw_writel(tmp, S3C64XX_SROM_BW);


__raw_writel(~(0xFFFFFFFF<<0), S3C64XX_SROM_BC1);
__raw_writel((0x0<<28)|(0x4<<24)|(0xd<<16)|(0x1<<12)|(0x4<<8)|(0x6<<4)|(0x0<<0), S3C64XX_SROM_BC1);


writel((readl(S3C64XX_GPNCON) & ~(0x3 <<20)) | (0x2 << 20), S3C64XX_GPNCON);/* GPN10 to EINT */
writel((readl(S3C64XX_GPNPUD) &~(0x3<<20)),S3C64XX_GPNPUD);


writel((readl(S3C64XX_EINT0CON0) & ~(0x7 <<20)) | (0x1 << 20), S3C64XX_EINT0CON0);/* EINT10 to high level triggered */


//writel((readl(S3C64XX_EINTFLTCON0)& ~(0x3 <<10)) | (0x1 << 10), S3C64XX_EINTFLTCON0);


writel((readl(S3C64XX_EINT0PEND)&~(0x1<<10)),S3C64XX_EINT0PEND);
writel(readl(S3C64XX_EINT0MASK) & ~(0x1 << 10), S3C64XX_EINT0MASK);/* EINT10 unmask */

//writel(readl(S3C64XX_VIC1INTENABLE) | (0x1 << 0), S3C64XX_VIC1INTENABLE);/* EINT10 enable */


#endif


PRINTK1("****************************dm9000x: ncs1 ***************************\n");
tmp = __raw_readl(S3C64XX_SROM_BW);
tmp &=~(0xF<<4);
// tmp |= (1<<4);
// tmp |= (1<<7)|(1<<6)|(1<<4);
tmp |= (1<<7) | (1<<4);  // not need iowait 

__raw_writel(tmp, S3C64XX_SROM_BW);


__raw_writel(~(0xFFFFFFFF<<0), S3C64XX_SROM_BC1);
__raw_writel((0x0<<28)|(0x4<<24)|(0xd<<16)|(0x1<<12)|(0x4<<8)|(0x6<<4)|(0x0<<0), S3C64XX_SROM_BC1);


writel((readl(S3C64XX_GPNCON) & ~(0x3 <<14)) | (0x2 << 14), S3C64XX_GPNCON);/* GPN7 to EINT */
writel((readl(S3C64XX_GPNPUD) &~(0x3<<14)),S3C64XX_GPNPUD);


writel((readl(S3C64XX_EINT0CON0) & ~(0x7 <<12)) | (0x1 << 12), S3C64XX_EINT0CON0);/* EINT7 to high level triggered */


writel((readl(S3C64XX_EINT0FLTCON0)& ~(0x3 <<6)) | (0x1 << 7), S3C64XX_EINT0FLTCON0);


writel((readl(S3C64XX_EINT0PEND)&~(0x1<<7)),S3C64XX_EINT0PEND);
writel(readl(S3C64XX_EINT0MASK) & ~(0x1 << 7), S3C64XX_EINT0MASK);/* EINT7 unmask */

//writel(readl(S3C64XX_VIC1INTENABLE) | (0x1 << 0), S3C64XX_VIC1INTENABLE);/* EINT7 enable */


//fusq end =============================================


//ncs5
#else
tmp = __raw_readl(S3C64XX_SROM_BW);
tmp &=~(0xF<<20);
// tmp |= (1<<23) | (1<<22) | (1<<20);
tmp |= (1<<23) | (1<<20);  // not need iowait 
__raw_writel(tmp, S3C64XX_SROM_BW);


__raw_writel(~(0xFFFFFFFF<<0), S3C64XX_SROM_BC5);
__raw_writel((0x0<<28)|(0x4<<24)|(0xd<<16)|(0x1<<12)|(0x4<<8)|(0x6<<4)|(0x0<<0), S3C64XX_SROM_BC5);


writel((readl(S3C64XX_GPNCON) & ~(0x3 <<2)) | (0x2 << 2), S3C64XX_GPNCON);/* GPN12 to EINT */
writel((readl(S3C64XX_GPNPU) &~(0x3<<2)),S3C64XX_GPNPU);

writel((readl(S3C64XX_EINT0CON0) & ~(0x7 <<4)) | (0x1 << 4), S3C64XX_EINT0CON0);/* EINT12 to both edge triggered */


writel((readl(S3C64XX_EINTFLTCON0)& ~(0x3 <<14)) | (0x1 << 15), S3C64XX_EINTFLTCON0);

writel((readl(S3C64XX_EINTPEND)&~(0x1<<1)),S3C64XX_EINTPEND);
writel(readl(S3C64XX_EINT0MASK) & ~(0x1 << 1), S3C64XX_EINT0MASK);/* EINT12 unmask */


/****************************************************************/
#endif
/* Init network device */
ndev = alloc_etherdev(sizeof (struct board_info));
if (!ndev) {
printk("%s: could not allocate device.\n", CARDNAME);
return -ENOMEM;
}


SET_NETDEV_DEV(ndev, &pdev->dev);


PRINTK2("dm9000_probe()");


/* setup board info structure */
db = (struct board_info *) ndev->priv;
memset(db, 0, sizeof (*db));


spin_lock_init(&db->lock);


if (pdev->num_resources < 2) {
ret = -ENODEV;
goto out;
} else if (pdev->num_resources == 2) {
base = pdev->resource[0].start;


if (!request_mem_region(base, 4, ndev->name)) {
ret = -EBUSY;
goto out;
}


ndev->base_addr = base;
ndev->irq = pdev->resource[1].start;
db->io_addr = (void __iomem *)base;
db->io_data = (void __iomem *)(base + DM9000_CMD);
/* ensure at least we have a default set of IO routines */
dm9000_set_io(db, 2);


} else {
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);


if (db->addr_res == NULL || db->data_res == NULL ||
   db->irq_res == NULL) {
printk(KERN_ERR PFX "insufficient resources\n");
ret = -ENOENT;
goto out;
}


i = res_size(db->addr_res);
db->addr_req = request_mem_region(db->addr_res->start, i,
 pdev->name);


if (db->addr_req == NULL) {
printk(KERN_ERR PFX "cannot claim address reg area\n");
ret = -EIO;
goto out;
}


db->io_addr = ioremap(db->addr_res->start, i);


if (db->io_addr == NULL) {
printk(KERN_ERR "failed to ioremap address reg\n");
ret = -EINVAL;
goto out;
}


iosize = res_size(db->data_res);
db->data_req = request_mem_region(db->data_res->start, iosize,
 pdev->name);


if (db->data_req == NULL) {
printk(KERN_ERR PFX "cannot claim data reg area\n");
ret = -EIO;
goto out;
}


db->io_data = ioremap(db->data_res->start, iosize);


if (db->io_data == NULL) {
printk(KERN_ERR "failed to ioremap data reg\n");
ret = -EINVAL;
goto out;
}


/* fill in parameters for net-dev structure */


ndev->base_addr = (unsigned long)db->io_addr;
ndev->irq = db->irq_res->start;


/* ensure at least we have a default set of IO routines */
dm9000_set_io(db, iosize);
}


/* check to see if anything is being over-ridden */
if (pdata != NULL) {
/* check to see if the driver wants to over-ride the
* default IO width */


if (pdata->flags & DM9000_PLATF_8BITONLY)
dm9000_set_io(db, 1);


if (pdata->flags & DM9000_PLATF_16BITONLY)
dm9000_set_io(db, 2);


if (pdata->flags & DM9000_PLATF_32BITONLY)
dm9000_set_io(db, 4);


/* check to see if there are any IO routine
* over-rides */


if (pdata->inblk != NULL)
db->inblk = pdata->inblk;


if (pdata->outblk != NULL)
db->outblk = pdata->outblk;


if (pdata->dumpblk != NULL)
db->dumpblk = pdata->dumpblk;
}


printk("dm9000_probe2, io_addr:%08x, data_addr_%08x.\n", db->io_addr, db->io_data);


dm9000_reset(db);


printk("%s: dm9000_probe3\n", CARDNAME);


/* try two times, DM9000 sometimes gets the first read wrong */
for (i = 0; i < 2; 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;
printk("%s: read wrong id 0x%08x\n", CARDNAME, id_val);
}


if (id_val != DM9000_ID) {
printk("%s: wrong id: 0x%08x\n", CARDNAME, id_val);
ret = -ENODEV;
goto out;
}


/* from this point we assume that we have found a DM9000 */

/* I/O mode */
db->io_mode = ior(db, DM9000_ISR) >> 6;/* ISR bit7:6 keeps I/O mode */
db->chip_ver = ior(db, DM9000_CHIPR);
printk(KERN_INFO "dm9000 revision 0x%02x  io_mode %02x \n", db->chip_ver, db->io_mode);


/* driver system function */
ether_setup(ndev);


ndev->open = &dm9000_open;
ndev->hard_start_xmit    = &dm9000_start_xmit;
ndev->tx_timeout         = &dm9000_timeout;
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
ndev->stop = &dm9000_stop;
ndev->set_multicast_list = &dm9000_hash_table;
#ifdef CONFIG_NET_POLL_CONTROLLER
ndev->poll_controller= &dm9000_poll_controller;
#endif


#ifdef DM9000_PROGRAM_EEPROM
program_eeprom(db);
#endif
db->msg_enable       = NETIF_MSG_LINK;
db->mii.phy_id_mask  = 0x1f;
db->mii.reg_num_mask = 0x1f;
db->mii.force_media  = 0;
db->mii.full_duplex  = 0;
db->mii.dev     = ndev;
db->mii.mdio_read    = dm9000_phy_read;
db->mii.mdio_write   = dm9000_phy_write;


/* Read SROM content */
for (i = 0; i < 64; i++)
((u16 *) db->srom)[i] = read_srom_word(db, i);


/* Set Node Address */
printk("db->srom[i]:");
for (i = 0; i < 6; i++){
/* ndev->dev_addr[i] = db->srom[i]; not right */
printk("%02x ", db->srom[i]);
ndev->dev_addr[i] = ior(db, DM9000_PAR+i); /* right MAC address */
}
/***************************************************/
   if (!is_valid_ether_addr(ndev->dev_addr)){
printk("%s: con201 Invalid ethernet MAC address. using default config,  Please "
      "set using ifconfig\n", ndev->name);
ndev->dev_addr[0] = 0x00;
ndev->dev_addr[1] = 0xe0;
ndev->dev_addr[2] = 0x4a;
ndev->dev_addr[3] = 0xbc;
ndev->dev_addr[4] = 0x15;
ndev->dev_addr[5] = 0xe7;
}
/***************************************************/


if (!is_valid_ether_addr(ndev->dev_addr))
printk("%s: con201 Invalid ethernet MAC address.  Please "
      "set using ifconfig\n", ndev->name);


platform_set_drvdata(pdev, ndev);
ret = register_netdev(ndev);


if (ret == 0) {
DECLARE_MAC_BUF(mac);
printk("%s: dm9000 at %p,%p IRQ %d MAC: %s\n",
      ndev->name,  db->io_addr, db->io_data, ndev->irq,
      print_mac(mac, ndev->dev_addr));
}
return 0;


out:
printk("%s: not found (%d).\n", CARDNAME, ret);


dm9000_release_board(pdev, db);
free_netdev(ndev);


return ret;
}


/*
 *  Open the interface.
 *  The interface is opened whenever "ifconfig" actives it.
 */
static int
dm9000_open(struct net_device *dev)
{
board_info_t *db = (board_info_t *) dev->priv;


PRINTK1("entering %s\n",__FUNCTION__);

/* GPIO0 on pre-activate PHY, Reg 1F is not set by reset */
iow(db, DM9000_GPR, 1);/* REG_1F bit0 activate phyxcer */
mdelay(1); /* delay needs by DM9000B */

/* Initialize DM9000 board */
// dm9000_reset(db);
dm9000_init_dm9000(dev);


if (request_irq(dev->irq, &dm9000_interrupt, DM9000_IRQ_FLAGS, dev->name, dev))
return -EAGAIN;


/* Init driver variable */
db->dbug_cnt = 0;


/* set and active a timer process */
init_timer(&db->timer);
db->timer.expires  = DM9000_TIMER_WUT;
db->timer.data     = (unsigned long) dev;
db->timer.function = &dm9000_timer;
add_timer(&db->timer);


mii_check_media(&db->mii, netif_msg_link(db), 1);
netif_start_queue(dev);


return 0;
}


/*
 * Initilize dm9000 board
 */
static void
dm9000_init_dm9000(struct net_device *dev)
{
board_info_t *db = (board_info_t *) dev->priv;


PRINTK1("entering %s\n",__FUNCTION__);
if(!(DM9000_PHY & ior(db, DM9000_NSR)))
{
/* GPIO0 on pre-activate PHY */
iow(db, DM9000_GPCR, GPCR_GEP_CNTL);/* Let GPIO0 output */
iow(db, DM9000_GPR, 1);/* REG_1F bit0 activate phyxcer */
udelay(100);

dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET);/* PHY RESET */
udelay(100);
if(0x19 == db->chip_ver)
dm9000_phy_write(dev, 0, 0x1b, 0xe100);
// dm9000_phy_write(dev, 0, 0x14, 0x0200);

iow(db, DM9000_GPR, 0);/* Enable PHY */
do
{
udelay(500);
}while(0x46 != ior(db, DM9000_VIDL));
}

dm9000_reset(db);


/* Program operating register */
iow(db, DM9000_IMR, IMR_PAR);
iow(db, DM9000_TCR, 0);       /* TX Polling clear */
iow(db, DM9000_BPTR, 0x3f);/* Less 3Kb, 200us */
iow(db, DM9000_FCR, 0x28);/* Flow Control */
iow(db, DM9000_SMCR, 0);        /* Special Mode */
/* clear TX status */
iow(db, DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
iow(db, DM9000_ISR, ISR_CLR_STATUS); /* Clear interrupt status */

iow(db, 0x2d, 0x80);
// iow(db, 0x39, 0x00);

if(0x19 == db->chip_ver)
{
iow(db, 0x38, 0x2b);
iow(db, DM9000_SMCR, 0x08);        /* Special Mode */
}
// else
// {
// iow(db, 0x38, 0x6b);
// }


/* Set address filter table */
dm9000_hash_table_impl(dev);


/* Activate DM9000 */
iow(db, DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);
/* Enable TX/RX interrupt mask */
iow(db, DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM);


/* Init Driver variable */
db->tx_pkt_cnt = 0;
db->wait_reset = 0;
dev->trans_start = 0;
}


#define txtfmt "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x.\n"
#define datfmt(d) d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8],d[9],d[10],d[11],d[12],d[13],d[14],d[15]
#define dprint(data) printk(txtfmt, datfmt(data))
/*
 *  Hardware start transmission.
 *  Send a packet to media from the upper layer.
 */
static int
dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned long flags;
board_info_t *db = (board_info_t *) dev->priv;
u16 save_mwr, check_mwr, calc_mwr;


if (db->tx_pkt_cnt > 0){
PRINTK("db->tx_pkt_cnt error, = %d.\n", db->tx_pkt_cnt);
return 1;
}


/* 停止接收 */
netif_stop_queue(dev);

/* 关闭接收发送中断 */
iow(db, DM9000_IMR, IMR_PAR);

spin_lock_irqsave(&db->lock, flags);
db->tx_pkt_cnt++;
dev->stats.tx_bytes += skb->len;
dev->stats.tx_packets++;

save_mwr = (ior(db, 0xfb) << 8) | ior(db, 0xfa);
calc_mwr = save_mwr + skb->len;
if(skb->len & 0x01) 
if(!db->io_mode) calc_mwr++;
if(calc_mwr > 0x0bff ) calc_mwr -= 0x0c00;


/* Set TX length to DM9000 */
iow(db, DM9000_TXPLL, skb->len & 0xff);
iow(db, DM9000_TXPLH, (skb->len >> 8) & 0xff);

/* Move data to DM9000 TX RAM */
writeb(DM9000_MWCMD, db->io_addr);
(db->outblk)(db->io_data, skb->data, skb->len);


/* Issue TX polling command */
iow(db, DM9000_TCR, TCR_TXREQ);/* Cleared after TX complete */


dev->trans_start = jiffies;/* save the time stamp */


check_mwr = (ior(db, 0xfb) << 8) | ior(db, 0xfa);
if(calc_mwr != check_mwr)
{
printk(KERN_INFO "TX: fifo error %04x %04x %04x %04x\n", save_mwr, skb->len, calc_mwr, check_mwr);
iow(db, 0xfb, (calc_mwr >> 8) & 0xff);
iow(db, 0xfa, calc_mwr & 0xff);
}


spin_unlock_irqrestore(&db->lock, flags);


/* 重新启动接收发送中断 */
iow(db, DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM);

/* free this SKB */
dev_kfree_skb(skb);


return 0;
}


static void
dm9000_shutdown(struct net_device *dev)
{
board_info_t *db = (board_info_t *) dev->priv;
PRINTK1("entering %s\n",__FUNCTION__);


/* RESET device */
dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET);/* PHY RESET */
iow(db, DM9000_GPR, 0x01);/* Power-Down PHY */
udelay(100);
iow(db, DM9000_IMR, IMR_PAR);/* Disable all interrupt */
iow(db, DM9000_RCR, 0x00);/* Disable RX */
}


/*
 * Stop the interface.
 * The interface is stopped when it is brought.
 */
static int
dm9000_stop(struct net_device *ndev)
{
board_info_t *db = (board_info_t *) ndev->priv;


PRINTK1("entering %s\n",__FUNCTION__);


/* deleted timer */
del_timer(&db->timer);


netif_stop_queue(ndev);
netif_carrier_off(ndev);


/* free interrupt */
free_irq(ndev->irq, ndev);


dm9000_shutdown(ndev);


return 0;
}


/*
 * DM9000 interrupt handler
 * receive the packet to upper layer, free the transmitted packet
 */


static void
dm9000_tx_done(struct net_device *dev, board_info_t * db)
{
#if 0
int tx_status = ior(db, DM9000_NSR);/* Got TX status */


if (tx_status & (NSR_TX2END | NSR_TX1END)) {
/* One packet sent complete */
db->tx_pkt_cnt--;
dev->stats.tx_packets++;


 static unsigned int dm9000_function_count = 0;
 dm9000_function_count++;
 
/* Queue packet check & send */
if (db->tx_pkt_cnt > 0) {
iow(db, DM9000_TXPLL, db->queue_pkt_len & 0xff);
iow(db, DM9000_TXPLH, (db->queue_pkt_len >> 8) & 0xff);
iow(db, DM9000_TCR, TCR_TXREQ);
dev->trans_start = jiffies;
}
// auto reset
else if(dm9000_function_count >= 1000)
{
//dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET);/* PHY RESET */
iow(db, DM9000_NCR, NCR_RST);// dm9000 reset
//iow(db, DM9000_GPR, 0x01);/* Power-Down PHY */
//iow(db, DM9000_GPR, 0);/* Enable PHY */

/* Program operating register */
//iow(db, DM9000_TCR, 0);       /* TX Polling clear */
iow(db, DM9000_BPTR, 0x3f);/* Less 3Kb, 200us */
iow(db, DM9000_FCR, 0xff);/* Flow Control */
//iow(db, DM9000_SMCR, 0);        /* Special Mode */
/* clear TX status */
iow(db, DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
iow(db, DM9000_ISR, ISR_CLR_STATUS); /* Clear interrupt status */

/* Set address filter table */
//dm9000_hash_table_impl(dev);

/* Activate DM9000 */
iow(db, DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);

//dm9000_hash_table_impl(dev);/* Set DM9000 multicast address*/
dm9000_function_count = 0;
}

netif_wake_queue(dev);
}
#else
int tx_status = ior(db, DM9000_TCR);/* Got TX status */


if (tx_status & TCR_TXREQ)
{
// dev->stats.tx_fifo_errors++;
}
else
{
if(db->tx_pkt_cnt && !db->wait_reset)
{
/* One packet sent complete */
db->tx_pkt_cnt = 0;
dev->trans_start = 0;
netif_wake_queue(dev);
}
}
#endif
}


static irqreturn_t
dm9000_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
board_info_t *db;
int int_status;
u8 reg_save;


  static unsigned int dm9000_function_count = 0;
  dm9000_function_count++;
  if(dm9000_function_count == 1000){  
PRINTK3("entering %s 1000 times.\n",__FUNCTION__);
dm9000_function_count = 0;
  }


if (!dev) {
PRINTK1("dm9000_interrupt() without DEVICE arg\n");
return IRQ_HANDLED;
}


/* A real interrupt coming */
db = (board_info_t *) dev->priv;
spin_lock(&db->lock);


/* Save previous register address */
reg_save = readb(db->io_addr);


/* Disable all interrupts */
iow(db, DM9000_IMR, IMR_PAR);


/* Got DM9000 interrupt status */
int_status = ior(db, DM9000_ISR);/* Got ISR */
iow(db, DM9000_ISR, int_status);/* Clear ISR status */


/* PRINTK3("%s, int_status = %08x.\n",__FUNCTION__, int_status); */


/* Received the coming packet */
if (int_status & ISR_PRS)
dm9000_rx(dev);

int_status |= ior(db, DM9000_ISR);/* Got ISR */


/* Trnasmit Interrupt check */
if (int_status & ISR_PTS)
{
iow(db, DM9000_ISR, ISR_PTS);/* Clear ISR status */
dm9000_tx_done(dev, db);
}


/* Re-enable interrupt mask */
iow(db, DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM);


/* Restore previous register address */
writeb(reg_save, db->io_addr);


spin_unlock(&db->lock);


return IRQ_HANDLED;
}


/*
 *  A periodic timer routine
 *  Dynamic media sense, allocated Rx buffer...
 */
static void
dm9000_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
board_info_t *db = (board_info_t *) dev->priv;


  static unsigned int dm9000_function_count = 0;
  dm9000_function_count++;
  if(dm9000_function_count == 1000){  
PRINTK3("entering %s 1000 times.\n",__FUNCTION__);
dm9000_function_count = 0;
  }


mii_check_media(&db->mii, netif_msg_link(db), 0);


/* Set timer again */
db->timer.expires = DM9000_TIMER_WUT;
add_timer(&db->timer);
}


struct dm9000_rxhdr {
u16 RxStatus;
u16 RxLen;
} __attribute__((__packed__));


/*
 *  Received a packet and pass to upper layer
 */
static void
dm9000_rx(struct net_device *dev)
{
board_info_t *db = (board_info_t *) dev->priv;
struct dm9000_rxhdr rxhdr;
struct sk_buff *skb;
u8 rxbyte, *rdptr;
bool GoodPacket;
int RxLen;
int save_mrr, calc_mrr, check_mrr;


/* Check packet ready or not */
do {
ior(db, DM9000_MRCMDX);/* Dummy read */
save_mrr = (ior(db, 0xf5) << 8) | ior(db, 0xf4);
/* Get most updated data */
rxbyte = ior(db, DM9000_MRCMDX);

/* Status check: this byte must be 0 or 1 */
if (rxbyte != DM9000_PKT_RDY) 
{
/* Status check: this byte must be 0 or 1 */
if(rxbyte)
{
dev_warn(db->dev, "status check fail: %x point %04x \n", rxbyte, save_mrr);
iow(db, DM9000_RCR, 0x00);/* Stop Device */
iow(db, DM9000_ISR, IMR_PAR);/* Stop INT request */

db->wait_reset = 1;
dev->trans_start = 1;
}
return;
}


/* A packet ready now  & Get status/length */
GoodPacket = true;
writeb(DM9000_MRCMD, db->io_addr);


(db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));

calc_mrr = save_mrr + 4;
if(calc_mrr > 0x3fff) calc_mrr -= 0x3400;

check_mrr = (ior(db, 0xf5) << 8) | ior(db, 0xf4);
if(calc_mrr != check_mrr)
{
printk(KERN_INFO "RX: 4 byte error %04x %04x %04x \n", save_mrr, calc_mrr, check_mrr);
iow(db, 0xf5, (save_mrr >> 8) & 0xff);
iow(db, 0xf4, save_mrr & 0xff);
continue;
}

writeb(DM9000_MRCMD, db->io_addr);


RxLen = rxhdr.RxLen;


/* Packet Status check */
if (RxLen < 0x40) {
GoodPacket = false;
PRINTK1("Bad Packet received (runt)\n");
}


if (RxLen > DM9000_PKT_MAX) {
PRINTK1("RST: RX Len:%x\n", RxLen);
}


if (rxhdr.RxStatus & 0xbf00) {
if (rxhdr.RxStatus & 0x100) {
PRINTK1("fifo error\n");
dev->stats.rx_fifo_errors++;
}
if (rxhdr.RxStatus & 0x200) {
PRINTK1("crc error\n");
dev->stats.rx_crc_errors++;
GoodPacket = false;
}
if (rxhdr.RxStatus & 0x8000) {
PRINTK1("length error\n");
dev->stats.rx_length_errors++;
GoodPacket = false;
}
}


/* Move data from DM9000 */
if (GoodPacket
   && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
skb_reserve(skb, 2);
rdptr = (u8 *) skb_put(skb, RxLen - 4);

if(DM9000_MRCMD != inb(db->io_addr))
{
printk(KERN_INFO "RX: fifo read index error %02x\n", inb(db->io_addr));
writeb(DM9000_MRCMD, db->io_addr);
}


/* Read received packet from RX SRAM */
(db->inblk)(db->io_data, rdptr, RxLen);
dev->stats.rx_bytes += RxLen;


/* Pass to upper layer */
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->stats.rx_packets++;

check_mrr = (ior(db, 0xf5) << 8) | ior(db, 0xf4);
if(calc_mrr != check_mrr)
{
printk(KERN_INFO "RX: fifo error %04x %04x %04x %04x\n", save_mrr, RxLen, calc_mrr, check_mrr);
iow(db, 0xf5, (calc_mrr >> 8) & 0xff);
iow(db, 0xf4, calc_mrr & 0xff);
}


} else {
/* need to dump the packet's data */
iow(db, 0xf5, (calc_mrr >> 8) & 0xff);
iow(db, 0xf4, calc_mrr & 0xff);

// (db->dumpblk)(db->io_data, RxLen);
}
} while (rxbyte == DM9000_PKT_RDY);
}


/*
 *  Read a word data from SROM
 */
static u16
read_srom_word(board_info_t * db, int offset)
{
iow(db, DM9000_EPAR, offset);
iow(db, DM9000_EPCR, EPCR_ERPRR);
mdelay(8); /* according to the datasheet 200us should be enough,
  but it doesn't work */
iow(db, DM9000_EPCR, 0x0);
udelay(200);
return ((u16)(ior(db, DM9000_EPDRL)) + ((u16)(ior(db, DM9000_EPDRH)) << 8));
}


#ifdef DM9000_PROGRAM_EEPROM
/*
 * Write a word data to SROM
 */
static void
write_srom_word(board_info_t * db, int offset, u16 val)
{
iow(db, DM9000_EPAR, offset);
iow(db, DM9000_EPDRH, ((val >> 8) & 0xff));
iow(db, DM9000_EPDRL, (val & 0xff));
iow(db, DM9000_EPCR, EPCR_WEP | EPCR_ERPRW);
mdelay(8); /* same shit */
iow(db, DM9000_EPCR, 0);
}


/*
 * Only for development:
 * Here we write static data to the eeprom in case
 * we don't have valid content on a new board
 */
static void
program_eeprom(board_info_t * db)
{
u16 eeprom[] = { 0x0c00, 0x007f, 0x1300,/* MAC Address */
0x0000, /* Autoload: accept nothing */
0x0a46, 0x9000,/* Vendor / Product ID */
0x0000, /* pin control */
0x0000,
}; /* Wake-up mode control */
int i;
for (i = 0; i < 8; i++)
write_srom_word(db, i, eeprom[i]);
}
#endif




/*
 *  Calculate the CRC valude of the Rx packet
 *  flag = 1 : return the reverse CRC (for the received packet CRC)
 *         0 : return the normal CRC (for Hash Table index)
 */


static unsigned long
cal_CRC(unsigned char *Data, unsigned int Len, u8 flag)
{


       u32 crc = ether_crc_le(Len, Data);


       if (flag)
               return ~crc;


       return crc;
}


/*
 *  Set DM9000 multicast address
 */
 static void
dm9000_hash_table_impl(struct net_device *dev)
{
board_info_t *db = (board_info_t *) dev->priv;
struct dev_mc_list *mcptr = dev->mc_list;
int mc_cnt = dev->mc_count;
u32 hash_val;
u16 i, oft, hash_table[4];


  //PRINTK("dm9000_hash_table_impl()\n"); 


/* spin_lock_irqsave(&db->lock,flags); */


for (i = 0, oft = 0x10; i < 6; i++, oft++)
iow(db, oft, dev->dev_addr[i]);


/* Clear Hash Table */
for (i = 0; i < 4; i++)
hash_table[i] = 0x0;


/* broadcast address */
hash_table[3] = 0x8000;


/* the multicast address in Hash Table : 64 bits */
for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f;
hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
}


/* Write the hash table to MAC MD table */
for (i = 0, oft = 0x16; i < 4; i++) {
iow(db, oft++, hash_table[i] & 0xff);
iow(db, oft++, (hash_table[i] >> 8) & 0xff);
}


/* spin_unlock_irqrestore(&db->lock,flags);*/
}


static void
dm9000_hash_table(struct net_device *dev)
{
board_info_t *db = (board_info_t *) dev->priv;
unsigned long flags;

PRINTK2("dm9000_hash_table()\n");


spin_lock_irqsave(&db->lock,flags);


dm9000_hash_table_impl (dev);

spin_unlock_irqrestore(&db->lock,flags);
}




/*
 *   Read a word from phyxcer
 */
static int
dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
{
board_info_t *db = (board_info_t *) dev->priv;
unsigned long flags;
unsigned int reg_save;
int ret;


spin_lock_irqsave(&db->lock,flags);


/* Save previous register address */
reg_save = readb(db->io_addr);


/* Fill the phyxcer register into REG_0C */
iow(db, DM9000_EPAR, DM9000_PHY | reg);


iow(db, DM9000_EPCR, 0xc);/* Issue phyxcer read command */
do
{
udelay(100); /* Wait read complete */
}while(0x0c != ior(db, DM9000_EPCR));
iow(db, DM9000_EPCR, 0x0);/* Clear phyxcer read command */


/* The read data keeps on REG_0D & REG_0E */
ret = (ior(db, DM9000_EPDRH) << 8) | ior(db, DM9000_EPDRL);


/* restore the previous address */
writeb(reg_save, db->io_addr);


spin_unlock_irqrestore(&db->lock,flags);


return ret;
}


/*
 *   Write a word to phyxcer
 */
static void
dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg, int value)
{
board_info_t *db = (board_info_t *) dev->priv;
unsigned long flags;
unsigned long reg_save;


spin_lock_irqsave(&db->lock,flags);


/* Save previous register address */
reg_save = readb(db->io_addr);


/* Fill the phyxcer register into REG_0C */
iow(db, DM9000_EPAR, DM9000_PHY | reg);


/* Fill the written data into REG_0D & REG_0E */
iow(db, DM9000_EPDRL, (value & 0xff));
iow(db, DM9000_EPDRH, ((value >> 8) & 0xff));


iow(db, DM9000_EPCR, 0xa);/* Issue phyxcer write command */
do
{
udelay(500); /* Wait write complete */
}while(0x0a != ior(db, DM9000_EPCR));
iow(db, DM9000_EPCR, 0x0);/* Clear phyxcer write command */


/* restore the previous address */
writeb(reg_save, db->io_addr);


spin_unlock_irqrestore(&db->lock,flags);
}


static int
dm9000_drv_suspend(struct platform_device *dev, pm_message_t state)
{
struct net_device *ndev = platform_get_drvdata(dev);
PRINTK("Enter dm9000_drv_suspend.\n");


if (ndev) {
if (netif_running(ndev)) {
netif_device_detach(ndev);
dm9000_shutdown(ndev);
}
}
return 0;
}


static int
dm9000_drv_resume(struct platform_device *dev)
{
struct net_device *ndev = platform_get_drvdata(dev);
board_info_t *db = (board_info_t *) ndev->priv;
PRINTK("Enter dm9000_drv_resume.\n");


if (ndev) {


if (netif_running(ndev)) {
// dm9000_reset(db);
dm9000_init_dm9000(ndev);


netif_device_attach(ndev);
}
}
return 0;
}


static int
dm9000_drv_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
PRINTK("Enter dm9000_drv_remove.\n");


platform_set_drvdata(pdev, NULL);


unregister_netdev(ndev);
dm9000_release_board(pdev, (board_info_t *) ndev->priv);
free_netdev(ndev);/* free device structure */


PRINTK1("clean_module() exit\n");


return 0;
}


static struct platform_driver dm9000_driver = {
.driver = {
.name    = "dm9000_con201",
.owner = THIS_MODULE,
},
.probe   = dm9000_probe,
.remove  = dm9000_drv_remove,
.suspend = dm9000_drv_suspend,
.resume  = dm9000_drv_resume,
};


static int __init
dm9000_init(void)
{
printk(KERN_INFO "%s Ethernet Driver\n", CARDNAME);


return platform_driver_register(&dm9000_driver);/* search board and register */
}


static void __exit
dm9000_cleanup(void)
{
platform_driver_unregister(&dm9000_driver);
}


module_init(dm9000_init);
module_exit(dm9000_cleanup);


MODULE_AUTHOR("Sascha Hauer, Ben Dooks");
MODULE_DESCRIPTION("Davicom DM9000 network driver");
MODULE_LICENSE("GPL");

 

2014-07-05 15:39:28 mrwangwang 阅读数 1454
  • 网络设备驱动介绍-linux驱动开发第11部分

    本课程是linux驱动开发的第11个课程,主要内容是linux的网络驱动的介绍,首先讲述了网络设备驱动接口和之前讲的2种的不同,然后以一个虚拟网卡驱动源码学习了网卡驱动的框架,后分析了一个实际网卡DM9000的驱动细节。

    4314 人正在学习 去看看 朱有鹏

版权声明: 可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息。

说明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文件中有如下体现:

[c-sharp] view plaincopy
  1. #define S3C2410_CS4 (0x20000000)  
  2. #define MACH_MINI2440_DM9K_BASE (S3C2410_CS4 + 0x300)  
  3. static struct resource mini2440_dm9k_resource[] __initdata = {   
  4.     [0] = {   
  5.         .start = MACH_MINI2440_DM9K_BASE,   
  6.         .end   = MACH_MINI2440_DM9K_BASE + 3,   
  7.         .flags = IORESOURCE_MEM   
  8.     },   
  9.     [1] = {   
  10.         .start = MACH_MINI2440_DM9K_BASE + 4,   
  11.         .end   = MACH_MINI2440_DM9K_BASE + 7,   
  12.         .flags = IORESOURCE_MEM   
  13.     },   
  14.     [2] = {   
  15.         .start = IRQ_EINT7,   
  16.         .end   = IRQ_EINT7,   
  17.         .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,   
  18.     }   
  19. };  

另外在Mach文件中还定义了DM9000平台设备,设备名称为“dm9000”,设备资源就是上面定义的IO和中断资源。代码清单如下:

[c-sharp] view plaincopy
  1. static struct dm9000_plat_data mini2440_dm9k_pdata __initdata = {  
  2.     .flags      = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),  
  3. };  
  4.   
  5. static struct platform_device mini2440_device_eth __initdata = {  
  6.     .name       = "dm9000",  
  7.     .id     = -1,  
  8.     .num_resources  = ARRAY_SIZE(mini2440_dm9k_resource),  
  9.     .resource   = mini2440_dm9k_resource,  
  10.     .dev        = {  
  11.         .platform_data  = &mini2440_dm9k_pdata,  
  12.     },  
  13. };  

这个DM9000平台设备作为众多平台设备中的一个在扳子初始化的时候就被添加到了总线上。代码清单如下:

[c-sharp] view plaincopy
  1. MACHINE_START(MINI2440, "MINI2440")  
  2.     /* Maintainer: Michel Pollet <buserror@gmail.com> */  
  3.     .phys_io    = S3C2410_PA_UART,  
  4.     .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,  
  5.     .boot_params    = S3C2410_SDRAM_PA + 0x100,  
  6.     .map_io     = mini2440_map_io,  
  7.     .init_machine   = mini2440_init, /*初始化函数*/  
  8.     .init_irq   = s3c24xx_init_irq,  
  9.     .timer      = &s3c24xx_timer,  
  10. MACHINE_END  
  
[c-sharp] view plaincopy
  1. static void __init mini2440_init(void)  
  2. {  
  3.     ...  
  4.          ...  
  5.     platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));  
  6.   
  7.         ...  
  8.         ...  
  9. }  
[c-sharp] view plaincopy
  1. static struct platform_device *mini2440_devices[] __initdata = {  
  2.     &s3c_device_usb,  
  3.     &s3c_device_wdt,  
  4. /*  &s3c_device_adc,*/ /* ADC doesn't like living with touchscreen ! */  
  5.     &s3c_device_i2c0,  
  6.     &s3c_device_rtc,  
  7.     &s3c_device_usbgadget,  
  8.     &mini2440_device_eth, /*dm9000是众多平台设备中的一个*/  
  9.     &mini2440_led1,  
  10.     &mini2440_led2,  
  11.     &mini2440_led3,  
  12.     &mini2440_led4,  
  13.     &mini2440_button_device,  
  14.     &s3c_device_nand,  
  15.     &s3c_device_sdi,  
  16.     &s3c_device_iis,  
  17.     &mini2440_audio,  
  18. /*  &s3c_device_timer[0],*/ /* buzzer pwm, no API for it */  
  19.     /* remaining devices are optional */  
  20. };  
  

 

二、两个重要的结构体简单介绍:sk_buff和net_device

   *sk_buff

    如果把网络传输看成是运送货物的话,那么sk_buff就是这个“货物”了,所有经手这个货物的人都要干点什么事儿,要么加个包装,要么印个戳儿等等。收货的时候就要拆掉这些包装,得到我们需要的货物(payload data)。没有货物你还运输什么呢?由此可见sk_buff的重要性了。关于sk_buff的详细介绍和几个操作它的函数,参考本博客转载的一篇文章:“linux内核sk_buff的结构分析”,写得非常明白了。赞一个~

  *net_device

    又是一个庞大的结构体。好吧,我承认我从来就没有看全过这个结构体。它在内核中就是指代了一个网络设备。驱动程序需要在探测的时候分配并初始化这个结构体,然后使用register_netdev来注册它,这样就可以把操作硬件的函数与内核挂接在一起。

   

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

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

[c-sharp] view plaincopy
  1. static struct platform_driver dm9000_driver = {  
  2.     .driver = {  
  3.         .name    = "dm9000",  
  4.         .owner   = THIS_MODULE,  
  5.     },  
  6.     .probe   = dm9000_probe,  
  7.     .remove  = __devexit_p(dm9000_drv_remove),  
  8.     .suspend = dm9000_drv_suspend,  
  9.     .resume  = dm9000_drv_resume,  
  10. };  

  

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

[c-sharp] view plaincopy
  1. static const struct net_device_ops dm9000_netdev_ops = {  
  2.     .ndo_open       = dm9000_open,  
  3.     .ndo_stop       = dm9000_stop,  
  4.     .ndo_start_xmit     = dm9000_start_xmit,  
  5.     .ndo_tx_timeout     = dm9000_timeout,  
  6.     .ndo_set_multicast_list = dm9000_hash_table,  
  7.     .ndo_do_ioctl       = dm9000_ioctl,  
  8.     .ndo_change_mtu     = eth_change_mtu,  
  9.     .ndo_validate_addr  = eth_validate_addr,  
  10.     .ndo_set_mac_address    = eth_mac_addr,  
  11. #ifdef CONFIG_NET_POLL_CONTROLLER  
  12.     .ndo_poll_controller    = dm9000_poll_controller,  
  13. #endif  
  14. };  

 

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

[c-sharp] view plaincopy
  1. static const struct ethtool_ops dm9000_ethtool_ops = {  
  2.     .get_drvinfo        = dm9000_get_drvinfo,  
  3.     .get_settings       = dm9000_get_settings,  
  4.     .set_settings       = dm9000_set_settings,  
  5.     .get_msglevel       = dm9000_get_msglevel,  
  6.     .set_msglevel       = dm9000_set_msglevel,  
  7.     .nway_reset     = dm9000_nway_reset,  
  8.     .get_link       = dm9000_get_link,  
  9.     .get_eeprom_len     = dm9000_get_eeprom_len,  
  10.     .get_eeprom     = dm9000_get_eeprom,  
  11.     .set_eeprom     = dm9000_set_eeprom,  
  12. };  

 

   * board_info结构体。用来保存芯片相关的一些私有信息。具体在代码中分析。下面是这个结构体的清单。

[c-sharp] view plaincopy
  1. /* Structure/enum declaration ------------------------------- */  
  2. typedef struct board_info {  
  3.   
  4.     void __iomem    *io_addr;   /* Register I/O base address */  
  5.     void __iomem    *io_data;   /* Data I/O address */  
  6.     u16      irq;       /* IRQ */  
  7.   
  8.     u16     tx_pkt_cnt;  
  9.     u16     queue_pkt_len;  
  10.     u16     queue_start_addr;  
  11.     u16     dbug_cnt;  
  12.     u8      io_mode;        /* 0:word, 2:byte */  
  13.     u8      phy_addr;  
  14.     u8      imr_all;  
  15.   
  16.     unsigned int    flags;  
  17.     unsigned int    in_suspend :1;  
  18.     int     debug_level;  
  19.   
  20.     enum dm9000_type type;  
  21.   
  22.     void (*inblk)(void __iomem *port, void *data, int length);  
  23.     void (*outblk)(void __iomem *port, void *data, int length);  
  24.     void (*dumpblk)(void __iomem *port, int length);  
  25.   
  26.     struct device   *dev;        /* parent device */  
  27.   
  28.     struct resource *addr_res;   /* resources found */  
  29.     struct resource *data_res;  
  30.     struct resource *addr_req;   /* resources requested */  
  31.     struct resource *data_req;  
  32.     struct resource *irq_res;  
  33.   
  34.     struct mutex     addr_lock; /* phy and eeprom access lock */  
  35.   
  36.     struct delayed_work phy_poll;  
  37.     struct net_device  *ndev;  
  38.   
  39.     spinlock_t  lock;  
  40.   
  41.     struct mii_if_info mii;  
  42.     u32     msg_enable;  
  43. } board_info_t;  

 

 

下面看一下具体代码。

分析代码还是从init顺序开始。

     1. 注册平台驱动。

    主要完成的任务是:将驱动添加到总线上,完成驱动和设备的match,并执行驱动的probe函数。代码清单如下:

[c-sharp] view plaincopy
  1. static struct platform_driver dm9000_driver = {  
  2.     .driver = {  
  3.         .name    = "dm9000"/*用这个名字完成驱动和设备的match*/  
  4.         .owner   = THIS_MODULE,  
  5.     },  
  6.     .probe   = dm9000_probe,  
  7.     .remove  = __devexit_p(dm9000_drv_remove),  
  8.     .suspend = dm9000_drv_suspend,  
  9.     .resume  = dm9000_drv_resume,  
  10. };  
  11.   
  12. static int __init  
  13. dm9000_init(void)  
  14. {  
  15.     printk(KERN_INFO "%s Ethernet Driver, V%s/n", CARDNAME, DRV_VERSION);  
  16.   
  17.     return platform_driver_register(&dm9000_driver);  
  18. }  

 

    2. probe函数。

   主要完成的任务是:探测设备获得并保存资源信息,根据这些信息申请内存和中断,最后调用register_netdev注册这个网络设备。以下是代码清单,可以分成几个部分来看:

   1) 首先定义了几个局部变量:

         struct dm9000_plat_data *pdata = pdev->dev.platform_data;
         struct board_info *db; /* Point a board information structure */
         struct net_device *ndev;

   2) 初始化一个网络设备。关键系统函数:alloc_etherdev()

   3) 获得资源信息并将其保存在board_info变量db中。关键系统函数:netdev_priv(),  platform_get_resource()

   4) 根据资源信息分配内存,申请中断等等, 并将申请后的资源信息也保存到db中,并且填充ndev中的参数。 关键系统函数:request_mem_region(),  ioremap()。 自定义函数:dm9000_set_io()

   5) 完成了第4步以后,回顾一下db和ndev中都有了什么:

       struct board_info *db:

                 addr_res -- 地址资源

                 data_res -- 数据资源

                 irq_res    -- 中断资源

                 addr_req -- 分配的地址内存资源

                 io_addr   -- 寄存器I/O基地址

                 data_req -- 分配的数据内存资源

                 io_data   -- 数据I/O基地址

                 dumpblk  -- IO模式

                 outblk     -- IO模式

                 inblk        -- IO模式

                 lock         -- 自旋锁(已经被初始化)

                 addr_lock -- 互斥锁(已经被初始化)

        struct net_device *ndev:

                 base_addr  -- 设备IO地址

                 irq              -- 设备IRQ号

     6) 设备复位。硬件操作函数dm9000_reset()

     7) 读一下生产商和制造商的ID,应该是0x9000 0A46。 关键函数:ior()

     8) 读一下芯片类型。

     ========以上步骤结束后我们可以认为已经找到了DM9000========

     9) 借助ether_setup()函数来部分初始化ndev。因为对以太网设备来讲,很多操作与属性是固定的,内核可以帮助完成。

    10) 手动初始化ndev的ops和db的mii部分。

    11) (如果有的话)从EEPROM中读取节点地址。这里可以看到mini2440这个板子上没有为DM9000外挂EEPROM,所以读取出来的全部是0xff。见函数dm9000_read_eeprom。 关于外挂EEPROM,可以参考datasheet上的7.EEPROM Format一节。

    12)  很显然ndev是我们在probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方法,使用函数platform_set_drvdata()可以将ndev保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了。

    13) 使用register_netdev()注册ndev。

下面是代码清单:

[c-sharp] view plaincopy
  1. static int __devinit  
  2. dm9000_probe(struct platform_device *pdev)  
  3. {  
  4.     struct dm9000_plat_data *pdata = pdev->dev.platform_data;  
  5.     struct board_info *db;  /* Point a board information structure */  
  6.     struct net_device *ndev;  
  7.     const unsigned char *mac_src;  
  8.     int ret = 0;  
  9.     int iosize;  
  10.     int i;  
  11.     u32 id_val;  
  12.   
  13.     /* Init network device */  
  14.         /*使用alloc_etherdev()来生成一个net_device结构体,并对其公有成员赋值*/  
  15.     ndev = alloc_etherdev(sizeof(struct board_info));  
  16.     if (!ndev) {  
  17.         dev_err(&pdev->dev, "could not allocate device./n");  
  18.         return -ENOMEM;  
  19.     }  
  20.   
  21.     SET_NETDEV_DEV(ndev, &pdev->dev);  
  22.   
  23.     dev_dbg(&pdev->dev, "dm9000_probe()/n");  
  24.   
  25.     /* setup board info structure */  
  26.     db = netdev_priv(ndev);  
  27.     memset(db, 0, sizeof(*db));  
  28.   
  29.     db->dev = &pdev->dev;  
  30.     db->ndev = ndev;  
  31.   
  32.     spin_lock_init(&db->lock);  
  33.     mutex_init(&db->addr_lock);  
  34.   
  35.     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);  
  36.   
  37.     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  38.     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);  
  39.     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  40.   
  41.     if (db->addr_res == NULL || db->data_res == NULL ||  
  42.         db->irq_res == NULL) {  
  43.         dev_err(db->dev, "insufficient resources/n");  
  44.         ret = -ENOENT;  
  45.         goto out;  
  46.     }  
  47.   
  48.     iosize = res_size(db->addr_res);  
  49.     db->addr_req = request_mem_region(db->addr_res->start, iosize,  
  50.                       pdev->name);  
  51.   
  52.     if (db->addr_req == NULL) {  
  53.         dev_err(db->dev, "cannot claim address reg area/n");  
  54.         ret = -EIO;  
  55.         goto out;  
  56.     }  
  57.   
  58.     db->io_addr = ioremap(db->addr_res->start, iosize);  
  59.   
  60.     if (db->io_addr == NULL) {  
  61.         dev_err(db->dev, "failed to ioremap address reg/n");  
  62.         ret = -EINVAL;  
  63.         goto out;  
  64.     }  
  65.   
  66.     iosize = res_size(db->data_res);  
  67.     db->data_req = request_mem_region(db->data_res->start, iosize,  
  68.                       pdev->name);  
  69.   
  70.     if (db->data_req == NULL) {  
  71.         dev_err(db->dev, "cannot claim data reg area/n");  
  72.         ret = -EIO;  
  73.         goto out;  
  74.     }  
  75.   
  76.     db->io_data = ioremap(db->data_res->start, iosize);  
  77.   
  78.     if (db->io_data == NULL) {  
  79.         dev_err(db->dev, "failed to ioremap data reg/n");  
  80.         ret = -EINVAL;  
  81.         goto out;  
  82.     }  
  83.   
  84.     /* fill in parameters for net-dev structure */  
  85.     ndev->base_addr = (unsigned long)db->io_addr;  
  86.     ndev->irq    = db->irq_res->start;  
  87.   
  88.     /* ensure at least we have a default set of IO routines */  
  89.     dm9000_set_io(db, iosize);  
  90.   
  91.     /* check to see if anything is being over-ridden */  
  92.     if (pdata != NULL) {  
  93.         /* check to see if the driver wants to over-ride the 
  94.          * default IO width */  
  95.   
  96.         if (pdata->flags & DM9000_PLATF_8BITONLY)  
  97.             dm9000_set_io(db, 1);  
  98.   
  99.         if (pdata->flags & DM9000_PLATF_16BITONLY)  
  100.             dm9000_set_io(db, 2);  
  101.   
  102.         if (pdata->flags & DM9000_PLATF_32BITONLY)  
  103.             dm9000_set_io(db, 4);  
  104.   
  105.         /* check to see if there are any IO routine 
  106.          * over-rides */  
  107.   
  108.         if (pdata->inblk != NULL)  
  109.             db->inblk = pdata->inblk;  
  110.   
  111.         if (pdata->outblk != NULL)  
  112.             db->outblk = pdata->outblk;  
  113.   
  114.         if (pdata->dumpblk != NULL)  
  115.             db->dumpblk = pdata->dumpblk;  
  116.   
  117.         db->flags = pdata->flags;  
  118.     }  
  119.  
  120. #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL  
  121.     db->flags |= DM9000_PLATF_SIMPLE_PHY;  
  122. #endif  
  123.   
  124.     dm9000_reset(db);  
  125.   
  126.     /* try multiple times, DM9000 sometimes gets the read wrong */  
  127.     for (i = 0; i < 8; i++) {  
  128.         id_val  = ior(db, DM9000_VIDL);  
  129.         id_val |= (u32)ior(db, DM9000_VIDH) << 8;  
  130.         id_val |= (u32)ior(db, DM9000_PIDL) << 16;  
  131.         id_val |= (u32)ior(db, DM9000_PIDH) << 24;  
  132.   
  133.         if (id_val == DM9000_ID)  
  134.             break;  
  135.         dev_err(db->dev, "read wrong id 0x%08x/n", id_val);  
  136.     }  
  137.   
  138.     if (id_val != DM9000_ID) {  
  139.         dev_err(db->dev, "wrong id: 0x%08x/n", id_val);  
  140.         ret = -ENODEV;  
  141.         goto out;  
  142.     }  
  143.   
  144.     /* Identify what type of DM9000 we are working on */  
  145.   
  146.     id_val = ior(db, DM9000_CHIPR);  
  147.     dev_dbg(db->dev, "dm9000 revision 0x%02x/n", id_val);  
  148.   
  149.     switch (id_val) {  
  150.     case CHIPR_DM9000A:  
  151.         db->type = TYPE_DM9000A;  
  152.         break;  
  153.     case CHIPR_DM9000B:  
  154.         db->type = TYPE_DM9000B;  
  155.         break;  
  156.     default:  
  157.         dev_dbg(db->dev, "ID %02x => defaulting to DM9000E/n", id_val);  
  158.         db->type = TYPE_DM9000E;  
  159.     }  
  160.   
  161.     /* from this point we assume that we have found a DM9000 */  
  162.   
  163.     /* driver system function */  
  164.     ether_setup(ndev);  
  165.   
  166.     ndev->netdev_ops = &dm9000_netdev_ops;  
  167.     ndev->watchdog_timeo = msecs_to_jiffies(watchdog);  
  168.     ndev->ethtool_ops    = &dm9000_ethtool_ops;  
  169.   
  170.     db->msg_enable       = NETIF_MSG_LINK;  
  171.     db->mii.phy_id_mask  = 0x1f;  
  172.     db->mii.reg_num_mask = 0x1f;  
  173.     db->mii.force_media  = 0;  
  174.     db->mii.full_duplex  = 0;  
  175.     db->mii.dev       = ndev;  
  176.     db->mii.mdio_read    = dm9000_phy_read;  
  177.     db->mii.mdio_write   = dm9000_phy_write;  
  178.   
  179.     mac_src = "eeprom";  
  180.   
  181.     /* try reading the node address from the attached EEPROM */  
  182.     for (i = 0; i < 6; i += 2)  
  183.         dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);  
  184.   
  185.     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {  
  186.         mac_src = "platform data";  
  187.         memcpy(ndev->dev_addr, pdata->dev_addr, 6);  
  188.     }  
  189.   
  190.     if (!is_valid_ether_addr(ndev->dev_addr)) {  
  191.         /* try reading from mac */  
  192.           
  193.         mac_src = "chip";  
  194.         for (i = 0; i < 6; i++)  
  195.             ndev->dev_addr[i] = ior(db, i+DM9000_PAR);  
  196.     }  
  197.   
  198.     if (!is_valid_ether_addr(ndev->dev_addr))  
  199.         dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "  
  200.              "set using ifconfig/n", ndev->name);  
  201.   
  202.     platform_set_drvdata(pdev, ndev);  
  203.     ret = register_netdev(ndev);  
  204.   
  205.     if (ret == 0)  
  206.         printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)/n",  
  207.                ndev->name, dm9000_type_to_char(db->type),  
  208.                db->io_addr, db->io_data, ndev->irq,  
  209.                ndev->dev_addr, mac_src);  
  210.     return 0;  
  211.   
  212. out:  
  213.     dev_err(db->dev, "not found (%d)./n", ret);  
  214.   
  215.     dm9000_release_board(pdev, db);  
  216.     free_netdev(ndev);  
  217.   
  218.     return ret;  
  219. }  

 

    3. platform_driver的remove, suspend和resume的实现

        remove函数的功能是把设备从内核中移除,释放内存区域。该函数在卸载模块时被调用。代码清单如下:

[c-sharp] view plaincopy
  1. static int __devexit  
  2. dm9000_drv_remove(struct platform_device *pdev)  
  3. {  
  4.     struct net_device *ndev = platform_get_drvdata(pdev);  
  5.   
  6.     platform_set_drvdata(pdev, NULL);  
  7.   
  8.     unregister_netdev(ndev);  
  9.     dm9000_release_board(pdev, (board_info_t *) netdev_priv(ndev));  
  10.     free_netdev(ndev);      /* free device structure */  
  11.   
  12.     dev_dbg(&pdev->dev, "released and freed device/n");  
  13.     return 0;  
  14. }  

 

        suspend函数并不真正把设备从内核中移除,而只是标志设备为removed状态,并设置挂起标志位,最后关闭设备。代码清单如下:

[c-sharp] view plaincopy
  1. static int  
  2. dm9000_drv_suspend(struct platform_device *dev, pm_message_t state)  
  3. {  
  4.     struct net_device *ndev = platform_get_drvdata(dev);  
  5.     board_info_t *db;  
  6.   
  7.     if (ndev) {  
  8.         db = netdev_priv(ndev);  
  9.         db->in_suspend = 1;  
  10.   
  11.         if (netif_running(ndev)) {  
  12.             netif_device_detach(ndev);  
  13.             dm9000_shutdown(ndev);  
  14.         }  
  15.     }  
  16.     return 0;  
  17. }  

      resume函数将挂起的设备复位并初始化,软后将设备标志为attached状态,并设置挂起标志位。代码清单如下:

[c-sharp] view plaincopy
  1. static int  
  2. dm9000_drv_resume(struct platform_device *dev)  
  3. {  
  4.     struct net_device *ndev = platform_get_drvdata(dev);  
  5.     board_info_t *db = netdev_priv(ndev);  
  6.   
  7.     if (ndev) {  
  8.   
  9.         if (netif_running(ndev)) {  
  10.             dm9000_reset(db);  
  11.             dm9000_init_dm9000(ndev);  
  12.   
  13.             netif_device_attach(ndev);  
  14.         }  
  15.   
  16.         db->in_suspend = 0;  
  17.     }  
  18.     return 0;  
  19. }  

 

     4. 下面看一下用于填充net_device中netdev_ops和ethtool_ops的一些函数。

    代码在上面已经写出来了,为了看着方便在下面再写一遍,可以看出虽然mini2440的板子上没有为DM9000挂EEPROM,但这里还是定义了操作EEPROM的函数。就是说写驱动的时候是不考虑具体的板子的,你板子用不用是你的事,但是我们的驱动应该所有的功能都考虑进去。这也体现了驱动和平台分离的设计思想。

[c-sharp] view plaincopy
  1. static const struct net_device_ops dm9000_netdev_ops = {  
  2.     .ndo_open       = dm9000_open,  
  3.     .ndo_stop       = dm9000_stop,  
  4.     .ndo_start_xmit     = dm9000_start_xmit,  
  5.     .ndo_tx_timeout     = dm9000_timeout,  
  6.     .ndo_set_multicast_list = dm9000_hash_table,  
  7.     .ndo_do_ioctl       = dm9000_ioctl,  
  8.     .ndo_change_mtu     = eth_change_mtu,  
  9.     .ndo_validate_addr  = eth_validate_addr,  
  10.     .ndo_set_mac_address    = eth_mac_addr,  
  11. #ifdef CONFIG_NET_POLL_CONTROLLER  
  12.     .ndo_poll_controller    = dm9000_poll_controller,  
  13. #endif  
  14. };  
[c-sharp] view plaincopy
  1. static const struct ethtool_ops dm9000_ethtool_ops = {  
  2.     .get_drvinfo        = dm9000_get_drvinfo,  
  3.     .get_settings       = dm9000_get_settings,  
  4.     .set_settings       = dm9000_set_settings,  
  5.     .get_msglevel       = dm9000_get_msglevel,  
  6.     .set_msglevel       = dm9000_set_msglevel,  
  7.     .nway_reset     = dm9000_nway_reset,  
  8.     .get_link       = dm9000_get_link,  
  9.     .get_eeprom_len     = dm9000_get_eeprom_len,  
  10.     .get_eeprom     = dm9000_get_eeprom,  
  11.     .set_eeprom     = dm9000_set_eeprom,  
  12. };  

  

    *dm9000_open()

    进行的工作有 向内核注册中断,复位并初始化dm9000,检查MII接口,使能传输等。代码清单如下:

[c-sharp] view plaincopy
  1. /* 
  2.  *  Open the interface. 
  3.  *  The interface is opened whenever "ifconfig" actives it. 
  4.  */  
  5. static int  
  6. dm9000_open(struct net_device *dev)  
  7. {  
  8.     board_info_t *db = netdev_priv(dev);  
  9.     unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;  
  10.   
  11.     if (netif_msg_ifup(db))  
  12.         dev_dbg(db->dev, "enabling %s/n", dev->name);  
  13.   
  14.     /* If there is no IRQ type specified, default to something that 
  15.      * may work, and tell the user that this is a problem */  
  16.   
  17.     if (irqflags == IRQF_TRIGGER_NONE)  
  18.         dev_warn(db->dev, "WARNING: no IRQ resource flags set./n");  
  19.   
  20.     irqflags |= IRQF_SHARED;  
  21.   
  22.     if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))/*注册一个中断,中断处理函数为dm9000_interrupt()*/  
  23.         return -EAGAIN;  
  24.   
  25.     /* Initialize DM9000 board */  
  26.     dm9000_reset(db);  
  27.     dm9000_init_dm9000(dev);  
  28.   
  29.     /* Init driver variable */  
  30.     db->dbug_cnt = 0;  
  31.   
  32.     mii_check_media(&db->mii, netif_msg_link(db), 1);  
  33.     netif_start_queue(dev);  
  34.       
  35.     dm9000_schedule_poll(db);/*之前在probe函数中已经使用INIT_DELAYED_WORK来初始化一个延迟工作队列并关联了一个操作函数dm9000_poll_work(), 此时运行schedule来调用这个函数*/  
  36.   
  37.     return 0;  
  38. }  

   

    *dm9000_stop()

     做的工作基本上和open相反。代码清单如下:

[c-sharp] view plaincopy
  1. /* 
  2.  * Stop the interface. 
  3.  * The interface is stopped when it is brought. 
  4.  */  
  5. static int  
  6. dm9000_stop(struct net_device *ndev)  
  7. {  
  8.     board_info_t *db = netdev_priv(ndev);  
  9.   
  10.     if (netif_msg_ifdown(db))  
  11.         dev_dbg(db->dev, "shutting down %s/n", ndev->name);  
  12.   
  13.     cancel_delayed_work_sync(&db->phy_poll); /*杀死延迟工作队列phy_poll*/  
  14.   
  15.         /*停止传输并清空carrier*/  
  16.     netif_stop_queue(ndev);  
  17.     netif_carrier_off(ndev);  
  18.   
  19.     /* free interrupt */  
  20.     free_irq(ndev->irq, ndev);  
  21.   
  22.     dm9000_shutdown(ndev);  
  23.   
  24.     return 0;  
  25. }  

 

    *dm9000_start_xmit()

    重要的发送数据包函数。从上层发送sk_buff包。在看代码之前先来看一下DM9000是如何发送数据包的。

    

如上图所示,在DM9000内部SRAM中,地址0x0000~0x0BFF是TX Buffer, 地址0x0C00~0x3FFF是RX Buffer。在发送一个包之前,包中的有效数据必须先被存储到TX Buffer中并且使用输出端口命令来选择MWCMD寄存器。包的长度定义在TXPLL和TXPLH中。最后设置TXCR寄存器的bit[0] TXREQ来自动发送包。如果设置了IMR寄存器的PTM位,则DM9000会产生一个中断触发在ISR寄存器的bit[1]=PTS=1, 同时设置一个完成标志在NSR寄存器的bit[2]=TX1END或者 bit[3]=TX2END,表示包已经发送完了。发送一个包的具体步骤如下:

Step 1: 检查存储数据宽度。通过读取中断状态寄存器(ISR)的bit[7:6]来确定是8bit,16bit还是32bit。

Step 2: 写数据到TX SRAM中。

Step 3: 写传输长度到TXPLL和TXPLH寄存器中。

Step 4: 设置TXCR寄存器的bit[0]TXREQ来开始发送一个包。

 

代码清单如下,让我们看看在获得自旋锁这段期间都干了些什么:

[c-sharp] view plaincopy
  1. /* 
  2.  *  Hardware start transmission. 
  3.  *  Send a packet to media from the upper layer. 
  4.  */  
  5. static int  
  6. dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)  
  7. {  
  8.     unsigned long flags;  
  9.     board_info_t *db = netdev_priv(dev);  
  10.   
  11.     dm9000_dbg(db, 3, "%s:/n", __func__);  
  12.   
  13.     if (db->tx_pkt_cnt > 1)  
  14.         return NETDEV_TX_BUSY;  
  15.   
  16.         /*获得自旋锁*/  
  17.     spin_lock_irqsave(&db->lock, flags);   
  18.   
  19.     /* Move data to DM9000 TX RAM */  
  20.         /*下面四行代码将skb中的data部分写入DM9000的TX RAM,并更新已发送字节数和发送计数*/  
  21.     writeb(DM9000_MWCMD, db->io_addr);  
  22.   
  23.     (db->outblk)(db->io_data, skb->data, skb->len);  
  24.     dev->stats.tx_bytes += skb->len;  
  25.   
  26.     db->tx_pkt_cnt++;  
  27.     /* TX control: First packet immediately send, second packet queue */  
  28.         /*如果发送的是第一个包,则设置一下包的长度后直接发送*/  
  29.         /*如果发的不是第一个包,*/  
  30.     if (db->tx_pkt_cnt == 1) {  
  31.         /* Set TX length to DM9000 */  
  32.         iow(db, DM9000_TXPLL, skb->len);  
  33.         iow(db, DM9000_TXPLH, skb->len >> 8);  
  34.   
  35.         /* Issue TX polling command */  
  36.         iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */  
  37.   
  38.         dev->trans_start = jiffies;  /* save the time stamp */  
  39.     } else {  
  40.         /* Second packet */  
  41.                 /*如果发送的是第二个数据包(表明队列中此时有包发送),则将其加入队列中:将skb->len和skb->ip_summed(控制校验操作)赋值给board_info_t中有关队列的相关成员。调用函数netif_stop_queue(dev),通知内核现在queue已满,不能再将发送数据传到队列中,注:第二个包的发送将在tx_done中实现。*/  
  42.         db->queue_pkt_len = skb->len;  
  43.         netif_stop_queue(dev);  
  44.     }  
  45.   
  46.         /*释放自旋锁*/  
  47.     spin_unlock_irqrestore(&db->lock, flags);  
  48.   
  49.     /* free this SKB */  
  50.     dev_kfree_skb(skb);  
  51.   
  52.     return 0;  
  53. }  

 

    *dm9000_timeout()

    当watchdog超时时调用该函数。主要的功能是保存寄存器地址,停止队列,重启并初始化DM9000,唤醒队列,恢复寄存器地址。

    代码清单如下:

[c-sharp] view plaincopy
  1. /* Our watchdog timed out. Called by the networking layer */  
  2. static void dm9000_timeout(struct net_device *dev)  
  3. {  
  4.     board_info_t *db = netdev_priv(dev);  
  5.     u8 reg_save;  
  6.     unsigned long flags;  
  7.   
  8.     /* Save previous register address */  
  9.     reg_save = readb(db->io_addr);  
  10.     spin_lock_irqsave(&db->lock, flags);  
  11.   
  12.     netif_stop_queue(dev);  
  13.     dm9000_reset(db);  
  14.     dm9000_init_dm9000(dev);  
  15.     /* We can accept TX packets again */  
  16.     dev->trans_start = jiffies;  
  17.     netif_wake_queue(dev);  
  18.   
  19.     /* Restore previous register address */  
  20.     writeb(reg_save, db->io_addr);  
  21.     spin_unlock_irqrestore(&db->lock, flags);  
  22. }  

 

    *dm9000_hash_table()

    该函数用来设置DM9000的组播地址。代码清单如下:

[c-sharp] view plaincopy
  1. /* 
  2.  *  Set DM9000 multicast address 
  3.  */  
  4. static void  
  5. dm9000_hash_table(struct net_device *dev)  
  6. {  
  7.     board_info_t *db = netdev_priv(dev);  
  8.     struct dev_mc_list *mcptr = dev->mc_list;  
  9.     int mc_cnt = dev->mc_count;  
  10.     int i, oft;  
  11.     u32 hash_val;  
  12.     u16 hash_table[4];  
  13.     u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;  
  14.     unsigned long flags;  
  15.   
  16.     dm9000_dbg(db, 1, "entering %s/n", __func__);  
  17.   
  18.     spin_lock_irqsave(&db->lock, flags);  
  19.   
  20.     for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)  
  21.         iow(db, oft, dev->dev_addr[i]);  
  22.   
  23.     /* Clear Hash Table */  
  24.     for (i = 0; i < 4; i++)  
  25.         hash_table[i] = 0x0;  
  26.   
  27.     /* broadcast address */  
  28.     hash_table[3] = 0x8000;  
  29.   
  30.     if (dev->flags & IFF_PROMISC)  
  31.         rcr |= RCR_PRMSC;  
  32.   
  33.     if (dev->flags & IFF_ALLMULTI)  
  34.         rcr |= RCR_ALL;  
  35.   
  36.     /* the multicast address in Hash Table : 64 bits */  
  37.     for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {  
  38.         hash_val = ether_crc_le(6, mcptr->dmi_addr) & 0x3f;  
  39.         hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);  
  40.     }  
  41.   
  42.     /* Write the hash table to MAC MD table */  
  43.     for (i = 0, oft = DM9000_MAR; i < 4; i++) {  
  44.         iow(db, oft++, hash_table[i]);  
  45.         iow(db, oft++, hash_table[i] >> 8);  
  46.     }  
  47.   
  48.     iow(db, DM9000_RCR, rcr);  
  49.     spin_unlock_irqrestore(&db->lock, flags);  
  50. }  

 

    *dm9000_ioctl()

    从源码可以看出,dm9000的ioctl实际上是使用了mii的ioctl。代码清单如下:

[c-sharp] view plaincopy
  1. static int dm9000_ioctl(struct net_device *dev, struct ifreq *req, int cmd)  
  2. {  
  3.     board_info_t *dm = to_dm9000_board(dev);  
  4.   
  5.     if (!netif_running(dev))  
  6.         return -EINVAL;  
  7.   
  8.     return generic_mii_ioctl(&dm->mii, if_mii(req), cmd, NULL);  
  9. }  

 

    *dm9000_poll_controller()

    当内核配置Netconsole时该函数生效。代码清单如下:

[c-sharp] view plaincopy
  1. #ifdef CONFIG_NET_POLL_CONTROLLER  
  2. /* 
  3.  *Used by netconsole 
  4.  */  
  5. static void dm9000_poll_controller(struct net_device *dev)  
  6. {  
  7.     disable_irq(dev->irq);  
  8.     dm9000_interrupt(dev->irq, dev);  
  9.     enable_irq(dev->irq);  
  10. }  
  11. #endif  

 

    *dm9000_get_drvinfo()

    该函数去的设备的基本信息(设备名,版本,总线名)传给ethtool_drvinfo结构体变量。代码清单如下:

[c-sharp] view plaincopy
  1. static void dm9000_get_drvinfo(struct net_device *dev,  
  2.                    struct ethtool_drvinfo *info)  
  3. {  
  4.     board_info_t *dm = to_dm9000_board(dev); /*to_dm9000_board实际上就是调用了netdev_priv(dev)*/  
  5.   
  6.     strcpy(info->driver, CARDNAME);  
  7.     strcpy(info->version, DRV_VERSION);  
  8.     strcpy(info->bus_info, to_platform_device(dm->dev)->name);  
  9. }  

 

    *dm9000_get_settings()

    该函数得到由参数cmd指定的设置信息。

    *dm9000_set_settings()

    该函数设置由参数cmd指定的信息。

    *dm9000_get_msglevel()

    *dm9000_set_msglevel()

    这两个函数设置和取得message level,实际是设置和取得board_info中的msg_enable信息。

    *dm9000_nway_reset()

    重启mii的自动协商

    *dm9000_get_link()

    该函数的到link状态。如果带外部PHY,则返回mii链接状态。 否则返回DM9000 NSR寄存器数值。

    *dm9000_get_eeprom_len()
      dm9000_get_eeprom()
      dm9000_set_eeprom()

    这三个函数用来读写eeprom。

 

   5. 与数据传输有关的函数。

     上面已经分析了一个与数据传输有关的函数,那就是发送数据的函数dm9000_start_xmit()。这里再来分析数据的接收。再看具体代码之前还是来看看DM9000的数据接收的过程。

      接收的数据存储在RX SRAM中,地址是0C00h~3FFFh。存储在RX_SRAM中的每个包都有4个字节的信息头。可以使用MRCMDX和MRCMD寄存器来得到这些信息。第一个字节用来检查数据包是否接收到了RX_SRAM中,如果这个字节是"01",意味着一个包已经接收。如果是"00",则还没有数据包被接收到RX_SRAM中。第二个字节保存接收到的数据包的信息,格式和RSR寄存器一样。根据这个格式,接收到的包能被校验是正确的还是错误的包。第三和第四字节保存了接收的数据包的长度。这四个字节以外的其他字节就是接收包的数据。看下图可以更好的理解这种格式。

根据包的结构可以知道接收一个包应该按照下面的步骤来进行:

第一步:判断包是否已经接收过来了。需要用到MRCMDX寄存器。MRCMDX寄存器是存储数据读命令寄存器(地址不增加)。 这个寄存器只是用来读接收包标志位"01"。下面这段代码是一个例子,用来判断RX ready:

 

[c-sharp] view plaincopy
  1. u8 RX_ready = ior( IOaddr, 0xF0 );         /* dummy read the packet ready flag */  
  2. RX_ready = (u8) inp( IOaddr + 4 );         /* got the most updated data */  
  3. if ( RX_ready == 1 ) {                     /* ready check: this byte must be 0 or 1 */  
  4.        /* check the RX status and to get RX length (see datasheet ch.5.6.3) */  
  5.        /* income RX a packet (see datasheet ch.5.6.4) */  
  6. else if ( RX_ready != 0 ) {              /* stop device and wait to reset device */  
  7.        iow( IOaddr, 0xFF, 0x80 );          /* stop INT request */  
  8.        iow( IOaddr, 0xFE, 0x0F );          /* clear ISR status */  
  9.        iow( IOaddr, 0x05, 0x00 );          /* stop RX function */  
  10.        u8 device_wait_reset = TRUE;        /* raise the reset flag */  
  11. }  

 第二步:检查包的状态和长度。需要用到MRCMD寄存器(存储数据读命令,读指针自动增加)。下面这段例子代码用来读RX状态和长度。

[c-sharp] view plaincopy
  1. u8 io_mode = ior( IOaddr, 0xFE ) >> 6; /* ISR bit[7:6] keep I/O mode */  
  2. outp( IOaddr, 0xF2 );                /* trigger MRCMD reg_F2h with read_ptr++ */  
  3. /* int RX_status : the RX packet status, int RX_length : the RX packet length */  
  4. if ( io_mode == 2 ) {                /* I/O byte mode */  
  5. RX_status = inp( IOaddr + 4 ) + ( inp( IOaddr + 4 ) << 8 );  
  6. RX_length = inp( IOaddr + 4 ) + ( inp( IOaddr + 4 ) << 8 );        }  
  7. else if ( io_mode == 0 ) {           /* I/O word mode */  
  8. RX_status = inpw( IOaddr + 4 );  
  9. RX_length = inpw( IOaddr + 4 );             }  
  10. else if ( io_mode == 1 ) {           /* I/O dword mode */  
  11. (u32) status_tmp = inpl( IOaddr + 4 );           /* got the RX 32-bit dword data */  
  12. RX_status = (u16)( status_tmp & 0xFFFF );  
  13. RX_length = (u16)( ( status_tmp >> 16 ) & 0xFFFF );          }  

第三步:读包的数据。也需要MRCMD寄存器。例子代码如下:

[c-sharp] view plaincopy
  1. /* u8 RX_data[] : the data of the received packet */  
  2. if ( io_mode == 2 ) {                 /* I/O byte mode */  
  3. for ( i = 0 ; i < RX_length ; i++ ) /* loop to read a byte data from RX SRAM */  
  4.   RX_data[ i ] = (u8) inp( IOaddr + 4 );          }  
  5. else if ( io_mode == 0 ) {            /* I/O word mode */  
  6. int length_tmp = ( RX_length + 1 ) / 2;  
  7. for ( i = 0 ; i < length_tmp ; i++ ) /* loop to read a word data from RX SRAM */  
  8.  ( (u16 *)RX_data)[ i ] = inpw( IOaddr + 4 );           }  
  9. else if ( io_mode == 1 ) {            /* I/O dword mode */  
  10. int length_tmp = ( RX_length + 3 ) / 4;  
  11. for ( i = 0 ; i < length_tmp ; i++ ) /* loop to read a dword data from RX SRAM */  
  12.  ( (u32 *)RX_data)[ i ] = inpl( IOaddr + 4 ); }         /* inpl() is inport 32-bit I/O */  

 

 

下面的dm9000_rx()函数实际上是按照上面这三个步骤来实现的,具体实现并不一定是要参照例子代码。 注意这里按照DM9000接收包的格式定义了一个结构体dm9000_rxhdr用来表示头部的四个字节。代码清单如下:

[c-sharp] view plaincopy
  1. struct dm9000_rxhdr {  
  2.     u8  RxPktReady;  
  3.     u8  RxStatus;  
  4.     __le16  RxLen;  
  5. } __attribute__((__packed__));  

接收函数代码如下:

[c-sharp] view plaincopy
  1. /* 
  2.  *  Received a packet and pass to upper layer 
  3.  */  
  4. static void  
  5. dm9000_rx(struct net_device *dev)  
  6. {  
  7.     board_info_t *db = netdev_priv(dev);  
  8.     struct dm9000_rxhdr rxhdr;  
  9.     struct sk_buff *skb;  
  10.     u8 rxbyte, *rdptr;  
  11.     bool GoodPacket;  
  12.     int RxLen;  
  13.   
  14.     /* Check packet ready or not */  
  15.     do {  
  16.         ior(db, DM9000_MRCMDX); /* Dummy read */  
  17.   
  18.         /* Get most updated data */  
  19.                 /*读一下最新数据的第一个字节*/  
  20.         rxbyte = readb(db->io_data);  
  21.   
  22.         /* Status check: this byte must be 0 or 1 */  
  23.                 /*DM9000_PKT_RDY定义是0x01,如果第一个字节大于0x01,则不是正确的状态。因为第一个字节只能是01h或00h*/  
  24.         if (rxbyte > DM9000_PKT_RDY) {  
  25.             dev_warn(db->dev, "status check fail: %d/n", rxbyte);  
  26.             iow(db, DM9000_RCR, 0x00);  /* Stop Device */  
  27.             iow(db, DM9000_ISR, IMR_PAR);   /* Stop INT request */  
  28.             return;  
  29.         }  
  30.   
  31.         if (rxbyte != DM9000_PKT_RDY)  
  32.             return;  
  33.   
  34.         /* A packet ready now  & Get status/length */  
  35.         GoodPacket = true;  
  36.         writeb(DM9000_MRCMD, db->io_addr);  
  37.   
  38.         (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));/*一次性读入四个字节的内容到rxhdr变量*/  
  39.   
  40.         RxLen = le16_to_cpu(rxhdr.RxLen);  
  41.   
  42.         if (netif_msg_rx_status(db))  
  43.             dev_dbg(db->dev, "RX: status %02x, length %04x/n",  
  44.                 rxhdr.RxStatus, RxLen);  
  45.   
  46.         /* Packet Status check */  
  47.         if (RxLen < 0x40) {  
  48.             GoodPacket = false;  
  49.             if (netif_msg_rx_err(db))  
  50.                 dev_dbg(db->dev, "RX: Bad Packet (runt)/n");  
  51.         }  
  52.   
  53.         if (RxLen > DM9000_PKT_MAX) {  
  54.             dev_dbg(db->dev, "RST: RX Len:%x/n", RxLen);  
  55.         }  
  56.   
  57.         /* rxhdr.RxStatus is identical to RSR register. */  
  58.         if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |  
  59.                       RSR_PLE | RSR_RWTO |  
  60.                       RSR_LCS | RSR_RF)) {  
  61.             GoodPacket = false;  
  62.             if (rxhdr.RxStatus & RSR_FOE) {  
  63.                 if (netif_msg_rx_err(db))  
  64.                     dev_dbg(db->dev, "fifo error/n");  
  65.                 dev->stats.rx_fifo_errors++;  
  66.             }  
  67.             if (rxhdr.RxStatus & RSR_CE) {  
  68.                 if (netif_msg_rx_err(db))  
  69.                     dev_dbg(db->dev, "crc error/n");  
  70.                 dev->stats.rx_crc_errors++;  
  71.             }  
  72.             if (rxhdr.RxStatus & RSR_RF) {  
  73.                 if (netif_msg_rx_err(db))  
  74.                     dev_dbg(db->dev, "length error/n");  
  75.                 dev->stats.rx_length_errors++;  
  76.             }  
  77.         }  
  78.   
  79.         /* Move data from DM9000 */  
  80.                 /*关键的代码就是这里啦。使用到了上面提到的sk_buff。将RX SRAM中的data段数据放入sk_buff,然后发送给上层,至于怎么发送,不用去驱动操心了。sk_buff的protocol全部搞定*/  
  81.         if (GoodPacket  
  82.             && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {  
  83.             skb_reserve(skb, 2);  
  84.             rdptr = (u8 *) skb_put(skb, RxLen - 4);  
  85.   
  86.             /* Read received packet from RX SRAM */  
  87.   
  88.             (db->inblk)(db->io_data, rdptr, RxLen);  
  89.             dev->stats.rx_bytes += RxLen;  
  90.   
  91.             /* Pass to upper layer */  
  92.             skb->protocol = eth_type_trans(skb, dev);  
  93.             netif_rx(skb);  
  94.             dev->stats.rx_packets++;  
  95.   
  96.         } else {  
  97.             /* need to dump the packet's data */  
  98.   
  99.             (db->dumpblk)(db->io_data, RxLen);  
  100.         }  
  101.     } while (rxbyte == DM9000_PKT_RDY);  
  102. }  

 

   6. 中断处理相关函数

   DM9000的驱动程序采用了中断方式而非轮询方式。触发中断的时机发生在:1)DM9000接收到一个包以后。2)DM9000发送完了一个包以后。

   中断处理函数在open的时候被注册进内核。代码清单如下:

[c-sharp] view plaincopy
  1. static irqreturn_t dm9000_interrupt(int irq, void *dev_id)  
  2. {  
  3.     struct net_device *dev = dev_id;  
  4.     board_info_t *db = netdev_priv(dev);  
  5.     int int_status;  
  6.     unsigned long flags;  
  7.     u8 reg_save;  
  8.   
  9.     dm9000_dbg(db, 3, "entering %s/n", __func__);  
  10.   
  11.     /* A real interrupt coming */  
  12.   
  13.     /* holders of db->lock must always block IRQs */  
  14.     spin_lock_irqsave(&db->lock, flags);  
  15.   
  16.     /* Save previous register address */  
  17.     reg_save = readb(db->io_addr);  
  18.   
  19.     /* Disable all interrupts */  
  20.     iow(db, DM9000_IMR, IMR_PAR);  
  21.   
  22.     /* Got DM9000 interrupt status */  
  23.     int_status = ior(db, DM9000_ISR);   /* Got ISR */  
  24.     iow(db, DM9000_ISR, int_status);    /* Clear ISR status */  
  25.   
  26.     if (netif_msg_intr(db))  
  27.         dev_dbg(db->dev, "interrupt status %02x/n", int_status);  
  28.   
  29.     /* Received the coming packet */  
  30.         /*如果是由于收到数据而触发的中断,显然调用dm9000_rx()把数据取走,传递给上层*/  
  31.     if (int_status & ISR_PRS)  
  32.         dm9000_rx(dev);  
  33.   
  34.     /* Trnasmit Interrupt check */  
  35.         /*如果是由于发送完了数据而触发的中断,则调用dm9000_tx_done()函数,下面具体分析这个函数*/  
  36.     if (int_status & ISR_PTS)  
  37.         dm9000_tx_done(dev, db);  
  38.   
  39.     if (db->type != TYPE_DM9000E) {  
  40.         if (int_status & ISR_LNKCHNG) {  
  41.             /* fire a link-change request */  
  42.             schedule_delayed_work(&db->phy_poll, 1);  
  43.         }  
  44.     }  
  45.   
  46.     /* Re-enable interrupt mask */  
  47.     iow(db, DM9000_IMR, db->imr_all);  
  48.   
  49.     /* Restore previous register address */  
  50.     writeb(reg_save, db->io_addr);  
  51.   
  52.     spin_unlock_irqrestore(&db->lock, flags);  
  53.   
  54.     return IRQ_HANDLED;  
  55. }  

   *dm9000_tx_done()

   注:dm9000可以发送两个数据包,当发送一个数据包产生中断后,要确认一下队列中有没有第2个包需要发送。

    (1)读取dm9000寄存器NSR(Network Status Register)获取发送的状态,存在变量tx_status中;

    (2)如果发送状态为NSR_TX2END(第2个包发送完毕)或者NSR_TX1END(第1个包发送完毕),则将待发送的数据包数量(db->tx_pkt_cnt)减1,已发送的数据包数量(dev->stats.tx_packets)加1;

    (3)检查变量db->tx_pkt_cnt(待发送的数据包)是否大于0(表明还有数据包要发送),则调用函数dm9000_send_packet发送队列中的数据包;

    (4)调用函数netif_wake_queue(dev)通知内核可以将待发送的数据包进入发送队列。

[c-sharp] view plaincopy
  1. /* 
  2.  * DM9000 interrupt handler 
  3.  * receive the packet to upper layer, free the transmitted packet 
  4.  */  
  5.   
  6. static void dm9000_tx_done(struct net_device *dev, board_info_t *db)  
  7. {  
  8.     int tx_status = ior(db, DM9000_NSR);    /* Got TX status */  
  9.   
  10.     if (tx_status & (NSR_TX2END | NSR_TX1END)) {  
  11.         /* One packet sent complete */  
  12.         db->tx_pkt_cnt--;  
  13.         dev->stats.tx_packets++;  
  14.   
  15.         if (netif_msg_tx_done(db))  
  16.             dev_dbg(db->dev, "tx done, NSR %02x/n", tx_status);  
  17.   
  18.         /* Queue packet check & send */  
  19.         if (db->tx_pkt_cnt > 0) {  
  20.             iow(db, DM9000_TXPLL, db->queue_pkt_len);  
  21.             iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8);  
  22.             iow(db, DM9000_TCR, TCR_TXREQ);  
  23.             dev->trans_start = jiffies;  
  24.         }  
  25.         netif_wake_queue(dev);  
  26.     }  
  27. }  

 

 

   7. 一些操作硬件细节的函数。

   在看函数之前还是先来看一下DM9000 CMD Pin 和Processor并行总线的连接关系。CMD管脚用来设置命令类型。当CMD管脚拉高时,这个命令周期访问DATA_PORT。 如果拉低, 则这个命令周期访问ADDR_PORT。见下图:

当然,内存映射的I/O空间读写还是采用最基本的readb(), readw(), readl(), writeb(), writew(), writel() , readsb(), readsw(), readsl(), writesb(), writesw(), writesl() 。

在DM9000的驱动中还自定义了几个函数,方便操作。

     * ior()

        从IO端口读一个字节。代码清单如下:

[c-sharp] view plaincopy
  1. static u8  
  2. ior(board_info_t * db, int reg)  
  3. {  
  4.     writeb(reg, db->io_addr); /*写reg到ADDR_PORT,用来选择寄存器*/  
  5.     return readb(db->io_data); /*从DATA_PORT读一个字节,用来读寄存器*/  
  6. }  

 

    * iow()

        向IO端口写一个字节。代码清单如下:

[c-sharp] view plaincopy
  1. /* 
  2.  *   Write a byte to I/O port 
  3.  */  
  4.   
  5. static void  
  6. iow(board_info_t * db, int reg, int value)  
  7. {  
  8.     writeb(reg, db->io_addr);  
  9.     writeb(value, db->io_data);  
  10. }  

 

此外还有dm9000_outblk_8bit(), dm9000_outblk_16bit(), dm9000_outblk_32bit(), dm9000_inblk_8bit(), dm9000_inblk_16bit(), dm9000_inblk_32bit()等等。不一一解释。

天猫爆款 联想 ibm ThinkPad E320 129862C 代替55C 笔记本 包邮

 

 

======================THE END======================


转自:http://blog.csdn.net/ypoflyer/article/details/6209922

2015-07-06 19:32:39 gotowu 阅读数 693
  • 网络设备驱动介绍-linux驱动开发第11部分

    本课程是linux驱动开发的第11个课程,主要内容是linux的网络驱动的介绍,首先讲述了网络设备驱动接口和之前讲的2种的不同,然后以一个虚拟网卡驱动源码学习了网卡驱动的框架,后分析了一个实际网卡DM9000的驱动细节。

    4314 人正在学习 去看看 朱有鹏

 转载请注明出处:http://blog.csdn.net/gotowu/article/details/46329809

10、DM9000驱动中有两个中断函数,dm9000_interruptdm9000_wol_interrupt

1dm9000_interrupt:触发中断的时机发生在:

1DM9000接收到一个包以后。

2DM9000发送完了一个包以后

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;

	dm9000_dbg(db, 3, "entering %s\n", __func__);

	/* A real interrupt coming */

	/* holders of db->lock must always block IRQs */
	//获取自旋锁,关中断
	spin_lock_irqsave(&db->lock, flags);

	/* Save previous register address */
	reg_save = readb(db->io_addr);

	/* Disable all interrupts */
	iow(db, DM9000_IMR, IMR_PAR);

	/* Got DM9000 interrupt status */
	int_status = ior(db, DM9000_ISR);	/* Got ISR */
	iow(db, DM9000_ISR, int_status);	/* Clear ISR status */

	if (netif_msg_intr(db))
		dev_dbg(db->dev, "interrupt status %02x\n", int_status);

	/* Received the coming packet */
	//如果是接收中断,读取接收的数据并存入skbuff,并提交协议上一层 
	if (int_status & ISR_PRS)                 //PRS: packet receive latch
		dm9000_rx(dev);

	/* Trnasmit Interrupt check */
	 //由于发送完了数据而触发的发送中断,如果还有包未发完,继续发送。
	if (int_status & ISR_PTS)
		dm9000_tx_done(dev, db);
	
//如果是DM9000E系列的芯片,作特别处理
	if (db->type != TYPE_DM9000E) {
		if (int_status & ISR_LNKCHNG) {
			/* fire a link-change request */
			schedule_delayed_work(&db->phy_poll, 1);
		}
	}

	/* Re-enable interrupt mask */
	iow(db, DM9000_IMR, db->imr_all);

	/* Restore previous register address */
	writeb(reg_save, db->io_addr);

	spin_unlock_irqrestore(&db->lock, flags);

	return IRQ_HANDLED;
}

(2)dm9000_wol_interrupt : 这个中断函数主要做的事情是读dm9000寄存器NSR,WCR,根据读到的内容作出相应提示.

static irqreturn_t dm9000_wol_interrupt(int irq, void *dev_id)
{
	struct net_device *dev = dev_id;
	board_info_t *db = netdev_priv(dev);
	unsigned long flags;
	unsigned nsr, wcr;
 //关中断,获得自旋锁
	spin_lock_irqsave(&db->lock, flags);

	nsr = ior(db, DM9000_NSR);
	wcr = ior(db, DM9000_WCR);         //WCR :wake up control register

	dev_dbg(db->dev, "%s: NSR=0x%02x, WCR=0x%02x\n", __func__, nsr, wcr);

	if (nsr & NSR_WAKEST) {              //WAKEST :wakeup event status
		/* clear, so we can avoid */
		iow(db, DM9000_NSR, NSR_WAKEST);

		if (wcr & WCR_LINKST)
			dev_info(db->dev, "wake by link status change\n");
		if (wcr & WCR_SAMPLEST)
			dev_info(db->dev, "wake by sample packet\n");
		if (wcr & WCR_MAGICST )
			dev_info(db->dev, "wake by magic packet\n");
		if (!(wcr & (WCR_LINKST | WCR_SAMPLEST | WCR_MAGICST)))
			dev_err(db->dev, "wake signalled with no reason? "
				"NSR=0x%02x, WSR=0x%02x\n", nsr, wcr);

	}
 //释放自旋锁,恢复本地中断 
	spin_unlock_irqrestore(&db->lock, flags);

	return (nsr & NSR_WAKEST) ? IRQ_HANDLED : IRQ_NONE;
}

11、在中断中,如果还有包未发完,则会调用dm9000_tx_done

该函数首先读取dm9000寄存器NSRNetwork Status Register)获取发送的状态,存在变量tx_status中,如果发送状态为NSR_TX2END(第2个包发送完毕)或者NSR_TX1END(第1个包发送完毕),则将待发送的数据包数量(db->tx_pkt_cnt)减1,已发送的数据包数量(dev->stats.tx_packets)加1

static void dm9000_tx_done(struct net_device *dev, board_info_t *db)
{
	int tx_status = ior(db, DM9000_NSR);	/*  NSR: 网络状态寄存器,读取dm9000寄存器*/
		
	if (tx_status & (NSR_TX2END | NSR_TX1END)) {
		/* One packet sent complete */
		db->tx_pkt_cnt--;
		dev->stats.tx_packets++;

		if (netif_msg_tx_done(db))
			dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);

		/* Queue packet check & send */
		if (db->tx_pkt_cnt > 0)                    //db->tx_pkt_cnt(待发送的数据包)
			dm9000_send_packet(dev, db->queue_ip_summed,
					   db->queue_pkt_len);
		netif_wake_queue(dev);                //通知内核可以将待发送的数据包进入发送队列
	}    
}


11、超时函数 dm9000_timeout

发送数据失败时,系统会调用dm9000_timeout函数。当传输数据超时时,意味发送操作失败或硬件进入未知状态。在超时函数中会调用netif_wake_queue()函数来重新启动设备发送队列。主要的功能是保存寄存器地址,停止队列,重启并初始化DM9000,唤醒队列,恢复寄存器地址。

static void dm9000_timeout(struct net_device *dev)
{
	board_info_t *db = netdev_priv(dev);
	u8 reg_save;
	unsigned long flags;

	/* Save previous register address */
	spin_lock_irqsave(&db->lock, flags);
	reg_save = readb(db->io_addr);         //保存寄存器地址

	netif_stop_queue(dev);                      //停止队列
	dm9000_reset(db);                            //复位
	dm9000_init_dm9000(dev);             //初始化
	/* We can accept TX packets again */
	dev->trans_start = jiffies; /* prevent tx timeout 记录最后的数据包开始发送的时间戳*/
	netif_wake_queue(dev);                //重启发送队列

	/* Restore previous register address */
	writeb(reg_save, db->io_addr);            //恢复寄存器地址
	spin_unlock_irqrestore(&db->lock, flags);
}

13、设置多播地址dm9000_hash_table

static void dm9000_hash_table(struct net_device *dev)
{
	board_info_t *db = netdev_priv(dev);
	unsigned long flags;

	spin_lock_irqsave(&db->lock, flags); //设置自旋锁,同时保存中断设置
	dm9000_hash_table_unlocked(dev); //调用dm9000_hash_table_unlocked来进行设置
	spin_unlock_irqrestore(&db->lock, flags);//解锁
}

dm9000_hash_table_unlocked

static void dm9000_hash_table_unlocked(struct net_device *dev)
{
	board_info_t *db = netdev_priv(dev);
	struct netdev_hw_addr *ha;
	int i, oft;
	u32 hash_val;
	u16 hash_table[4];
	u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN; //RCR :接收控制寄存器
	//DIS_LONG:discard long packet,超出1522字节. DIS_CRC:discard crc error packet.  RXEN:RX enable
	dm9000_dbg(db, 1, "entering %s\n", __func__);

	for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)        //PAR:物理地址寄存器
		iow(db, oft, dev->dev_addr[i]);        //将物理地址写入PAR寄存器  PAB 0-5

	/* Clear Hash Table */
	for (i = 0; i < 4; i++)
		hash_table[i] = 0x0;

	/* broadcast address */
	hash_table[3] = 0x8000;
								//IFF : interface flags 
	if (dev->flags & IFF_PROMISC)     //  IFF_PROMISC:    receive all packets
	
		rcr |= RCR_PRMSC;              //PRMSC : promiscuous mode  混杂模式

	if (dev->flags & IFF_ALLMULTI)    //  IFF_ALLMULTI : receive all multicast packets
		rcr |= RCR_ALL;                  //ALL : pass all multicast

	/* the multicast address in Hash Table : 64 bits */
	netdev_for_each_mc_addr(ha, dev) {              //遍历链表
		hash_val = ether_crc_le(6, ha->addr) & 0x3f;
		hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
	}

	/* Write the hash table to MAC MD table */
	for (i = 0, oft = DM9000_MAR; i < 4; i++) {            //给MAR寄存器写值 MAR : multicast address register
		iow(db, oft++, hash_table[i]);
		iow(db, oft++, hash_table[i] >> 8);
	}

	iow(db, DM9000_RCR, rcr);                //设置RCR接收控制寄存器
}



2012-12-27 19:35:44 zhaigch 阅读数 759
  • 网络设备驱动介绍-linux驱动开发第11部分

    本课程是linux驱动开发的第11个课程,主要内容是linux的网络驱动的介绍,首先讲述了网络设备驱动接口和之前讲的2种的不同,然后以一个虚拟网卡驱动源码学习了网卡驱动的框架,后分析了一个实际网卡DM9000的驱动细节。

    4314 人正在学习 去看看 朱有鹏

分析内核自带的dm9000网卡驱动程序:
      static struct platform_driver dm9000_driver = {
       .driver = {
        .name    = "dm9000",
        .owner  = THIS_MODULE,
       },
       .probe   = dm9000_probe,
       .remove  = dm9000_drv_remove,
       .suspend = dm9000_drv_suspend,
       .resume  = dm9000_drv_resume,
      };
1 从驱动的入口函数开始分析
  dm9000_init
    >platform_driver_register(&dm9000_driver)//这里注册了个平台driver驱动
   
       ===>>linux-2.6.22\arch\arm\plat-s3c24xx\devs.c
            /*加入DM9000网卡支持*/
      #include <linux/dm9000.h>
      #include <linux/ioport.h>
      #define DM9000_BASE 0x20000000
      static struct resource s3c_dm9000_resource[] ={
       [0] = {
        .start = DM9000_BASE,
        .end   =  DM9000_BASE +0x03 ,
        .flags =  IORESOURCE_MEM,
       },
       [1] = {
        .start = DM9000_BASE + 0x4,
        .end   =  DM9000_BASE + 0x4 +0x3 ,
        .flags =  IORESOURCE_MEM,
       },
       [2] = {
        .start = IRQ_EINT7,
        .end   = IRQ_EINT7,
        .flags =  IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
       }
      };
      
      static struct dm9000_plat_data s3c_device_dm9000_platdata = {
       .flags = DM9000_PLATF_16BITONLY,
      };
      
      struct platform_device s3c_device_dm9000 = {
       .name = "dm9000",
       .id      = -1,
       .num_resources = ARRAY_SIZE(s3c_dm9000_resource),
       .resource   = s3c_dm9000_resource,
       .dev ={
         .platform_data = &s3c_device_dm9000_platdata,
        }
      };
   ===>>EXPORT_SYMBOL(s3c_device_dm9000);

    那么接下来就分析平台driver的probe函数。
    >dm9000_probe
      >配置网卡所使用的内存bank相关的memory controller:bwscon、bankcon4、s3c2410_gpio_cfgpin
      >struct net_device *ndev = alloc_etherdev //分配一个网卡结构体
      > db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0)
      > db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    > db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    > request_mem_region//为相关结构体成员映射内存空间
    > dm9000_set_io(db, 2);
      db->outblk  = dm9000_outblk_16bit;
    > dm9000_reset(db)
    > ether_setup(ndev);//设置net_device一些通用的成员
    > ndev->open   = &dm9000_open;
    ndev->hard_start_xmit    = &dm9000_start_xmit; //最重要的函数:用于发包
    ndev->tx_timeout         = &dm9000_timeout;
    ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
    ndev->stop   = &dm9000_stop;
    ndev->get_stats   = &dm9000_get_stats;
    ndev->set_multicast_list = &dm9000_hash_table;
   > is_valid_ether_addr(ndev->dev_addr) //检测是否是有效的mac地址,如果无效可以手动地写入MAC地址
   > platform_set_drvdata(pdev, ndev);
   > ret = register_netdev(ndev);//注册网卡
2 接下来分析网卡的open函数 dm9000_open
   dm9000_open
     > request_irq(dev->irq, &dm9000_interrupt, IRQF_SHARED, dev->name, dev)//注册中断
     > dm9000_reset
     > dm9000_init_dm9000(dev)
     > init_timer(&db->timer);。。。。。等操作
       加入一个定时器,定时2秒,
       处理函数:dm9000_timer
                    >mii_check_media(&db->mii, netif_msg_link(db), 0);
     > mii_check_media(&db->mii, netif_msg_link(db), 1);//检查mii接口的duplex 改变
     > netif_start_queue(dev);//启动网卡数据队列
3 接下来分析网卡的发包函数dm9000_start_xmit (ndev->hard_start_xmit = &dm9000_start_xmit)
   dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)//发包函数
      > netif_stop_queue(dev);
      > iow(db, DM9000_IMR, IMR_PAR);/* Disable all interrupts */
       writeb(DM9000_MWCMD, db->io_addr); /* Move data to DM9000 TX RAM */
     > (db->outblk)(db->io_data, skb->data, skb->len)
       ||
       >也就是调用: dm9000_outblk_16bit(db->io_data, skb->data, skb->len) //拷贝skb->data中数据到网卡数据io
            >writesw(db->io_data, skb->data, (skb->len+1) >> 1);
     > 第一次发包?立即发:进行入队列-》db->tx_pkt_cnt++; db->queue_pkt_len = skb->len;
     > dev_kfree_skb(skb);
     > netif_wake_queue
     > iow(db, DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM);/* Re-enable interrupt */ 
4 收包分析
   当有数据包时,引发中断,调用中断注册的处理函数dm9000_interrupt
   dm9000_interrupt
      >dm9000_rx
         >(db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));
          ||
          dm9000_inblk_16bit(void __iomem *reg, void *data, int count)
               readsw(reg, data, (count+1) >> 1);
         > skb = dev_alloc_skb(RxLen + 4)
         > skb_reserve(skb, 2)
         > rdptr = (u8 *) skb_put(skb, RxLen - 4)
         > skb->protocol = eth_type_trans(skb, dev);
      > netif_rx(skb);//向上报告,接收到的数据
  
  

没有更多推荐了,返回首页