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

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;

}

=====================================================================

你可能感兴趣的:(linux)