基于platform总线的中断(按键)字符设备驱动设计

长平狐 发布于 2012/09/03 12:46
阅读 366
收藏 1
1、platform简介
为了理解platform总线设备驱动模型的实际运用,我首先分析了基于S3C2410的看门狗驱动实现过程,我本着将自己学过的知识在温习一遍的态度,完成了基于platform平台总线的外部中断(按键)的基本实现过程,分别采用了混杂字符设备和一般字符设备进行了操作,其中混杂字符设备驱动、应用程序参照了 Y-Kee http://blog.csdn.net/ayangke,QQ:843308498的基本框架。
platform总线是总线设备驱动模型中的一种,内核帮助实现了总线部分,我们在设计驱动的过程中只需要完成驱动和设备的添加即可。其中设备的添加包括添加设备名和相关的硬件资源(中断号和寄存器分布)。而驱动的添加除了添加必要的驱动模型以外,还需要得到硬件的具体资源,并对资源进行相应的操作和处理。
2、中断处理简介
中断是处理外设与处理器速度不匹配过程中常用的方法,基本的思想是,当产生中断以后,CPU必须停止当前处理的任务,转去执行中断处理程序,待中断处理程序处理完成以后再回到当前处理任务的过程。嵌入式处理器中的中断主要包括两部分CPU的内部中断(主要是异常)以及外设的中断(外部中断)。同时有的中断可以被屏蔽,而有的中断又不能被屏蔽,又可以将中断分为屏蔽和不可屏蔽中断。根据入口的跳转方法又可以将中断分为向量中断和非向量中断。向量中断通常是不同的中断号有不同的处理程序入口地址(硬件提供),而非向量中断通常是指共享型的中断,入口地址通常是由用户软件提供。

在linux内核中对中断的操作主要包括两部分:1、中断的申请和释放;2、中断处理程序设计
其中的中断的申请和释放,具体的采用函数:
  1. /*申请中断*/
  2. request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
  3. /*释放中断*/
  4. void free_irq(unsigned int irq, void *dev_id)
在request_irq中的参数:
1、unsigned int irq表示具体的中断号,一般而言,针对不同的CPU,中断号存在一定的差别。
2、irq_handler_t handler表示一个回调函数,具体的就是中断操作的具体函数,称为中断处理函数。根据
typedef irqreturn_t (*irq_handler_t)(int, void *);
irq_handler_t 是一个函数指针,也就是中断处理函数应该是一个参数分别为int和void*,返回值为irqreturn_t,是枚举型参数。
  1. enum irqreturn {
  2.     IRQ_NONE,
  3.     IRQ_HANDLED,
  4.     IRQ_WAKE_THREAD,
  5. };

  6. typedef enum irqreturn irqreturn_t;
  7. #define IRQ_RETVAL(x)    ((x) != IRQ_NONE)
其中中断处理函数的形式就是一个函数,采用一般的C语言就可以实现。并没有什么特别需要注意的事项,但是需要保证的是中断要尽可能的短小精悍,不要出现长时间等待以及睡眠等形式,不能出现互信息量、信号量等并发机制,只能采用自旋锁实现。
3、unsigned long flags参数表示的中断的触发方式(高低电平触发等)或者处理方式(快速、一般中断),其中的IRQF_DISABLE表示快速中断模式,IRQF_SHARED表示共享中断模式。
  
 /*可以选择的基本触发方式和处理方式*/
  1. #include <linux/interrupt.h>
 /*中断处理方式*/
  1. #ifndef IRQF_DISABLED
  2. #define IRQF_DISABLED SA_INTERRUPT
  3. #define IRQF_SAMPLE_RANDOM SA_SAMPLE_RANDOM
  4. #define IRQF_SHARED SA_SHIRQ
  5. #define IRQF_PROBE_SHARED SA_PROBEIRQ
  6. #define IRQF_PERCPU SA_PERCPU
  7. #ifdef SA_TRIGGER_MASK
  8. #define IRQF_TRIGGER_NONE 0
  9. #define IRQF_TRIGGER_LOW SA_TRIGGER_LOW
  10. #define IRQF_TRIGGER_HIGH SA_TRIGGER_HIGH
  11. #define IRQF_TRIGGER_FALLING SA_TRIGGER_FALLING
  12. #define IRQF_TRIGGER_RISING SA_TRIGGER_RISING
  13. #define IRQF_TRIGGER_MASK SA_TRIGGER_MASK
  14. #else /*中断触发方式*/
  15. #define IRQF_TRIGGER_NONE 0
  16. #define IRQF_TRIGGER_LOW 0
  17. #define IRQF_TRIGGER_HIGH 0
  18. #define IRQF_TRIGGER_FALLING 0
  19. #define IRQF_TRIGGER_RISING 0
  20. #define IRQF_TRIGGER_MASK 0
  21. #endif
  22. #endif

