字符设备驱动程序编写举例_第1页
字符设备驱动程序编写举例_第2页
字符设备驱动程序编写举例_第3页
字符设备驱动程序编写举例_第4页
字符设备驱动程序编写举例_第5页
已阅读5页,还剩17页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Linux 应用开发菜鸟进阶第 11 章对字符设备驱动的模块框架有一个宏观的认识:#include #include #include #include #include/这里根据实际的驱动需要添加头文件static int mem_major = 251;/*这里定义驱动需要的一些静态数据或者指针,当然作为全局变量,一般不要轻易使用这些静态变量,它们很占内存,并且浪费/实现 file_operation 中挂接的函数static const struct file_operations mem_operation=.owner = THIS_MODULE,;/根据驱动需要实现相应的系统调用函

2、数static int mymem_init(void)*/模块驱动的函数static void mymem_exit(void)/模块的函数MODULE_AUTHOR(Lin Hui);MODULE_LICENSE(GPL);/定义模块编写的作者以及遵循的协议module_init(mymem_init);module_exit(mymem_exit);/定义模块初始化函数以上就是一个驱动基本不变的部分,字符变化的部分进行详细的讲解。首先是字符设备的。字符设备的主要分为 4。步:设备号、分配设备号、定义并初始化 file_operation 结构体和字符设备的其中,设备号与分配设备号在 11

3、.1 节中已经详述,这里不再重复。下面介绍字符设备的详细步骤。(1) 设备号。(2) 分配设备号。(3) 定义并初始化 file_operations 结构体。file_operations 结构体用于连接设备号和驱动程序的操作。在该结构体内部包含一组函数指针,这些函数用来实现系统调用。通常情况下,要如下几个函数。(1) struct module *owner:用来指向拥有该结构体的模块。(2) ssize_t read(struct file *filp,char user *buf,size_t count,loff_t *f_ops):用来从设备中数据。其中,filp 为文件属性结构体

4、指针。buf 为用户态函数使用的字符内存缓冲。count 为要的数据数。f_ops 为文件指针的偏移量。ssize_t write(struct file *filp,const char user *buf,size_t count,loff_t *f_ops):用来向设备输入数据。(3) int open(struct inode *inode,struct file *):该函数用来打开一个设备文件。(4) int release(struct inode *inode,struct file *):该函数用来关闭一个设备文件。该结构体的初始化形式如下例:struct file_oper

5、ations scull_fops = .owner = THIS_MODULE,.read = read,.write = write,.open = open,.release = release,struct cdev 结构来表示字符设备。在进行内核调用设备的操作之前,必须分配一个或者多个该结构体。该结构体包含在头文件中,一般步骤如下。内核或首先定义该结构体:struct cdev my_cdev;然后即可初始化该结构体,使用如下函数进行初始化:int cdev_init(struct cdev*dev,struct file_operations*fops).再后定义该结构体中的一个所

6、有者字段:my_cdev.owner = THIS_MODULE;最后向模块添加该结构体:int cdev_add(struct cdev*dev,dev_t dev_num,usigned int count);其中,dev 是 cdev 结构体,dev_num 是该设备对应的第一个设备编号,count 则是与该设备关联的设备编号数量。以上就是一个字符设备驱动需要做的事情,当然在字符设备的过程中,可能还会涉及一些内核数据的处理,比如分配字符设备驱动需要的空间,以及初始化一些内核协议等工作。因此不同驱动数据的处理要添加不同的 Linux 内核技术,这才是字符设备驱动开发的核心,这些内容都会在后

7、续章节中通过实际的例子进行分析和学习。11.2.2 字符设备的对于字符设备驱动的,需要做的工作就是对。我们知道 Linux 内核的时或者数据处理时,申请到的字符设备相当珍贵,所以不用的时候就要驱动的数据予以出来,其中包括注销设备号、移除字符设备、申请到的 Linux 内核空间、申请到的Linux 内核子系统相关的数据结构等。一般的字符设备驱动的(1)移除字符设备函数:包含以下两方面内容。void cdev_del(struct cdev *dev);(2)注销设备号函数:unregister_chrdev_region(dev_t first,unsigned int count);以上两个函

