基於platform總線的中斷(按鍵)字符設備驅動設計

基於platform總線的中斷(按鍵)字符設備驅動設計

於platform總線的中斷(按鍵)字符設備驅動設計 2011-12-23 13:02:02

分類: LINUX

我的環境:
主機:Fedora 14,內核版本2.6.38.1
開發板:ARM9 tq2440
移植內核版本:linux-2.6.30.4

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-<[email protected]>");
然後是驅動實現代碼:
  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 (i = 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(i = 0;i < 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(i = 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-<[email protected]>");
應用程序
  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總線實現中斷的操作,基本中斷的操作。
中斷操作設備驅動集合:



之前接觸到的字符設備驅動是非常單純的Linux字符設備驅動,他不具備工程中Linux驅動中的設備與驅動分離思想設備驅動的分層思想,不具備“總線-設備-驅動”模型的概念。接下來通過分析platform設備驅動模型的搭建過程來看看Linux的設備驅動模型究竟是怎樣的?

platform驅動模型搭建:

(1)platform核心層:爲設備層和驅動層提供註冊接口、爲設備層和驅動層的匹配提供標準

①搭建總線框架:


struct bus_type {
    const char      *name;
    struct bus_attribute    *bus_attrs;
    struct device_attribute *dev_attrs;
    struct driver_attribute *drv_attrs;
    int (*match)(struct device *dev, struct device_driver *drv); //#####
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*suspend_late)(struct device *dev, pm_message_t state);
    int (*resume_early)(struct device *dev);
    int (*resume)(struct device *dev);
    struct dev_pm_ops *pm;
    struct bus_type_private *p; //看到這個private就有點C++類中的限定域關鍵字了,這個類的私有成員
};

總線類實例化:platform總線

1
2
3
4
5
6
7
struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_attrs  = platform_dev_attrs,
    .match      = platform_match, //關鍵成員
    .uevent     = platform_uevent,
    .pm     = PLATFORM_PM_OPS_PTR,
};
註冊platform總線過程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
platform_bus_init()
{
    .....
    error =  bus_register(&platform_bus_type);//註冊platform總線的核心工作
    .....
}
bus_register(struct bus_type *bus)
{
    //創建bus的屬性文件  
    retval = bus_create_file(bus, &bus_attr_uevent);
    ......
    //在/sys/bus/bus->name目錄下創建devices目錄
    priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);
    ....
    //在/sys/bus/bus->name目錄下創建drivers目錄
    priv->drivers_kset = kset_create_and_add("drivers", NULL,&priv->subsys.kobj);
    //初始化總線設備\總線驅動鏈表
    klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
    klist_init(&priv->klist_drivers, NULL, NULL);
}
核心層的功績:初始化了klist_devices和klist_drivers兩個鏈表,沒有談到如何判斷設備和驅動匹配?“.match=platform_match”有初始化,但是什麼時候被調用?

當一個驅動掛接到該總線的時候,該總線的match方法被調用。同樣的,當一個設備掛接到該總線時,platform_match也會被調用。也就是說核心層只提供匹配的方法!不會幫他們去匹配,這人生大事要他們自己去完成!
這就好辦了,都是掛接到總線上的時候,往後分析時肯定會遇到,先暫時放着,先看看他的實現:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(drv);
 
 
    /* match against the id table first */
    if (pdrv->id_table) //看看drv的id_table中是否有現成匹配的設備記錄
        return platform_match_id(pdrv->id_table, pdev) != NULL;
 
 
    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0); /* match成功,strcmp返回0,語句邏輯返回1 */

}


②爲設備層提供註冊API、提供自動匹配接口函數
設備基類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct device {
    struct device           *parent;
    struct device_private   *p;
    struct kobject          kobj;
    const char              *init_name;     /* initial name of the device 這個就是傳統的bus_id,具體到每一個設備之後當做默認值 */
    struct device_type      *type;
    ......
    struct bus_type         *bus;           /* type of bus device is on */
    struct device_driver    *driver;        /* which driver has allocated this device */
    void                    *driver_data;   /* data private to the driver */
    void                    *platform_data; /* Platform specific data, device core doesn't touch it */
    ......
    void                    (*release)(struct device *dev);
};



