2416开发记录十一:按键驱动(platform/中断)

在前面几章的基础上编写了一个按键中断的驱动,并验证成功。
这里用到了字符设备驱动,platform驱动,并有资源的获取,算是比较全面的platform驱动了。

首先是设备模块代码

//my2416PlatformKeyDev.c
#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>
#include <linux/gpio.h>  //2.6.32.2内核版本要求
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>  //2.6.32.2内核版本要求
/* 参考arch/arm/plat-s3c24xx/devs.c */


/*1. 根据芯片手册来获取资源*/
static struct resource key_resource[] = {
 /*EINT0*/
    [0]=
    {
        .flags = IORESOURCE_IRQ,//flags可以为IORESOURCE_IO, IORESOURCE_MEM, IORESOURCE_IRQ, IORESOURCE_DMA等如当flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;当flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。
        .start = IRQ_EINT0,//资源的起始地址,结束地址
        .end = IRQ_EINT0,
        .name = "S3C24XX_EINT0",
    },
    /*EINT1*/
    [1]=
    {
        .flags = IORESOURCE_IRQ,
        .start = IRQ_EINT1,
        .end = IRQ_EINT1,
        .name = "S3C24xx_EINT1",
    },
    /*EINT2*/
    [2]=
    {
        .flags = IORESOURCE_IRQ,
        .start = IRQ_EINT2,
        .end = IRQ_EINT2,
        .name = "S3C24xx_EINT2",
    },
    /*EINT3*/
    [3]=
    {
        .flags = IORESOURCE_IRQ,
        .start = IRQ_EINT3,
        .end = IRQ_EINT3,
        .name = "S3C24xx_EINT3",
    },
    [4]=
    {
        .flags = IORESOURCE_IRQ,
        .start = IRQ_EINT4,
        .end = IRQ_EINT4,
        .name = "S3C24xx_EINT4",
    },
};

void key_release(struct device *dev)
{ 

}

/*1.构建平台设备结构体,将平台资源加入进来,需要注意的是platform_device 实质上是经过处理过的设备,在platform_device结构体中存在一个设备结构体,与之前的设备存在差别的是引入了设备资源。这些设备资源就能实现对设备寄存器,中断等资源的访问。*/
struct platform_device key_device = {
 .name    = "myplatformkey", /* 设备名,使用名为"myplatformkey"的平台驱动 ,注册后,会在/sys/device/platform目录下创建一个以name命名的目录,并且创建软连接到/sys/bus/platform/device下。*/
 .id    = -1,/*设备id,一般为-1,如果是-1,表示同样名字的设备只有一个举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1  如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。 */
 .dev = {//结构体中内嵌的device结构体。
  .release = key_release,
 },
 .num_resources   = ARRAY_SIZE(key_resource),/* 设备所使用各类资源数量 */
 .resource   = key_resource,//定义平台设备的资源
};

/*2。把我们的设备资源挂在到虚拟总线的设备连表中去,
如果没有定义上面的struct platform_device led_device,那么需要下面的init函数*/
int key_dev_init(void)
{
 platform_device_register(&key_device); //platform设备的初注册  
 return 0;
}
/*如果没有定义上面的struct platform_device led_device,那么需要使用platform_device_alloc()函数分配一个platform_device结构体,然后使用platform_device_add_resources函数添加资源,最后使用platform_device_add函数


struct platform_device *my_buttons_dev;  

static int __init platform_dev_init(void)  
{  
    int ret;  

    my_buttons_dev = platform_device_alloc("my_buttons", -1);     
    platform_device_add_resources(my_buttons_dev,key_resource,6);//添加资源  
    ret = platform_device_add(my_buttons_dev); //platform设备的注册  
    if(ret)  
        platform_device_put(my_buttons_dev);  
    return ret;  
}   
*/

void key_dev_exit(void)
{
 platform_device_unregister(&key_device);
}

module_init(key_dev_init);
module_exit(key_dev_exit);

MODULE_AUTHOR("Zhao Yidong <zmurder@outlook.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A sample key module dev int");
MODULE_ALIAS("key module dev int");

对应的makefile和以前的差不多,就不写出来了。

驱动模块代码

//my2416PlatformKeyDriver.c
#include <linux/miscdevice.h>
 #include <linux/delay.h>
 #include <asm/irq.h>
 #include <mach/regs-gpio.h>
 #include <mach/hardware.h>
 #include<mach/irqs.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>
 #include <linux/gpio.h>  //2.6.32.2内核版本要求
