linux的硬件驱动(linux设备驱动程序)
大家好,今天小编来为大家解答linux的硬件驱动这个问题,linux设备驱动程序很多人还不知道,现在让我们一起来看看吧!
Linux 操作系统是什么
Linux也是众多操作系统之一,要想知道 Linux是什么,首先得说一说什么是操作系统。
计算机是一台机器,它按照用户的要求接收信息、存储数据、处理数据,然后再将处理结果输出(文字、图片、音频、视频等)。计算机由硬件和软件组成:
硬件是计算机赖以工作的实体,包括显示器、键盘、鼠标、硬盘、CPU、主板等;
软件会按照用户的要求协调整台计算机的工作,比如 Windows、Linux、Mac OS、Android等操作系统,以及 Office、QQ、迅雷、微信等应用程序。
操作系统(Operating System,OS)是软件的一部分,它是硬件基础上的第一层软件,是硬件和其它软件沟通的桥梁(或者说接口、中间人、中介等)。
操作系统会控制其他程序运行,管理系统资源,提供最基本的计算功能,如管理及配置内存、决定系统资源供需的优先次序等,同时还提供一些基本的服务程序,例如:
文件系统
提供计算机存储信息的结构,信息存储在文件中,文件主要存储在计算机的内部硬盘里,在目录的分层结构中组织文件。文件系统为操作系统提供了组织管理数据的方式。
设备驱动程序
提供连接计算机的每个硬件设备的接口,设备驱动器使程序能够写入设备,而不需要了解执行每个硬件的细节。简单来说,就是让你能吃到鸡蛋,但不用养一只鸡。
用户接口
操作系统需要为用户提供一种运行程序和访问文件系统的方法。如常用的 Windows图形界面,可以理解为一种用户与操作系统交互的方式;智能手机的 Android或 iOS系统,也是一种操作系统的交互方式。
系统服务程序
当计算机启动时,会自启动许多系统服务程序,执行安装文件系统、启动网络服务、运行预定任务等操作。打个比方,操作系统就好像是一个政府,其它软件都会被它管控;操作系统在给其他软件提供各种便利的同时,还会约束其他软件不能为所欲为。
目前流行的服务器和 PC端操作系统有 Linux、Windows、UNIX等,手机操作系统有 Android、iOS、Windows Phone(简称 WP),嵌入式操作系统有Windows CE、PalmOS、eCos、uClinux等。这套《Linux入门教程》主要讲解 Linux这种操作系统的应用,并不想过多涉及操作系统的类型和功能等理论性的知识,本节的目的是让读者明白,操作系统也是软件的一种,只不过它是位于硬件和应用程序之间的底层的软件,它除了会为应用程序保驾护航,还会“杀死”不听话的应用程序,具有双面性。以上对操作系统的认知虽然只是“皮毛”,但它足以让我们学习 Linux了。
linux驱动程序结构框架及工作原理分别是什么
一、Linux device driver的概念
系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能:
1、对设备初始化和释放;
2、把数据从内核传送到硬件和从硬件读取数据;
3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据;
4、检测和处理设备出现的错误。
在Linux操作系统下有三类主要的设备文件类型,一是字符设备,二是块设备,三是网络设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。
已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。
最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。
二、实例剖析
我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理。把下面的C代码输入机器,你就会获得一个真正的设备驱动程序。
由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如 open,read,write,close…,注意,不是fopen, fread,但是如何把系统调用和驱动程序关联起来呢?这需要了解一个非常关键的数据结构:
STruct file_operatiONs{
int(*seek)(struct inode*,struct file*, off_t,int);
int(*read)(struct inode*,struct file*, char,int);
int(*write)(struct inode*,struct file*, off_t,int);
int(*readdir)(struct inode*,struct file*, struct dirent*,int);
int(*select)(struct inode*,struct file*, int,select_table*);
int(*ioctl)(struct inode*,struct file*, unsined int,unsigned long);
int(*mmap)(struct inode*,struct file*, struct vm_area_struct*);
int(*open)(struct inode*,struct file*);
int(*release)(struct inode*,struct file*);
int(*fsync)(struct inode*,struct file*);
int(*fasync)(struct inode*,struct file*,int);
int(*check_media_change)(struct inode*,struct file*);
int(*revalidate)(dev_t dev);
}
这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。
下面就开始写子程序。
#include<linux/types.h>基本的类型定义
#include<linux/fs.h>文件系统使用相关的头文件
#include<linux/mm.h>
#include<linux/errno.h>
#include<asm/segment.h>
unsigned int test_major= 0;
static int read_test(struct inode*inode,struct file*file,char*buf,int count)
{
int left;用户空间和内核空间
if(verify_area(VERIFY_WRITE,buf,count)==-EFAULT)
return-EFAULT;
for(left= count; left> 0; left--)
{
__put_user(1,buf,1);
buf++;
}
return count;
}
这个函数是为read调用准备的。当调用read时,read_test()被调用,它把用户的缓冲区全部写1。buf是read调用的一个参数。它是用户进程空间的一个地址。但是在read_test被调用时,系统进入核心态。所以不能使用buf这个地址,必须用__put_user(),这是kernel提供的一个函数,用于向用户传送数据。另外还有很多类似功能的函数。请参考,在向用户空间拷贝数据之前,必须验证buf是否可用。这就用到函数verify_area。为了验证BUF是否可以用。
static int write_test(struct inode*inode,struct file*file,const char*buf,int count)
{
return count;
}
static int open_test(struct inode*inode,struct file*file)
{
MOD_INC_USE_COUNT;模块计数加以,表示当前内核有个设备加载内核当中去
return 0;
}
static void release_test(struct inode*inode,struct file*file)
{
MOD_DEC_USE_COUNT;
}
这几个函数都是空操作。实际调用发生时什么也不做,他们仅仅为下面的结构提供函数指针。
struct file_operations test_fops={?
read_test,
write_test,
open_test,
release_test,
};
设备驱动程序的主体可以说是写好了。现在要把驱动程序嵌入内核。驱动程序可以按照两种方式编译。一种是编译进kernel,另一种是编译成模块(modules),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。
int init_module(void)
{
int result;
result= register_chrdev(0,"test",&test_fops);对设备操作的整个接口
if(result< 0){
printk(KERN_INFO"test: can't get major number\n");
return result;
}
if(test_major== 0) test_major= result;/* dynamic*/
return 0;
}
在用insmod命令将编译好的模块调入内存时,init_module函数被调用。在这里,init_module只做了一件事,就是向系统的字符设备表登记了一个字符设备。register_chrdev需要三个参数,参数一是希望获得的设备号,如果是零的话,系统将选择一个没有被占用的设备号返回。参数二是设备文件名,参数三用来登记驱动程序实际执行操作的函数的指针。
如果登记成功,返回设备的主设备号,不成功,返回一个负值。
void cleanup_module(void)
{
unregister_chrdev(test_major,"test");
}
在用rmmod卸载模块时,cleanup_module函数被调用,它释放字符设备test在系统字符设备表中占有的表项。
一个极其简单的字符设备可以说写好了,文件名就叫test.c吧。
下面编译:
$ gcc-O2-DMODULE-D__KERNEL__-c test.c–c表示输出制定名,自动生成.o文件
得到文件test.o就是一个设备驱动程序。
如果设备驱动程序有多个文件,把每个文件按上面的命令行编译,然后
ld?-r?file1.o?file2.o?-o?modulename。
驱动程序已经编译好了,现在把它安装到系统中去。
$ insmod?–f?test.o
如果安装成功,在/proc/devices文件中就可以看到设备test,并可以看到它的主设备号。要卸载的话,运行:
$ rmmod test
下一步要创建设备文件。
mknod/dev/test c major minor
c是指字符设备,major是主设备号,就是在/proc/devices里看到的。
用shell命令
$ cat/proc/devices
就可以获得主设备号,可以把上面的命令行加入你的shell script中去。
minor是从设备号,设置成0就可以了。
我们现在可以通过设备文件来访问我们的驱动程序。写一个小小的测试程序。
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
main()
{
int testdev;
int i;
char buf[10];
testdev= open("/dev/test",O_RDWR);
if( testdev==-1)
{
printf("Cann't open file\n");
exit(0);
}
read(testdev,buf,10);
for(i= 0; i< 10;i++)
printf("%d\n",buf[i]);
close(testdev);
}
编译运行,看看是不是打印出全1
以上只是一个简单的演示。真正实用的驱动程序要复杂的多,要处理如中断,DMA,I/O port等问题。这些才是真正的难点。上述给出了一个简单的字符设备驱动编写的框架和原理,更为复杂的编写需要去认真研究LINUX内核的运行机制和具体的设备运行的机制等等。希望大家好好掌握LINUX设备驱动程序编写的方法。
linux怎么查看已装好硬件驱动
linux系统中的设备驱动是否安装好一般检查几个方面:1、系统日志。嵌入式系统多是直接dmesg一下,看有没有设备关键字相关的出错信息(通用系统可检查/var/log/messages文件)。
2、已加载的模块。检查模块加载列表中有没有相关设备的模块。
lsmod
3、设备列表。检查已加载的设备中有没有相关设备
cat/proc/devices
4、设备入口。如果以上检查都正常,还需要检查设备目录下设备入口是否已经创建
ls/dev/xxxx
如果以上检查都正常(驱动模块已经正常加载、设备入口存在且没有错误),而设备还不能正常工作,就需要检查设备驱动是否与芯片匹配或者驱动中的硬件资源配置是否与硬件对应了。