派生類:platform設備
1
2
3
4
5
6
7
8
struct platform_device {
    const char      *name;
    int             id;  // 硬件設備的象徵/代表
    struct device   dev; // 由此繼承基類
    u32             num_resources;
    struct resource * resource;//這個驅動使用的資源
    struct platform_device_id   *id_entry;
};
註冊platform設備函數調用關係:
platform_device_register(struct platform_device *pdev)
platform_device_add(struct platform_device *pdev)
pdev->dev.bus = &platform_bus_type;
device_add(&pdev->dev);
bus_attach_device(struct device *dev)
device_attach(dev);
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
bus_for_each_drv()函數的實現:
1
2
3
4
5
6
7
8
bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
             void *data, int (*fn)(struct device_driver *, void *))
{
    ......
    while ((drv = next_driver(&i)) && !error)
        error = fn(drv, data);
    ......
}
分析:
首先關心他的最後一個形參(*fn),他在註冊platform_device時最終被重定向到__device_attach()函數,回調函數的使用在內核源碼裏邊屢見不鮮!因爲它可以減少很多重複的代碼。
現在分析的焦點轉移到__device_attach函數:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
__device_attach(struct device_driver *drv, void *data)
{
    struct device *dev = data;
    if (!driver_match_device(drv, dev))
        return 0;
    return driver_probe_device(drv, dev); //match成功就執行這個函數,他最終調用really_probe()函數
}
driver_match_device(struct device_driver *drv,struct device *dev)
{
    return drv->bus->match ? drv->bus->match(dev, drv) : 1; //看到這一句,上面留下的疑問就解決了:原來核心層留下的匹配判斷標準match接口就是在這裏被調用的!!!好爽!^_^
}
really_probe(struct device *dev, struct device_driver *drv)
{
......
    if (dev->bus->probe) //如果bus_type結構裏邊的probe成員有定義就優先調用他的
    {
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    }
    else if (drv->probe) //沒有就調用匹配到的drv結構裏邊的probe成員函數
    {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }
    driver_bound(dev);//bound是綁定的意思,即將match成功的設備加入驅動的設備鏈表
    ......
}
③爲驅動層提供API、提供自動匹配接口函數
驅動基類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct device_driver {
    const char      *name;
    struct bus_type     *bus;
    struct module       *owner;
    const char      *mod_name;  /* used for built-in modules */
    int  (*probe) (struct device *dev);
    int  (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int  (*suspend) (struct device *dev, pm_message_t state);
    int  (*resume) (struct device *dev);
    struct attribute_group **groups;
    struct dev_pm_ops *pm;
    struct driver_private *p;
};
驅動派生類:
1
2
3
4
5
6
7
8
9
10
11
struct platform_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;  //繼承基類
    struct platform_device_id *id_table;
};
註冊platform_driver驅動結構體函數執行流程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
platform_driver_register(struct platform_driver *drv)
{
    /*下面進行一系列的判斷,如果派生的platform_driver中沒有對特有成員進行初始化,設置成默認的 */
    drv->driver.bus = &platform_bus_type;   //指向這個驅動所屬的bus類型:platform
    if (drv->probe)  //有重定向
        drv->driver.probe = platform_drv_probe;
    if (drv->remove) //有重定向
        drv->driver.remove = platform_drv_remove;
    ......
    return driver_register(&drv->driver); 【進入分析】
    //註冊的關鍵材料是platform_driver->driver->bus:關鍵是爲了註冊總線的類型platform_bus_type
}
driver_register(struct device_driver *drv)
{
    ......
    struct device_driver *other;
    ......
    other = driver_find(drv->name, drv->bus); //在該總線上查找是否有該設備驅動名對應的驅動
    if (other) { //如果設備已經存在對應的驅動就:出錯,驅動已經存在
        put_driver(other);
        printk(KERN_ERR "Error: Driver '%s' is already registered, "
            "aborting...\n", drv->name);
        return -EEXIST;
    }
    bus_add_driver(drv);  /* 在總線上添加這個驅動,成功的話最終結果:在bus/platform/drivers目錄下面生成“name”對應的目錄 ,並且會生成 bind  module  uevent  unbind 四個文件*/
    ......
}
繼續深入分析:
bus_add_driver(struct device_driver *drv)
driver_attach(drv); /* 試圖將驅動和設備綁定起來 */
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);//到這裏可以非常明顯的發現和設備層做的事情非常相似,幾乎是對稱出現
/* 對總線上的每一個設備都會拿來執行__driver_attach,他在這裏被用作回調函數,看看是否匹配,這個函數和__device_attach函數做的事情基本一樣這裏就不再累述了*/

(2)設備層:主要工作就是把核心層提供的API用起來
1.設置好platform_device結構體成員:主要是name、resource、num_resources、id、dev->release、

2.通過platform_device_register()把這個結構體鏈入核心層的klist_devices鏈表

(3)驅動層:同樣是把核心層提供的接口函數用起來
1.設置好platform_driver結構體成員:probe、remove、driver->name
2.通過platform_driver_register()函數把這個結構體鏈入核心層的klist_drivers鏈表
3.實現probe成員函數
4.通常最後纔去完成probe函數用到的材料,一般是file_operation結構體成員,這樣應用層就可以通過這個接口來操作設備
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章