一个简单的演示用的Linux字符设备驱动程序.docx

上传人:b****1 文档编号:13557804 上传时间:2023-06-15 格式:DOCX 页数:9 大小:18.52KB
下载 相关 举报
一个简单的演示用的Linux字符设备驱动程序.docx_第1页
第1页 / 共9页
一个简单的演示用的Linux字符设备驱动程序.docx_第2页
第2页 / 共9页
一个简单的演示用的Linux字符设备驱动程序.docx_第3页
第3页 / 共9页
一个简单的演示用的Linux字符设备驱动程序.docx_第4页
第4页 / 共9页
一个简单的演示用的Linux字符设备驱动程序.docx_第5页
第5页 / 共9页
一个简单的演示用的Linux字符设备驱动程序.docx_第6页
第6页 / 共9页
一个简单的演示用的Linux字符设备驱动程序.docx_第7页
第7页 / 共9页
一个简单的演示用的Linux字符设备驱动程序.docx_第8页
第8页 / 共9页
一个简单的演示用的Linux字符设备驱动程序.docx_第9页
第9页 / 共9页
亲,该文档总共9页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

一个简单的演示用的Linux字符设备驱动程序.docx

《一个简单的演示用的Linux字符设备驱动程序.docx》由会员分享,可在线阅读,更多相关《一个简单的演示用的Linux字符设备驱动程序.docx(9页珍藏版)》请在冰点文库上搜索。

一个简单的演示用的Linux字符设备驱动程序.docx

一个简单的演示用的Linux字符设备驱动程序

实现如下的功能:

--字符设备驱动程序的结构及驱动程序需要实现的系统调用

--可以使用cat命令或者自编的readtest命令读出"设备"里的内容

--以8139网卡为例,演示了I/O端口和I/O内存的使用

本文中的大部分内容在LinuxDeviceDriver这本书中都可以找到,

这本书是Linux驱动开发者的唯一圣经。

=======================================================

