第二篇 字符设备驱动之按键

按键字符设备用到了关于中断和等待队列的知识:

基于友善之臂micro2440,linux内核版本为2.6.29.4。

目的:控制六个开关,每个开关能够单独打开和关闭。

源代码如下所示:

头文件 key.h:

#ifndef __KEY_H
#define __KEY_H

#define KEY_MAJOR 200
#define DRIVER_NAME "key_driver"
#define KEY_NR 6        //六个设备,注册六个次设备号

struct irq_key_descriptor{
     unsigned int irq;
     unsigned int flags;
     const char *dev_name;
};

#endif


源文件 key.c:

#include<linux/module.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/interrupt.h>
#include<mach/regs-gpio.h>
#include<mach/hardware.h>
#include<linux/irq.h>
#include<asm/irq.h>
#include<linux/types.h>
#include<linux/platform_device.h>
#include<linux/slab.h>
#include<asm/uaccess.h>
#include "key.h"

unsigned int key_major=KEY_MAJOR;
module_param(key_major,int,S_IRUGO);

dev_t dev_num = MKDEV(KEY_MAJOR,0);   // MKDEV宏中必须是常量

volatile bool condition=0;
struct cdev cdevp;

DECLARE_WAIT_QUEUE_HEAD(wait_head); //静态定义一个等待队列

struct irq_key_descriptor key_irq_table[]={
     {IRQ_EINT8,IRQF_DISABLED,"key0"},
     {IRQ_EINT11,IRQF_DISABLED,"key1"},
     {IRQ_EINT13,IRQF_DISABLED,"key2"},
     {IRQ_EINT14,IRQF_DISABLED,"key3"},
     {IRQ_EINT15,IRQF_DISABLED,"key4"},
     {IRQ_EINT19,IRQF_DISABLED,"key5"},
};

//可以不要,因为有专门的函数可以通过中断口得到具体的IO端口

unsigned long GPIO_key_table[]={
     S3C2410_GPG0,
     S3C2410_GPG3,
     S3C2410_GPG5,
     S3C2410_GPG6,
     S3C2410_GPG7,
     S3C2410_GPG11,
};

irqreturn_t key_interrupt(int irq,void *dev_id){
     volatile unsigned int *press_count=(volatile unsigned int *)dev_id;
     *press_count=*press_count+1;
     condition=1;
     printk("%d times interrupt has happened!\n",*press_count);
     wake_up_interruptible(&wait_head);
     return IRQ_HANDLED;
}

int key_open(struct inode *inode,struct file *filp){
     long err;
     unsigned long dev_number=MINOR(inode->i_rdev);
     struct irq_key_descriptor key_irq = key_irq_table[dev_number];
     unsigned int *dev_id;

     dev_id = kmalloc(sizeof(unsigned int),GFP_KERNEL);
     if(!dev_id){
          printk(KERN_WARNING "memory kmalloc went wrong!\n");
          return 0;
 }
 memset((void *)dev_id,0,sizeof(unsigned int));

 err = request_irq(key_irq.irq,key_interrupt,key_irq.flags,key_irq.dev_name,dev_id);
 if (err<0){
      printk(KERN_WARNING "irq request went wrong!\n");
      return -EBUSY;
 }
 filp->private_data = dev_id;
 printk(KERN_NOTICE "key%ld is opened\n",dev_number);
 return 0;
}

int key_release(struct inode *inode,struct file *filp){
     unsigned int *dev_id = (unsigned int *)(filp->private_data);
     unsigned long dev_number = MINOR(inode->i_rdev);
     struct irq_key_descriptor key_irq = key_irq_table[dev_number];
 
    free_irq(key_irq.irq,(void *)dev_id);
    return 0;
}