4、const char *name表示具体的设备名。
5、void *dev,这个参数比较灵活,可以选择不同的值,当中断选择为共享中断模式时,dev必须是唯一的;而当中断选择为其他处理模式时,该参数可以为NULL,也可以用来传递需要处理的变量或者资源。具体后面分析。

释放函数则相对来说简单很多,主要是释放的中断号和相关的处理数据。free_irq()针对共享设备非常有用,因为不能关闭中断disable_irq(),影响其他共享该中断号的设备。

3、功能简介
我这次完成的设计主要是采用platform设备驱动模型实现外部中断的设计。具体的操作主要是包括如下两步:
1、设备的添加(主要包括资源的添加和设备的注册)
2、驱动的设计(难点),首先要注册总线驱动,第二要完成具体驱动(外部中断或者按键)的设计(字符型驱动或者混杂设备驱动),最后实现对应的操作函数。

难点分析:
1、具体设备的结构体设计,设备包含的数据,这一步是驱动设计的重点。
2、中断函数的设计,需要处理那些数据,保证短小精悍。
3、具体操作的设计,主要包括初始化和具体函数的操作。

字符设备的驱动源码代码分析,关于混杂设备的参看引用文章。
首先是设备添加代码:
  1. #include<linux/module.h>
  2. #include<linux/init.h>
  3. #include<linux/kernel.h>
  4. #include<linux/string.h>
  5. #include<linux/platform_device.h>
  6. /*硬件相关的头文件*/
  7. #include<mach/regs-gpio.h>
  8. #include<mach/hardware.h>
  9. #include<linux/gpio.h>

  10. /*这个是硬件(CPU)密切相关的中断号*/
  11. #include<mach/irqs.h>

  12. /*硬件资源量,这是根据tq2440开发板确定的*/
  13. static struct resource tq2440_button_resource[]=
  14. {
  15.     /*EINT0*/
  16.     [0]=
  17.     {
  18.         .flags = IORESOURCE_IRQ,
  19.         .start = IRQ_EINT0,
  20.         .end = IRQ_EINT0,
  21.         .name = "S3C24XX_EINT0",
  22.     },
  23.     /*EINT1*/
  24.     [1]=
  25.     {
  26.         .flags = IORESOURCE_IRQ,
  27.         .start = IRQ_EINT1,
  28.         .end = IRQ_EINT1,
  29.         .name = "S3C24xx_EINT1",
  30.     },
  31.     /*EINT2*/
  32.     [2]=
  33.     {
  34.         .flags = IORESOURCE_IRQ,
  35.         .start = IRQ_EINT2,
  36.         .end = IRQ_EINT2,
  37.         .name = "S3C24xx_EINT2",
  38.     },
  39.     /*EINT4*/
  40.     [3]=
  41.     {
  42.         .flags = IORESOURCE_IRQ,
  43.         .start = IRQ_EINT4,
  44.         .end = IRQ_EINT4,
  45.         .name = "S3C24xx_EINT4",
  46.     },
  47. };

  48. static struct platform_device tq2440_button_device=
  49. {
  50.     /*设备名*/
  51.     .name = "tq2440_button",
  52.     .id = -1,
  53.     /*资源数*/
  54.     .num_resources = ARRAY_SIZE(tq2440_button_resource),
  55.     /*资源指针*/
  56.     .resource = tq2440_button_resource,
  57. };

  58. static int __init tq2440_button_init(void)
  59. {
  60.     int ret ;
  61.     /*设备注册*/
  62.     ret = platform_device_register(&tq2440_button_device);
  63. }

  64. static void __exit tq2440_button_exit(void)
  65. {
  66.     /*设备的注销*/
  67.     platform_device_unregister(&tq2440_button_device);
  68. }

  69. /*加载与卸载*/
  70. module_init(tq2440_button_init);
  71. module_exit(tq2440_button_exit);

  72. /*LICENSE和作者信息*/
  73. MODULE_LICENSE("GPL");
  74. MODULE_AUTHOR("GP-<gp19861112@yahoo.com.cn>");