8、数一般用在模块出口函数中。在介绍字符设备驱动的前我们就已经学习了字符设备驱动模型中的编程架构,下面将针对这些内容进行详细的解说。1.确定一些版本信息和建立内核头文件#include #include MODULE_AUTHOR(Lin Hui); MODULE_LICENSE(GPL);上面添加的是 Linux 内核驱动必需的一些头文件、编写驱动模块用到的协议的定义以及模块开发者的说明,这些是模型中的一部分。2.建立系统调用与驱动程序之间的关联将系统调用和驱动程序关联起来需要一个非常关键的数据结构struct file_operations。file_operations 结构中每一个成员的名

9、字都对应着一个系统调用。用户进程利用系统调用在 对设备文件进行读写等操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后这个数据结构相应的函数指针,接着把权交给该内核函数。这是 Linux 的设备驱动程序工作的基本原理。驱动开发的主要工作就是编写这些系统调用子函数,并填充进file_operations 数据结构的各个域, 也就是编写file_operations 结构体。_read 、_write 等函数和构建3.驱动程序的编写(mymem.c) 包含基本的头文件和驱动需要的头文件。 编写基本功能函数,比如_read()、_write()、_joctl()等。这些函数被调用时系

10、统进入态。 定义 struct file_operations 结构的对象,填充结构体。结构体能的顺序不能改变,若一些功能没有实现就用 NULL 填充,已经实现的功能如 read()、write()分别被添加到对应的位置。这一步实现的是函数的。到这里驱动程序的主体就写好了,现在需要把驱动程序嵌入内核。设备驱动程序,使用 register_chrdev_region 或者 alloc_chrdev_region 函数字符型设备。dev_t devno = MKDEV(mem_major,0); if(mem_major)result = register_chrdev_region(devno,

11、1,mymem);elseresult = alloc_chrdev_region(&devno,0,1,mymem); mem_major = MAJOR(devno);此时已经完成了设备驱动程序的大部分内容。当然,如果需要实现混杂设备驱动程序的编写以及自创建设备文件,则需要实现这些内容。在用 rmmod 卸载模块时,mymem_exit 函数被调用,它备表中占有的表项。字符设备 mymem 在系统字符设static void mymem_exit(void)cdev_del(&cdev);/ unregister_chrdev_region(MKDEV(mem_major,0),1);到这

12、里,mymem.c 基本就编写完了。可以说,一个简单的字符设备就写好了。4.编译到目前为止已经编写好了驱动程序,下面就要编写 makefile 文件了。makefile 文件用于编译我们所写的驱动模块代码编译文件,这个文件的格式必须要掌握,如果读者还没有掌握,那么复习一下基础知识,再接着看下面的代码:ifneq ($(KERNELRELEASE),)obj-m :=mymem.o elseKDIR :=/home/linhui/kernel/linux-2.6.29 all:make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-

13、linux- clean:rm -f *.ko *.o *.mod.o *.mod.c *.symvers modul* * endif上面的 makefile 文件是编译 Linux 内核代码的一个样例,其中需要改变的只有 KDIR 中的内核路径代码,还有就是我们使用的具体代码运行的架构以及编译工具,在这里选择 arm 和arm-linux-。当然,要注意 Linux 内核代码路径必须经过预先的编译才能地编译我们的代码。也就是说,必须先编译一次内核代码并使用同样的代码架构和编译工具。至此 makefile 已经编写完成,只需要执行 make 命令就可以完成驱动程序的编译了。$make驱动程序

