您的位置:首页 > 运维架构 > Linux

<2012 11 4 > linux设备驱动程序开发初探(3) 练习:从零写一个查询式按键驱动程序

2012-11-04 17:21 465 查看
linux设备驱动程序开发初探(3) 练习:从零写一个查询式按键驱动程序

步骤:
①先写出驱动程序框架
实例化一个file_operation结构体,其中描述操作
②填充框架,按照原理图操作硬件
-----------------------------------------------------
补充一个内容,经典书籍《深入理解linux内核》中对于设备驱动程序的一个描述,能够帮助理解驱动程序的地位与作用:
“内核通过设备驱动程序(device driver)与IO设备交互。
设备驱动程序包含在内核中,由控制一个或者多个设备的数据结构和函数组成,这些设备包括硬盘、键盘、鼠标、监视器、网络接口及连接到SCSI(andrew注:小型计算机系统接口,主要连接硬盘、软驱、光驱、打印机、扫描仪等设备)总线上的设备。
通过特定的接口,每个驱动程序与内核中的其余部分(甚至与其他驱动程序)相互作用的这种方式有以下优点:
· 可以把特定设备的代码封装在特定的模块中。
· 厂商可以在不了解内核源代码而只知道接口规范的情况下,就能增加新的设备。
· 内核以统一的方式对待所有设备,并且通过相同的接口访问这些设备。
· 可以把设备驱动程序写成模块,并动态地把它们装进内核而不需要重新启动系统。不再需要时,也可以动态地卸下模块,以减少存储在RAM中内核映像的大小。”
-----------------------------------------------------
second.c
----------
拷贝头文件:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>


实例化一个file_operation结构体,其中描述操作,写出框架:

static struct file_operations second_drv_fops = {
.owner = THIS_MODULE,
.open = second_drv_open,
.write = second_drv_read,
};


实现操作框架:

static int second_drv_open(stuct inode *inode, struct file *file)
{
return 0;
}

ssize_t second_drv_read(struct file *file,char __user *buf,size_t size,loff_t *ppos)
//注意返回的数据类型
{
return 0;
}

//入口注册函数:
int major;
static int second_drv_init(void)
{
major = register_chrdev(0,"second_drv",&second_drv_fops);
return 0;
}
//卸载函数:
static void second_drv_exit(void)
{
unregister_chrdev(major,"second_drv");
}
//修饰为内核统一的接口:
module_init(second_drv_init);
module_exit(second_drv_exit);


给sys提供更多的信息,供mdev机制使用。程序开头定义两个类:

static struct class *seconddrv_class;
static stuct class_device *seconddrv_class_dev;


在入口函数中创建类,在类下面创建设备:

seconddrv_class = class_create(THIS_MODULE, "seconddrv");
seconddrv_class_dev = class_device_create(seconddrv_class, NULL, MKDEV(major, 0), NULL, "buttons");//设备节点的名字叫buttons


在卸载函数中卸载:

class_device_unregister(seconddrv_class_dev);
class_destroy(seconddrv_class);

//最后加上license
MODULE_LICENSE("GPL");


至此,框架编写完成,下面加入硬件功能
======================================================================

假设我们要查询s3c2440这个SoC的GPF0、GPF2、GPG3、GPG11四个引脚的按键情况。
首先要在驱动程序的
开头定义寄存器:

volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;

volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;


入口函数里进行地址映射 (物理地址===>虚拟地址):

gpfcon = (volatile unsigned long *)ioremap(0x56000050,16); //io remap 重映射
gpfdat = gpfcon + 1;

gpgcon = (volatile unsigned long *)ioremap(0x56000060,16);
gpgdat = gpfcon + 1;


//在出口函数中解除地址映射关系:
iounmap(gpfcon);
iounmap(gpgcon);

//在open函数里配置引脚(配置GPF0、GPF2、GPG3、GPG11为输入):
*gpfcon &= ~( (0x3<<(0*2)) | (0x3 << (2*2)));
*gpgcon &= ~( (0x3<<(3*2)) | (0x3 << (11*2)));


在read函数里查询按键值(返回4个引脚的电平):
定义一个数组存储键值

unsigned char key_vals[4];
int regval;

if (size != sizeof(key_vals)) //验证是否传入4数据大小的buf
return -EINVAL; //返回错误值

regval = *gpfdat;
key_vals[0] = (regval & (1<<0)) ? 1:0;
key_vals[1] = (regval & (1<<2)) ? 1:0;

regval = *gpgdat;
key_vals[2] = (regval & (1<<3)) ? 1:0;
key_vals[3] = (regval & (1<<11)) ? 1:0;

copy_to_user(buf,key_vals,sizeof(key_vals)); //从内核向用户空间buf拷贝数据,长度为size(key_vals),sizeof返回数组的大小

return sizeof(key_vals);


==============================================================
最后,编写Makefile进行编译链接,并加载模块,用下面的程序测试:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* seconddrvtest
*/
int main(int argc, char **argv)
{
int fd;
unsigned char key_vals[4];
int cnt = 0;

fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}

while (1)
{
read(fd, key_vals, sizeof(key_vals));
if (!key_vals[0] || !key_vals[1] || !key_vals[2] || !key_vals[3])
{
printf("%04d key pressed: %d %d %d %d\n", cnt++, key_vals[0], key_vals[1], key_vals[2], key_vals[3]);
}
}

return 0;
}


=====================================================================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: