Linux設備驅動工程師之路——platform按鍵驅動 來自於衡陽師範學院08電2

 

一 、重要知識點:

1.platform設備模型

    從Linux 2.6起引入了一套新的驅動管理和註冊機制,platform_device和platform_driver,Linux中大部分的設備驅動都可以使用這套機制。platform是一條虛擬的總線。設備用platform_device表示,驅動用platform_driver進行註冊,Linux platform driver機制和傳統的device driver機制(通過driver_register進行註冊)相比,一個明顯的優勢在於platform機制將設備本身的資源註冊進內核,由內核統一管理,在驅動中使用這些資源時通過platform device提供的標準結構進行申請並使用。這樣提高了驅動和資源的獨立性,並且具有較好的可移植性和安全性(這些標準接口是安全的)。

    pltform機制本身使用並不複雜,由兩部分組成:platform_device和platform_driver。通過platform機制開發底層驅動的大致流程爲:定義platform_deive->註冊platform_device->定義platform_driver->註冊platform_driver。

    首先要確認的就是設備的資源信息,例如設備的地址,中斷號等。

1)platform_device

在 2.6 內核中 platform 設備用結構體 platform_device 來描述,該結構體定義在 kernel/include/linux/platform_device.h 中,

structplatform_device {

 const char * name;

 u32  id;

 struct device dev;

 u32  num_resources;

 struct resource * resource;

};

該結構一個重要的元素是resource ,該元素存入了最爲重要的設備資源信息,定義在kernel/include/linux/ioport.h 中,

structresource {

 const char *name;//資源的名稱

 unsigned long start, end;//資源起始的和結束的物理地址

 unsigned long flags;//資源的類型,比如MEM,IO,IRQ類型

 struct resource *parent, *sibling, *child;//資源鏈表的指針

};

structplatform_device的分配使用

structplatform_device *platform_device_alloc(const char *name, int id)

name是設備名,id,設備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。

註冊平臺設備,使用函數

intplatform_device_add(struct platform_device *pdev)

註銷使用

voidplatform_device_unregister(struct platform_device *pdev)

2)platform_driver

在平臺設備驅動中獲取平臺設備資源使用

structresource *platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num)

該函數用於獲取dev設備的第num個類型爲type的資源,如果獲取失敗,則返回NULL。例如 platform_get_resource(pdev,IORESOURCE_IRQ, 0)。

平臺驅動描述使用

structplatform_driver {

 int (*probe)(struct platform_device *);

 int (*remove)(struct platform_device *);

 void (*shutdown)(struct platform_device *);

 int (*suspend)(struct platform_device *, pm_message_t state);

 int (*suspend_late)(struct platform_device *, pm_message_t state);

 int (*resume_early)(struct platform_device *);

 int (*resume)(struct platform_device *);

 struct device_driver driver;

};

Probe()函數必須驗證指定設備的硬件是否真的存在,probe()可以使用設備的資源,包括時鐘,platform_data等,Platform driver可以通過下面的函數完成對驅動的註冊:

int platform_driver_register(structplatform_driver *drv);一般來說設備是不能被熱插拔的,所以可以將probe()函數放在init段裏面來節省driver運行時候的內存開銷:

int platform_driver_probe(struct platform_driver *drv, int (*probe)(structplatform_device *));

註銷使用void platform_driver_unregister(struct platform_driver *drv)

2.中斷處理

在Linux驅動程序中,爲設備實現一箇中斷包含 兩個步驟1.向內核註冊(申請中斷)中斷 2.實現中斷處理函數

request_irq用於實現中斷的註冊

