芯片解决方案--SL8541e-OpenHarmony蓝牙适配分析及方案

一、OpenHarmony 蓝牙架构

 

 

  • APP 蓝牙应用程序,通过调用蓝牙接口(interface)实现应用程序的功能。

  • Bluetooth 蓝牙框架层,主要包括interface和services, interface负责向上层应用程序提供功能接口。services则负责interface接口和蓝牙协议栈的实现。

  • HDF 硬件驱动框架,包含了OpenHarmony定义的vendor interface以及厂商的vendor lib。

  • HardWare 蓝牙硬件设备,主要为内核态驱动。

 

二、OpenHarmony 蓝牙适配方法

蓝牙芯片厂商通常会提供hal层的vendor lib,OpenHarmony蓝牙的适配只需要将HDI接口对接到厂商的lib就可以从上到下打通蓝牙功能。由于芯片厂商vendor lib的接口可能与OpenHarmony Bluetooth HDI定义的接口不完全一致,需要增加适配层来进行接口的转换。

OpenHarmony HDI调用厂商lib的实现

 

js接口enableBluetooth()在RK3568上的实现:

  • 上层调用到HDI层的HdiInit函数时,会调用IHciInterface这个对外接口类的Init函数。而该函数实际的实现为HciInterfaceImpl::Init()。此时流程进到了driver层。

  • driver层定义了VendorInterface类作为与vendor lib间的接口。

    VendorInterface::Initialize函数用于动态加载厂商lib,并且获取到lib的接口保存到vendorInterface_中。这样HDI与厂商lib间的钩子函数就挂接上了。

  • 宏BT_VENDOR_INTERFACE_SYMBOL_NAME定义为"BLUETOOTH_VENDOR_LIB_INTERFACE",就是厂商lib对应so文件中定义的接口名字,在VendorInterface::Initialize函数中会调用对应的init和op函数,用来使能BLE。

    bool VendorInterface::Initialize(
        InitializeCompleteCallback initializeCompleteCallback, const ReceiveCallback &receiveCallback)
    {
        ...
        // -->加载 vendor lib,动态库的名字必须为libbt_vendor.z.so
        vendorHandle_ = dlopen(BT_VENDOR_NAME, RTLD_NOW);
        if (vendorHandle_ == nullptr) {
            HDF_LOGE("VendorInterface dlopen %{public}s failed, error code: %{public}s", BT_VENDOR_NAME, dlerror());
            return false;
        }
    
        // -->读取符号 BLUETOOTH_VENDOR_LIB_INTERFACE
        vendorInterface_ =
            reinterpret_cast<bt_vendor_interface_t *>(dlsym(vendorHandle_, BT_VENDOR_INTERFACE_SYMBOL_NAME));
        if (vendorInterface_ == nullptr) {
            HDF_LOGE("VendorInterface dlsym %{public}s failed.", BT_VENDOR_INTERFACE_SYMBOL_NAME);
            return false;
        }
    
        ...
    
        // -->调用vendor lib中的init函数,vendorCallbacks_为hci的callback函数
        int result = vendorInterface_->init(&vendorCallbacks_, address.data());
        if (result != 0) {
            HDF_LOGE("vendorInterface_->init failed.");
            return false;
        }
    
        // -->调用vendor lib中的op函数,opcode为BT_OP_POWER_ON
        result = vendorInterface_->op(bt_opcode_t::BT_OP_POWER_ON, nullptr);
        if (result != 0) {
            HDF_LOGE("vendorInterface_->op BT_OP_POWER_ON failed.");
            return false;
        }
    
        ...
    
        // --> 调用vendor lib中的op函数,opcode为BT_OP_INIT
        vendorInterface_->op(bt_opcode_t::BT_OP_INIT, nullptr);
    
        return true;
    }

挂接vendor lib接口

HDI层定义了vendor lib的接口,具体参考

