S3C2440驱动之按键驱动(一)

来源:互联网 发布:淘宝买家要发票怎么办 编辑:程序博客网 时间:2024/04/30 11:32

上一章写了一篇LED驱动,里面填充了file_operations结构体的.write  .open .ioctl函数,现在为了继续填充这个结构体部分,填充下.read函数。所以想到了按键属于输入设备 需要用.read函数。

整个框架和上一篇的一样,只是加了一个.read函数,这里先采用查询法来实现。在下一篇时采用中断法,不多说,上代码。

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>

 

/*#define definition*/
#define DEVICE_NAME "button_drv"  //设备名称  和文件系统里设备节点名称区分开
//#define BUTTON_MAJOR 0  //主设备号

 

 

static unsigned long led_table[] =
{
 S3C2410_GPF0,
 S3C2410_GPF1,
 S3C2410_GPF2,
 S3C2410_GPF4,
  
};

static unsigned int led_cfg_table[] =
{
 S3C2410_GPF0_INP,
 S3C2410_GPF1_INP,
 S3C2410_GPF2_INP,
 S3C2410_GPF4_INP ,
};

//.open func 打开设备时 将四个按键置于输入状态
static int button_drv_open(struct inode *inode, struct file *filp)
{
 int i;
 for(i = 0; i < 4; i++)
 {
  s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);  
 }
 printk("button_drv_open\n");
 /*配置GPIF0 1 2 4 为输入*/
 
 return 0;
}

//系统.read函数,read要做的就是返回数据到内核空间,这里是按键,那么肯定返回按键状态到用户空间
ssize_t button_read_drv(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
 //int regval;    //接收GPIO的数据
 unsigned char key_vals[4];   //保存要传递给用户空间的4个按键口
 if(size != sizeof(key_vals))   //size为用户空间返回的字长度
  {
   return  -EINVAL;
  }
 
   key_vals[0] = s3c2410_gpio_getpin(S3C2410_GPF0) & (1 << 0) ? 1:0;
 key_vals[1] = s3c2410_gpio_getpin(S3C2410_GPF1) & (1 << 1) ? 1:0;
 key_vals[2] = s3c2410_gpio_getpin(S3C2410_GPF2) & (1 << 2) ? 1:0;
 key_vals[3] = s3c2410_gpio_getpin(S3C2410_GPF4) & (1 << 4) ? 1:0;

 copy_to_user(buf, key_vals, sizeof(key_vals)); //返回key_vals数组数据到用户空间
 return  (sizeof(key_vals));
}

//define the file_operations
static struct file_operations button_drv_fops = {
 .owner = THIS_MODULE,
 .open =  button_drv_open,
 .read = button_read_drv,

};


static struct class *button_drv_class;  //定义一个类 方便在/dev目录下建立一个节点
int major;  //自动分配主设备号
int button_drv_init(void)
{
 //int ret;
 //ret = register_chrdev(FIRST_MAJOR, DEVICE_NAME, &myfirst_drv_fops);
 major = register_chrdev(0, DEVICE_NAME, &button_drv_fops);
 //if(ret < 0)//
 if(major < 0)
  {
    printk("button_drv can not register major number!\n");
    return major;
  }
 /*注册一个类  使得mdev可以在/dev目录下自动建立设备节点 不需要手动mknod dev c 224 0这样的操作*/
 button_drv_class = class_create(THIS_MODULE, DEVICE_NAME);
 if(IS_ERR(button_drv_class))
  {
    printk("error, fail to init button_drv_class");
    return -1;
  }
 /*如果上面成功注册进mdev的话 下面自动在/dev目录下创建一个设备节点*/
 device_create(button_drv_class, NULL, MKDEV(major, 0), NULL, "BUTTON_TEST"); //这个就是在/dev下创建的BUTTON_TEST 设备节点了 应用程序调用
 printk("BUTTON_DRV initialized! \n");
 return 0;
}

void button_drv_exit(void)
{
 unregister_chrdev(major, DEVICE_NAME);  //卸载驱动
 device_destroy(button_drv_class, MKDEV(major, 0)); //删掉设备节点
 class_destroy(button_drv_class);  //注销类
 printk("rm -rf BUTTON_DRV! \n");
 
}

module_init(button_drv_init);
module_exit(button_drv_exit);

MODULE_LICENSE("GPL");  //获得授权

 

/*以后的makefile文件都不用贴出来了,因为和上一篇的都一样 只是函数名改了下而已*/

/*用户测试程序*/

#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

 

static int fd;  //用于存放打开设备时返回的文件句柄
unsigned char key_vals[4];
int cnt = 0;


int main(int argc, char **argv)
{

 fd = open("/dev/BUTTON_TEST", O_RDWR);  //打开设备  并读写权限
 if(fd < 0)
  {
   perror("open device failed");
   exit(1);
  } 
  
 while(1)
  {
 
   read(fd, key_vals, sizeof(key_vals));  //执行读函数,将内核数据交互到用户空间
   if(!key_vals[0] || !key_vals[1] || !key_vals[2] || !key_vals[3])
    {
      printf("%d key pressed %d %d %d %d \n", cnt++, key_vals[0] , key_vals[1], key_vals[2], key_vals[3]);
    }
   
 
   
  }
  return 0;

}

我们将测试程序和驱动拷到文件系统 执行 发现每按下一个按键 相应的数据显示为0

但是查询法最大的弊端就是:这个应用程序将一直占用cpu资源 我们将程序运行于后台./button_test &  而用采用top命令查看 这个进程占用cpu资源90%以上 在top下按q退出监视状态,所以下一篇我们就开始着手基于中断法的按键驱动了