然后是驱动实现代码:
  1. #include<linux/types.h>
  2. #include<linux/kernel.h>
  3. #include<linux/init.h>
  4. #include<linux/module.h>
  5. #include<linux/platform_device.h>
  6. #include<mach/irqs.h>
  7. #include<linux/irq.h>
  8. #include<mach/regs-gpio.h>
  9. #include<linux/device.h>
  10. #include<linux/string.h>
  11. #include<linux/cdev.h>
  12. #include<linux/fs.h>
  13. #include<linux/spinlock.h>
  14. #include<linux/wait.h>
  15. #include<linux/interrupt.h>
  16. #include<linux/uaccess.h>
  17. #include<linux/poll.h>


  18. #define NUM_RESOURCE    4

  19. /*主设备号*/
  20. int dev_major = -1;

  21. /*中断结构体定义*/
  22. struct irqs
  23. {
  24.     int pirqs[NUM_RESOURCE];
  25.     char *names[NUM_RESOURCE];
  26. }irqs;
  27. /*完成具体设备的结构体设计*/
  28. struct tq2440_button
  29. {
  30.     /*添加具体的字符设备结构*/
  31.     struct cdev cdev;
  32.     /*用于自动创建设备*/
  33.     struct class *myclass;
  34.     /*引用次数统计表*/
  35.     unsigned int count;
  36.     /*添加并行机制*/
  37.     spinlock_t lock;
  38.     /*添加等待队列*/
  39.     wait_queue_head_t read_wait_queue;
  40.     /*数据*/
  41.     int bepressed;
  42.     /*案件值*/
  43.     int key_values;
  44. };

  45. static struct tq2440_button tq2440_button;

  46. static irqreturn_t tq2440_button_interrupt_handler(int irq,void *dev_id)
  47. {
  48.     /*得到传递过来的参数*/
  49.     struct tq2440_button * dev = dev_id;
  50.     int i;
  51.     /*根据得到的irq值确定具体的按键值*/
  52.     for (= 0; i < NUM_RESOURCE; ++ i)
  53.     {
  54.         /*判断条件*/
  55.         if(irq == irqs.pirqs[i])
  56.         {
  57.             /*对关键数据添加并行机制*/
  58.             spin_lock(&(dev->lock));
  59.             /*确定被按下的值*/
  60.             dev->key_values = i ;
  61.             /*表示有数据可以读*/
  62.             dev->bepressed = 1;
  63.             spin_unlock(&(dev->lock));
  64.             /*唤醒等待的队列*/
  65.             wake_up_interruptible(&(dev->read_wait_queue));    
  66.         }
  67.     }
  68.     /*返回值*/
  69.     return IRQ_RETVAL(IRQ_HANDLED);
  70. }

  71. static int tq2440_button_open(struct inode *inode,struct file *filp)
  72. {
  73.     int i = 0,ret = 0;
  74.     
  75.     /*中断申请*/
  76.     /*这句话主要是实现间接控制,但是还是可以直接控制*/
  77.     filp->private_data = &tq2440_button;
  78.     
  79.     /*修改被打开的次数值*/
  80.     spin_lock(&(tq2440_button.lock));    
  81.     tq2440_button.count ++ ;
  82.     spin_unlock(&(tq2440_button.lock));

  83.     /*如果是第一次打开则需要申请中断,这是比较推荐的方法,也可以在probe函数中申请中断*/
  84.     if(1==tq2440_button.count)
  85.     {
  86.         for(= 0;< NUM_RESOURCE; ++ i)
  87.         {
  88.             /*request_irq操作*/
  89.             ret = request_irq(irqs.pirqs[i],
  90.                 tq2440_button_interrupt_handler,
  91.                 IRQ_TYPE_EDGE_BOTH,irqs.names[i],(void *)&tq2440_button);
  92.         
  93.             if(ret)
  94.             {
  95.                 break;
  96.             }
  97.         }
  98.         if(ret)/*错误处理机制*/
  99.         {
  100.             i --;
  101.             for(; i >=0; --i)
  102.             {
  103.                 /*禁止中断*/
  104.                 
  105.                 disable_irq(irqs.pirqs[i]);
  106.             
  107.                 free_irq(irqs.pirqs[i],(void *)&tq2440_button);
  108.             }
  109.             return -EBUSY;
  110.         }
  111.     }    
  112.     return 0;
  113. }

  114. /*closed函数*/
  115. static int tq2440_button_close(struct inode *inode,struct file *filp)
  116. {
  117.     /*确保是最后一次使用设备*/
  118.     int i = 0;
  119.     if(tq2440_button.count == 1)
  120.     {
  121.         for(= 0; i < NUM_RESOURCE; ++ i)
  122.         {
  123.             free_irq(irqs.pirqs[i],(void *)&tq2440_button);
  124.         }
  125.     }
  126.     /*更新设备文件引用次数*/
  127.     spin_lock(&(tq2440_button.lock));
  128.     tq2440_button.count = 0;
  129.     spin_unlock(&(tq2440_button.lock));

  130.     return 0;
  131. }

  132. static unsigned long tq2440_button_read(struct file *filp,char __user *buff,
  133.                     size_t count,loff_t offp)
  134. {
  135.     /*设备操作*/
  136.     struct tq2440_button *dev = filp->private_data;
  137.     
  138.     unsigned long err;
  139.         if(!dev->bepressed)/*确保没有采用非堵塞方式读标志*/
  140.         {
  141.         if(filp->f_flags & O_NONBLOCK)
  142.             return -EAGAIN;
  143.         else
  144.             /*添加等待队列,条件是bepressed*/
  145.             wait_event_interruptible(dev->read_wait_queue,dev->bepressed);
  146.     }    

  147.     /*复制数据到用户空间*/
  148.     err = copy_to_user(buff, &(dev->key_values),min(sizeof(dev->key_values),count));
  149.     
  150.     /*修改标志表示没有数据可读了*/
  151.     spin_lock(&(dev->lock));
  152.     dev->bepressed = 0;
  153.     spin_unlock(&(dev->lock));
  154.     
  155.     /*返回数据量*/
  156.     
  157.     return err ? -EFAULT:min(sizeof(dev->key_values),count);
  158. }


  159. static unsigned int tq2440_button_poll(struct file *filp,
  160.                 struct poll_table_struct *wait)
  161. {
  162.     struct tq2440_button *dev = filp->private_data;

  163.     unsigned int mask = 0;
  164.     
  165.     /*将结构体中的等待队列添加到wait_table*/    
  166.     poll_wait(filp,&(dev->read_wait_queue),wait);

  167.     /*
  168.     返回掩码
  169.     POLLIN|POLLRDNORM表示有数据可读
  170.     */
  171.     if(dev->bepressed)
  172.     {
  173.         mask |= POLLIN | POLLRDNORM;
  174.     }

  175.     return mask;
  176. }

  177. /*设备的具体操作函数*/
  178. static const struct file_operations tq2440_fops=
  179. {
  180.     .owner = THIS_MODULE,
  181.     .open = tq2440_button_open,
  182.     .release = tq2440_button_close,
  183.     .read = tq2440_button_read,
  184.     .poll = tq2440_button_poll,
  185. };


  186. /*remove函数实现字符设备的注销操作*/

  187. static int tq2440_button_probe(struct platform_device *dev)
  188. {
  189.     printk("The driver found a device can be handler on platform bus\n");
  190.     
  191.     /*用来存储定义好的资源,即中断号*/
  192.     
  193.     struct resource * irq_resource;
  194.     struct platform_device *pdev = dev;
  195.     int i = 0,ret = 0;

  196.     /*接下来完成具体字符驱动结构体的初始化*/
  197.     /*1、设备号申请*/
  198.     
  199.     dev_t devno;

  200.     if(dev_major > 0)/*静态申请设备号*/
  201.     {
  202.         devno = MKDEV(dev_major,0);
  203.         ret = register_chrdev_region(devno,1,"tq2440_button");
  204.     }
  205.     else/*动态申请设备号*/
  206.     {
  207.         ret = alloc_chrdev_region(&devno,0,1,"tq2440_button");
  208.         dev_major = MAJOR(devno);
  209.     }
  210.     if(ret < 0)
  211.     {
  212.         return ret;
  213.     }

  214.     /*完成设备类的创建,主要实现设备文件的自动创建*/
  215.     tq2440_button.myclass = class_create(THIS_MODULE,"tq2440_button_class");
  216.  
  217.     /*2、完成字符设备的加载*/
  218.     cdev_init(&(tq2440_button.cdev),&tq2440_fops);
  219.     tq2440_button.cdev.owner = THIS_MODULE;
  220.     ret = cdev_add(&(tq2440_button.cdev),devno,1);    
  221.     if(ret)
  222.     {
  223.         printk("Add device error\n");
  224.         return ret;
  225.     }
  226.     
  227.         /*初始化自旋锁*/
  228.     spin_lock_init(&(tq2440_button.lock));
  229.     
  230.     /*修改引用次数值*/
  231.     
  232.     spin_lock(&(tq2440_button.lock));
  233.     /*被打开次数统计*/
  234.     tq2440_button.count = 0;    
  235.     /*键值*/
  236.     tq2440_button.key_values = -1;
  237.     spin_unlock(&(tq2440_button.lock));
  238.     
  239.     /*初始化等待队列*/
  240.     
  241.     init_waitqueue_head(&(tq2440_button.read_wait_queue));
  242.     
  243.     
  244.     /*设备的创建,实现设备文件自动创建*/
  245.     
  246.     device_create(tq2440_button.myclass,NULL,devno,NULL,"tq2440_button");

  247.     /*3.获得资源*/
  248.     for(; i < NUM_RESOURCE; ++ i)
  249.     {
  250.         /*获得设备的资源*/
  251.         irq_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);
  252.         
  253.         if(NULL == irq_resource)
  254.         {
  255.             return -ENOENT;
  256.         }
  257.         irqs.pirqs[i] = irq_resource->start;

  258.         /*实现名字的复制操作*/
  259.         //strcpy(tq2440_irqs.name[i],irq_resource->name);

  260.         /*这一句是将指针的地址指向一个具体的地址*/
  261.         irqs.names[i] = irq_resource->name;
  262.     }
  263.     /*将设备的指针指向中断号*/
  264.     
  265.     return 0;
  266. }

  267. /*probe函数实现字符设备的初始化操作*/
  268. static int tq2440_button_remove(struct platform_device *dev)
  269. {
  270.     printk("The driver found a device be removed from the platform bus\n");
  271.     
  272.     /*注销设备*/
  273.     device_destroy(tq2440_button.myclass,MKDEV(dev_major,0));
  274.     /*字符设备注销*/
  275.     cdev_del(&(tq2440_button.cdev));
  276.     /*注销创建的设备类*/
  277.     class_destroy(&(tq2440_button.myclass));
  278.     /*释放设备号*/
  279.     unregister_chrdev_region(MKDEV(dev_major,0),1);
  280.     return 0;
  281. }

  282. /*完成平台总线结构体的设计*/
  283. static const struct platform_driver tq2440_button_driver = 
  284. {
  285.     /*完成具体设备的初始化操作*/
  286.     .probe = tq2440_button_probe,
  287.     /*完成具体设备的退出操作*/
  288.     .remove = tq2440_button_remove,
  289.     .driver = 
  290.     {
  291.         .owner = THIS_MODULE,
  292.         .name = "tq2440_button",
  293.     },
  294. };

  295. /*总线设备初始化过程*/
  296. static int __init platform_driver_init(void)
  297. {
  298.     int ret;
  299.     
  300.     /*总线驱动注册*/
  301.     ret = platform_driver_register(&tq2440_button_driver);
  302.     
  303.     /*错误处理*/
  304.     if(ret)
  305.     {
  306.         platform_driver_unregister(&tq2440_button_driver);
  307.         return ret;
  308.     }

  309.     return 0;
  310. }

  311. static void __exit platform_driver_exit(void)
  312. {
  313.     /*总线驱动释放*/
  314.     platform_driver_unregister(&tq2440_button_driver);
  315. }

  316. /*加载和卸载*/
  317. module_init(platform_driver_init);
  318. module_exit(platform_driver_exit);

  319. /*LICENSE和作者信息*/
  320. MODULE_LICENSE("GPL");
  321. MODULE_AUTHOR("GP-<gp19861112@yahoo.com.cn>");
