`
isiqi
  • 浏览: 15997774 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

Linux下DM9000网卡驱动实验

阅读更多
1.1.1 网络驱动程序的特点
网络驱动程序是介于硬件和内核之间传送数据包,不是面向流的设备,不能象/dev/tty1那样简单的映射到文件系统的节点上。Linux调用这些接口的方式是给他们分配一个独立的名字(如eth0)。这样的名字在文件系统中并没有对应项。内核和网络设备驱动程序之间的通信与字符设备驱动程序和快设备驱动程序与内核间的通信是完全不同的。内核不再调用read/write,它调用与数据包传送相关的函数。
1.1.2 DM9000以太网芯片介绍
网络控制器选用DAVICOM公司的DM9000快速以太网控制处理器,合成了MAC, PHY, MMU。该处理器配备有标准10/<chmetcnv w:st="on" unitname="m" sourcevalue="100" hasspace="False" negative="False" numbertype="1" tcsc="0"></chmetcnv>100M自适应,16K大容量的FIFO,4路多功能GPIO,掉电,全双工工作等功能。物理层支持以太网接口协议。由于数据有时是以猝发形式收到的,因此,DM9000还集成有接收缓冲区,以便在接收到数据时能把数据放到这个缓冲区中,然后由数据链路层直接从该缓冲区里取走数据。链路层通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡,它们一起处理与电缆的物理接口细节数据,它的缓冲区可用来暂时存储要发送或接收的帧。
网络控制器包括MACPHY两个部分,其中MAC层控制器作为逻辑控制。
1.1.3 2410与DM9000的硬件连接
DM9000的模块示意图
<shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"></shapetype><stroke joinstyle="miter"></stroke><formulas></formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f><lock v:ext="edit" aspectratio="t"></lock>
接口电路图:
  以下是一个网络驱动程序完整源码,这仅仅只是一个可编译,加载和卸载的驱动程序,不能完成任何实际的工作。不过,可把它当作模板,网络设备的驱动程序就从这个模板上添加而成。并且,针对每一个驱动程序的接口,都做了介绍和编程指导。
程序清单 1‑01 简单的网络程序模板 test_net.c

/*********************************************************************************
*
* net_test.c a simple net device driver sample
*
*
*********************************************************************************
*/
#ifndef __KERNEL__
#define__KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#define __NO_VERSION__
#include <linux kernel.h=""></linux>
#include <linux module.h=""></linux>
#include <linux sched.h=""></linux>
#include <linux init.h=""></linux>
#include <linux in.h=""></linux>
#include <linux netdevice.h=""></linux>
#include <linux etherdevice.h=""></linux>
#include <linux ip.h=""></linux>
#include <linux tcp.h=""></linux>
static int test_init(struct net_device *dev);
static int test_open(struct net_device *dev);
static int test_release(struct net_device *dev);
static int test_config(struct net_device *dev, struct ifmap *map);
static int test_tx(struct sk_buff *skb, struct net_device *dev);
inttest_init_module(void);
void test_cleanup(void);
#define DEVICE_NAME "test" //内核模块名称
module_init(test_init_module);
module_exit(test_cleanup);
//定义结构体变量并初始化必须的成员
static struct net_devicenet_test =
{
init:test_init,
};
static int test_init(struct net_device *dev)
{
ether_setup(dev);//初始化结构体变量中与硬件无关的成员
strcpy(dev->name,"eth0");
dev->open =test_open;
dev->stop =test_release;
dev->set_config =test_config;
dev->hard_start_xmit =test_tx;
dev->flags &=~(IFF_BROADCAST|IFF_LOOPBACK|IFF_MULTICAST);
//设置驱动中感兴趣的成员...
SET_MODULE_OWNER(dev);//设置owner成员
return 0;
}
//当网络设备驱动程序不能自动识别硬件配置时,系统管理员会使用配制工具将正确的硬件配置传递给
//驱动程序。实际上就是调用驱动程序的set_config()方法。
static int test_config(struct net_device *dev, struct ifmap *map)
{
return 0;
}
static int test_tx(struct sk_buff *skb, struct net_device *dev)
{
return 0;
}
static int test_open(struct net_device *dev)
{
MOD_INC_USE_COUNT;
netif_start_queue(dev);//第一次调用open()方法必须调用
return 0;
}
static int test_release(struct net_device *dev)
{
netif_stop_queue(dev);//最后一次调用stop()方法必须调用
MOD_DEC_USE_COUNT;
return 0;
}
inttest_init_module(void)
{
int result = 0;
//初始化模块自身(包括请求一些资源)
result = register_netdev(&net_test);//注册网络设备驱动程序
if(result < 0)
{
printk(KERN_ERR DEVICE_NAME":error % i registering device \"%s\"\n", result , net_test.name);
return result;
}
printk(KERN_ERR DEVICE_NAME ":init OK\n");
return 0;
}
void test_cleanup(void)
{
unregister_netdev(&net_test); //卸载驱动程序
}
//////////////////////////////////////////////////////////////
1.2.2 步骤二:在Linux平台上编译和运行简单的网络设备驱动程序
编译驱动程序需要编写Makefile文件,与其他驱动的Makefile类似。
# Makefile for net_test.c
# a simple net device driver
# Aka 2006.08
OBJS = net_test.o
SRC= net_test.c
CC =/usr/local/arm/2.95.3/bin/arm-linux-gcc
LD =/usr/local/arm/2.95.3/bin/arm-linux-ld
#INCLUDE = -I/HHARM2410-R3/kernel/include/linux -I/HHARM2410-R3/kernel/include
INCLUDE = -I/9200E/arm-kernel/linux-<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False"></chsdate>2.4.19-rmk7/include
CFLAGS = -D__KERNEL__-DMODULE -O2 -Wall -Wstrict-prototypes -Wno-trigraphs -Wmissing-prototypes
All: $(OBJS)
%.o:%.c
$(CC) $(CFLAGS) $(INCLUDE) -c $^ -o $@
.PHONY: clean
clean:
-rm -f *.o
distclean:
@make clean
rm -f tags *~
编译,测试驱动文件net_test.o,要把这个文件下载到HHARM2410的开发平台上,需要建立好主机与开发板的连接和开发环境。参考附录一建立开发环境。
在主机上编译驱动程序,在源文件目下输入
[localhost]# make
生成驱动文件模块test_net.o
通过网络或者NFS,把驱动文件net_test.o传到开发板的系统中。
加载驱动模块如下
[localhost]# insmod test_net.o
卸载驱动模块如下:
[localhost]# rmmod test_net.o

1.1.1 步骤三:网络驱动程序的数据结构和抽象接口

首先熟悉以下重要的数据结构,然后对照DM9000的驱动程序,察看具体数据结构的用法。

保存网络设备信息的结构体net_device

net_device结构存储了网络设备的操作方法和其他信息。其定义如下面的清单。仅仅列出了部分常用的成员,对于普通的网络设备驱动程序已足够。想详细了解的结构体参考LDD(Linux Device Driver)或内核源码/include/linux/netdevice.h

程序清单 02

struct net_device

{

char name[IFNAMSIZ];

unsigned short flags; /* interface flags (a <personname w:st="on" productid="la BSD"></personname> la BSD) */

unsigned long base_addr; /* device I/O address */

unsigned int irq; /* device IRQ number */

unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address */

unsigned char addr_len; /* hardware address length */

unsigned long trans_start; /* Time (in jiffies) of last Tx */

unsigned long last_rx; /* Time of last Rx */

int watchdog_timeo;

void *priv; /* pointer to private data */

struct module *owner; /* open/release and usage marking */

int (*init)(struct net_device *dev);

int (*open)(struct net_device *dev);

int (*stop)(struct net_device *dev);

int (*set_config)(struct net_device *dev, struct ifmap *map);

int (*hard_start_xmit) (struct sk_buff *skb,struct net_device *dev);

int (*set_mac_address)(struct net_device *dev, void *addr);

//..............

};

网卡实现的抽象接口

init(),open(),stop(),set_config()方法在模板中已经清楚地介绍了,这里重点讲解hard_start_xmit()方法。当内核需要发送一个数据包时,他调用hard_start_xmit()方法将数据放到一个输出队列。数据包包含在一个套接字缓冲区结构体(struct sk_buff)变量中。对于驱动程序来说,sk_buff只是一个数据包,不用关心里面复杂的结构成员。

程序清单 03

static int test_tx(struct sk_buff * skb , struct net_device * dev)

{

int len;

netif_stop_queue(dev); //让内核暂停调用hard_start_xmit()方法

len = skb->len < ETH_ZLEN ? ETH_ZLEN :skb->len;

//len个数据写入网络设备硬件缓冲区并启动发送;

dev->trans_start = jiffies;  //记录时间

dev_kfree_skb(skb);

//等待数据发送完毕

netif_wake_queue(dev);

return 0;

}

static int test_set_mac_address(struct net_device * dev, void *addr)

{

struct socketaddr * mac_addr;

mac_addr = addr;

if(netif_running(dev))

{

Return –EBUSY;

}

memcpy(dev->dev_addr , mac_addr->sa_data , dev->addr_len);

//改变mac地址,与具体硬件相关。

return 0;

}

数据接收与中断服务程序

一般网络驱动程序不对发送数据进行缓存,而是直接使用硬件的发送功能把数据发送出去。接收数据则通过硬件中断来通知的。在中断处理程序中,把硬件帧信息填入一个sk_buff结构中,然后调用netif_rx()传递给上层处理。当然,在使用中断之前,需要向系统注册中断处理程序。

下面是中断服务程序的写法:

static void net_irq_handle(int irq, void * dev_id, struct pt_regs * regs)

{

struct net_device * dev;

struct sk_buff *skb;

unsigned int length;

u8 * dec;

dev = (struct net_device *)dev_id;

if(发送成功)

{

 清除发送中断标志;

   netif_wake_queue(dev);

}

if(接收成功)

{

清除接受中断标志;

  length = 数据包长度;

skb = dev_alloc_skb(length + 2);

if(!skb)

{

return;

}

skb_reserve(skb, 2);

dec = skb->data;

//把数据包(length长度)读到dec指向的内存中;

skb->dev = dev;

skb->protocol = eth_type_trans(skb, dev);

skb->ip_summed = CHECKSUM_UNNECESSARY;

netif_rx(skb);

dev->last_rx = jiffies;

}

if(出错)

   错误处理

清除中断源;

}

1.1.1 步骤四:分析DM9000的网卡驱动程序

驱动程序共包含三个文件dm9000x.c dm9000.c dm9000.h,都存放在drivers/net/目录下,其中dm9000x.c 主要包括以下函数:

底层硬件操作函数:这些函数与硬件相关,与驱动程序编写相关不大。

void outb(unsigned char value, unsigned long addr)

void outw(unsigned short value, unsigned long addr)

unsigned char inb(unsigned long addr)

unsigned short inw(unsigned long addr)

u8 ior(board_info_t *db, int reg) // Read a byte from I/O port

void iow(board_info_t *db, int reg, u8 value) // Write a byte to I/O port

驱动程序的函数接口,

int init_module(void) //加载设备驱动程序

void cleanup_module(void) //卸载设备驱动程序

static void dmfe_init_dm9000(struct DEVICE *dev) /* Initilize DM910X board */

dmfe_probe(struct DEVICE *dev) //初始化net_device 结构体

static int dmfe_open(struct DEVICE *dev)

static int dmfe_stop(struct DEVICE *dev)

static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev) //发送一个数据包

static void dmfe_packet_receive(struct DEVICE *dev, board_info_t *db) //接收数据包,被中断调用

其中int init_module(void)函数调用了dmfe_probe(struct DEVICE *dev)来初始化net_device结构体

if(strcmp(dev->name,"eth0")==0)

if(strcmp(dev->name,"eth1")==0)

if(strcmp(dev->name,"eth3")==0)

outb(DM9000_VID_L, iobase);

id_val = inb(iobase + 4);

outb(DM9000_VID_H, iobase);

id_val |= inb(iobase + 4) << 8;

outb(DM9000_PID_L, iobase);

id_val |= inb(iobase + 4) << 16;

outb(DM9000_PID_H, iobase);

id_val |= inb(iobase + 4) << 24;

if (id_val == DM9000_ID) {

printk("HHTech DM9000 %s I/O: %x,VID: %x,MAC: ", dev->name,iobase, id_val);

dm9000_count++;

/* Init network device */

dev = init_etherdev(dev, 0);

/* Allocated board information structure */

irqline = 3;

db = (void *)(kmalloc(sizeof(*db), GFP_KERNEL|GFP_DMA));

memset(db, 0, sizeof(*db));

dev->priv = db; /* link device and board info */

db->next_dev = dmfe_root_dev;

dmfe_root_dev = dev;

db->ioaddr = iobase;

db->io_data = iobase + 4;

/* driver system function */

dev->base_addr = iobase;

//dev->irq = 68; //INT4 166;//irqline;

dev->open = &dmfe_open;

dev->hard_start_xmit = &dmfe_start_xmit;

dev->stop = &dmfe_stop;

dev->get_stats = &dmfe_get_stats;

dev->set_multicast_list = &dm9000_hash_table;

dev->do_ioctl = &dmfe_do_ioctl;

dmfe_open(struct DEVICE *dev)中,申请了中断处理程序,然后调用了

dmfe_init_dm9000(dev);初始化DM9000芯片。

static int dmfe_open(struct DEVICE *dev)

{

if(request_irq(dev->irq, &dmfe_interrupt,SA_INTERRUPT/*SA_SHIRQ*/,

"DM9000 device",dev))

return -EAGAIN;

/* Initilize DM910X board */

dmfe_init_dm9000(dev);

netif_wake_queue(dev); //add by simon 2001.9.4 for kernel 2.4

MOD_INC_USE_COUNT;

}

Stop()方法停止网络驱动程序:

static int dmfe_stop(struct DEVICE *dev)

{

netif_stop_queue(dev); //add by simon 2001.9.4 for kernel 2.4

/* free interrupt */

free_irq(dev->irq, dev);

/* RESET devie */

phy_write(db, 0x00, 0x8000); /* PHY RESET */

iow(db, 0x<chmetcnv w:st="on" unitname="F" sourcevalue="1" hasspace="False" negative="False" numbertype="1" tcsc="0"></chmetcnv>1f, 0x01); /* Power-Down PHY */

iow(db, 0xff, 0x80); /* Disable all interrupt */

iow(db, 0x05, 0x00); /* Disable RX */

MOD_DEC_USE_COUNT;

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics