Linux字符设备驱动编写指南 (linux字符驱动编写)

在操作系统中,设备驱动程序是一个非常重要的组件。它们沟通计算机硬件设备与操作系统,使其获得正确的输入和输出,进而让用户使用这些设备。在Linux中,驱动程序可以分为字符设备驱动和块设备驱动。这篇文章将重点介绍。

1. 了解字符设备驱动和Linux内核

在开始编写字符设备驱动之前,需要先对字符设备驱动和Linux内核有一定的了解。字符设备驱动是一种设备驱动程序,用于连接操作系统和字符设备,如串口、键盘、鼠标等。Linux内核是操作系统的核心部分,负责管理计算机硬件的资源和运行程序的资源,其中也包含了设备驱动程序的管理。因此,了解这些定义和概念将有助于更好地理解Linux字符设备驱动程序的编写。

2. 设计字符设备驱动程序

在开始编写字符设备驱动程序之前,需要设计它的接口和功能。这涉及到向内核注册设备驱动程序,并提供一组数据结构和操作函数,以便内核可以和设备进行通信和管理。数据结构和操作函数将被称为“设备节点”,并被保存在内核的一个“设备文件系统”中。在此过程中,需要考虑的因素包括操作系统和设备的整体架构、设备的通信接口、数据传输协议等。

3. 编写字符设备驱动程序

一旦完成了设计,就可以开始编写驱动程序。因为Linux内核已经定义好了一些通用的设备驱动接口,所以设计程序时通常可以借鉴这些接口。然后,通过函数来实现各种驱动功能,并将这些函数与驱动程序的主体代码进行链接。这些函数包括open,read,write,close等,它们分别表示打开设备、读取设备、写入设备和关闭设备的功能。其中,每个函数都需要有正确的参数和返回值,才能正确地完成其任务。

4. 测试和调试驱动程序

当驱动程序编写完毕后,就需要进行测试和调试。在Linux内核中,可以使用模块加载器来显示驱动程序的消息,以帮助进行测试和调试。模块加载器负责将驱动程序加载到内核中,并在设备的前端添加一个设备节点。然后,可以通过命令行输入来测试驱动程序的功能,从而确定是否需要进行进一步的修补和调试。

结语:

Linux字符设备驱动程序的设计和编写需要一定的技能和知识。但是,如果按照上述的步骤,通过逐步学习和实践,就可以掌握这些技能,成功地编写出一个高效、可靠的驱动程序。因此,对于任何一个想要进一步深入了解操作系统和硬件资源交互的技术人员来说,学习和掌握Linux字符设备驱动程序编写技术是非常有必要的。

相关问题拓展阅读:

如何系统的学习Linux驱动开发

先熟悉module的使用和编写

一、什么是module

从名字上看就是模块的意思,我个人的理解就是一个一个的小程序,可以进行动态的安装和卸载,而在这里面就实现一些功能,其中驱动就是在module中。

二、编写一个最简单的module

编写一个最简单的module只需要两个文件即可:Makefile和.c

(1)Makefile

1 #ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个 2 #KERN_VER = $(shell uname -r) 3 #KERN_DIR = /lib/modules/$(KERN_VER)/build

6 #开发板的linux内核的源码树目蔽耐录 7 KERN_DIR = /root/3288_5.1/kernel

8 9 obj-m += module_test.o10 11 all:make -C $(KERN_DIR) M=`pwd` modules

13 14 .PHONY: clean

15 clean:make -C $(KERN_DIR) M=`pwd` modules clean

在上面的Makefile中,最核心的一句就是make -C $(KERN_DIR) M=`pwd` modules

这句话的意思就是进宏手春入到kernel的源码目薯腔录中,使用内核源码中的编译规格来进行编译这个模块。

其目的主要是为了将module和kernel保持一致,在加载module时就不会出错。

(2)module_test.c

1 #include// module_init module_exit 2 #include// __init __exit 3 4 // 模块安装函数 5 static int __init chrdev_init(void) 6 {

7 printk(KERN_INFO “chrdev_init helloworld init\n”);return 0;10 }11 12 // 模块卸载函数13 static void __exit chrdev_exit(void)14 {printk(KERN_INFO “chrdev_exit helloworld exit\n”);16 }17 18 module_init(chrdev_init);19 module_exit(chrdev_exit);20 21 // MODULE_这种宏作用是用来添加模块描述信息22 MODULE_LICENSE(“GPL”); // 描述模块的许可证23 MODULE_AUTHOR(“xuan”); // 描述模块的作者24 MODULE_DESCRIPTION(“module test”); // 描述模块的介绍信息25 MODULE_ALIAS(“alias “);// 描述模块的别名信息

(3)编译和安装module

进入到代码的文件夹中,输入:make

然后,将生成的.ko文件放到平台中,输入:inod .ko

根据这个思路去学习

建议在看看这本书《Linux就该这么学》

在学习之前一直对驱动开发非常的陌生,感觉有点神秘。不知道驱动开发和普通的程序开发究竟有什么不同;它的基本框架又是什么样的;他的开发环境有什么特殊的地方;以及怎么写编写一个简单的字符设备驱动前编译加载,下面我就对这些问题一个一个的介绍。

一、驱动的基本框架

1.  那么究竟什么是驱动程序,它有什么用呢:

l     驱动是硬件设备与应用程序之间的一个中间软件层

l 它使得某个特定硬件能够响应一个定义良好的内部编程接口,同时完全隐蔽了设备的工作细节

l   渣岩丛  用户通过一组与具体设备无关的标准化的调用来完成相应的操作

l 驱动程序的任务就是把这些标准化的系统调用映射到具体设备对于实际硬件的特定操作上

l    如樱 驱动程序是内核的一部分,可以使用中断、DMA等操作

l     驱动程序在用户态和内核态之间传递数据

2.  Linux驱动的基本框架

3.  Linux下设备驱动程序的一般可以分为以下三类

1)字符设备

a)所有能够象字节流一样访问的设备都通过字符设备来实现

b)它们被映射为文件系统中的节点,通常在/dev/目录下面

c)一般要包含open read write close等系统调用的实现

2)块设备

d)通常是指诸如磁盘、内存、Flash等可以容纳文件系统的存储设备。

e)块设备也是通过文件系统来访问,与字符设备的区别是:内核管理数据的方式不同

f)它允许象字符设备一样枣困以字节流的方式来访问,也可一次传递任意多的字节。

3)网络接口设备

g)通常它指的是硬件设备,但有时也可能是一个软件设备(如回环接口loopback),它们由内核中网络子系统驱动,负责发送和接收数据包。

h)它们的数据传送往往不是面向流的,因此很难将它们映射到一个文件系统的节点上。

 

二、怎么搭建一个驱动的开发环境

因为驱动是要编译进内核,在启动内核时就会驱动此硬件设备;或者编译生成一个.o文件, 当应用程序需要时再动态加载进内核空间运行。因此编译任何一个驱动程序都要链接到内核的源码树。所以搭建环境的之一步当然是建内核源码树

1.怎么建内核源码树

a) 首先看你的系统有没有源码树,在你的/lib/ modules目录下会有内核信息,比如我当前的系统里有两个版本:

#ls /lib/ modules

2.6.15-rc7  2.6.21-1.3194.fc7

查看其源码位置:

## ll /lib/modules/2.6.15-rc7/build

lrwxrwxrwx 1 root root 19:19 /lib/modules/2.6.15-rc7/build -> /root/xkli/linux-2.6.15-rc7

发现build是一个链接文件,其所对应的目录就是源码树的目录。但现在这里目标目录已经是无效的了。所以得自己重新下载

b)下载并编译源码树

有很多网站上可以下载,但官方网址是:

下载完后当然就是解压编译了

# tar –xzvf linux-2.6.16.54.tar.gz

#cd linux-2.6.16.54

## make menuconfig (配置内核各选项,如果没有配置就无法下一步编译,这里可以不要改任何东西)

#make

如果编译没有出错。那么恭喜你。你的开发环境已经搭建好了

三、了解驱动的基本知识

1.设备号

1)什么是设备号呢?我们进系统根据现有的设备来讲解就清楚了:

