13.Linux驱动基础-request_firmware升级

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 "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

  • 4
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值