14、已经编译完成,现在把它安装到系统中。$ insmod -f mymem.o后在/proc/devices 文件中就可以看到设备test,并可以看到主设备号。如果要卸载,安装则运行如下命令: rmmod mymem5.创建设备节点上一章中已经介绍了如何手动地创建设备节点以及自创建设备文件的知识,这时就可以派上用场了。手动创建方式如下:$ mkmod /dev/mymem c major minorc 指字符设备,major 是主设备号,minor 是从设备号,一般可以设置为 0。如果是自动创建设备驱动,则不需要做这一步。至于详细的自动创建设备文件的内容,在前面几章中曾提到,在接下来的实际字符设备

15、驱动的开发中也会用到。第 12 章我们开发一个基于内存的字符设备驱动。该字符设备驱动的开发基于上一章用到的字符设备驱动的开发流程来实现。在这个驱动中,需要在内存中分配一块 4MB 大小的内存空间,在驱动中提供对该内存的读写、和方法,用户空间的进程能够通过 Linux 相应的系统调用这片内存以及操作。在这一章中,学习到如下知识:1.简单字符设备驱动 mymem 的数据结构的填充。2.简单字符设备驱动 mymem 的与。3. 简单字符设备驱动 mymem 的打开和关闭以及 llseek 函数的实现。4. 简单字符设备驱动 mymem 的应用程序的编写和测试。12.1 简单字符驱动的数据结构在开发一

16、个简单的字符设备驱动之前,先要设计好整个驱动涉及的数据结构。这非常关键, 因为我们开发出来的驱动其实是对底层数据进行处理,考虑到字符设备驱动是基于内存的模拟硬件,因此设计一个合理的保存字符设备内容的结构非常关键。12.1.1 定义字符设备驱动的设备数据结构这一章需要开发的是一个基于内存的简单字符驱动,所以驱动结构内容需要包含内存的首地址以及需要开辟出来的内存大小。我们需要定义一个关于字符设备的头文件,这个文件用来保存该字符设备的设备信息。在这里,我们的设备是基于一块 4MB 的内存,可以实现数据的读、写和3 个简单的功能,因此定义一个 mem_dev 的设备结构体来保存内存的数据首地址和数据区

17、的大小。考虑到Linux 内核的内存空间异常珍贵,所以内存空间不能分配过多,否则如果分配的内存大于 1GB,那么程序和内核有可能出现现象。鉴于此我们只分配 4MB 的空间用于实验。创建一个头文件 mymem.h,mymem.h 中的实现内容如下:#ifndef _MYMEM_H_#define _MYMEM_H_ #ifndef MEMDEV_MAJOR#define MEMDEV_MAJOR 251#endif#ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif/define the struct of mem chardev.struct

18、mem_devchar*data;/poiter to the memoryunsigned long size;/define the size of the memory;#endif头文件中的内容还包含了默认的主设备号 251、需要分配内存的大小以及一个 mem_dev 的数据结构,其中的 mem_dev 结构体保存的是分配内存的首地址以及分配到的大小。在以后的驱动编写中要养成一个习惯,就是编写一个驱动代码的头文件,将驱动中用到的一些常量、设备驱动用到的字符设备驱动数据结构保存在这个头文件中。这样无论是从驱动的阅读上还是驱动结构上,都可以使驱动数据与驱动代码,以便于在驱动测试或者驱动修改

19、时快速数据或者代码的位置,从而有利于驱动的开发。同时这也是一名优秀的驱动开发者必备的一项技能。12.1.2 定义 file_operation 结构和挂接相应的系统调用函数在定义了字符设备的数据结构和头文件之后,需要确定字符设备驱动的功能和需要实现的函数操作,也就是上层应用需要的系统调用。我们设计的字符设备驱动需要实现字符设备的打开,并可以对内存空间的数据进行读、写、内存以及内存空间的等功能,因此需要实现 open、read、write、llseek 和 release 5 个功能函数。file_operation 结构体如下。static const struct file_operatio