#include <linux/platform_device.h>
#include <linux/cdev.h>
 #include <linux/interrupt.h>  
 #include <linux/poll.h>  
 #include <linux/irq.h>    
#include<linux/sched.h>

#define DEVICE_NAME "mykeys"
#define DRIVER_NAME "my2416keys"//加载驱动之后会在/dev/目录下发现my2416keys,应用程序可以使用

#define MYKEY_SIZE 0x1000//全局内存最大4K
#define MEM_CLEAR 0x1//清零全局内存
#define MYKEY_MAJOR 251 //预设的mykeys的主设备号

#define NUM_RESOURCE    5//资源数

static int mykey_major = MYKEY_MAJOR;

struct class *mykey_class;
static struct device *mykeyDevice=NULL;

 /*中断结构体定义*/
struct irqs
{
    int pirqs[NUM_RESOURCE];
    char *names[NUM_RESOURCE];
}irqs;
struct mykey_dev
{
   struct cdev cdev;//cdev结构体
   //unsigned char mem[MYKEY_SIZE];//全局内存
};
struct mykey_dev *mykey_devp;//设备结构体指针

//定义并初始化等待队列头
static DECLARE_WAIT_QUEUE_HEAD(key_waitq);  


static volatile int ev_press = 0;   
static volatile int key_values;  


 //定义GPIO管脚
 static unsigned long key_table [] =
  {
      S3C2410_GPF(0),  
      S3C2410_GPF(1),
      S3C2410_GPF(2),
      S3C2410_GPF(3),
      S3C2410_GPF(4),
 };
 //设置管脚模式
 static unsigned int key_cfg_table [] =
  {
      S3C2410_GPF0_EINT0, //随内核版本中定义类型的变化,在arch/arm/mach-sc2410/include/mach/Regs-gpio.h文件中定义
      S3C2410_GPF1_EINT1,
      S3C2410_GPF2_EINT2,
      S3C2410_GPF3_EINT3,
      S3C2410_GPF4_EINT4,
 };
/*
 static int my2416_keys_ioctl(struct file* filp, unsigned int cmd,unsigned long arg)
 {
   switch(cmd)
   {
      case KEY_ON:
         s3c2410_gpio_setpin(S3C2410_GPB(1), KEY_ON);
         break;
      case KEY_OFF:
         //s3c2410_gpio_setpin(key_table[arg], !cmd);
         s3c2410_gpio_setpin(S3C2410_GPB(1), KEY_OFF);
         break;
      default:
         printk("KEY control:no cmd\n");
         printk("KEY control are KEY_ON or KEY_OFF\n");
         return(-EINVAL);
   }
   return 0;
 }*/

