我们都晓得显存不装驱动程序显存不能正常工作,在计算机系统中,硬件设备的操作和控制须要通过驱动程序来实现,驱动程序在操作系统和硬件设备之间起到桥梁联接的作用。本文将从为什么须要安装驱动程序开始,以Linux系统中的LED驱动为例,逐渐解释驱动程序的本质,以及操作系统是怎样调用到驱动程序,并展示编撰ARMLinuxLED驱动的具体步骤和代码。
为何硬件设备正常工作须要驱动程序?
计算机系统中存在着各类不同类型的硬件设备,比如复印机、显示器、网络插口卡等。这种设备一般由不同的制造商生产,因此其工作原理和通讯合同也可能各不相同。为了实现操作系统与那些硬件设备的良好协作,须要一个中间层来处理硬件细节,提供一个标准的插口供操作系统和应用程序使用,这就是驱动程序的作用。应用程序通过操作系统调用到驱动程序,因而实现操控硬件linux系统编程,所以驱动程序中必然是操作硬件的具体细节代码。
驱动程序的本质
驱动程序本质上是一段软件代码,它完善了硬件设备与操作系统之间的桥梁。通过这个桥梁,操作系统可以通过通用的插口调用,而无需关心硬件设备的底层细节。驱动程序通过向操作系统提供标准的命令和函数linux防火墙设置,致使应用程序可以便捷地与硬件设备进行交互。
这儿我们以Linux系统设备驱动为例来说明一下。
在Linux系统中,驱动程序是内核的一部份,它们以模块的方式存在。内核模块是一种可以在运行时加载和卸载的代码,准许将驱动程序动态地添加到内核中。Linux驱动程序一般由一系列的反弹函数组成,这种函数定义了驱动程序与内核之间的插口。
驱动程序在Linux系统中的位置
上图中绿色部份就是驱动程序,我们可以看见,应用程序Application通过调用操作系统内核提供的系统调用插口(SystemcallInterface),系统调用通过虚拟文件系统(VFS)提供的设备文件找到对应的驱动程序模块,因而调用到驱动程序中对应的操作函数linux不用装驱动吗?,因而实现操控硬件。
应用程序怎样访问到驱动程序?
详尽open过程
这儿我们以Linux字符设备为例,来详尽说明一下应用程序是怎样访问到驱动程序的。
如上图,应用程序例如要实现打开某一个字符设备的操作,那应用程序中必须调用系统调用open函数,并提供设备文/dev/led。
1.open系统调用通过字符设备文件(/dev/led)找到inode
2.按照inode的信息创建file
3.按照inode中成员i_mode确定设备类型c(mknod创建inode,并将c主次设备号,文件名等信息存入到inode中)
4.按照inode中成员i_rdev确定设备号,依据设备号可以找到cdev结构体(内核中很多字符设备,每位字符设备对应一个cdev结构体)
5.将找到的cdev结构体表针存入inode成员i_cdev中
6.将找到的cdev中成员ops(file_operation)地址给file中成员f_ops
7.open按照file中的f_ops找到驱动中的file_opertions,这样就可以调用其中对应的open
所以驱动是以模块的方式装入内核,之后实现file_operation中对应的那些插口来为下层应用调用到。驱动中须要实现的那些插口函数代码就是操作硬件的细节代码,细节代码和硬件相关性很大。
记住一句话:应用程序是通过主设备号对应上驱动程序的。
ARMLinuxLED驱动程序样例
Linux应用程序通过系统调用插口与设备驱动程序进行通讯。对于字符设备,常见的系统调用包括open、write、read、ioctl、close等。应用程序通过这种系统调用向设备驱动发出命令,实现对硬件设备的控制。
以下是一个我写的完整的可运行的led驱动代码。假如你没学过linux驱动肯定看不懂全部代码,不过没关系,我只是借用这个代码来讲解一下驱动程序究竟是要写哪些。
#include
#include
#include
#include
#include
#include
#include
#include
#define LED_ON _IO('C',0)
#define LED_OFF _IO('C',1)
#define GPX2CON 0x11000c40
#define GPX2DAT 0x11000c44
static int led_major = 0;
struct led_dev{
struct cdev led_cdev;
dev_t devnum;
volatile unsigned int *gpx2con;
volatile unsigned int *gpx2dat;
}led_dev;
int led_open(struct inode * inodep, struct file *filep)
{
printk("led device opened !n");
return 0;
}
int led_close(struct inode *inodep, struct file *filep)
{
printk("led device closedn");
return 0;
}
ssize_t led_read(struct file *filep, char __user *ubuf, size_t size, loff_t *loff)
{
return 0;
}
ssize_t led_write(struct file *filep , const char __user *ubuf, size_t size, loff_t *loff)
{
return 0;
}
long led_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
switch(cmd){
case LED_ON:
iowrite32(ioread32(led_dev.gpx2dat) | (0x1 << 7), led_dev.gpx2dat);
break;
case LED_OFF:
iowrite32(ioread32(led_dev.gpx2dat) & (~(0x1 << 7)), led_dev.gpx2dat);
break;
default:
return -EINVAL;
}
return 0;
}
struct file_operations led_ops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_close,
.read = led_read,
.write = led_write,
.unlocked_ioctl = led_ioctl
};
static int led_init(void)
{
int result;
//1.alloc device num
result = alloc_chrdev_region(&(led_dev.devnum),0,1,"led");
if(result < 0){
printk("chrdev device num alloc errorn");
return result;
}
led_major = MAJOR(led_dev.devnum);
printk("led major = %dn",led_major);
//2.init cdev
cdev_init(&led_dev.led_cdev,&led_ops);
//3.add cdev
result = cdev_add(&led_dev.led_cdev,led_dev.devnum,1);
if(result < 0){
printk("cdev add errorn");
goto err1;
}
//4.map led register
led_dev.gpx2con = ioremap(GPX2CON, sizeof(int));
if(led_dev.gpx2con == NULL){
printk("led device gpx2con remap failed!n");
goto err2;
}
led_dev.gpx2dat = ioremap(GPX2DAT, sizeof(int));
if(led_dev.gpx2dat == NULL){
printk("led device gpx2dat ioremap failed!n");
goto err3;
}
//5.setting GPX2_7 pin output mode
iowrite32((ioread32(led_dev.gpx2con) & (~(0xf << 28))) | ((0x1 << 28)), led_dev.gpx2con);
iowrite32(ioread32(led_dev.gpx2dat) & (~(0x1 << 7)), led_dev.gpx2dat);
printk("led driver module init ok!n");
return 0;
err3:
iounmap(led_dev.gpx2con);
err2:
cdev_del(&led_dev.led_cdev);
err1:
unregister_chrdev_region(led_dev.devnum,1);
return result;
}
static void led_exit(void)
{
//1.del cdev
cdev_del(&led_dev.led_cdev);
//2.release chrdev device num
unregister_chrdev_region(led_dev.devnum,1);
printk("led driver module exit!n");
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("albert@gmail.com");
MODULE_DESCRIPTION("the Driver for led device");
module_init(led_init);
module_exit(led_exit);
重点在file_operations这个结构体,可以看见我在驱动中实现了led_open,led_close,led_write和led_ioctl五个具体操作插口,下层就是通过这个5个表针调到驱动中这五个函数。
file_operations
具体实现led灯亮灭的代码就在led_ioctl这个函数,这个不同硬件电路具体代码不一样,
ioctl细节代码
并且可以看见虽然就是操作寄存器,设置寄存器对应位高低电平能够实现灯亮灭。操作硬件就是操作寄存器这是通用底层逻辑。
我们来看一下驱动程序对应的应用层测试代码。
#include
#include
#include
#include
#include
#include
#define LED_ON _IO('C',0)
#define LED_OFF _IO('C',1)
int main(int argc, char **argv)
{
int fd = open(argv[1], O_RDWR);
if(-1 == fd){
printf("open device error");
return -1;
}
while(1){
ioctl(fd, LED_ON);
sleep(2);
ioctl(fd, LED_OFF);
sleep(2);
}
close(fd);
return 0;
}
应用层代码就是调用ioctl系统调用来实现等的2秒闪动功能。应用层这儿通过调用ioctl可以通过文件系统调到驱动程序中注册的file_operations中unlocked_ioctl表针指向的led_ioctl函数linux不用装驱动吗?,进而实现led的亮灭。
后续持续更新系列高质量文章,码字不易,认为写的不错欢迎关注、点赞、收藏以及提问。