在一般的使用中,可能會需要使用到升級固件這個功能,在linux的系統中提供了固件子系統這個設備模型來幫助快速的升級固件。
在這裏先將linux中提供的常用的接口來說明下:
內核的固件接口:
把固件發送到設備後,需要使用下面的函數釋放內核中的結構:
由於request_firmware()需要用戶空間的操作,因此在返回前他將保持睡眠狀態。如果當驅動程序必須要使用固件,而又不能進入睡眠時,可以使用下面的異步函數:
固件子系統使用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例子可以給你,你只要把這段代碼改成你的即可。
例子:
如果只是想使用的話,到了這裏就可以結束了,這就是內核中如何的升級固件的接口。如果想看看是如何實現的,可以向下繼續討論。當然,自己理解的也不一定對。
要分析,這裏我們就從使用的函數入口,看下內核中是如何來處理這種事情的。
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進程中創建的。
到了這裏,可以看到,整個用戶空間的下載的過程就結束了。
下面來看下上面提到的在kernel中如何在sys中建立bin_attr的過程,這個過程其實是封裝在request_firmware中的。
當然你也可以採用下面的這個辦法來自己建立,當然這個過程就需要應用來主動發起,而不是驅動主動發起。
代碼到了這裏,可以結束了,底層也實現了,上層也知道從哪裏獲取了。
在這裏先將linux中提供的常用的接口來說明下:
內核的固件接口:
[cpp] view
plaincopy
- #include <linux/firmware.h>
- int request_firmware(const struct firmware **firmware_p, const char *name,
- struct device *device)
- //request_firmware()調用要求用戶空間爲內核定位並提供一個固件映像文件。
- //firmware_p:指向firmware image的指針;
- //name:firmware文件名稱;
- //device:將要加載firmware的設備;
- /**
- * @name will be used as $FIRMWARE in the uevent environment and
- * should be distinctive enough not to be confused with any other
- * firmware image for this or any other device.
- **/
把固件發送到設備後,需要使用下面的函數釋放內核中的結構:
[cpp] view
plaincopy
- void release_firmware(struct firmware *fw);
由於request_firmware()需要用戶空間的操作,因此在返回前他將保持睡眠狀態。如果當驅動程序必須要使用固件,而又不能進入睡眠時,可以使用下面的異步函數:
[cpp] view
plaincopy
- 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例子可以給你,你只要把這段代碼改成你的即可。
例子:
[cpp] view
plaincopy
- retval = request_firmware(&cust_firmware, devices->fw_fname, dev);
- if (retval < 0) {
- pr_err("%s: %s request firmware failed(%d)\n", __func__,
- devices->fw_fname, retval);
- } else {
- /* check and start upgrade */
- devices_upgrade_start();//這裏就是自己實現的函數,跟實際的物理硬件相關
- release_firmware(cust_firmware);
- }
如果只是想使用的話,到了這裏就可以結束了,這就是內核中如何的升級固件的接口。如果想看看是如何實現的,可以向下繼續討論。當然,自己理解的也不一定對。
要分析,這裏我們就從使用的函數入口,看下內核中是如何來處理這種事情的。
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進程中創建的。
[cpp] view
plaincopy
- ueventd_main
- ---->device_init()
- ----->open_uevent_socket()
- -----> s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); //創建一個socket來監聽KOBJECT_UEVENT;
- ---->handle_device_fd(); //調用uevent的NETLINK_KOBJECT_UEVENT的socket處理函數
- -----> parse_event(msg, &uevent);//解析上傳的uevent數據;
- ----->handle_firmware_event(); //開始處理firmware的event
- ----->process_firmware_event();//打開dev的attr結點
- ---->load_firmware();//下載firmware
- ---->write(loading_fd, "1", 1); /* start transfer */
- ---->寫入firmware
- ----> if(!ret)
- write(loading_fd, "0", 1); /* successful end of transfer */
- else
- write(loading_fd, "-1", 2); /* abort transfer */
到了這裏,可以看到,整個用戶空間的下載的過程就結束了。
下面來看下上面提到的在kernel中如何在sys中建立bin_attr的過程,這個過程其實是封裝在request_firmware中的。
當然你也可以採用下面的這個辦法來自己建立,當然這個過程就需要應用來主動發起,而不是驅動主動發起。
[cpp] view
plaincopy
- static ssize_t firmware_write(struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
- {
- struct device *dev = container_of(kobj, struct device, kobj);
- struct cust_devices *ts = dev_get_drvdata(dev);
- LOCK(ts->mutex);
- //處理firmware的寫操作,這個就涉及到相關的與具體的硬件來交互;
- UNLOCK(ts->mutex);
- return size;
- }
- static ssize_t firmware_read(struct kobject *kobj,
- struct bin_attribute *ba,
- char *buf, loff_t pos, size_t size)
- {
- int count = 0;
- u8 reg_data;
- struct device *dev = container_of(kobj, struct device, kobj);
- struct cust_devices *ts = dev_get_drvdata(dev);
- LOCK(ts->mutex);
- //讀取底層的firmware mode之類的屬性,可以在這裏實現;
- UNLOCK(ts->mutex);
- return count;
- }
- static struct bin_attribute cust_devices_firmware = {
- .attr = {
- .name = "firmware",
- .mode = 0644,
- },
- .size = XXX, //是否可以超過PAGE_SIZE????
- .read = firmware_read,
- .write = firmware_write,
- };
- struct bin_attribute {
- struct attribute attr;
- size_t size;
- void *private;
- ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,
- char *, loff_t, size_t);
- ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *,
- char *, loff_t, size_t);
- int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,
- struct vm_area_struct *vma);
- };
- if (sysfs_create_bin_file(&dev->kobj, &devices_firmware))
- printk(KERN_ERR "%s: unable to create file\n",
- __func__);
代碼到了這裏,可以結束了,底層也實現了,上層也知道從哪裏獲取了。