#ls -l /dev/

crwxrwxrwx 1 root root     1,1 16:36 null

crwroot root     4,1 16:35 systty

crw-rw-rw- 1 root tty,1 16:36 tty

crw-rwroot tty,1 16:35 tty0

在日期前面的两个数(如之一列就是1,3)就是表示的设备号,之一个是主设备号,第二个是从设备号

2)设备号有什么用呢?

l 传统上, 主编号标识设备相连的驱动. 例如, /dev/null 和 /dev/zero 都由驱动 1 来管理, 而虚拟控制台和串口终端都由驱动 4 管理

l 次编号被内核用来决定引用哪个设备. 依据你的驱动是如何编写的自己区别

3)设备号结构类型以及申请方式

l   在内核中, dev_t 类型(在 中定义)用来持有设备编号, 对于 2.6.0 内核, dev_t 是 32 位的量, 12 位用作主编号, 20 位用作次编号.

l   能获得一个 dev_t 的主或者次编号方式:

  MAJOR(dev_t dev); //主要

MINOR(dev_t dev);//次要

l   但是如果你有主次编号, 需要将其转换为一个 dev_t, 使用: MKDEV(int major, int minor);

4)怎么在程序中分配和释放设备号

在建立一个字符驱动时需要做的之一件事是获取一个或多个设备编号来使用. 可以达到此功能的函数有两个:

l一个是你自己事先知道设备号的

register_chrdev_region, 在 中声明:

int register_chrdev_region(dev_t first, unsigned int count, char *name);

first 是你要分配的起始设备编号. first 的次编号部分常常是 0,count 是你请求的连续设备编号的总数. name 是应当连接到这个编号范围的设备的名子; 它会出现在 /proc/devices 和 sysfs 中.

l第二个是动态动态分配设备编号

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);

使用这个函数, dev 是一个只输出的参数, 它在函数成功完成时持有你的分配范围的之一个数. fisetminor 应当是请求的之一个要用的次编号; 它常常是 0. count 和 name 参数如同给 request_chrdev_region 的一样.

5)设备编号的释放使用

不管你是采用哪些方式分配的设备号。使用之后肯定是要释放的,其方式如下:

void unregister_chrdev_region(dev_t first, unsigned int count);

6)

2.驱动程序的二个最重要数据结构

1)file_operation

倒如字符设备scull的一般定义如下:

struct file_operations scull_fops = {

.owner =  THIS_MODULE, 

 .llseek =  scull_llseek, 

 .read =  scull_read, 

 .write =  scull_write, 

 .ioctl =  scull_ioctl, 

 .open =  scull_open, 

 .release =  scull_release,  

};

file_operation也称为设备驱动程序接口

定义在 , 是一个函数指针的. 每个打开文件(内部用一个 file 结构来代表)与它自身的函数相关连( 通过包含一个称为 f_op 的成员, 它指向一个 file_operations 结构). 这些操作大部分负责实现系统调用, 因此, 命名为 open, read, 等等

2)File

定义位于include/fs.h

struct file结构与驱动相关的成员

lmode_t f_mode      标识文件的读写权限

lloff_t f_pos当前读写位置

lunsigned int_f_flag 文件标志,主要进行阻塞/非阻塞型操作时检查

lstruct file_operation * f_op  文件操作的结构指针

lvoid * private_data 驱动程序一般将它指向已经分配的数据

lstruct dentry* f_dentry  文件对应的目录项结构

3.字符设备注册

1)内核在内部使用类型 struct cdev 的结构来代表字符设备. 在内核调用你的设备操作前, 必须编写分配并注册一个或几个这些结构. 有 2 种方法来分配和初始化一个这些结构.

l如果你想在运行时获得一个独立的 cdev 结构,可以这样使用:

struct cdev *my_cdev = cdev_alloc();

my_cdev->ops = &my_fops;

l如果想将 cdev 结构嵌入一个你自己的设备特定的结构; 你应当初始化你已经分配的结构, 使用:

void cdev_init(struct cdev *cdev, struct file_operations *fops);