20、ns mem_operation=.owner = THIS_MODULE,.read = mem_read,.write = mem_write,.llseek = mem_llseek,.open = mem_open,.release = mem_release,;这里有一个技巧,就是当读驱动比较多时,只要看一下这个 file_operation 的结构,就会发现这个驱动所要完成的功能了。当然,这只是文件系统、I2C 等设备就没有这么简单了。功能相对简单的字符设备驱动程序,sysfs12.2 简单字符驱动设计在定义好需要的驱动设备的数据结构后,就要开始设计驱动程序了。在编写驱动程序之前,

21、 需要有一个清晰的字符设备驱动编程框架,也就是在没填充驱动实现内容之前需要的一个代码架构,具体如下。#include #include #include #include #include #include #include #include #include #include #include #include/device_create()funtion use! #includemymem.hstatic int mem_major = 251;struct mem_dev *mem_devp; /dev struct poiter struct cdev cdev;/static de

22、v_t devno; module_param(mem_major,int ,S_IRUGO);/open the memory deviceint mem_open(struct inode *inode,struct file *filp)int mem_release(struct inode * inode,struct file * filp)/realize the open funtionstatic ssize_t mem_read(struct file *filp,char user *buf,size_t size,loff_t *ppos)static loff_t m

23、em_llseek(struct file *filp,loff_t offset,int whence)static const struct file_operations mem_operation=.owner = THIS_MODULE,.read = mem_read,.write = mem_write,.llseek = mem_llseek,.open = mem_open,.release = mem_release,;/*static int malloc_device(dev_t *devno)static void mymem_exit(void)MODULE_AUT

24、HOR(Lin Hui); MODULE_LICENSE(GPL);module_init(mymem_init); module_exit(mymem_exit);如上所述,我们得到了一个字符设备驱动的基本编程框架,在这个框架里分别要实现mymem_init 和 mymem_exit 驱动加载和函数,mem_read 读函数,mem_write 写函数,mem_llseek 内存函数,mem_open 字符驱动打开函数,mem_release 字符驱动函数。接下来,将分模块来实现以上这些函数。到这里,读者应该对字符设备驱动编写有一个宏观的了解了。“骨架”都搭好了,接下来就是“填肉”了。12.

25、2.1 字符设备驱动的加载与卸载mymem 字符设备驱动的模块加载和卸载与上一章介绍的字符设备的与区别不大,但是在这里,利用在前面几章中讲到的动态加载设备文件的相关知识,也就是说,不需要自创建设备文件,而是由内核自动完成。字符设备驱动的加载mymem_init():static int mymem_init(void)int result;struct class * myclass;/apply for dev numberdev_t devno = MKDEV(mem_major,0); if(mem_major)result = register_chrdev_region(devno,

26、1,mymem);elseresult = alloc_chrdev_region(&devno,0,1,mymem); mem_major = MAJOR(devno);if(resultsize = MEMDEV_SIZE;mem_devp-data = kmalloc(MEMDEV_SIZE,GFP_KERNEL); memset(mem_devp-data,0,MEMDEV_SIZE); sema_init(&mem_devp-sem,1);/initialize the semaphore myclass = class_create(THIS_MODULE,my_device_dr

27、iver);device_create(myclass,NULL,MKDEV(mem_major,0),NULL,mymem);return 0;mymem 字符设备驱动的卸载mymem_exit():在字符设备驱动的卸载中,需要将申请的字符设备进行,并且申请到的内存以及申请到的设备号。static void mymem_exit(void)cdev_del(&cdev);/ kfree(mem_devp);/unregister_chrdev_region(MKDEV(mem_major,0),1);/unregister_chrdev_region(devno,1);/12.2.2 字符设

28、备驱动的打开与关闭在字符设备的驱动中,特别是在这些较为简单的字符设备中,一般都为驱动的 open 与close 函数实现过多的内容。但是这里有一点需要注意,在驱动开发中,大部分的 Linux 驱动工程师都会遵循一个“”,就是将文件的私有数据 private_data 指向设备结构体,在read()、write()、ioctl()、llseek()等函数中通过 private_data设备结构体。这是因为在这些函数的原型参数中没有 struct inode*这个结构体。这样就很容易封装好设备结构与字符设备的内容。而在字符设备的关闭中则不做任何动作,因为当应用程序关闭打开的驱动文件后,我们的驱动只

29、需要返回一个 0 表示返回即可。字符设备的打开mem_open():从 inode 中获得字符设备驱动的结构数据并保存到 file 文件的私有数据中。/open the memory deviceint mem_open(struct inode *inode,struct file *filp)struct mem_dev *dev;/gain the minor numberint num = MINOR(inode-i_rdev); if(num = 1)return -ENODEV;dev = &mem_de/um;filp- private_data = dev;return 0;字

30、符设备的关闭mem_release():字符设备驱动回 0 即可。函数不需要实现任何动作,只需要返int mem_release(struct inode * inode,struct file * filp)return 0;12.2.3 字符设备驱动的读写函数我们设计的是一个基于内存的字符设备,因此驱动中的读写函数是整个驱动的中心。在读函数中,需要把 open 中保存在 file 私有数据区的内容提取出来,也就是字符设备中所保存的内存首地址及字符设备长度。将保存在内存空间的数据通过 copy_to_user()函数空间。具体的驱动读函数 mem_read()实现如下:到用户/realize

31、 the open funtionstatic ssize_t mem_read(struct file *filp,char user *buf,size_t size,loff_t *ppos)unsigned long p = *ppos; unsigned int count = size; int ret = 0;struct mem_dev *dev = filp-private_data; if(p = MEMDEV_SIZE)return 0;if(count MEMDEV_SIZE-p) count = MEMDEV_SIZE - p;/realize the copy_to

32、_user() funtion if(copy_to_user(buf,(void*)(dev-data+p),count)ret = -EFAULT;else*ppos +=count; ret = count;printk(KERN_INFO read %u bytes from %lu(driver print)nn,count,p);return ret;和读函数一样,我们需要获得保存在 file 中的字符设备私有数据的内容,并将用户空间的数据通过 copy_from_user()函数到内存中保存。具体的驱动写函数 mem_write()实现如下:static ssize_t mem_

33、write(struct file *filp,const char user *buf,size_t size,loff_t *ppos)unsigned long p = *ppos; unsigned int count = size; int ret = 0;struct mem_dev *dev = filp-private_data; if(p =MEMDEV_SIZE)return 0;if(count MEMDEV_SIZE - p) count = MEMDEV_SIZE - p;if(copy_from_user(dev-data + p,buf,count) ret =

34、-EFAULT;else*ppos += count; ret = count;printk(KERN_INFO writen %u bytes from %lu(driver print)n,count, p);return ret;12.2.4 字符设备驱动的 llseek 函数在上层应用中,可能会用到 llseek 这个系统调用来文件,所以在底层驱动中需要实现一个操作函数。其中的可以在文件头(SEEK_SET,0)、当前位置(SEEK_CUR,1)、文件尾(SEEK_END,2),而 whence 就是第 3 个参数,表示当前位置使用 0、1、2 三个数字进行区别。字符设备驱动的 mem

35、_llseek()函数实现如下:static loff_t mem_llseek(struct file *filp,loff_t offset,int whence)loff_t newpos; switch(whence)case 0: /SEEK_SETnewpos = offset;break;case 1:/SEEK_CURnewpos = filp-f_pos + offset; break;case 2: /SEEK_ENDnewpos = MEMDEV_SIZE;break; default:return -EINVAL;if(newposMEMDEV_SIZE)return

36、-EINVAL; filp-f_pos = newpos; return newpos;12.3 应用程序测试12.3.1 应用程序设计原理至此,我们已经开发出一个简单的字符设备驱动,可以加载到 Linux 内核中。但是还没有设计应用程序,所以这一节将开发一个应用程序来测试我们的驱动是否可以使用。在测试应用程序中需要实现的内容有使用 open()函数打开字符设备驱动,使用 write()函数向字符设备驱动中写入内容,通过 llseek()函数将读出来的内容打印出来。12.3.2 程序代码下面是应用程序的代码:内存的位置,并通过 read()函数读内存中的内容。最后#include#includ

37、e #include #include #include #includeint main()int fp; int tmp;char buf4096;/char mybuf4096;strcpy(buf,This my first char driver!); printf(BUF(befor write):%sn,buf);fp=open(/dev/mymem,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); if(fp = 0)printf(open mymem faile!n); return -1;printf(open mymem success!n); tmp =

38、 write(fp,buf,50);if(tmp = 0)printf(write faile!n); return -1;printf(write %s to mymem!n,buf);lseek(fp,0,SEEK_SET);strcpy(buf,buf is NULL!); printf(buf(after write): %sn,buf); tmp = read(fp,buf,50);if(tmp=0)printf(read mymem faile!n); return -1;printf(buf(after read):%sn,buf); return 0;在应用测试程序中,需要充分

39、地调度系统调用函数来测试我们的驱动功能。我们的设计思路是(1)先向驱动中写数据,然后打印我们写进去的数据 char1。(2)接着调用 llseek 函数重新操作文件的位置,将位置设为文件头。(3)调用 read 函数将我们写进去的数据 char2当完成以上 3 步时,会发现写进去的 char1 与出来并打印在终端上。出来的 char2 的数据相同,这说明驱动已经可以工作了,即驱动程序可以正常运行。当然,驱动的正确运行并不等于说我们写的驱动没有错误,也许我们的驱动还有 bug(缺陷)。有时候,我们写的程序存在严重的缺陷,这个就有赖于我们对驱动程序的认识和学习,从而不断积累经验。12.4 简单字符

40、设备驱动运行效果12.4.1 makefile 程序的编写首先需要编写一个 makefile 文件来帮助我们编译编写的 mymem.c 的驱动程序。makefile 文件的内容如下:ifneq ($(KERNELRELEASE),)obj-m := mymem.o elseKDIR := /home/guoqian/6/motion/src/kernel/linux-2.6.29 all:make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux- clean:rm -f *.ko *.o *.mod.o *.mod.c

41、*.symvers modul* *endif12.4.2 字符设备驱动运行效果图 12.1 和图 12.2 是编写好的驱动代码、驱动程序应用程序的编译以及运行效果图。图 12.1 驱动的编译以及应用测试程序的编译图 12.2 在开发板上测试的驱动测试图从实践中学7.3 GPIO 驱动程序实例7.3.1 GPIO 工作原理Linux 应用程序开发FS2410 开发板的 S3C2410 处理器具有 117 个多功能通用 I/O(GPIO)端口引脚,包括 GPIO 8 个端口组,分别为 GPA(23 个输出端口)、GPB(11 个输入/输出端口)、GPC(16 个输入/ 输出端口)、GPD(16

42、个输入/输出端口)、GPE(16 个输入/输出端口)、GPF(8 个输入/输出端口)、GPH(11 个输入/输出端口)。根据各种系统设计的需求,通过软件方法可以将这些端口配置成具有相应功能(例如,外部中断或数据总线)的端口。为了这些端口,S3C2410 处理器为每个端口组分别提供几种相应的寄存器,其中最常用的有端口配置寄存器(GPACON GPHCON)和端口数据寄存器(GPADAT GPHDAT)。因为大部分 I/O 引脚可以提供多种功能,通过配置寄存器(PnCON)设定每个引脚用于何种目的,数据寄存器的每位将对应于某个引脚上的输入或输出,所以通过对数据寄存器(PnDAT)的位读写,可以进行

43、对每个端口的输入或输出。在此主要以发光二极管(LED)和蜂鸣器为例,讨论 GPIO 设备的驱动程序。它们的硬件驱动电路的原理图如图 7.4 所示。图 7.4 LED(左)和蜂鸣器(右)的驱动电路原理图在图 7.4 中,使用 S3C2410 处理器的通用 I/O 口 GPF4、GPF5、GPF6 和 GPF7 分别直接驱动LED D12、D11、D10 及 D9,而使用 GPB0 端口驱动蜂鸣器。4 个 LED 分别在对应端口(GPF4GPF7)为低电输出。发亮,而蜂鸣器在 GPB0 为高电。这 5 个端口的数据流方向均为在表 7.15 中,详细描述了 GPF 的主要寄存器。GPB 的相关寄存器

44、的描述与此类似,具体可以参考 S3C2410 处理器。表 7.15 GPF 端口(GPF0GPF7)的主要寄存器为了驱动 LED 和蜂鸣器,首先通过端口配置寄存器将 5 个相应寄存器配置为输出模式,然后通过对端口数据寄存器的写操作,实现对每个 GPIO 设备的(发亮或)。在 7.3.2 节中介绍的驱动程序中,s3c2410_gpio_cfgpin()函数和 s3c2410_gpio_pullup()函数将进行对某个端口的配置,而 s3c2410_gpio_setpin()函数则实现向数据寄存器的某个端口的输出。7.3.2 GPIO 驱动程序GPIO 驱动程序的主要宏定义如下:/* gpio_d

45、rv.h */#ifndef FS2410_GPIO_SET_H #define FS2410_GPIO_SET_H#include#define #define #defineGPIO_DEVICE_NAME gpioGPIO_DEVICE_FILENAME /dev/gpioLED_NUM4#define GPIO_IOCTL_MAGIC G#define LED_D09_SWT#define LED_D10_SWT #define LED_D11_SWT#define LED_D12_SWT_IOW(GPIO_IOCTL_MAGIC, 0, unsigned int)_IOW(GPIO_