应用程序
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #include<sys/ioctl.h>
  6. #include<sys/stat.h>
  7. #include<sys/select.h>
  8. #include<sys/time.h>
  9. #include<errno.h>

  10. int main()
  11. {
  12.     int buttons_fd;
  13.     int key_value = 0;
  14.     
  15.     /*open函数测试*/
  16.     buttons_fd = open("/dev/tq2440_button",0);

  17.     if(buttons_fd < 0)
  18.     {
  19.         perror("open device buttons\n");
  20.         exit(1);
  21.     }

  22.     while(1)
  23.     {
  24.         fd_set rds;
  25.         int ret;

  26.         FD_ZERO(&rds);
  27.         FD_SET(buttons_fd,&rds);
  28.         /*poll函数测试*/
  29.         ret = select(buttons_fd + 1,&rds,NULL,NULL,NULL);
  30.         if(ret < 0)
  31.         {
  32.             perror("select");
  33.             exit(1);
  34.         }
  35.         if(ret == 0)
  36.         {
  37.             printf("Timeout.\n");
  38.         }
  39.         else if(FD_ISSET(buttons_fd,&rds))
  40.         {
  41.     /*read函数测试*/
  42.             int ret = read(buttons_fd,&key_value,sizeof key_value);
  43.             if(ret != sizeof key_value)
  44.             {
  45.                 if(errno != EAGAIN)
  46.                     perror("read buttons\n");
  47.                 continue;
  48.             }
  49.             else
  50.             {
  51.                 printf("buttons_value:%d\n",key_value+1);
  52.             }
  53.         }
  54.     }
  55.     /*release函数测试*/
  56.     close(buttons_fd); 
  57.     return 0;
  58. }
采用plotform总线实现中断的操作,基本中断的操作。

原文链接:http://blog.csdn.net/hanchaoman/article/details/7220351
加载中
返回顶部
顶部