/foundation/communication/bluetooth/services/bluetooth/hardware/include/bt_vendor_lib.h文件。BLE的适配也就是要将以下3处接口挂接上并实现。

  1. bt_vendor_interface_t结构体,定义了vendor lib对外的接口,其中包括3个函数: init, op, close,适配的主要工作就是实现这3个函数。

     typedef struct {
         /**
          * Set to sizeof(bt_vndor_interface_t)
          */
         size_t size;
    
         /**
          * Caller will open the interface and pass in the callback routines
          * to the implemenation of this interface.
          */
         int (*init)(const bt_vendor_callbacks_t* p_cb, unsigned char* local_bdaddr);
    
         /**
          * Vendor specific operations
          */
         int (*op)(bt_opcode_t opcode, void* param);
    
         /**
          * Closes the interface
          */
         void (*close)(void);
     } bt_vendor_interface_t;

    需要在vendor lib中定义bt_vendor_interface_t,以下为参考代码:

    // vendor lib接口定义
    const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE = {
       sizeof(bt_vendor_interface_t),
       init,
       op,
       cleanup };
  2. bt_vendor_callbacks_t是OpenHarmony提供给厂商lib调用的钩子函数,在调用bt_vendor_interface_t.init函数时,需要把bt_vendor_callbacks_t传递给厂商lib。具体请看VendorInterface::Initialize()函数。

/**
 * initialization callback.
 */
typedef void (*init_callback)(bt_op_result_t result);

/** 
 * call the callback to malloc a size of buf.
 */
typedef void* (*malloc_callback)(int size);

/**
 * call the callback to free buf
 */
typedef void (*free_callback)(void* buf);

/**
 *  hci command packet transmit callback
 *  Vendor lib calls cmd_xmit_cb function in order to send a HCI Command
 *  packet to BT Controller. 
 *
 *  The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of
 *  HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command
 *  packet.
 */
typedef size_t (*cmd_xmit_callback)(uint16_t opcode, void* p_buf);

typedef struct {
    /**
     * set to sizeof(bt_vendor_callbacks_t)
     */
    size_t size;

    /* notifies caller result of init request */
    init_callback init_cb;

    /* buffer allocation request */
    malloc_callback alloc;

    /* buffer free request */
    free_callback dealloc;

    /* hci command packet transmit request */
    cmd_xmit_callback xmit_cb;
} bt_vendor_callbacks_t;
  • init_cb 用于通知配置结果;

  • alloc 用于申请内存;

  • dealloc 用于释放内存;

  • xmit_cb 用于下发hci命令;

    OpenHarmony中这些钩子函数的定义

// vendor_interface.cpp

bt_vendor_callbacks_t VendorInterface::vendorCallbacks_ = {
    .size = sizeof(bt_vendor_callbacks_t),
    .init_cb = VendorInterface::OnInitCallback,
    .alloc = VendorInterface::OnMallocCallback,
    .dealloc = VendorInterface::OnFreeCallback,
    .xmit_cb = VendorInterface::OnCmdXmitCallback,
};
  1. 各op操作的实现

op函数要对以下各op code做相应的处理。

/**
 * BT vendor lib cmd.
 */
typedef enum {
    /**
     * Power on the BT Controller.
     * @return 0 if success.
     */
    BT_OP_POWER_ON,

    /**
     * Power off the BT Controller.
     * @return 0 if success.
     */
    BT_OP_POWER_OFF,

    /**
     * Establish hci channels. it will be called after BT_OP_POWER_ON.
     * @param int (*)[HCI_MAX_CHANNEL].
     * @return fd count.
     */
    BT_OP_HCI_CHANNEL_OPEN,

    /**
     * Close all the hci channels which is opened.
     */
    BT_OP_HCI_CHANNEL_CLOSE,

    /**
     * initialization the BT Controller. it will be called after BT_OP_HCI_CHANNEL_OPEN.
     * Controller Must call init_cb to notify the host once it has been done.
     */
    BT_OP_INIT,

    /**
     * Get the LPM idle timeout in milliseconds.
     * @param (uint_32 *)milliseconds, btc will return the value of lpm timer.
     * @return 0 if success.
     */
    BT_OP_GET_LPM_TIMER,

    /**
     * Enable LPM mode on BT Controller.
     */
    BT_OP_LPM_ENABLE,

    /**
     * Disable LPM mode on BT Controller.
     */
    BT_OP_LPM_DISABLE,

    /**
     * Wakeup lock the BTC.
     */
    BT_OP_WAKEUP_LOCK,

    /**
     * Wakeup unlock the BTC.
     */
    BT_OP_WAKEUP_UNLOCK,

    /**
     * transmit event response to vendor lib.
     * @param (void *)buf, struct of HC_BT_HDR.
     */
    BT_OP_EVENT_CALLBACK
} bt_opcode_t;
  1. 厂商lib文件名字 因为HDI在加载厂商lib时是写死了so的名字,因此要保证厂商提供的lib文件名字为libbt_vendor.z.so。

