本文共 4392 字,大约阅读时间需要 14 分钟。
在一般的使用中,可能会需要使用到升级固件这个功能,在linux的系统中提供了固件子系统这个设备模型来帮助快速的升级固件。 在这里先将linux中提供的常用的接口来说明下: 内核的固件接口: - #include <linux/firmware.h>
- int request_firmware(const struct firmware **firmware_p, const char *name,
- struct device *device)
-
-
-
-
-
-
-
-
-
把固件发送到设备后,需要使用下面的函数释放内核中的结构: - void release_firmware(struct firmware *fw);
由于request_firmware()需要用户空间的操作,因此在返回前他将保持睡眠状态。如果当驱动程序必须要使用固件,而又不能进入睡眠时,可以使用下面的异步函数: - 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例子可以给你,你只要把这段代码改成你的即可。 例子: - 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 {
-
- 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进程中创建的。 - ueventd_main
- ---->device_init()
- ----->open_uevent_socket()
- -----> s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
- ---->handle_device_fd();
- -----> parse_event(msg, &uevent);
- ----->handle_firmware_event();
- ----->process_firmware_event();
- ---->load_firmware();
- ---->write(loading_fd, "1", 1);
- ---->写入firmware
- ----> if(!ret)
- write(loading_fd, "0", 1);
- else
- write(loading_fd, "-1", 2);
到了这里,可以看到,整个用户空间的下载的过程就结束了。 下面来看下上面提到的在kernel中如何在sys中建立bin_attr的过程,这个过程其实是封装在request_firmware中的。 当然你也可以采用下面的这个办法来自己建立,当然这个过程就需要应用来主动发起,而不是驱动主动发起。 - 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);
-
- 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);
-
- UNLOCK(ts->mutex);
- return count;
- }
-
-
- static struct bin_attribute cust_devices_firmware = {
- .attr = {
- .name = "firmware",
- .mode = 0644,
- },
- .size = XXX,
- .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__);
代码到了这里,可以结束了,底层也实现了,上层也知道从哪里获取了。 转载地址:http://ibjqi.baihongyu.com/