在调试过程中采用模块加
载 ,方便调试 ,省去了反复编
译 内核 的麻烦 ,调试成功后将
其 编译进 内核 ,以获得 更高的
运行效率 。
跌加教测 步骣 口图 1。
◆ 通 过 模 块 加 载 命 令
insmod把网络设备驱动程序插
入 到内核之 中。
◆ 入 口函数 init_module()
通过调 用 register_.netdev0函
数在 Linux系统 中注册该 网络
设备 。
图1模块加载步骤图
放系统li{=界
◆如果注册成 功 ,则调用
init函数 指针所指 向的初始化
函数来对设备初始化 ,将设备
的neLdevice数据结构插入到
dev
. . .base链表的末尾。
初始化结束后 ,就可 以打
开设备 ,进行 数据包 的发送和
接收。在需要卸载模块 时 ,先
关 闭设 备 ,再通过执行模块卸
载命令 rmmod来调用驱动程
序 中的 cleanup_moduIe()函
数 卸 载该 模块 。
2.初始化
◆ 网络设备 的初始化直接
在 Lmodule()函数里完成 。
首先通过检测相应物 理设备 的
硬件特性来判断 网卡设备是否
真正存在 ,CS8900芯片挂接
的地址是 oxfe000300,检 测到
此 ,即是检测到 网卡存在 。
◆ 用从硬件检测到 的数值
来对 网卡数据结构 net_.device
中的相 关项进行初始化 ,并填
写一些必要的函数指针。
N et
—
devic'e 》open
net
_
open;
N et device一>§toP
=net
_
close;
NeLdevice->tx_ti meout
net
_
timeout;
N e t
—
d e v i c e 一>
watchdogjimeo=HZ:
N e t
—
d e v i c e 一>
侧 洲 ni 洲 撇
Net
—
deVice一>get
_
stats
=net
_
get
_
stats;
N e t
—
d e v i c e ·>
s e t m u I t i c a s t Ii s t
nR
OPEN SOURCE
2006
WORLD M0N下HLY
U
Q 1
I
.08
维普资讯 http://www.cqvip.com
嵌入式
mbedded Applicatjon摩舅⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯..
责编/何晓龙 hxl@ccu.com.cn 美编/庆琨 ,
=s~ multicasLlist;
N e气-|d e v a e-》
毫e t。 m a C
—
a d d e|s S
=set
_
m ac
_
address;
◆ Unux内核注册谈没备。
I=regiStet,het媳e
(&dev_cs89x0)
并申请中断服务: 一。
retvaI= request q
(unsigned int irq,void(*hander)
(int,void 。struct pt.regS*),un,
signed long irqJlagsiconst
char devname,void dev
_ id);
3.数据包的发送与接收
实现 Linux网络驱动程序最
关键的两个过程就是数据 包的
发送和接收 ,对这两个过程处理
的好坏将直接影响到驱动程序
的整体运行质量。图2是网络数
据包传输的整个的流程。
4.一发送数据包
◆ 应用程序通过系统调用
进入到 内核空间 ,把数据包传
送到协议栈的顶端。
◆ 经过 TCP/IP协议层
的处理 ,把分配好的数据 包传
送到 MAC层的缓冲 区中。
◆ 由 DMA 将数据包传送
到网络芯 片。
5.接收数据包
◆ 接收到 的数据包通过网
络芯片校验 后 ,通过 DMA传人
到 MAC层的缓冲 区中。
◆ 发送到 lP网络层的接收
哇 泡 队列中,由TCP/lP协
议层对数据包进行相应的处理。
◆ 从内核空间返回到用户
空间 ,应用 程序接 收数据包 。
6.驱动程序的编写
用户进程利用 系统调用对
设备 文 件进 行 诸 如 read和
write等 函数操作 ,它是通过设
备文件的主设备号找到相应的
驱动程序 ,然后读取函数的相
应指针 ,接着 把相应的控制权
交给功能函数 。下面介绍该网
卡驱动程序 中的主要函数 :
◆Static int cs8gxO probe
(struct net_device dev。int
ioaddr)
在初始化部分设定 了网卡
C$8900芯片挂接的地址 io=
OxfeO00300,通过这个函数对
网络设备进行扫描来确定是否
图2网络数据包传输流程图
82 拜放系缝l”-I捍
2o06.O8
是网卡。
◆ static int net oPen
(struct net device dev)
内核根据 主设备号来调 用
open函数分解 出设备文件的次
设备号,提供驱动程序初始化
的能力 ,为 以后的读写操作做
准备 ,同时还递增设备 的使用
计数 ,防止设备文件在 关闭前
模 块就被卸载出内核 。
◆ static void net timer
(unsigned long data)和 static
void net
_ timeout(struct
net
_ device dev)
如果系统 就需要驱动程序
等待 ,通过调用 net_timer0来做
一 个小的延迟,等到系统空闲以
后 ,再调用 函数 net_timeout0唤
醒驱动程序的进程 。
◆ i⋯ t—m o d u l e()与
cleanup
_ module()
前—个 函数用于模块 的加载
与 卸 载 , 在 运 行 加 载 模 块
i n g m o d 命 令 后 系 统 调 用
iniU11oduleO函数完成驱动模块
的初 始化 工作 ;C l e a n u P—
module()函数在运行卸载模块
rmmod命令后 由系统调用 ,完成
驱动模块的卸载时的清除工作 。
◆ s t a t i c s t r u c t
net
_
device
_
stats net
_
get
_
stats
(struct net—device dev)
此 函数用于得 知网卡的当
前状态 ,是打开状态还 是关闭。
◆ static void net rx(struct
net
_
device dev)
该 函数 完成从数据缓冲区
中取得数据 ,完成数据的拷贝 。
◆ s t a t i c i n t
维普资讯 http://www.cqvip.com
net
_ send packet(struct sk_buff
skb,struct net device dev)
该函数将 把存放在 socket
缓冲区中的数据发送到物理设
备 ,该 缓 冲 区是 由数 据 结 构
sk
_ buff来表示 的。
◆ static void net interrupt
(int irq,void dev_id,struct
pt
. _ regs regs)
该函数完成从物理设备 接
收数据 。由于 数据包 的接 收是
一 个被动 的过程 ,所 以通 过中
断机制来完成 。当有数据 到达
时 ,硬件就 会产生 中断信 号 ,
由于此时 已经向系统注册好 中
断处理函数 ,内核就 会切 断当
前进程 的执行 ,转 向相 应的中
断处理程 序 ,中断 处理程 序 代
码如下 :一
neLinterrupt(),.即数据包接收
程序来处理数据包的接收 ,
ioaddI=dev一>base
. _ addr }.取
得网-ff的地址“
Ip=:《struct neLtocal*)dev->priv;
lf(t(_reg_PORTB_ISR &
Ox0OOO40OO)) 判断是否发生中断 ,
return
CleaLG P10 C 1 7 interrupt(~
,.清除以前的中断 .,
while((status=readword(dev。
ISQ PORT))),“取得中断,
Iif(net_debug’4)
printk %s:event=%04x~n",
deN name.status);
S W i t C h C搴t a t u S &
ISQ
_
EVENT
_ MASK) l_ll根据中断类
型进行相应的处理 .,
t 。 C 零 a e
ISQ
_ RECEIVER EVENT: 接收
一 个数琚包“
。 net
_ _~dev);
break1
case IS。_力RANSMITTE足
EVENT:, 传输事件,放到等待队列
中 ’|
Ip,>stats.tx packets++;
ne wa _queue(dev) ,.唤
强等待队 ’ ,
break;
case ISQ
_
BUFFER
—
EVEiNT: ,
’对数撬进行强砖 理’}I
if fStatUS &
READY-FOR_TX)
netif
. _
wake
_ _queue(dev);/*缓冲
区空闲,将其唤醒放人其中 ,
C a 8 e
IsQ-RX_MISS_EVE NT : 对丢事
件的处理 ,
⋯ )
将驱动程序编译进内核
通 过前 述 的 i nsmod和
rmmodfi~p,令来动态的加载和卸
载该驱动程序模 块 ,应监视调
试方法对程序进行调试 ,即使
用 Printk函数来完成相应 的工
作。程序正是调 试成功后 ,我
们将其编译进 内核 ,使其成为
内核 的一部分。具体修 改驱动
源代码如下 :
◆将 “#define_KERNEL_、
#define MoDULE、 #include
、 #include
目录