ssize_t key_read(struct file *filp,char __user *buffer,size_t count,loff_t *offp){
     long err;
     unsigned int *dev_id = (unsigned int*)(filp->private_data);
     wait_event_interruptible(wait_head,condition);
     condition = 0;
     
     err = copy_to_user(buffer,dev_id,count);
     printk(KERN_NOTICE "err is %ld\n",err);
     *dev_id = 0;
     if(!err)
     return 0;
     else{
     printk(KERN_NOTICE "Data haven't copy to user space\n");
     return -EFAULT;
 }
}

struct file_operations key_ops={
     .owner = THIS_MODULE,
     .open = key_open,
     .release = key_release,
     .read = key_read,
};

int key_cdev_setup(void){
     int err;
    cdev_init(&cdevp,&key_ops);
    cdevp.owner = THIS_MODULE;
    cdevp.ops = &key_ops;
    cdev_add(&cdevp,dev_num,KEY_NR);
    if(IS_ERR(&err)){
        printk(KERN_NOTICE"Error %d addint key_driver\n",err);
        return err;
    }
   return 0;
}

static int __init keyd_init(void){
 int ret;
 unsigned int i;
 if(key_major){
     ret = register_chrdev_region(dev_num,KEY_NR,DRIVER_NAME);
 }
 else{
    ret = alloc_chrdev_region(&dev_num,0,KEY_NR,DRIVER_NAME);
 }
 if(ret<0){
    printk(KERN_WARNING "key:can't get major device id %d\n",key_major);
    return ret;
 }
 ret = key_cdev_setup();
 if(ret!=0){
     return ret;
 }
 for(i=0;i<6;i++){
      s3c2410_gpio_cfgpin(GPIO_key_table[i],S3C2410_GPIO_IRQ);
      s3c2410_gpio_pullup(GPIO_key_table[i],1);      //Disable the pullup function
      set_irq_type(key_irq_table[i].irq,IRQ_TYPE_EDGE_FALLING);
      s3c2410_gpio_irqfilter(GPIO_key_table[i],1,S3C2410_EINTFLT_EXTCLK|63);
 }
 printk("device_driver installed!\n");
 return 0;
}

static void __exit keyd_exit(void){
     cdev_del(&cdevp);
     unregister_chrdev_region(dev_num,KEY_NR);
     printk("device_driver unistalled successfully!\n");
}

module_init(keyd_init);
module_exit(keyd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Xxing");

测量文件key_test.c:

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

int main(int argc,char *argv[]){
 volatile unsigned int count=0;
 int err;
 unsigned int ret=0;
 int fd;
 
 fd = open(argv[1],O_RDONLY);
 if(fd<0){ printf("can not open file %d\n",argv[1]);
  return -1;
 }
 
 while(1){
  err = read(fd,&ret,1);
  if(err){
   printf("data is not read\n");
   continue;
  }

  printf("return data is: %d\n",ret);

  if(ret){
   count++;
   printf("key has been press %d times\n",count);
  }
 }
}

需要注意的问题:

1,file结构体read函数的第二个参数为(char __user *)类型,因此从用户空间传过来的指针会强制转换成此类型,在调用copy_to_user(void *to,const void *from,unsigned long n)函数时,无论传入的第一个参数和第二个是指向什么样的数据类型,都只会拷贝char类型的数据到to所指向的地址。这样理解是正确的吗?因为当我把传入的第一个参数强制转换成我所需要的数据类型(如unsigned int)后(此时n设置为1)依然只拷贝1个字节的数据。

2,不管是测试文件还是驱动文件,使用指针时必须要先初始化指针,即要明确给定一个地址赋给指针。可用内存分配的函数。

3,卸载模块函数,要先删除设备再释放设备号,如果相反,则卸载模块后在/proc/devices文件中还会存在设备号与设备名,即仍然占用着设备号。

4,测试文件中,打开文件要用绝对路径。 不要忘了"/"

5,所写的驱动文件没有加上防抖的功能,因此会出现按一次键发生多次中断的情况。驱动还需要完善。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值