三、8541E 蓝牙开源lib接口适配分析

bt_vendor_interface_t 结构体

typedef struct {
    size_t size;
    int (*init)(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr);
    int (*op)(bt_vendor_opcode_t opcode, void *param);
    void (*cleanup)(void);
} bt_vendor_interface_t;

8541E定义的bt_vendor_interface_t结构体中定义的三个函数原型与OpenHarmony一致,只是关闭lib库的cleanup函数与OpenHarmony中的close函数名不同,适配时只需要封装转换函数即可。

bt_vendor_callbacks_t 结构体

此结构体是由协议层实现的函数,通过钩子函数的形式传递到底层厂商lib中进行调用。其中xmit_cb是用于下发hci命令的。

/* vendor initialize/configuration callback */
typedef void (*cfg_result_cb)(bt_vendor_op_result_t result);

/* datapath buffer allocation callback (callout)
 *
 *  Vendor Lib needs to request a buffer through the alloc callout function
 *  from HCI lib if the buffer is for constructing a HCI Command packet which
 *  will be sent through xmit_cb to BT Controller.
 *
 *  For each buffer allocation, the requested size needs to be big enough to
 *  accommodate the below header plus a complete HCI packet --
 *      typedef struct
 *      {
 *          uint16_t       event;
 *          uint16_t       len;
 *          uint16_t       offset;
 *          uint16_t       layer_specific;
 *       } HC_BT_HDR;
 *
 *  HCI lib returns a pointer to the buffer where Vendor lib should use to
 *  construct a HCI command packet as below format:
 *
 *  --------------------------------------------
 *  |   HC_BT_HDR   |   HCI command            |
 *  --------------------------------------------
 *  where
 *      HC_BT_HDR.event = 0x2000;
 *      HC_BT_HDR.len = length of HCI command;
 *      HC_BT_HDR.offset = 0;
 *      HC_BT_HDR.layer_specific = 0;
 *  For example, a HCI_RESET Command will be formed as
 *  ------------------------
 *  | HCI_BT_HDR  |03|0c|00|
 *  ------------------------
 *  with
 *      HC_BT_HDR.event = 0x2000;
 *      HC_BT_HDR.len = 3;
 *      HC_BT_HDR.offset = 0;
 *      HC_BT_HDR.layer_specific = 0;
 */
typedef void* (*malloc_cb)(int size);


/* datapath buffer deallocation callback (callout) */
typedef void (*mdealloc_cb)(void *p_buf);

/* define callback of the cmd_xmit_cb
 *
 *  The callback function which HCI lib will call with the return of command
 *  complete packet. Vendor lib is responsible for releasing the buffer passed
 *  in at the p_mem parameter by calling dealloc callout function.
 */
typedef void (*tINIT_CMD_CBACK)(void *p_mem);


/* hci command packet transmit callbac (callout)
 *
 *  Vendor lib calls xmit_cb callout function in order to send a HCI Command
 *  packet to BT Controller. The buffer carrying HCI Command packet content
 *  needs to be first allocated through the alloc callout function.
 *  HCI lib will release the buffer for Vendor lib once it has delivered the
 *  packet content to BT Controller.
 *
 *  Vendor lib needs also provide a callback function (p_cback) which HCI lib
 *  will call with the return of command complete packet.
 * 
 *  The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of
 *  HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command
 *  packet. 
*/
typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void *p_buf,
                               tINT_CMD_CBACK p_cback);

typedef void (*cfg_a2dp_cb)(bt_vendor_op_result_t result, bt_vendor_opcode_t op,
                            uint8_t bta_av_handle);

typedef struct {
    size_t size;

    /* notifise caller result of firmward configuration request */
    cfg_result_cb fwcfg_cb;

    /* notifies caller result of sco configuration request */
    cfg_result_cb scocfg_cb;

    /* notifies caller result of lpm enable/disable */
    cfg_result_cb lpm_cb;

    /* notifies the result of codec setting */
    cfg_result_cb audio_state_cb;

    /* buffer allocation request */
    malloc_cb alloc;

    /* buffer deallocation request */
    mdealloc_cb dealloc;

    /* hci command packet transmit request */
    cmd_xmit_cb xmit_cb;

    /* notifies caller completion of epilog process */
    cfg_result_cb epilog_cb;

    /* notifies status of a2dp offload cmd's */
    cfg_a2dp_cb a2dp_offload_cb;
} bt_vendor_callbacks_t;

