开源项目fwupd开发


前言

在linux系统上,固件升级基本上都离不开fwupd。之前做过的一个项目就是在linux系统上使用fwupd对ble蓝牙设备进行通信,但fwupd上使用ble蓝牙通信的插件很少,只有高通的工程师在开发而且代码还没有合并到主分支,官方文档也没有提供详细的接口说明和开发文档。在这篇文章里我尽量详细的介绍一下该怎么在fwupd中开发属于自己的插件。

fwupd源码地址:https://github.com/fwupd/fwupd
lvfs地址:https://fwupd.org/
fwupd插件开发文档:https://fwupd.github.io/libfwupdplugin/fwupdmgr.html#description


fwupd架构

fwupd主要由 lvfsfwupd 两部分组成,两者缺一不可。lvfs 主要是用来存储和管理固件包,并为 fwupd 提供固件网络下载。而 fwupd 是实际与硬件交互的,将从lvfs获取到的固件通过自己编写的流程烧录到硬件中。

开发新插件

我们先把代码拉下来看一下目录结构,我拉的是 https://github.com/fwupd/fwupd/tree/wip/d4s/bluez-example 这个带ble蓝牙demo分支的。进入到 fwupd/plugins 这个目录,我们开发新插件主要是在这个目录下。

一,编译fwupd

我们首先编译公版的fwupd,进入到虚拟环境后再去开发和调试。因为进入虚拟环境后输入的命令是我们自己编译后的fwupd,而不是系统自带的公版fwupd。
编译和调试的流程在这里给出链接(使用linux系统物理机,因为fwupd是直接与硬件交互的使用虚拟机可能中间会有隔离):https://github.com/fwupd/fwupd/blob/wip/d4s/bluez-example/docs/building.md

二,文件结构

我们打开 fwupd\plugins\audio-s5gen2 文件夹,逐个介绍讲解一下。

  1. .quirk文件 这个配置文件是用来识别我们的设备的,以 audio-s5gen2.quirk 文件为例。
    [BLUETOOTH\ALIAS_C5171]
    Plugin = audio_s5gen2
    ProxyGType = FuQcS5gen2BleDevice
    AudioS5gen2Gaia2VendorId = 0x000A
    AudioS5gen2Gaia3VendorId = 0x001D
    
    [BLUETOOTH\ALIAS_C5171] 是识别的名称,C5171是蓝牙名,更改成自己设备的蓝牙名后编译输入 fwupdtool get-devices 命令就可以识别到自己的设备了。也可以通过蓝牙的PID和VID去识别,[BLUETOOTH\VID_0000&PID_0000][BLUETOOTH\VID_0000] 都可以,效果是一样的。
    Plugin 是配置需要用到的插件,这里直接配置成文件夹的名称就好了。
    ProxyGType 是配置使用哪个类型去代理,因为一个插件的文件夹里面可能存在多个类型的设备,每个设备需要的控制代码不一致。
    剩下两个是自定义值,有需要的小伙伴可以自己看看源码这两个值的作用。
  2. .c .h .rs文件 这三个文件一般是关联的,.rs 文件主要是用来定义数据结构的,就是我们通信时使用的结构体可以在这里定义,fwupd会自动生成函数去创建,解析这个结构体的函数,就免去我们自己去处理数据结构的代码。具体使用可以参考实例文件。
  3. fu_xxx_plugin.c fu_xxx_plugin.h文件 这两个主要是管理和配置不同类型设备的文件。
  4. meson.build文件 就是编译的配置文件,这个大同小异,直接复制现有的插件,修改一下就好了。如果是使用蓝牙的插件可以加上下面的代码去做判断。
    if bluez.allowed()
    	# 配置项
    	xxx
    endif
    

三、代码结构

