概述
上一节中所实现的最小驱动模块,实现了一个驱动的加载与卸载,。本节将通过驱动实例——GPIO驱动,来具体讲解一个有实际作用的驱动到底是怎么写出来的。
最简单的驱动开始(字符设备)
字符设备是能够像字节流一样被访问的设备,当对字符设备发出读写请求,相应的 I/O 操作立即发生。Linux 系统中很多设备都是字符设备,如字符终端、串口、键盘、鼠标等。在嵌入式 Linux 开发中,接触最多的就是字符设备以及驱动。
设备节点和设备号
设备节点
设备(包括硬件设备)在 Linux 系统下,表现为设备节点,也称设备文件。设备文件是一种特殊的文件,它们存储在文件系统中(通常在/dev 目录下),但它们仅占用文件目录项而不涉及存储数据。事实上,它们仅仅记录了其所属的设备类别、主设备号和从设备号等设备相关信息。
来看两个典型的设备文件的详细信息:
# ls -l /dev/ttyS0 /dev/sda1
以/dev/ttyS0 的信息为例,对其中几项进行说明。
当程序打开一个设备文件时,内核就可以获取对应设备的设备类型、主设备号和次设备号等信息,内核也就知道了程序需要操作使用哪个设备驱动程序。在程序随后对这个文件的操作都会调用相应的驱动程序的函数,同时把从设备号传递给驱动程序。
设备编号
设备编号由主设备号和从设备号构成。在 Linux 内核中,使用 dev_t 类型来保存设备编号。在Linux 内核中,dev_t 是一个 32 位数,高 12 位是主设备号,低 20 位是次设备号。
主设备号标识设备对应的驱动程序,告诉 Linux 内核使用哪个驱动程序驱动该设备。如果多个设备使用同一个驱动程序,则它们拥有相同的主设备号。例如/dev/ttyS0~3、/dev/ttyS8 这 5 个设备,拥有相同的主设备号 4,说明它们使用同一份驱动:
# ls -l /dev/ttyS*
主设备号由系统来维护,尽管 Linux 可以容纳大量的设备,但是在使用主设备号的时候,注意一定不要使用系统已经使用的主设备号。一般来说,231~239 这几个设备号是系统没有分配的,用户可以自行安排使用。当前运行系统占用了哪些主设备号,可通过查看/proc/devices 文件得到。例如:
# cat /proc/devices
开始编写GPIO驱动
GPIO驱动可以分为以下几个组成部分:
头文件:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include <linux/clk.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gcr.h>
#include <mach/irqs.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
2.open函数实现
int qgpio_open(struct inode *inode,struct file *filp)
{undefined
//需要执行的代码
return nonseekable_open(inode,filp);
}
当应用使用open()函数打开驱动时,驱动内部会自动调用这个qgpio_open函数,一般情况下,open函数里面放该驱动的初始化代码,因为应用只会在程序开头调用一次open()函数。
3.ioctl函数
字符型设备驱动通讯接口函数一般有read()、write()和ioctl()。read()函数为读取函数,一般读取一个数组,参数为数组首地址和数组长度。write()函数为写入函数,一般写入一个数组,参数为数组首地址和数组长度。ioctl()可读可写,但是一次只能读或者写一个32位的数据。根据GPIO的操作特性,我们选择ioctl函数进行操作。
long qgpio_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{undefined
//需要执行的代码
}
4.fops定义
fops定义主要作用是将我们写的各项函数接口告知给内核驱动。比如说,驱动的open函数是qgpio_open(),可以这样表达:.open = qgpio_open,
struct file_operations qgpio_fops = {undefined
.owner = THIS_MODULE,
.open = qgpio_open,
.unlocked_ioctl = qgpio_ioctl,
};
5.init函数和exit函数
关于模块的初始化,和模块的退出,8.2节已经介绍过了,这里就不再赘述。
static int __init qgpio_init (void)
{undefined
//需要执行的函数
return 0;
}
static void __exit qgpio_exit (void)
{undefined
//需要执行的函数
}
module_init (qgpio_init);
module_exit (qgpio_exit);
6.许可证
MODULE_LICENSE ("GPL");
GPIO硬件操作
nuc972_gpio.c中提供了GPIO操作的函数接口,其编程方式可裸机编程大致相同,这里就不做说明,详情可以参考裸机开发手册。
ioctl对指令的解析
unsigned short iocmd;
unsigned short portx;
iocmd=cmd/0xff;
portx=cmd%0xff;
switch(iocmd)
{undefined
case 1:
set_gpio_direction(portx,arg);
break;
case 2:
return get_gpio_direction(portx);
break;
case 3:
set_gpio_odr(portx,arg);
break;
case 4:
return get_gpio_odr(portx);
break;
case 5:
return get_gpio_idr(portx);
break;
default:
return -EINVAL;
}
return 0;
自动获得设备号和设备节点
int result;
devno = MKDEV(QGPIO_MAJOR, QGPIO_MINOR);
if (QGPIO_MAJOR)
result = register_chrdev_region(devno, 2, "qgpio");
else
{undefined
result = alloc_chrdev_region(&devno, 0, 2, "qgpio");
QGPIO_MAJOR = MAJOR(devno);
}
printk("MAJOR IS %dn",QGPIO_MAJOR);
my_class = class_create(THIS_MODULE,"qgpio_class"); //类名
if(IS_ERR(my_class))
{undefined
printk("Err: failed in creating class.n");
return -1;
}
device_create(my_class,NULL,devno,NULL,"qgpio"); //设备名
if (result<0)
{undefined
printk (KERN_WARNING "hello: can't get major number %dn", QGPIO_MAJOR);
return result;
}
cdev_init(&cdev, &qgpio_fops);
cdev.owner = THIS_MODULE;
cdev_add(&cdev, devno, NUMBER_OF_DEVICES);
printk (KERN_INFO "qgpio driver Registeredn");
return 0;
自动注销设备号和节点
cdev_del (&cdev);
device_destroy(my_class, devno);
//delete device node under /dev必须先删除设备,再删除class类
class_destroy(my_class);
//delete class created by us
unregister_chrdev_region (devno,NUMBER_OF_DEVICES);
printk (KERN_INFO "char driver cleaned upn");
最后
以上就是呆萌舞蹈为你收集整理的(4)NUC980----GPIO驱动的全部内容,希望文章能够帮你解决(4)NUC980----GPIO驱动所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复