‰| 蛾
Linux内核 中网络设备驱动程序的编写需要对 内核 有一 定 了解 ,本文详细介 绍
Linux网络驱动程序开发全过程 ,总结 了调试
与编译进 内核的过程 ,并且给 出
了嵌入式 Linux下网络设备驱动程序开发中应注意的问
。
ARM嵌入式 Linux网卡驱动开发
Linux作为一个开放源代码
的操作系统 ,具有稳定 、高效 、
易裁 减 和 硬件 支 持 广 泛等 特
点 ,被广泛应用于嵌入 式系统
开发领域 。在Linux系统下
驱动程序简洁 、操作方便 、功
能强大 ,但是支持的函数却较
少 ,只有一些来源于 内核 Ker-
nel中的函数。因此 ,在嵌入式
Linux系统开发应用时,需要开
发 自己专用 的网络接 口卡 ,这
时 不 仅 要 在 硬 件 上 保 证 与
Linux的兼容性 ,而且在软件上
需要开发全新的驱动程序 。本
文就 以 ARM 的 MC9328MX1
平台网卡 C$8900A的驱动为
例来 介绍 Linux驱动程序 的具
体实现。
80 Jf放系统 ill= 2006
.o8
网络设备工作原理
在 Linux操作系统中,为了
方便对各类外 围I/o设备 的管
理 ,将所有设备归结为三类 ,即
字符设备(如键盘、LCD等)、块
设备(如硬盘 、CF卡等)和 网络
设备(如 网卡等)。类似于对字符
设备和块设备 的处理 ,为了屏
蔽 网络环境 中物理 网络设备 的
多榉 l生,Linux利用面 向对象的
思想对所有 的网络物理设备进
行抽象 ,并且定义 了一个统一
的接 口。对 于所有 网络硬件的
访 问都是通过接 口进行的 ,接
口向用户提供 了一个对于所有
类型的网络硬件一致化的操作
集合 ,从而屏 蔽了x,-t~-种网络
■ 刘莲花 郭文成 张 东军
芯片的具体访 问方式 ,提高 了
程 序 的易 用性 和 通 用性 。在
Linux系统内核中存在字符设备
管理表 chrdevs和块设备管理
表 blkdevs,这两张保存着指向
file—operations结构的指针设备
管理表分别用来描述各种字符
设备驱动程序和块设备驱动程
序。此外,在 内核 中也存在着一
张网络接 口管理表 dev_base,
但与前两张表不同,dev_base
是指向net_device结构的指针,
因 为 网 络 设 备 是 通 过
Bet
_ device数据结构来表示 。
D e v
—
ba se实 际上 是 一条
net
_ device结构链表的表头,在
系统初始化完成 以后 ,系统检
测到的网络设备将 自动地保存
维普资讯 http://www.cqvip.com
在 这张链表 中,其 中每一个链
表节点表示一个 系统 中存在的
物理网络设备。发送数据前 ,网
络子系统根据系统路 由表选择
相应的网络接 口进行数据传输,
当接收到数据包时 ,通过驱动
程序登记 的中断服务程序进行
数据的接收处理 。
本 文驱动 CS8900的关键
就是按照芯 片要求 向CS8900
所挂接 的地址写入 特定数据 ,
来配置 CS8900的工作方 式 ,
超 时、数据 长度 、协议类 型 、
MAC地址 ,以及接收方 式等 。
然后 每次接 收 到 CS8900产
生 的 中 断 时 , 就 只 需 从
CS8900所 挂接的地址读取数
据 即可。
CS8900网卡驱动程序
实现过程
本开发过程采用一款基于
ARM920T核心的嵌入 式微处
理器 ,该处理器 内嵌了以太 网
MAC层的处理机能 ,并且根据
IEEE802.3中关于 MlI接 口标
准 的规定 ,通过该标准接 口与
CS8900网卡 芯片进行连接 ,
实现 网络的连接 ,两者共 同构
成 网络中的硬件层。
1.实现模式
Linux驱动程序的加载有两
种方式 :一种是编译成模块 以
供动态加 载 ,使 用 insmod/
rmmod;~l载或卸载设备驱动程
J『字;另一种是静态编译进 内核。
模块设计是 Linux中特有的技
术 ,它使 Linux内核功能易于扩
展 ,采用模块方 式来设计Linux
网络设备 驱动程序 也很方便 ,
并且能够形成固定的模式 。在
嵌入式设备 中,硬件一般只是
完成 比较特定、专一的功能 ,一
般选择将驱动程序静态编译进
内核 ,这样可以获得更高 的效
率 。根据这两种方式的特点 ,本
工程在调试过程中采用模块加
载 ,方便调试 ,省去了反复编
译 内核 的麻烦 ,调试成功后将
其 编译进 内核 ,以获得 更高的
运行效率 。
跌加教测 步骣 口图 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
目录下 ,修改config.in文件。找
到 booI’Netwo rk device
s U P P o r t’ C O N F I G
—
NETDEVICES,在其下面添加
bool’ adding Ethernet driver
to kernel’CoNFIG
—
CS89XO。
◆修改⋯/drivers/net下的
Space.c文件 ,在 ethif._probe
()中增加如 下代码 :
extern int cs89x0_probe
(struc£device )
街fdef CONFIGl_CS89X0
cs89x0
_ probe(dev)
#endif
Space.c初 始化 device结
构,这样可 以实现对网络设备
的检测 。
◆修改此 目录下的Makefile
文件,对其增加代码如下 :
ifeq($(CONFIG_CS89X0)
,Y)
L
— —
OBJS+=cs89x0.o
Endif
这样编译 LinUX内核 时选
择 CS8900设备时 ,就会编译
cs89x0.c文件 ,生成 cs89x0.o
文件 。
◆重新编译 内核 ,在 Linux
源 目录下执行 :
#make {menuconflg
(下转 第 84页)
200 08
0P毯N S0U随e篷WORLD MONTHL_丫 83 6
.
u u
维普资讯 http://www.cqvip.com
嵌入式
mbedded Application率 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
责编 /何晓龙 hxl@CCU.com.CR 美编 /庆琨
袖珍数据库 m S Q L应用
mSQL(MiniSQL)是嵌入式
数据库家族 中的佼佼者,目前最
新版本是 mSQL 3.7。mSQL作
为一种小型的关系数据库系统,
由于它 占用的系统资料极少 ,所
以不 能 完全 支 持某 些 标 准 的
SQL功能。从 mSQL2.0起,通
过 W3一mSQL,应用程序可将
mSQL直接嵌入到HTML源代码
中而实现快速开发。从mSQL 3.
0开始 ,又增加了许多新特陛,一
个是单进程服务器程序msql3d,
另一 个 是 多进 程 服务 器 程 序
msql3 。
_
broker
安装
目前 ,mSQL官方 网站为
http://www.hughes.COm.au,它 以
两种形式发布,一种是以RPM软
件包方式 ,另一种是用tar压缩的
源代码方式。对于 RPM软件包
方式的软件包安装很简单 ,只用
一 条命令就可以完成 :
【r(,o嗵 host m0f m一
而对于 以源 代码方 式发布
的mSQL安装则要麻烦一些 。首
先用gunzip解压软件包“gunzip
msql一3.0.tar.gz”;接着用 .,
setup进行设置编译选项;然后
就可以开始编译 mSQL源程序
“make all”l最后执行 “make
install”即可完成安装 。
mSQL的系统配置文件名为
(上接 第 83页)
#make dep
#make Image
至此,该网卡驱动就 已经被
编译进 内核 ,可以正常使用 了,
也可 以在 /proc/device目录下
查看此设备的信息。
本 文首 先在调 试 中用 动态
模块加载 ,然后将其编译进 内
核 ,充分利用 了两种模 式的优
点 。通过对嵌入 式Linux系统 网
卡驱动程 序开 发的具体实 现 ,
总结 了嵌入式 Linux下网络驱
84 开放系统精I捍
20o6.o8
动程序编写 的一般方法及其特
点 ,可使其形成一个模板 。但
要注意和硬件平台的结合 。设
计驱动 时,其 中包括许多用户
模式下使用 的函数 ,在驱动程
序 中是不一定可用的 ,因为驱
动程序是系统最信任的核心部
分 ,它位于系统的 内核。任何
的内存操作 、端 口操作都 没有
象用户模 式那样受到保护 ,一
旦有缺陷 ,将导致整个系统 的
崩溃 ,因此设计驱动程序要 十
分细心。调试 时要利用动 态的
模块加载 ,调试成功后在编译
进 内核。
■ 杨春
msq1.conf,位于默认安装 目录 ,
usr/Iocal/msql3下。在实际应用
中,常要修改 mSQL3.0配置文
件 中的 mSQL—User和 Admin—
Use哒 两个参数。mSQL-User指
明运行 mSQL数据库服务程序
的用户 ,而Admin—User指明能对
mSQL数据库系统执行特权操作
(如关闭数据库服务程序的运行 、
创建数据库等操作)的用户。
API函数
API函数使得 C语 言编写
的 数 据 库 访 问 程 序 可 以 与
m S Q L 数 据 库 引 擎 通 信 。
mSQL的API函数库名为 libsq1.
a,位于mSQL安装路径下的I.b
目录 中。库 中函数及重要的结
构都定义在 msq1.h中。所有的
API函数共约 40个 ,表 1对部
分主要 API函数作简单介绍 。
应用实例
下面程序 是用 C语言实现
调用 mSQL的 API函数对数据
库进行插入数据访 问。为便于
理解程序 ,建议仔细 阅读下面
一 段文件 msq1.h。
搦nclude
#include
溺ndude
稻nclude