文章目录
1.概述
request_firmware():将固件以二进制文件形式存储于文件系统之中,在内核启动后再从用户空间将固件传递至内核空间,内核空间解析固件获得文件数据,最后加载至硬件设备。requeset_firmware()必须等到文件系统挂载后才能得到用户空间的固件,否则会一直阻塞到可以获取固件
2.request_firmware
此函数为固件加载的API,函数原型为:
#include <linux/firmware.h>
int request_firmware(const struct firmware **fw, const char *name,struct device *device);
参数:
- fw:用于保存申请到的固件数据
- name:固件名字
- device:申请固件的设备
- 如果申请固件成功,函数返回0;申请固件失败,返回一个负数值(如-EINVAL、-EBUSY)。
2.1 struct firmware **fw
struct firmware结构体为,
struct firmware {
size_t size;
const u8 *data;
struct page **pages;
/* firmware loader private fields */
void *priv;
};
其中size为解析出来的固件的大小,data为解析出来的固件数据首地址,
2.2 char *name
上面说的固件名字,如“fw.bin"这样的字符串格式。实际上这些固件会存放在一个指定的目录下,需要指定的位置在目录/drivers/base/firmware_class.c
下
/* direct firmware loading support */
static char fw_path_para[256];
static const char * const fw_path[] = {
fw_path_para,
"/lib/firmware/updates/" UTS_RELEASE,
"/lib/firmware/updates",
"/lib/firmware/" UTS_RELEASE,
"/lib/firmware",
"/lib64/firmware",
"/mnt/update",
"/lib/firmware/image"
};
假如你的固件放置在vendor/firmware下,你只需要在fw_path中添加即可。如
static const char * const fw_path[] = {
fw_path_para,
"/lib/firmware/updates/" UTS_RELEASE,
"/lib/firmware/updates",
"/lib/firmware/" UTS_RELEASE,
"/lib/firmware",
"/lib64/firmware",
"/mnt/update",
"/vendor/firmware" //新增加
"/lib/firmware/image"
};
3.3 调用原理
在调用request_firmware()时,函数在 /sys/class/firmware 下创建一个以设备名为目录名的新目录,其中包含 3 个属性:
- loading:当固件加载时被置1,加载完毕被置0,如果被置-1则终止固件加载;
- data:内核获取固件接口,当loading被置1时,用户空间通过该属性接口传递固件至内核空间;
- device:符号链接,链接至/sys/devices/下相关设备目录。
当sysfs接口创建完毕,uevent会配合将固件通过sysfs节点写入内核
3.release_firmware
在固件加载成功之后,需要使用fw,函数原型为
void release_firmware(const struct firmware *fw);
4.使能固件升级宏
如果不使能下面的宏,会出现: Unknown symbol release_firmware 和: Unknown symbol request_firmware 的错误。
Device Drivers --->
Generic Driver Options --->
<*> Userspace firmware loading support`
或者直接在.config中配置
CONFIG_FW_LOADER=y
在内核中CONFIG_FW_LOADER
默认是关闭的,如果不使能这个宏,在申请fw时,会直接返回错误-EINVAL
// include/linux/firmware.h
#if defined(CONFIG_FW_LOADER) || (defined(CONFIG_FW_LOADER_MODULE) && defined(MODULE))
int request_firmware(const struct firmware **fw, const char *name,
struct device *device);
...
#else
static inline int request_firmware(const struct firmware **fw,
const char *name,
struct device *device)
{
return -EINVAL;
}
...
#endif
5.具体升级示例
5.1 添加升级路径
固件放置在vendor/firmware下,在fw_path中添加,固件名字fw.cfg
,将其push到vendor/firmware
下
static const char * const fw_path[] = {
fw_path_para,
"/lib/firmware/updates/" UTS_RELEASE,
"/lib/firmware/updates",
"/lib/firmware/" UTS_RELEASE,
"/lib/firmware",
"/lib64/firmware",
"/mnt/update",
"/vendor/firmware" //新增加
"/lib/firmware/image"
};
5.2 添加固件升级结点
request_firmware()函数如果在probe()中使用会一直阻塞至文件系统挂载获取固件,所以一般需要在驱动中将其封装为sysfs节点,以便在文件系统挂载后调用:
#define FIRMWARE_NAME "/vendor/firmware/fw.cfg"
struct data
{
struct work_struct work;
bool work_func_flags;
u8 *fw_data;
}
...
static int update_fw(struct data *data, const struct firmware *fw )
{
//取出fw->data进行升级操作
data->fw_data = (u8)fw->data;
return 0;
}
static void work_func(struct work_struct *work)
{
struct data *data =contain_of(work,struct data,data.work);
const struct firmware *fw = NULL;
/* 申请用户空间固件 */
ret = request_firmware(&fw, FIRMWARE_NAME, dev);
if (ret)
{
return -ENOENT;
goto out;
}
/* 升级固件至硬件设备 */
update_fw(fw);
out:
/* 释放firmware结构体 */
release_firmware(fw);
}
static void update_start_up()
{
if(!work_pending(data->work))
{
schedule_work(&data->work);
}
}
static ssize_t updata_fw_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
unsigned long value;
if (kstrtoul(buf, 10, &value))
return -EINVAL;
if (value == 1)
{
update_start_up();
}
return count;
}
// sys/devices/platform/update/load_fw
static DEVICE_ATTR(update_fw, S_IWUGO, NULL, update_fw_store);
...
static int update_probe(struct platform_device *pdev)
{
int ret;
struct data *data;
data = devm_kzlloc(data,sizeof(struct data),GFP_KERNEL)
data->work_func_flags = false;
platform_set_drvdata(pdev,data,)
/* 创建sysfs节点 */
ret = device_create_file(pdev, &dev_attr_update_fw);
if (ret)
{
return -EINVAL;
}
INIT_WORK(&work,work_func);
return 0;
}
static int update_remove(struct platform_device *pdev)
{
struct data *data = platform_get_drvdata(pdev);
if(data->work_func_flags)
{
cancel_work_sync(data->work);
}
}
...
6.异步方式升级固件
request_firmware是以同步的方式进行固件升级,如果要求进行升级时不上下文不进行睡眠操作,那么会用到异步升级API来加载固件,实际上其在调用工作队列的方式进行固件升级,在前面使用request_firmware时也用了工作队列实现:request_firmware_nowait
,函数原型为:
int request_firmware_nowait(
struct module *module, bool uevent,
const char *name, struct device *device, gfp_t gfp, void *context,
void (*cont)(const struct firmware *fw, void *context));
- module :模块名
- uevent :一般置为1
- name :固件名
- device :申请固件的设备结构体
- gfp :内核内存分配标志位,一般为GFP_KERNEL
- context:私有数据指针
- cont :回调函数
函数原型为:
int request_firmware_nowait(
struct module *module, bool uevent,
const char *name, struct device *device, gfp_t gfp, void *context,
void (*cont)(const struct firmware *fw, void *context))
{
struct firmware_work *fw_work;
fw_work = kzalloc(sizeof (struct firmware_work), gfp);
if (!fw_work)
return -ENOMEM;
fw_work->module = module;
fw_work->name = name;
fw_work->device = device;
fw_work->context = context;
fw_work->cont = cont;
fw_work->uevent = uevent;
if (!try_module_get(module)) {
kfree(fw_work);
return -EFAULT;
}
get_device(fw_work->device);
//初始化工作队列
INIT_WORK(&fw_work->work, request_firmware_work_func);
//调度工作队列
schedule_work(&fw_work->work);
return 0;
}
具体示例:
#define FIRMWARE_NAME "/vendor/firmware/fw.cfg"
...
void update_requset_fw_callback(const struct firmware *fw, void *context)
{
struct device *dev = context;
if (fw != NULL)
{
/* 加载固件至硬件设备 */
update_fw(fw, dev);
/* 释放firmware结构体 */
release_firmware(fw);
}
}
static ssize_t update_fw_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
unsigned long value;
const struct firmware *fw = NULL;
if (kstrtoul(buf, 10, &value))
{
return -EINVAL;
}
if (value == 1)
{
/* 申请用户空间固件 */
ret = request_firmware_nowait(THIS_MODULE, 1, FIRMWARE_NAME,
dev, GFP_KERNEL, dev, update_requset_fw_callback);
if (ret)
{
return -ENOENT;
}
}
return count;
}
...
参考:https://blog.csdn.net/zifehng/article/details/60321966