Linux 固件子系統----如何更新固件

在一般的使用中,可能會需要使用到升級固件這個功能,在linux的系統中提供了固件子系統這個設備模型來幫助快速的升級固件。

在這裏先將linux中提供的常用的接口來說明下:
內核的固件接口:
  1. #include <linux/firmware.h>  
  2. int request_firmware(const struct firmware **firmware_p, const char *name,  
  3.                  struct device *device)  
  4. //request_firmware()調用要求用戶空間爲內核定位並提供一個固件映像文件。  
  5. //firmware_p:指向firmware image的指針;  
  6. //name:firmware文件名稱;  
  7. //device:將要加載firmware的設備;  
  8. /** 
  9.  *      @name will be used as $FIRMWARE in the uevent environment and 
  10.  *      should be distinctive enough not to be confused with any other 
  11.  *      firmware image for this or any other device. 
  12.  **/  

把固件發送到設備後,需要使用下面的函數釋放內核中的結構:
  1. void release_firmware(struct firmware *fw);  

由於request_firmware()需要用戶空間的操作,因此在返回前他將保持睡眠狀態。如果當驅動程序必須要使用固件,而又不能進入睡眠時,可以使用下面的異步函數:
  1. int request_firmware_nowait(struct module *module, char *name, struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context));  

固件子系統使用sysfs和熱插拔機制工作。當調用request_firmware的時候,在/sys/class/firmware下降創建一個目錄,該設備使用設備名作爲它的目錄名。該目錄包含三個屬性:
loading:
     該屬性由負責加載固件的用戶空間進程設置爲1.當裝載過程完畢時,他將設置爲0.將loading設置爲-1,將終止固件裝載過程。
data:
    data是一個二進制屬性,用來接收固件數據。在設置完loading後,用戶空間進程將把固件寫入該屬性;
device:
    該屬性是到/sys/devices下相應入口的符號鏈接;


瞭解了基本的函數說明之後,這裏要了解利用系統的固件子系統來進行升級的步驟:
1、從內核層要發送請求到應用層,這裏就要用到我們所說的request_firmware來實現這個步驟;
2、從應用層copy固件到內核層,這裏就是應用層的編寫,簡單的open,write;
3、內核將接收到的固件通過固有的協議寫入硬件設備中;

這三個步驟就可以來完成此次的升級。如果只是簡單的使用的話,這裏可以有一個簡單的demo例子可以給你,你只要把這段代碼改成你的即可。
例子:
  1. retval = request_firmware(&cust_firmware, devices->fw_fname, dev);  
  2.     if (retval < 0) {  
  3.         pr_err("%s: %s request firmware failed(%d)\n", __func__,  
  4.                         devices->fw_fname, retval);  
  5.     } else {  
  6.         /* check and start upgrade */  
  7.         devices_upgrade_start();//這裏就是自己實現的函數,跟實際的物理硬件相關  
  8.                   
  9.         release_firmware(cust_firmware);  
  10.     }  

如果只是想使用的話,到了這裏就可以結束了,這就是內核中如何的升級固件的接口。如果想看看是如何實現的,可以向下繼續討論。當然,自己理解的也不一定對。
要分析,這裏我們就從使用的函數入口,看下內核中是如何來處理這種事情的。
request_firmware()
    ---->fw_get_builtin_firmware() //檢查是否在__start_builtin_fw和__end_builtin_fw之間有沒有指定的firmware
---->fw_create_instance() //通過device_create_bin_file來在sys下創建更新固件的接口
---->kobject_uevent() //通過kobject來上報uevent通知應用層需要更新firmware
---->wait_for_completion //等待應用加載萬firmware完成;

當然看到了這裏我們就可以瞭解到這裏只是簡單的將firmware的uevent事件上報給應用層來處理,這裏我們來追蹤下是誰在接收這個event事件。
這裏我們要看一下系統中的init.c文件。至於爲什麼要看這個文件,因爲我們從底層的request_firmware可以看到是通過kobject uevent來通知應用層,底層有需求,應用就需要滿足需求。
而uevent的監聽的建立則是在init進程中創建的。
  1. ueventd_main  
  2.     ---->device_init()  
  3.         ----->open_uevent_socket()  
  4.                 -----> s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); //創建一個socket來監聽KOBJECT_UEVENT;  
  5.     ---->handle_device_fd(); //調用uevent的NETLINK_KOBJECT_UEVENT的socket處理函數  
  6.         -----> parse_event(msg, &uevent);//解析上傳的uevent數據;  
  7.         ----->handle_firmware_event(); //開始處理firmware的event  
  8.             ----->process_firmware_event();//打開dev的attr結點  
  9.                 ---->load_firmware();//下載firmware  
  10.                     ---->write(loading_fd, "1", 1);  /* start transfer */  
  11.                     ---->寫入firmware  
  12.                     ----> if(!ret)  
  13.                             write(loading_fd, "0", 1);  /* successful end of transfer */  
  14.                         else  
  15.                             write(loading_fd, "-1", 2); /* abort transfer */  


到了這裏,可以看到,整個用戶空間的下載的過程就結束了。
下面來看下上面提到的在kernel中如何在sys中建立bin_attr的過程,這個過程其實是封裝在request_firmware中的。
當然你也可以採用下面的這個辦法來自己建立,當然這個過程就需要應用來主動發起,而不是驅動主動發起。
  1. static ssize_t firmware_write(struct kobject *kobj,  
  2.                 struct bin_attribute *bin_attr,  
  3.                 char *buf, loff_t pos, size_t size)  
  4. {  
  5.     struct device *dev = container_of(kobj, struct device, kobj);  
  6.     struct cust_devices *ts = dev_get_drvdata(dev);  
  7.     LOCK(ts->mutex);  
  8.     //處理firmware的寫操作,這個就涉及到相關的與具體的硬件來交互;  
  9.     UNLOCK(ts->mutex);  
  10.     return size;  
  11. }  
  12.   
  13.   
  14. static ssize_t firmware_read(struct kobject *kobj,  
  15.     struct bin_attribute *ba,  
  16.     char *buf, loff_t pos, size_t size)  
  17. {  
  18.     int count = 0;  
  19.     u8 reg_data;  
  20.     struct device *dev = container_of(kobj, struct device, kobj);  
  21.     struct cust_devices *ts = dev_get_drvdata(dev);  
  22.   
  23.   
  24.     LOCK(ts->mutex);  
  25.     //讀取底層的firmware mode之類的屬性,可以在這裏實現;  
  26.     UNLOCK(ts->mutex);  
  27.     return count;  
  28. }  
  29.   
  30.   
  31. static struct bin_attribute cust_devices_firmware = {  
  32.     .attr = {  
  33.         .name = "firmware",  
  34.         .mode = 0644,  
  35.     },  
  36.     .size = XXX, //是否可以超過PAGE_SIZE????  
  37.     .read = firmware_read,  
  38.     .write = firmware_write,  
  39. };  
  40. struct bin_attribute {  
  41.     struct attribute    attr;  
  42.     size_t          size;  
  43.     void            *private;  
  44.     ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,  
  45.             char *, loff_t, size_t);  
  46.     ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *,  
  47.              char *, loff_t, size_t);  
  48.     int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,  
  49.             struct vm_area_struct *vma);  
  50. };  
  51.   
  52.   
  53. if (sysfs_create_bin_file(&dev->kobj, &devices_firmware))  
  54.             printk(KERN_ERR "%s: unable to create file\n",  
  55.                 __func__);  


代碼到了這裏,可以結束了,底層也實現了,上層也知道從哪裏獲取了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章