46、IOCTL_MAGIC, 1, unsigned int)_IOW(GPIO_IOCTL_MAGIC, 2, unsigned int)_IOW(GPIO_IOCTL_MAGIC, 3, unsigned int)#define BEEP_SWT_IOW(GPIO_IOCTL_MAGIC, 4, unsigned int)#define#defineLED_SWT_ON0LED_SWT_OFF1#define#defineBEEP_SWT_ON1BEEP_SWT_OFF0#endif /* FS2410_GPIO_SET_H */GPIO 驱动的模块加载的部分如下:static struct

47、file_operations gpio_fops = /* GPIO 设备的 file_operations 结构定义 */.owner = THIS_MODULE,.open = gpio_open, /* 进行初始化配置 */.release = gpio_release, /* 关闭设备 */.read = gpio_read,.write = gpio_write,.ioctl = gpio_ioctl, /* 实现主要功能 */;static struct cdev gpio_devs; static int gpio_init(void)int result;dev_t dev

48、= MKDEV(major, 0); if (major) /* 设备号的动态分配 */result = register_chrdev_region(dev, 1, GPIO_DEVICE_NAME);else /* 设备号的动态分配 */result = alloc_chrdev_region(&dev, 0, 1, GPIO_DEVICE_NAME); major = MAJOR(dev);if (result owner = THIS_MODULE; dev-ops = fops;err = cdev_add (dev, devno, 1);if (err)printk (KERN_N

49、OTICE Error %d adding gpio %d, err, minor);在 open()操作函数中,对一些引脚进行配置,而 read()和 write()操作函数都是空函数。open()操作函数的实现如下:int gpio_open (struct inode *inode, struct file *filp) /* open()操作函数:进行寄存器配置 */ s3c2410_gpio_pullup(S3C2410_GPB0, 1); /* BEEP*/ s3c2410_gpio_pullup(S3C2410_GPF4, 1); /* LED D12 */ s3c2410_gpio_pullup(S3C2410_GPF5, 1); /* LED D11 */ s3c2410_gpio_pullup(S3C2410_GPF6, 1); /* LED D10 */ s3c2410_gpio_pullup(S3C2410_GPF7, 1); /* LED D9 */ s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP); s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); s3c2410_gpio_cfgpin(S3C

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论