intrequest_irq(unsigned in irq, void(*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void*dev_id)

向內核申請中斷號爲irq,中斷處理函數爲handler指針指向的函數,中斷標誌爲flag,設備名爲devname的中斷。成功返回0,或者返回一個錯誤碼。

當request_irq不用於共享中斷時,dev_id可以爲NULL,或者指向驅動程序自己的私有數據。但用於共享中斷時dev_id必須唯一。因爲free_irq時也需要dev_id做參數,這樣free_irq才知道要卸載共享中斷上哪個中斷服務處理函數。共享中斷會在後面講到。

在flag參數中,可以選以下參數

IRQF_DISABLED(SA_INTERRUPT)

如果設置該位,表示是一個“快速”中斷處理程序,如果沒有,那麼就是一個“慢速”中斷處理程序。

IRQF_SHARED(SA_SHITQ)

該位表示中斷可以在設備間共享。

快速/慢速中斷

這兩種類型的中斷處理程序的主要區別在於:快速中斷保證中斷處理的原子性(不被打斷),而慢速中斷則不保證。換句話說,也就是開啓中斷標誌位在運行快速中斷處理程序時

關閉的,因此在服務該中斷時,不會被其他類型的中斷打斷;而調用慢速中斷處理時,其他類型中斷扔可以得到服務。

    共享中斷

共享中斷就是將不同的設備掛到同一個中斷信號線上。linux對共享的支持主要是位PCI設備服務。

釋放中斷

voidfree_irq(unsigned int irq)

當設備不再需要使用中斷時(通常是設備關閉和驅動卸載時),應該使用該函數把他們返回給內核使用。

禁用中斷

voiddisable_irq(int irq)

當一些代碼中不能使用中斷時(如支持自旋鎖的上下文中)使用該函數禁用中斷。

啓用中斷

voidenable_irq(int irq)

當禁止後可以使用該函數重新啓用。

 

二、驅動代碼

  該驅動實現能夠讀取按鍵按下的鍵值,比如說如果是第一個鍵按下讀取的鍵值就爲1。

  platform平臺設備

  1. #include <linux/device.h>  
  2. #include <linux/string.h>  
  3. #include <linux/platform_device.h>  
  4. #include <linux/module.h>  
  5. #include <linux/kernel.h>  
  6. #include <linux/fs.h>  
  7. #include <linux/init.h>  
  8. #include <linux/delay.h>  
  9. #include <linux/poll.h>  
  10. #include <linux/irq.h>  
  11. #include <asm/irq.h>  
  12. #include <linux/interrupt.h>  
  13. #include <asm/uaccess.h>  
  14. #include <mach/regs-gpio.h>  
  15. #include <mach/hardware.h>  
  16. #include <linux/cdev.h>  
  17. #include <linux/miscdevice.h>  
  18. #include <linux/sched.h>  
  19. #include <linux/gpio.h>  
  20.   
  21. static struct resource key_resource[]=  
  22. {     
  23.     [0] = {  
  24.         .start = IRQ_EINT8,  
  25.         .end = IRQ_EINT8,  
  26.         .flags = IORESOURCE_IRQ,  
  27.     },  
  28.     [1] = {  
  29.         .start = IRQ_EINT11,  
  30.         .end = IRQ_EINT11,  
  31.         .flags = IORESOURCE_IRQ,  
  32.     },  
  33.     [2]= {  
  34.         .start = IRQ_EINT13,  
  35.         .end = IRQ_EINT13,  
  36.         .flags = IORESOURCE_IRQ,  
  37.     },  
  38.     [3] = {  
  39.         .start = IRQ_EINT14,  
  40.         .end = IRQ_EINT14,  
  41.         .flags = IORESOURCE_IRQ,  
  42.     },  
  43.     [4] = {  
  44.         .start = IRQ_EINT15,  
  45.         .end = IRQ_EINT15,  
  46.         .flags = IORESOURCE_IRQ,  
  47.     },  
  48.     [5] = {  
  49.         .start = IRQ_EINT19,  
  50.         .end = IRQ_EINT19,  
  51.         .flags = IORESOURCE_IRQ,  
  52.     },  
  53. };  
  54.   
  55. struct platform_device *my_buttons_dev;  
  56.   
  57. static int __init platform_dev_init(void)  
  58. {  
  59.     int ret;  
  60.       
  61.     my_buttons_dev = platform_device_alloc("my_buttons", -1);  
  62.       
  63.     platform_device_add_resources(my_buttons_dev,key_resource,6);//添加資源一定要用該函數,不能使用對platform_device->resource幅值  
  64.                                                                 //否則會導致platform_device_unregister調用失敗,內核異常。  
  65.       
  66.     ret = platform_device_add(my_buttons_dev);  
  67.       
  68.     if(ret)  
  69.         platform_device_put(my_buttons_dev);  
  70.       
  71.     return ret;  
  72. }  
  73.   
  74. static void __exit platform_dev_exit(void)  
  75. {  
  76.     platform_device_unregister(my_buttons_dev);  
  77. }  
  78.   
  79. module_init(platform_dev_init);  
  80. module_exit(platform_dev_exit);  
  81.   
  82. MODULE_AUTHOR("Y-Kee");  
  83. MODULE_LICENSE("GPL");  


 

platform平臺驅動

  1. //platform driver  
  2. #include <linux/module.h>  
  3. #include <linux/types.h>  
  4. #include <linux/miscdevice.h>  
  5. #include <linux/fs.h>  
  6. #include <linux/init.h>  
  7. #include <linux/platform_device.h>  
  8. #include <linux/interrupt.h>  
  9. #include <linux/clk.h>  
  10. #include <linux/uaccess.h>  
  11. #include <linux/io.h>  
  12. #include <mach/map.h>  
  13. #include <mach/regs-gpio.h>  
  14. #include <linux/poll.h>  
  15. #include <linux/irq.h>  
  16. #include <asm/unistd.h>  
  17. #include <linux/device.h>  
  18.   
  19.   
  20. static int buttons_irq[6];  
  21.   
  22. struct irq_des  
  23. {  
  24.     int *buttons_irq;  
  25.     char *name[6];  
  26. };  
  27.   
  28.   
  29. struct irq_des button_irqs = {   
  30.     .buttons_irq = buttons_irq,  
  31.     .name = {"KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5"},  
  32. };  
  33.   
  34. static volatile int key_values;  
  35.   
  36.   
  37. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  
  38.   
  39.   
  40. static volatile int ev_press = 0;  
  41.   
  42.   
  43. static irqreturn_t buttons_interrupt(int irq, void *dev_id)  
  44. {  
  45.     int i;  
  46.     for(i=0; i<6; i++){  
  47.         if(irq == buttons_irq[i]){  
  48.             key_values = i;  
  49.             ev_press = 1;  
  50.             wake_up_interruptible(&button_waitq);     
  51.         }  
  52.     }  
  53.       
  54.     return IRQ_RETVAL(IRQ_HANDLED);  
  55. }  
  56.   
  57.   
  58. static int s3c24xx_buttons_open(struct inode *inode, struct file *file)  
  59. {  
  60.     int i;  
  61.     int err = 0;  
  62.       
  63.     for (i = 0; i < 6; i++) {  
  64.         err = request_irq(button_irqs.buttons_irq[i], buttons_interrupt, IRQ_TYPE_EDGE_BOTH,   
  65.                           button_irqs.name[i], (void *)&button_irqs.buttons_irq[i]);  
  66.         if (err)  
  67.             break;  
  68.     }  
  69.   
  70.     if (err) {  
  71.         i--;  
  72.         for (; i >= 0; i--) {  
  73.         if (button_irqs.buttons_irq[i] < 0) {  
  74.         continue;  
  75.         }  
  76.         disable_irq(button_irqs.buttons_irq[i]);  
  77.             free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);  
  78.         }  
  79.         return -EBUSY;  
  80.     }  
  81.       
  82.     return 0;  
  83. }  
  84.   
  85.   
  86. static int s3c24xx_buttons_close(struct inode *inode, struct file *file)  
  87. {  
  88.     int i;  
  89.       
  90.     for (i = 0; i < 6; i++) {  
  91.     free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);  
  92.     }  
  93.   
  94.     return 0;  
  95. }  
  96.   
  97.   
  98. static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)  
  99. {  
  100.     unsigned long err;  
  101.   
  102.     if (!ev_press) {  
  103.     if (filp->f_flags & O_NONBLOCK)  
  104.         return -EAGAIN;  
  105.     else  
  106.         wait_event_interruptible(button_waitq, ev_press);  
  107.     }  
  108.       
  109.     ev_press = 0;  
  110.   
  111.     err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count));  
  112.   
  113.     return err ? -EFAULT : min(sizeof(key_values), count);  
  114. }  
  115.   
  116. static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)  
  117. {  
  118.     unsigned int mask = 0;  
  119.     poll_wait(file, &button_waitq, wait);  
  120.     if (ev_press)  
  121.         mask |= POLLIN | POLLRDNORM;  
  122.     return mask;  
  123. }  
  124.   
  125.   
  126. static struct file_operations dev_fops = {  
  127.     .owner   =   THIS_MODULE,  
  128.     .open    =   s3c24xx_buttons_open,  
  129.     .release =   s3c24xx_buttons_close,   
  130.     .read    =   s3c24xx_buttons_read,  
  131.     .poll    =   s3c24xx_buttons_poll,  
  132. };  
  133.   
  134. static struct miscdevice misc = {  
  135.     .minor = MISC_DYNAMIC_MINOR,  
  136.     .name = "my_buttons",  
  137.     .fops = &dev_fops,  
  138. };  
  139.   
  140.   
  141. static int my_plat_probe(struct platform_device *dev)  
  142. {  
  143.     int ret,i;  
  144.     struct resource *plat_resource;  
  145.     struct platform_device *pdev = dev;  
  146.       
  147.     printk("my platform dirver find my platfrom device.\n");  
  148.   
  149.     for(i=0; i<6; i++){  
  150.         plat_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);  
  151.         if(plat_resource == NULL)  
  152.             return -ENOENT;   
  153.         buttons_irq[i] = plat_resource->start;  
  154.     }  
  155.   
  156.     ret = misc_register(&misc);  
  157.     if(ret)  
  158.         return ret;  
  159.   
  160.       
  161.     return 0;  
  162. }  
  163.   
  164. static int my_plat_remove(struct platform_device *dev)  
  165. {  
  166.     printk("my platfrom device has removed.\n");  
  167.     misc_deregister(&misc);  
  168.     return 0;  
  169. }  
  170.   
  171. struct platform_driver my_buttons_drv = {   
  172.     .probe = my_plat_probe,  
  173.     .remove = my_plat_remove,  
  174.     .driver = {   
  175.         .owner = THIS_MODULE,  
  176.         .name = "my_buttons",  
  177.     },  
  178. };  
  179.   
  180. static int __init platform_drv_init(void)  
  181. {  
  182.     int ret;  
  183.   
  184.     ret = platform_driver_register(&my_buttons_drv);  
  185.       
  186.     return ret;  
  187. }  
  188.   
  189. static void __exit platform_drv_exit(void)  
  190. {  
  191.     platform_driver_unregister(&my_buttons_drv);  
  192. }  
  193.   
  194. module_init(platform_drv_init);  
  195. module_exit(platform_drv_exit);  
  196.   
  197. MODULE_AUTHOR("Y-Kee");  
  198. MODULE_LICENSE("GPL");  

