Recently, OH4 BT userspace encountered an issue where opening BT failed, and the driver needs to check what’s going on. However, nobody is checking this now. With no chice, I made a strong entrace. I specialize in BSP, but I haven’t dealt with BT that is another tech area, right?

BT Architecture

Firstly, there’s no introduction in official OpenHarmony website, I’m speechless…

There’s only one in code: https://gitee.com/openharmony/communication_bluetooth, but that is for foundation layer, I’m focused on driver.

However, I’m lucky, I found one from laval community, I change a little as below:

            ______________
|_BT services_|
|
------------------|----------------------
____________
|__BT HDI__|
|
------------------|----------------------bt_vendor_interface_t
_________|____________
|_vendor lib adapter_|
|
_______________
|__vendor lib__|
|
==================|=======================
_____|_________
kernel |__BT Driver__|

ok, I need to check BT HDI and below. The code about BT service is in foundation, BT HDI is in drivers/peripheral/bluetooth, check vendor lib adapter/vendor lib under vendor side such as device or vendor directory.

Troubleshooting

Compared to the normal log, I found the behavior to vendor lib cmd BT_OP_EVENT_CALLBACK is missing.

Definition is in drivers/peripheral/bluetooth/hci/hdi_service/implement/ohos_bt_vendor_lib.h:

/**
* BT vendor lib cmd.
*/
typedef enum {
[...]
/**
* transmit event response to vendor lib.
* @param (void *)buf, struct of HC_BT_HDR.
*/
BT_OP_EVENT_CALLBACK
} BtOpcodeT;

This cmd is called by VendorInterface::OnEventReceived(), ok, this article is just analyzing the calling relationship to this in BT HDI, of course see the mountain only since I am new to this area.

Analysis

Firstly, let me check struct bt_vendor_interface_t, which is defined in foundation, located at communication/bluetooth_service/services/bluetooth/hardware/include/bt_vendor_lib.h:

/**
* Bluetooth Host/Controller VENDOR Interface
*/
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;

init and op are all in the function VendorInterface::Initialize():

VendorInterface::Initialize()
|-> init()
|-> op(BT_OP_POWER_ON,)
|-> WatchHciChannel()
|-> watcher_.Start()
|-> op(BT_OP_INIT,)

There’s a watcher_ in it:

bool VendorInterface::Initialize(
[...]
if (!WatchHciChannel(receiveCallback)) { //tj: here
return false;
}

if (!watcher_.Start()) { //tj: here
HDF_LOGE("watcher start failed.");
return false;
}
[...]
}

Check WatchHciChannel() first:

bool VendorInterface::WatchHciChannel(const ReceiveCallback &receiveCallback)
{
int channel[HCI_MAX_CHANNEL] = {0};
int channelCount = vendorInterface_->op(BtOpcodeT::BT_OP_HCI_CHANNEL_OPEN, channel);
if (channelCount < 1 || channelCount > HCI_MAX_CHANNEL) {
HDF_LOGE("vendorInterface_->op BT_OP_HCI_CHANNEL_OPEN failed ret:%d.", channelCount);
return false;
}

if (channelCount == 1) {
auto h4 = std::make_shared<Hci::H4Protocol>(channel[0],
receiveCallback.onAclReceive,
receiveCallback.onScoReceive,
std::bind(&VendorInterface::OnEventReceived, this, std::placeholders::_1));
watcher_.AddFdToWatcher(channel[0], std::bind(&Hci::H4Protocol::ReadData, h4, std::placeholders::_1)); //tj:here
hci_ = h4;
}

channelCount == 1 is what I encountered, let me check the action first then h4 definition:

bool HciWatcher::AddFdToWatcher(int fd, HciDataCallback callback)
{
std::lock_guard<std::mutex> lock(fdsMutex_);
fds_[fd] = callback; //tj: here
ThreadWakeup();
return true;
}

fds_:

51     std::map<int, HciDataCallback> fds_;

HciWatcher::Start() would start a thread of WatcherThread():

106 void HciWatcher::WatcherThread()
107 {
108 fd_set readFds;
109 int nfds;
110 timeval *timeout = nullptr;
111
112 while (running_) {
[...]
145 } else {
146 if (FD_ISSET(wakeupPipe_[0], &readFds)) {
147 uint8_t buff;
148 TEMP_FAILURE_RETRY(read(wakeupPipe_[0], &buff, sizeof(buff)));
149 }
150 std::lock_guard<std::mutex> lock(fdsMutex_);
151 for (auto &&fd : fds_) {
152 if (FD_ISSET(fd.first, &readFds)) {
153 fd.second(fd.first); //tj: here
154 }
155 }
156 }
157 }
158 }

aha, check line 153, fd.second is the callback of AddFdToWatcher(), fd.first is just fd, and the usage:

AddFdToWatcher(channel[0], std::bind(&Hci::H4Protocol::ReadData, h4, std::placeholders::_1));

so, HciWatcher::WatcherThread would call Hci::H4Protocol::ReadData.

H4Protocol::ReadData -> H4Protocol::PacketCallback -> onEventReceive_(hciPacket_)

What’s the definition to onEventReceive_()? go back to check WatchHciChannel():

bool VendorInterface::WatchHciChannel(const ReceiveCallback &receiveCallback)
{
[...]
auto h4 = std::make_shared<Hci::H4Protocol>(channel[0],
receiveCallback.onAclReceive,
receiveCallback.onScoReceive,
std::bind(&VendorInterface::OnEventReceived, this, std::placeholders::_1));
H4Protocol::H4Protocol(
int fd, HciDataCallback onAclReceive, HciDataCallback onScoReceive, HciDataCallback onEventReceive)
: hciFd_(fd), onAclReceive_(onAclReceive), onScoReceive_(onScoReceive), onEventReceive_(onEventReceive)
{}

So, onEventReceive_(hciPacket_) is calling VendorInterface::OnEventReceived().

Ok, let me summarize the calling stack:

_________________________________  ____________________
|_VendorInterface::Initialize()_| |_OnEventReceived()_|
| /|\
_________________\|/______________________ |
|_HciWatcher::Start() -> WatcherThread()_| |
| |
_______________\|/________ |
|_H4Protocol::ReadData()_| |
|_________________|

Well done.

References