static irqreturn_t keys_interrupt(int irq, void *dev_id)  
{  
    int i;  
    for(i=0; i<NUM_RESOURCE; i++){  
        if(irq == irqs.pirqs[i]){  
            key_values = i;  
            ev_press = 1;  
            wake_up_interruptible(&key_waitq);     
        }  
    }  

    return IRQ_RETVAL(IRQ_HANDLED);  
}   
//打开函数
static int my2416_key_open(struct inode *inode, struct file *file)  
{   
   int i=0;
   int err = 0;  

   这里只定义了一个io口GPB1配置GPIO
   for (i = 0; i < NUM_RESOURCE; i++)
   {
       s3c2410_gpio_cfgpin(key_table[i], key_cfg_table[i]);
       //s3c2410_gpio_pullup(key_table[i], 1);//拉高引脚 根据原理图 按下为低电平
   }
   //申请中断
   for (i = 0; i < NUM_RESOURCE; i++) 
   {  //                                                 |上升沿下降沿触发
      err = request_irq(irqs.pirqs[i], keys_interrupt, IRQ_TYPE_EDGE_BOTH ,   
                          irqs.names[i], NULL);  
     if (err)  
        break;  
   }  

   if (err) 
   {  
      i--;  
      for (; i >= 0; i--) 
      {  
         if (irqs.pirqs[i] < 0) 
         {  
            continue;  
         }  
      disable_irq(irqs.pirqs[i]);  
            free_irq(irqs.pirqs[i], NULL);  
      }  
      return -EBUSY;  
   }  

   return 0;    
} 
static int my2416_key_close(struct inode *inode, struct file *file)  
{  
   int i;  

   for (i = 0; i < NUM_RESOURCE; i++) 
   {  
      free_irq(irqs.pirqs[i], NULL);  
   }  

    return 0;  
}   
static int my2416_key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)  
{  
    unsigned long err;  

    if (!ev_press) {  
    if (filp->f_flags & O_NONBLOCK)  
        return -EAGAIN;  
    else  
        //添加等待队列,条件是ev_press
        wait_event_interruptible(key_waitq, ev_press);  
    }  

    ev_press = 0;  

    err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count));  

    return err ? -EFAULT : min(sizeof(key_values), count);  
}   
static unsigned int my2416_key_poll( struct file *file, struct poll_table_struct *wait)  
{  
    unsigned int mask = 0;  
    poll_wait(file, &key_waitq, wait);  
    if (ev_press)  
        mask |= POLLIN | POLLRDNORM;//POLLIN|POLLRDNORM表示有数据可读
    return mask;  
}  

 //dev_fops操作指令集
 static struct file_operations my2416Led_fops =
 {
   .owner =THIS_MODULE, 
   .open    =   my2416_key_open,  
   .release =   my2416_key_close,  
   .read    =   my2416_key_read,  
   .poll    =   my2416_key_poll,  
   //.unlocked_ioctl = my2416_keys_ioctl,//这里必须是unlocked_ioctl而不是ioctl。
 };
 /*//第三步:混杂设备定义
 static struct miscdevice my2416Ledmisc =
  {
      .minor = MISC_DYNAMIC_MINOR,
      .name = DEVICE_NAME,//加载驱动之后会在/dev/目录下发现mykeys,应用程序可以使用
      .fops = &my2416Led_fops,
 };
*/

static void Key_setup_cdev(struct mykey_dev *dev,int index)
{
    int err, devno = MKDEV(mykey_major,index);
    /*初始化cdev,并将相关的文件操作添加进来*/
    cdev_init(&dev->cdev, &my2416Led_fops);
    dev->cdev.owner = THIS_MODULE;
    //dev->cdev.ops   = &my2416Led_fops;
    /*注册字符设备*/
    err = cdev_add(&dev->cdev, devno, 1);

    if (err)
        printk("Error %d\n", err);
    else
        printk("have finish add\n");
}
/*3。实现probe函数*/
static int key_probe(struct platform_device *pdev)
{
   int result;
   unsigned char i=0;
   struct resource * irq_resource;

   printk("key_probe\n");
   /*创建一个设备号*/
   dev_t devno=MKDEV(mykey_major,0);

   /*注册一个设备号*/
   /*如果定义了主设备号采用静态申请的方式*/
   if(mykey_major)
   {
      result=register_chrdev_region(devno,1,DEVICE_NAME);
   }
   else//动态申请设备号
   {
      result= alloc_chrdev_region(&devno,0,1,DEVICE_NAME);
      mykey_major=MAJOR(devno);
   }
   if(result<0)
   {
      printk (DEVICE_NAME " can't register\n");  
      return result;
   }

   printk("key devno\n");


   //动态申请设备结构体内存
   mykey_devp=kmalloc(sizeof(struct mykey_dev), GFP_KERNEL);
   if(!mykey_devp)//申请失败
   {
      printk("kmalloc faile\n");
      result=-ENOMEM;
      goto fail_malloc;
   }
   printk("kmalloc succeed\n");
  /*清除空间*/
   memset(mykey_devp,0,sizeof(struct mykey_dev));

   /*创建一个设备*/
   Key_setup_cdev(mykey_devp,0);

   //class_create和device_create函数是为了自动在/dev下创建DRIVER_NAME设备文件。
   //创建一个类,这个类存放于sysfs下面
   mykey_class=class_create(THIS_MODULE,DRIVER_NAME);
   if(IS_ERR(mykey_class))
   {
    result = PTR_ERR(mykey_class);
    printk("class create faikey\n");
    goto class_create_fail;
   }
   //在/dev目录下创建相应的设备节点
   mykeyDevice = device_create(mykey_class,NULL,devno,NULL,DRIVER_NAME);

   if(IS_ERR(mykeyDevice))
   {
    result = PTR_ERR(mykeyDevice);
    printk("device_create faile\n");
    goto device_create_faile;
   }

   //获得资源
   for(i=0; i < NUM_RESOURCE; ++ i)
   {
      /*获得设备的资源*/
      irq_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);

      if(NULL == irq_resource)
      {
          return -ENOENT;
      }
      irqs.pirqs[i] = irq_resource->start;
      /*实现名字的复制操作*/
      //strcpy(tq2440_irqs.name[i],irq_resource->name);
      /*这一句是将指针的地址指向一个具体的地址*/
      irqs.names[i] = irq_resource->name;

      printk("irqs name are %s\n",irq_resource->name);
   }