測試代碼

  1. /*  
  2.  *      Buttons Example for Matrix V  
  3.  *  
  4.  *      Copyright (C) 2004 capbily - friendly-arm  
  5.  *  [email protected]  
  6.  */  
  7. #include <stdio.h>  
  8. #include <stdlib.h>  
  9. #include <unistd.h>  
  10. #include <sys/ioctl.h>  
  11. #include <sys/types.h>  
  12. #include <sys/stat.h>  
  13. #include <fcntl.h>  
  14. #include <sys/select.h>  
  15. #include <sys/time.h>  
  16. #include <errno.h>  
  17.   
  18. int main(void)  
  19. {  
  20.     int buttons_fd;  
  21.     int key_value;  
  22.   
  23.     buttons_fd = open("/dev/buttons", 0);  
  24.     if (buttons_fd < 0) {  
  25.         perror("open device buttons");  
  26.         exit(1);  
  27.     }  
  28.   
  29.     for (;;) {  
  30.         fd_set rds;  
  31.         int ret;  
  32.   
  33.         FD_ZERO(&rds);  
  34.         FD_SET(buttons_fd, &rds);  
  35.   
  36.         ret = select(buttons_fd + 1, &rds, NULL, NULL, NULL);  
  37.         if (ret < 0) {  
  38.             perror("select");  
  39.             exit(1);  
  40.         }  
  41.         if (ret == 0) {  
  42.             printf("Timeout.\n");  
  43.         } else if (FD_ISSET(buttons_fd, &rds)) {  
  44.             int ret = read(buttons_fd, &key_value, sizeof key_value);  
  45.             if (ret != sizeof key_value) {  
  46.                 if (errno != EAGAIN)  
  47.                     perror("read buttons\n");  
  48.                 continue;  
  49.             } else {  
  50.                 printf("buttons_value: %d\n", key_value+1);  
  51.             }  
  52.                   
  53.         }  
  54.     }  
  55.   
  56.     close(buttons_fd);  
  57.     return 0;  
  58. }  

測試結果:

運行測試程序後按下第二個鍵,中斷上打印了多次按鍵的鍵值,產生原因是因爲按鍵抖動。導致按一下按鍵,產生多次中斷。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章