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

在一般的使用中,可能会需要使用到升级固件这个功能,在linux的系统中提供了固件子系统这个设备模型来帮助快速的升级固件。

在这里先将linux中提供的常用的接口来说明下:
内核的固件接口:
[cpp]  view plain copy
  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.  **/  

把固件发送到设备后,需要使用下面的函数释放内核中的结构:
[cpp]  view plain copy
  1. void release_firmware(struct firmware *fw);  

由于request_firmware()需要用户空间的操作,因此在返回前他将保持睡眠状态。如果当驱动程序必须要使用固件,而又不能进入睡眠时,可以使用下面的异步函数:
[cpp]  view plain copy
  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例子可以给你,你只要把这段代码改成你的即可。
例子:
[cpp]  view plain copy
  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进程中创建的。
[cpp]  view plain copy
  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中的。
当然你也可以采用下面的这个办法来自己建立,当然这个过程就需要应用来主动发起,而不是驱动主动发起。
[cpp]  view plain copy
  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__);  


代码到了这里,可以结束了,底层也实现了,上层也知道从哪里获取了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值