Linux驱动基础-request_firmware升级

本文详细解释了Linux内核中的request_firmware函数,用于加载固件并管理升级过程。介绍了structfirmware结构、固件命名、文件路径设置、异步升级方法以及sysfs接口的应用。
摘要由CSDN通过智能技术生成

文章目录
1.概述
2.request_firmware
2.1 struct firmware **fw
2.2 char *name
3.3 调用原理
3.release_firmware
4.使能固件升级宏
5.具体升级示例
5.1 添加升级路径
5.2 添加固件升级结点
6.异步方式升级固件
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);

1
2
3
4
参数:

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;
};

1
2
3
4
5
6
7
8
9
其中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"
};
1
2
3
4
5
6
7
8
9
10
11
12
假如你的固件放置在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"
};
1
2
3
4
5
6
7
8
9
10
11
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);
1
4.使能固件升级宏
如果不使能下面的宏,会出现: Unknown symbol release_firmware 和: Unknown symbol request_firmware 的错误。

 Device Drivers  --->

          Generic Driver Options  --->

             <*> Userspace firmware loading support`
1
2
3
4
5
或者直接在.config中配置

CONFIG_FW_LOADER=y
1
在内核中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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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"
};
1
2
3
4
5
6
7
8
9
10
11
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);
    }
}
...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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));

1
2
3
4
5
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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
具体示例:

#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;
}
...


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
参考:https://blog.csdn.net/zifehng/article/details/60321966
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/weixin_43824344/article/details/120617657

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值