2)一旦 cdev 结构建立, 最后的步骤是把它告诉内核, 调用:

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

说明:dev 是 cdev 结构, num 是这个设备响应的之一个设备号, count 是应当关联到设备的设备号的数目. 常常 count 是 1, 但是有多个设备号对应于一个特定的设备的情形.

3)为从系统去除一个字符设备, 调用:

void cdev_del(struct cdev *dev);

4.open 和 release

无根之木不活,无基之楼不立,无论是学习哪个领域知识,基础是重中之重。

针对学习linux驱动,滑卜我们来仔细谈谈:

个人认为C语言和数据结构就是重中之重!Linux系统更优秀的地方就在于内核。无论悄纯是进程调度,内存管理,还是数据的组织形式,而这些算法的基础就是一个高效而精简的数据结构,红黑树也好,内核链表也好,看不懂C语言,搞不清数据结构,基本上谈不上做驱动。基础不扎实不要谈上层建筑,资源不全面就不要谈合作,很简单的道理。

假设楼主的C语言和数据结构都是勉强过关的(分得清指针函数函数指针,能够熟练自定义链表并进信运穗行增删改查等链表或树的算法重组)。我们来谈下一个阶段,让我们对类Unix系统的基本操作有一个基本的了解,打开文件,切换路经,更改个密码,填个用户什么的。

接下来我们来谈驱动,做驱动就得有做驱动的样子,不知道楼主之前玩过裸板51或者AVR没有,如果没有,尽快搞通,资源很多,跟着人家把电平配一遍,来个矩阵键盘,组个跑马灯,有成就感的同时也打下了硬件基础,好处大大滴。

基于操作系统的驱动,即必须了解内核对驱动程序的管理方法,怎样划分一个具体的设备,是字符型?块设备?还是别的什么,或者从总线的角度来划分一个设备,i2c也好,USB也罢,还是数据地址控制这么一个三总线形式,都是有必要掌握的东西。

上升到应用,任何一个实践项目中很难会有一个板子让你从零开始写驱动,百分之九十九是拿来改,这就是一个熟练的过程,做到一定多的东西,就会下意识地觉得,这个地方可能会有问题,我需要留意一下。

总结一下,驱动工程师其实也是一种硬件翻译型工程师,其任务就是看懂手册上的表格,然后体现在代码里面。客观地说,我们这个行业门槛较高,队友较少,不过等到真正入门之后,那种成就感是无与伦比的。

可以读读内核源码,比如说minix。你看的那本书还不错,还有一本“设备驱动程序”,好像是电力出版社的。另外你可以搞两台虚拟机试着做做东西,写程序经验和感觉都很重要。

个人感觉书还是要纸质的书,确实如此,网上下的东西,看起来总觉得力不从心。

我旁让前个人的感觉是有兴趣内核源码要研究一下,很有帮助。另外有时候写东西对概念的理解很重要,VC其实也不错,因为运清现在会VC的人越来越少了,我曾经做过一段时间,滑羡那是n年前的事情了,因为一直做Linux和JAVA,现在已经彻底丢了,我们是做设备的,LInux,windows都需要做驱动,我们一直招这种人招不到,即使招到也是做应用的。

做嵌入式应用的话一般的编程就可以了。那么嵌入式驱动开陆纯发与内核开发的话就需要学习多个方面的知识。我就把这方面的要求给你交流一下:

(一家之言啊,自己多年从事嵌入式开发的一点感悟)

嵌入式驱动开发需要了解的知识大概有以下几类:

1 嵌入式操作系统驱动框架。每一个操作系统都有自己的构架,应该了解驱动在整个系统中的具置与构建驱动程序的主要事项

2 总线知识,比如PCI、USB总线。

3 芯片知识。驱动其实就是对设备上一些寄存器的配置、CPU与设备本身的通讯以及对不同命令的处理

4 要做好驱动,必须对所使用的CPU体系结构有一个比较深刻的认识

5 C++基本用不上,主要是C和汇编。

6 做驱动更好雹悉册要懂内核调试(比如源宏说linux)

关于linux字符驱动编写的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » Linux字符设备驱动编写指南 (linux字符驱动编写)