/*
   cdev_init(&(mykey_dev.cdev),&my2416Led_fops);
   mykey_dev.cdev.owner = THIS_MODULE;
   ret = cdev_add(&(mykey_dev.cdev),devno,1);    
   if(ret)
   {
       printk("Add device error\n");
       return ret;
   }
   printk (DEVICE_NAME " Initialized \n");
   return 0;
*/
fail_malloc:
   unregister_chrdev_region(devno,1);//释放设备号 
class_create_fail:
   unregister_chrdev_region(MKDEV(mykey_major, 0), 1);//释放设备号  
device_create_faile:
   class_destroy(mykey_class);/*注销创建的设备类*/
   return result;

}

int key_remove(struct platform_device *dev)
{
   /*注销设备*/
   device_destroy(mykey_class,MKDEV(mykey_major, 0));
    /*注销创建的设备类*/
   class_destroy(mykey_class);
    /*字符设备注销*/
   cdev_del(&mykey_devp->cdev);//注销cdev
   kfree(mykey_devp);/*释放设备结构体内存*/
   unregister_chrdev_region(MKDEV(mykey_major, 0), 1);//释放设备号
   printk(DEVICE_NAME " exit\n");

   return 0;
}

/*1。平台驱动定义*/ 
static struct platform_driver key_driver = {
 .probe  = key_probe,     /* 平台总线下增加一个平台设备时,调用枚举函数 */
 .remove  = key_remove,    /* 平台总线下去掉一个平台设备时,调用remove函数 */
 .driver  = {
  .name = "myplatformkey",       /* 能支持名为"myplatformkey"的平台设备 */
  .owner = THIS_MODULE,
 },
};

/*2。注册,把我们的驱动加入到平台设备驱动连表中去*/
static int key_drv_init(void)
{
   int ret;
   /*平台驱动注册*/ 
   ret=platform_driver_register(&key_driver);
   return ret;
}

static void __exit key_drv_exit(void)
 {
    /*平台驱动注销*/ 
    platform_driver_unregister(&key_driver);
 }

module_init(key_drv_init);
module_exit(key_drv_exit);

MODULE_LICENSE("GPL"); 

测试的应用程序

#include <stdio.h>
#include <stdlib.h>
#include<fcntl.h>        //open() close()
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>    //read()  wrie()
#include<sys/ioctl.h>
#include<sys/select.h>
#include<sys/time.h>
#include<errno.h>


#define DEVICE_NAME "/dev/my2416keys"

#define LED_ON 0
#define LED_OFF 1


int main()
{
    int buttons_fd;
    int key_value = 0;

    /*open函数测试*/
    printf("My2416 keys dev test!\n");

    buttons_fd=open(DEVICE_NAME,O_RDWR);
    printf("fd=%d\n",buttons_fd);

    if(buttons_fd < 0)
    {
        perror("open device buttons\n");
        exit(1);
    }

    while(1)
    {
        fd_set rds;
        int ret;

        FD_ZERO(&rds);
        FD_SET(buttons_fd,&rds);
        /*poll函数测试*/
        ret = select(buttons_fd + 1,&rds,NULL,NULL,NULL);
        if(ret < 0)
        {
            perror("select");
            exit(1);
        }
        if(ret == 0)
        {
            printf("Timeout.\n");
        }
        else if(FD_ISSET(buttons_fd,&rds))
        {
    /*read函数测试*/
            int ret = read(buttons_fd,&key_value,sizeof key_value);
            if(ret != sizeof key_value)
            {
                if(errno != EAGAIN)
                    perror("read buttons\n");
                continue;
            }
            else
            {
                printf("buttons_value:%d\n",key_value+1);
            }
        }
    }
    /*release函数测试*/
    close(buttons_fd);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值