先来看看整个驱动程序的入口,是char8139_init(这个函数

如果不指定MODULE_LICENSE("GPL",在模块插入内核的

时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的

"GPL"属性。

module_init(char8139_init;

module_exit(char8139_exit;

MODULE_LICENSE("GPL";

MODULE_AUTHOR("ypixunil";

MODULE_DESCRIPTION("WierdchardevicedriverforRealtek8139NIC";

接着往下看char8139_init(

staticint__initchar8139_init(void

{

intresult;

PDBG("hello.init.\n";

/*registerourchardevice*/

result=register_chrdev(char8139_major,"char8139",&char8139_fops;

if(result<0

{

PDBG("Cannotallocatemajordevicenumber!

\n";

returnresult;

}

/*register_chrdev(willassignamajordevicenumberandreturnifitcalled*with"major"parametersetto0*/

if(char8139_major==0

char8139_major=result;

/*allocatesomekernelmemoryweneed*/

buffer=(unsignedchar*(kmalloc(CHAR8139_BUFFER_SIZE,GFP_KERNEL;

if(!

buffer

{

PDBG("Cannotallocatememory!

\n";

result=-ENOMEM;

gotoinit_fail;

}

memset(buffer,0,CHAR8139_BUFFER_SIZE;

p_buf=buffer;

return0;/*everything'sok*/

init_fail:

char8139_exit(;

returnresult;

}

这个函数首先的工作就是使用register_chrdev(注册我们的设备的主设备号和系统调用。

系统调用对于字符设备驱动程序来说就是file_operations接口。

我们先来看看char8139_major的定义,

#defineDEFAULT_MAJOR145

/*datastructureusedbyourdriver*/

intchar8139_major=DEFAULT_MAJOR;/*majordevicenumber.ifinitialvalueis0,

thekernelwilldynamicallyassignamajordevice

numberinregister_chrdev(*/

这里我们指定我们的设备的主设备号是145,你必须找到一个系统中没有用的主设备号,

可以通过"cat/proc/devices"命令来查看系统中已经使用的主设备号。

[michael@char8139]$cat/proc/devices

Characterdevices:

1mem

2pty

3ttyp

4ttyS

5cua

7vcs

10misc

14sound

116alsa

128ptm

136pts

162raw

180usb

195nvidia

226drm

Blockdevices:

2fd

3ide0

22ide1

[michael@char8139]$

可见在我的系统中,145还没有被使用。

指定主设备号值得考虑。

像上面这样指定一个主设备号显然缺乏灵活性,而且不能保证

一个驱动程序在所有的机器上都能用。

可以在调用register_chrdev(时将第一个

参数,即主设备号指定为0,这样register_chrdev(会分配一个空闲的主设备号

作为返回值。

但是这样也有问题,我们只有在将模块插入内核之后才能得到我们设备

的主设备号(使用"cat/proc/devices",但是要操作设备需要在系统/dev目录

下建立设备结点,而建立结点时要指定主设备号。

当然,你可以写一个脚本来自动完成

这些事情。

总之,作为一个演示,我们还是指定主设备号为145

这样我们可以在/dev/目录下建立几个设备节点。

[root@char8139]$mknod/dev/char8139_0c1450

[root@char8139]$mknod/dev/char8139_0c14517

[root@char8139]$mknod/dev/char8139_0c14536

[root@char8139]$mknod/dev/char8139_0c145145

看一下我们建立的节点

[michael@char8139]$ll/dev/char8139*

crw-r--r--1rootroot145,02004-12-2620:

33/dev/char8139_0

crw-r--r--1rootroot145,172004-12-2620:

34/dev/char8139_1

crw-r--r--1rootroot145,362004-12-2620:

34/dev/char8139_2

crw-r--r--1rootroot145,1452004-12-2620:

34/dev/char8139_3[michael@char8139]$

我们建立了四个节点,使用了四个次设备号,后面我们会说明次设备号的作用。

再来看看我们的file_operations的定义。

这里其实只实现了read(,open(,release(三个

系统调用,ioctl(只是简单返回。

更有write(等函数甚至根本没有声明,没有声明的

函数系统可能会调用默认的操作。

structfile_operationschar8139_fops=

{

owner:

THIS_MODULE,

read:

char8139_read,

ioctl:

char8139_ioctl,

open:

char8139_open,

release:

char8139_release,

};

file_operations是每个字符设备驱动程序必须实现的系统调用,当用户对/dev中我们的设备对应

结点进行操作时,linux就会调用我们驱动程序中提供的系统调用。

比如用户敲入

"cat/dev/char8139_0"命令,想想cat这个应用程序的实现,首先它肯定调用C语言库里的open(

函数去打开/dev/char8139_0这个文件,到了系统这一层,系统会看到/dev/char8139_0不是普通

磁盘文件,而是一个代表字符设备的节点,所以系统会根据/dev/char8139_0的主设备号来查找是不是

已经有驱动程序使用这个相同的主设备号进行了注册,如果有,就调用驱动程序的open(实现。

为什么要这样干?

因为要提供抽象,提供统一的接口,别忘了操作系统的作用之一就是这个。

因为

我们的设备提供的统一的接口,所以cat这个应用程序使用一般的文件操作就能从我们的设备中读出数据,

而且more,less这些应用程序都能从我们的设备中读出数据。

现在来看看我们的设备

#defineCHAR8139_BUFFER_SIZE2000

unsignedchar*buffer=NULL;/*driverdatabuffer*/

unsignedchar*p_buf;

unsignedintdata_size=0;

我们的设备很简单,一个2000字节的缓冲区,data_size指定缓冲区中有效数据的字节数。

我们的设备

只支持读不支持写。

我们在char8139_init(中为缓冲区分配空间。

char8139_exit(里面的操作就是char8139_init(里面操作的反向操作。

现在我们来看看,假如用户调用了"cat/dev/char8139_3"这个命令会发生什么事情。

根据前面的介绍,我们驱动程序中的open(函数会被调用。

intchar8139_open(structinode*node,structfile*flip

{

inttype=MINOR(node->i_rdev>>4;

intnum=MINOR(node->i_rdev&0x0F;

/*putsomecharinbuffertoreflecttheminordevicenumber*/

*buffer=(unsignedchar('0';

*(buffer+1=(unsignedchar('x';

*(buffer+2=(unsignedchar('0'+type;

*(buffer+3=(unsignedchar('0'+num;

*(buffer+4=(unsignedchar('\n';

data_size+=5;

PDBG("Ok.Findtreasure!

8139I/Oportbase:

%x\n",detect_8139_io_port(;PDBG("OK.Findtreasure!

8139I/Omemorybaseaddress:

%lx\n",

detect_8139_io_mem(;

MOD_INC_USE_COUNT;

return0;

}

这里演示了次设备号的作用,它让我们知道用户操作的是哪一个"次设备",是/dev/char8139_0还是/dev/char8139_3,因为对不同的"次设备",具体的操作方法可能是不一样的,这样就为一个驱动程序控制多个类似的设备提供了可能。

我们根据次设备号的不同,在buffer中填入不同的字符(次设备号的16进制表示。

接着驱动程序中的read(函数会被调用,因为cat程序的实现就是读取文件中的内容。

ssize_tchar8139_read(structfile*filp,char*buf,size_tcount,loff_t*f_pos{

ssize_tret=0;

PDBG("copytouser.count=%d,f_pos=%ld\n",(intcount,(long*f_pos;

if(*f_pos>=data_size

returnret;

if(*f_pos+count>data_size

count=data_size-*f_pos;

if(copy_to_user(buf,p_buf,count

{

PDBG("OOps,copytousererror.\n";

return-EFAULT;

}

p_buf+=count;

*f_pos+=count;

ret=count;

returnret;

}

要正确的实现一个read(调用,你得想一想一个应用程序是如何调用read(从文件中读取数据的。

如果你想明白了就很简单,驱动程序所要做的就是把恰当的数据传递给应用程序,这是使用copy_to_user(函数完成的。

另外,我们必须得意识到,这里只是一个很简单的演示。

还有很多复杂的问题有待考虑,比如两个应用程序可能同时打开我们设备,我们的设备应该怎样反应(这取决于具体的设备应有的行为,还有互斥的问题。

然后我们看看I/O端口和I/O内存的操作。

这里使用8139网卡作为一个硬件实例来演示I/O端口和I/O内存的操作。

没有什么特别的,都是标准的步骤。

在使用时需要注意,如果你的系统中已经有8139网卡的驱动程序,必须先关掉网络设备,卸载驱动,然后再使用本驱动程序。

使用程序包的步骤:

(在我的Debian系统上如此,你的可能不同1.解压2.编译(/usr/src/linux处必须要有内核源代码3.ifconfigeth0down关掉网络设备rmmod8139too卸载原来的8139网卡驱动insmodchar8139.o插入我们的模块(insmod会出错,如果你现在运行的linux版本不是你编译本驱动程序时使用的内核源代码的版本,insmod时会报告模块版本与内核版本不一致。

这时,你得看看内核源代码中/include/linux/version.h文件,这个文件中的UTS_RELEASE定义了内核的版本号,你可以在驱动程序中预先定义这个宏为当前运行的内核的版本号,这样就能避免上述错误。

4.mknode(见本文前述5.试试我们的设备./readtest或者cat/dev/char8139_0或cat/dev/char8139_1或cat/dev/char8139_2或cat/dev/char8139_36.恢复系统rmmodchar8139modprobe8139tooifconfigeth0up如果你使用dhcp可能还需要运行dhclient=======================================================================

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 临时分类 > 批量上传

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2