下面主要介绍一下最主要的代码结构和最主要的函数,其实就是关键的函数和配置关键的回调函数,我们实际开发可以直接参考现有的插件。

  1. fu-xxx-plugin.c, fu-xxx-plugin.h 头文件基本上都一样的,修改一下名字就好了。这里主要说一下 .c 文件的代码。
    /*
     * Copyright 2023 Denis Pynkin <denis.pynkin@collabora.com>
     *
     * SPDX-License-Identifier: LGPL-2.1-or-later
     */
    
    #include "config.h"
    
    #include "fu-audio-s5gen2-device.h"
    #include "fu-audio-s5gen2-firmware.h"
    #include "fu-audio-s5gen2-hid-device.h"
    #include "fu-audio-s5gen2-plugin.h"
    
    struct _FuAudioS5gen2Plugin {
    	FuPlugin parent_instance;
    };
    
    G_DEFINE_TYPE(FuAudioS5gen2Plugin, fu_audio_s5gen2_plugin, FU_TYPE_PLUGIN)
    
    static void
    fu_audio_s5gen2_plugin_init(FuAudioS5gen2Plugin *self)
    {
    }
    
    static void
    fu_audio_s5gen2_plugin_constructed(GObject *obj)
    {
    	FuPlugin *plugin = FU_PLUGIN(obj);
    	fu_plugin_add_device_gtype(plugin, FU_TYPE_QC_S5GEN2_HID_DEVICE);
    	fu_plugin_set_device_gtype_default(plugin, FU_TYPE_QC_S5GEN2_DEVICE);
    	fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_QC_S5GEN2_FIRMWARE);
    }
    
    static void
    fu_audio_s5gen2_plugin_class_init(FuAudioS5gen2PluginClass *klass)
    {
    	FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass);
    	plugin_class->constructed = fu_audio_s5gen2_plugin_constructed;
    }
    
    fu_audio_s5gen2_plugin_class_init 函数里面配置回调函数,具体有哪些可以看代码或者文档,这里只讲最主要的。plugin_class->constructed 这个是要加的,并且需要在回调函数里面使用 fu_plugin_add_device_gtype 添加设备的 gtype 否则无法识别设备,如果有多个 gtype 设备可以使用 fu_plugin_set_device_gtype_default 函数设置默认设备。在 .quirk 中配置的 gtype 要跟这里的一致,不然也无法识别。
  2. fu-xxx-device.c, fu-xxx-device.h 这个是单独控制某个设备的代码,头文件基本一致,也是只讲源文件部分。
    static void
    fu_qc_s5gen2_ble_device_class_init(FuQcS5gen2BleDeviceClass *klass)
    {
    	FuDeviceClass *device_class = FU_DEVICE_CLASS(klass);
    	GObjectClass *object_class = G_OBJECT_CLASS(klass);
    	object_class->finalize = fu_qc_s5gen2_ble_device_finalize;
    	device_class->to_string = fu_qc_s5gen2_ble_device_to_string;
    	device_class->probe = fu_qc_s5gen2_ble_device_probe;
    	device_class->set_quirk_kv = fu_qc_s5gen2_ble_device_set_quirk_kv;
    	// 还有个关键的函数,这里没有的
    	// device_class->write_firmware = fu_xxx_write_firmware;
    }
    
    其实最主要的就是 object_class->finalizedevice_class->probedevice_class->write_firmware 这三个。
    2.1 object_class->finalize 这个是释放资源,没什么好说的。
    2.2 device_class->probe 这个函数会预先调用,可以看一下这个实例里面这个函数做了什么,其实就使用 bluez 后端去建立ble蓝牙收发的通信,示例代码里面使用是的 notify 的方式获取设备回复的数据,然后将注册是handle保存起来,就可以通过 fu_io_channel_read_raw 函数去读数据了,写数据不需要注册,直接使用 fu_bluez_device_write 函数往特征服务里面写数据就可以了 。这里要主要一下如果使用的是 fedora 的系统需要使用 sudo setenforce 0 这个命令关闭一下 selinux,否则 notify 函数会注册失败。其他系统我不清楚,最好都关闭一下。
    2.3 device_class->write_firmware 这个虽然在示例里面没有写出来,但是这个也是一个重要的函数回调,这个函数是控制烧写流程的。这个函数的实现可以参考一下其他的插件,虽然用到的后端不一样(通信方式)但是获取lvfs上的固件和写固件的动作是一样的,只是把读写的API替换一下而已。
  3. fu-xxx-firmware.c, fu-xxx-firmware.h 其实这里还有专门做固件解析的头文件和源码,虽然上面的示例代码里面没有提供出来,这个做固件解析的跟 xxx-device.c xxx-device.hxxx-plugin.c xxx-plugin.h 结构是差不多的,也是那套流程。就是在 fu_xxx_firmware_class_init(FuXxxFirmwareClass *klass) 这里配置回调函数,然后在我们插件的 meson.build文件 里面添加编译。这里就不详细说了,如果需要知道使用流程可以参考现有的插件。
  4. meson.build 注意这里的是 plugin\meson.build 而不是我们插件里面的 meson.build 需要在 plugin\meson.build 里面添加上我们的插件,这样编译 fwupd 时才会将我们的插件编译进去。

基本的结构和流程就是这样了,后面就是漫长的调试流程了。可能文章讲的比较粗糙,有其他问题可以在评论区提出来,我尽量的回答。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值