上一节中所实现的最小驱动模块,实现了一个驱动的加载与卸载,。本节将通过驱动实例——GPIO驱动,来具体讲解一个有实际作用的驱动到底是怎么写出来的。
最简单的驱动开始(字符设备)
字符设备是能够像字节流一样被访问的设备,当对字符设备发出读写请求,相应的 I/O 操作立即发生。Linux 系统中很多设备都是字符设备,如字符终端、串口、键盘、鼠标等。在嵌入式 Linux 开发中,接触最多的就是字符设备以及驱动。
设备节点和设备号
设备节点
设备(包括硬件设备)在 Linux 系统下,表现为设备节点,也称设备文件。设备文件是一种特殊的文件,它们存储在文件系统中(通常在/dev 目录下),但它们仅占用文件目录项而不涉及存储数据。事实上,它们仅仅记录了其所属的设备类别、主设备号和从设备号等设备相关信息。
来看两个典型的设备文件的详细信息:
1# 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,说明它们使用同一份驱动:
1# ls -l /dev/ttyS*
主设备号由系统来维护,尽管 Linux 可以容纳大量的设备,但是在使用主设备号的时候,注意一定不要使用系统已经使用的主设备号。一般来说,231~239 这几个设备号是系统没有分配的,用户可以自行安排使用。当前运行系统占用了哪些主设备号,可通过查看/proc/devices 文件得到。例如:
1# cat /proc/devices
开始编写GPIO驱动
GPIO驱动可以分为以下几个组成部分:
头文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#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函数实现
1
2
3
4
5
6
7
8
9int 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函数进行操作。
1
2
3
4
5
6
7long qgpio_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) {undefined //需要执行的代码 }
4.fops定义
fops定义主要作用是将我们写的各项函数接口告知给内核驱动。比如说,驱动的open函数是qgpio_open(),可以这样表达:.open = qgpio_open,
1
2
3
4
5
6
7
8
9struct file_operations qgpio_fops = {undefined .owner = THIS_MODULE, .open = qgpio_open, .unlocked_ioctl = qgpio_ioctl, };
5.init函数和exit函数
关于模块的初始化,和模块的退出,8.2节已经介绍过了,这里就不再赘述。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18static int __init qgpio_init (void) {undefined //需要执行的函数 return 0; } static void __exit qgpio_exit (void) {undefined //需要执行的函数 } module_init (qgpio_init); module_exit (qgpio_exit);
6.许可证
1MODULE_LICENSE ("GPL");
GPIO硬件操作
nuc972_gpio.c中提供了GPIO操作的函数接口,其编程方式可裸机编程大致相同,这里就不做说明,详情可以参考裸机开发手册。
ioctl对指令的解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25unsigned 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;
自动获得设备号和设备节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28int 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;
自动注销设备号和节点
1
2
3
4
5
6
7cdev_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驱动内容请搜索靠谱客的其他文章。
发表评论 取消回复