下图为二者定义的bt_vendor_callbacks_t的差异

 

  • OpenHarmony定义bt_vendor_callbacks_t中的init_cb与854d1E厂商lib中bt_vendor_callbacks_t的fwcfg_cb作用相同,可以直接适配。

  • OpenHarmony与厂商xmit_cb定义的作用相同,但是函数原型不同,厂商lib的xmit_cb定义中多了tINT_CMD_CBACK p_cback参数。需要在适配层实现一个与厂商xmit_cb原型一致的适配函数,其中调用OpenHarmony定义的xmit_cb,这样来实现xmit_cb的功能。

    xmit_cb函数的适配包括以下:

    1、p_cback的适配

    p_cback是厂商lib提供的函数,用于在发送完hci command后,处理接收到command complete event的。

    OpenHarmony定义的xmit_cb没有p_cback参数,而处理收到的cmd complete event是通过op BT_OP_EVENT_CALLBACK通知厂商lib的。

    适配方法:

    适配层实现一个与厂商xmit_cb原型一致的函数adapter_xmit_cb,其中调用OpenHarmony定义的xmit_cb。

    并且adapter_xmit_cb中保存hci command值以及p_cback,在OpenHarmony HDI调用op BT_OP_EVENT_CALLBACK时根据hci command调用对应的p_cback函数。

    2、p_cback钩子函数参数p_mem的适配

    p_cback的参数p_mem是command complete event报文的buffer,根据厂商lib定义的说明会在p_cback函数中释放这个buffer。

    而OpenHarmony框架上收到command complete event时,会调用vendorInterface_.op函数,参数op code为BT_OP_EVENT_CALLBACK,参数buff为hci event报文数据,而buff是由OpenHarmony HDI负责释放。这里需要处理这种重复释放的情况。

    适配方法:在op BT_OP_EVENT_CALLBACK针对command complete event的处理中,先申请与buff相同大小的内存并拷贝buff的内容,再根据command调用保存的厂商p_cback钩子函数,并将新申请的内存传递给p_cback函数由其处理并释放。而传递给op函数的buff则由OpenHarmony HDI来释放。

     

  • 其他8541E独有的callback函数scocfg_cb、lpm_cb、audio_state_cb、epilog_cb、a2dp_offload_cb都是用于通知配置结果,只单独实现简单的函数不做任何处理即可。

op code

OpenHarmony的bt_opcode_t与厂商的bt_vendor_opcode_t的差异不大,只是在调用的形式上有些差异,可以通过适配层的转换函数进行适配。

  • OpenHarmony的BT_OP_PORER_ON和BT_OP_POWER_OFF与厂商的BT_VND_OP_POWER_CTRL

     

  • OpenHarmony的BT_OP_GET_LPM_TIMER与厂商的BT_VND_OP_GET_LPM_IDLE_TIMEOUT

     

  • OpenHarmony的BT_OP_LPM_ENABLE和BT_OP_LPM_DISABLE与厂商的BT_VND_OP_LPM_SET_MODE

     

  • OpenHarmony的BT_OP_WAKEUP_LOCK和BT_OP_WAKEUP_UNLOCK与厂商的BT_VND_OP_LPM_WAKE_SET_STATE

     

  • OpenHarmony的BT_OP_HCI_CHANNEL_OPEN与厂商的BT_VND_OP_USERIAL_OPEN

     

  • OpenHarmony的BT_OP_HCI_CHANNEL_CLOSE与厂商的BT_VND_OP_USERIAL_CLOSE

     

  • OpenHarmony的BT_OP_INIT与厂商的BT_VND_OP_FW_CFG

     

  • OpenHarmony的BT_OP_EVENT_CALLBACK与厂商的BT_VND_OP_EVENT_CALLBACK

    OpenHarmony BT_OP_EVENT_CALLBACK是用于处理收到的cmd complete event(0x0E)和vendor specific event(0xFF)事件。

    而8541E厂商的BT_VND_OP_EVENT_CALLBACK是用于调试,属于厂商自定义的。因此在适配时分别对二个event进行处理:

    1、cmd complete event调用适配层保存的厂商钩子函数p_cback。

    在厂商调用xmit_cb时,适配层需要根据不同的command保存对应的p_cback钩子函数。

    2、vendor specific event调用厂商的op BT_VND_OP_EVENT_CALLBACK;

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