add nearlink driver

Signed-off-by: lilong32 <lilong32@huawei.com>
This commit is contained in:
lilong32
2026-07-21 21:34:53 +08:00
parent d85d3ac2ac
commit 9beb4a79bb
48 changed files with 5184 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
# NearLink<a name="ZH-CN_TOPIC_0000001148577119"></a>
- [Introduction](#section11660541593)
- [Directory Structure](#section161941989596)
- [Constraints](#section119744591305)
- [Usage](#section1312121216216)
- [DLI APIs](#section129654513264)
- [Repositories Involved](#section1371113476307)
## Introduction<a name="section11660541593"></a>
The NearLink driver module provides APIs for accessing and using the NearLink short-range communication protocol, including DLI initialization, data sending and receiving, and closing the NearLink DLI.
## Directory Structure<a name="section161941989596"></a>
```
/foundation/drivers/peripheral/nearlink
├── bundle.json # Component description and dependency configuration
├── drivers_peripheral_nearlink.gni # Build feature parameter configuration
├── dli # DLI module
│ └── test # DLI test cases
└── LICENSE # Copyright statement
└── bundle.json # Component description file
```
## Constraints<a name="section119744591305"></a>
The NearLink driver module is written in C++ and based on the DLI framework. It supports the standard system only.
## Usage<a name="section1312121216216"></a>
NearLink DLI APIs are provided. The DLI APIs include initialization, sending and receiving DLI packets, and close.
### DLI APIs<a name="section129654513264"></a>
- Initialize the NearLink HAL:
```
/* Initialize the NearLink HAL and register the callback. */
int32_t SleHalInit(const sptr<ISleHciCallback>& callbackObj);
```
- Send a packet:
```
/* Send a packet to the chip. */
int32_t SleSendHciPacket(const std::vector<uint8_t>& data);
```
- Receive a packet:
```
/* Receive a packet from the chip. */
int32_t hciPacketReceived(uint32_t type, const std::vector<uint8_t> &data);
```
- Close the NearLink DLI:
```
/* Close the NearLink DLI and release resources. */
int32_t Close();
```
## Repositories Involved<a name="section1371113476307"></a>
drivers\_peripheral\_nearlink
+69
View File
@@ -0,0 +1,69 @@
# 星闪组件<a name="ZH-CN_TOPIC_0000001148577119"></a>
- [简介](#section11660541593)
- [目录](#section161941989596)
- [约束](#section119744591305)
- [说明](#section1312121216216)
- [DLI接口使用说明](#section129654513264)
- [相关仓](#section1371113476307)
## 简介<a name="section11660541593"></a>
星闪(NearLink)驱动组件为设备提供接入与使用星闪短距通信协议的相关接口,包括DLI初始化,数据收发以及关闭星闪DLI等。
## 目录<a name="section161941989596"></a>
```
/foundation/drivers/peripheral/nearlink
├── bundle.json # 组件描述及依赖配置
├── drivers_peripheral_nearlink.gni # 编译特性参数配置
├── dli # DLI模块
│ └── test # DLI测试用例
└── LICENSE # 版权声明文件
└── bundle.json # 部件描述文件
```
## 约束<a name="section119744591305"></a>
星闪驱动组件使用C++语言编写,基于DLI框架,仅支持标准系统。
## 说明<a name="section1312121216216"></a>
目前提供星闪DLI接口,DLI接口包括初始化、发送接收DLI数据包、关闭等功能。
### DLI接口使用说明<a name="section129654513264"></a>
- 初始化星闪HAL
```
/* 初始化星闪HAL,注册回调 */
int32_t SleHalInit(const sptr<ISleHciCallback>& callbackObj);
```
- 发送数据包:
```
/* 向芯片发送数据包 */
int32_t SleSendHciPacket(const std::vector<uint8_t>& data);
```
- 接收数据包:
```
/* 接收来自芯片的数据包 */
int32_t hciPacketReceived(uint32_t type, const std::vector<uint8_t> &data);
```
- 关闭星闪DLI
```
/* 关闭星闪DLI,释放资源 */
int32_t Close();
```
## 相关仓<a name="section1371113476307"></a>
drivers\_peripheral\_nearlink
+47
View File
@@ -0,0 +1,47 @@
{
"name": "@ohos/drivers_peripheral_nearlink",
"description": "nearlink device driver",
"version": "4.1",
"license": "Apache License 2.0",
"publishAs": "code-segment",
"segment": {
"destPath": "drivers/peripheral/nearlink"
},
"dirs": {},
"scripts": {},
"component": {
"name": "drivers_peripheral_nearlink",
"subsystem": "hdf",
"features": [
"drivers_peripheral_nearlink_feature_support_send_disable_hci",
"drivers_peripheral_nearlink_reload_address"
],
"adapted_system_type": ["standard"],
"rom": "",
"ram": "",
"deps": {
"components": [
"c_utils",
"hdf_core",
"hilog",
"drivers_interface_nearlink",
"samgr",
"ipc",
"ffrt",
"bounds_checking_function",
"init"
],
"third_party": [
]
},
"build": {
"sub_component": [
"//drivers/peripheral/nearlink/dli:nearlink_entry",
"//drivers/peripheral/nearlink/off_find:off_find_entry"
],
"test": [
"//drivers/peripheral/nearlink/dli/test:nearlink_hdi_test"
]
}
}
}
+22
View File
@@ -0,0 +1,22 @@
# Copyright (C) 2026 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if (defined(ohos_lite)) {
group("nearlink_entry") {
deps = []
}
} else {
group("nearlink_entry") {
deps = [ "dli_service:hdf_nearlink_service" ]
}
}
+164
View File
@@ -0,0 +1,164 @@
# Copyright (C) 2026 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/config/components/hdi/hdi.gni")
import("../../drivers_peripheral_nearlink.gni")
ohos_shared_library("libsle_hci_interface_service_1.1") {
sanitize = {
cfi = true # Enable/disable control flow integrity detection
boundary_sanitize = true # Enable boundary san detection
cfi_cross_dso = true # Cross-SO CFI Checks
integer_overflow = true # Enable integer overflow detection
ubsan = true # Enable some Ubsan options
debug = false
}
branch_protector_ret = "pac_ret"
cflags = [
"-Os",
"-fdata-sections",
"-ffunction-sections",
"-fno-unwind-tables",
"-fno-asynchronous-unwind-tables",
"-fno-exceptions",
"-fno-rtti",
"-fPIC",
"-fno-common",
"-fstack-protector-strong",
"-fvisibility-inlines-hidden",
"-flto",
]
cflags_cc = cflags
ldflags = [
"-Wl,--as-needed",
"-Wl,--gc-sections",
"-Wl,--no-whole-archive",
]
include_dirs = [
".",
"implement",
]
sources = [
"sle_hci_interface_impl.cpp",
"implement/sle_address.cpp",
"implement/h4_protocol.cpp",
"implement/dli_protocol.cpp",
"implement/dli_watcher.cpp",
"implement/vendor_interface.cpp",
"implement/thread_util.cpp",
]
defines = []
if (drivers_peripheral_nearlink_reload_address) {
defines += [ "RELOAD_ADDRESS" ]
}
if (is_standard_system) {
external_deps = [
"c_utils:utils",
"drivers_interface_nearlink:nearlink_hci_idl_headers",
"hdf_core:libhdf_host",
"hdf_core:libhdf_utils",
"hilog:libhilog",
"ipc:ipc_core",
"ffrt:libffrt",
]
} else {
external_deps = [
"hilog:libhilog",
"ipc:ipc_core",
]
}
external_deps += [ "init:libbegetutil" ]
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_nearlink"
}
ohos_shared_library("libnearlink_hci_hdi_driver") {
sanitize = {
cfi = true # Enable/disable control flow integrity detection
boundary_sanitize = true # Enable boundary san detection
cfi_cross_dso = true # Cross-SO CFI Checks
integer_overflow = true # Enable integer overflow detection
ubsan = true # Enable some Ubsan options
debug = false
}
branch_protector_ret = "pac_ret"
cflags = [
"-Os",
"-fdata-sections",
"-ffunction-sections",
"-fno-unwind-tables",
"-fno-asynchronous-unwind-tables",
"-fno-exceptions",
"-fno-rtti",
"-fPIC",
"-fno-common",
"-fstack-protector-strong",
"-fvisibility-inlines-hidden",
"-flto",
]
cflags_cc = cflags
ldflags = [
"-Wl,--as-needed",
"-Wl,--gc-sections",
"-Wl,--no-whole-archive",
]
include_dirs = [
".",
]
sources = [ "sle_dli_interface_driver.cpp" ]
if (is_standard_system) {
external_deps = [
"c_utils:utils",
"hdf_core:libhdf_host",
"hdf_core:libhdf_ipc_adapter",
"hdf_core:libhdf_utils",
"hdf_core:libhdi",
"hilog:libhilog",
"ipc:ipc_single",
]
} else {
external_deps = [
"hilog:libhilog",
"ipc:ipc_single",
]
}
external_deps += [
"drivers_interface_nearlink:libnearlink_hci_stub_1.0",
"drivers_interface_nearlink:libnearlink_hci_stub_1.1",
]
shlib_type = "hdi"
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_nearlink"
}
group("hdf_nearlink_service") {
deps = [
":libnearlink_hci_hdi_driver",
":libsle_hci_interface_service_1.1",
]
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_DLI_INTERNAL_H
#define OHOS_HDI_NEARLINK_DLI_INTERNAL_H
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
typedef enum {
DLI_PACKET_TYPE_UNKNOWN = 0,
DLI_PACKET_TYPE_SLE_CMD = 0xA1,
DLI_PACKET_TYPE_SLE_EVENT = 0xA2,
DLI_PACKET_TYPE_SLE_ACB_DATA = 0xA3,
DLI_PACKET_TYPE_SLE_ICB_DATA = 0xA4,
DLI_PACKET_TYPE_MAX,
} DliPacketType;
struct PacketHeader {
uint8_t headerSize;
uint8_t dataLengthOffset;
uint8_t dataLengthSize;
};
// opcode (DLI_SLE_Cmd_Complete_Evt opcode)
const uint16_t SLE_DLI_COMMAND_COMPLETE_EVENT = 0x0002;
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_DLI_INTERNAL_H */
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include "nearlink_hdf_log.h"
#include "dli_protocol.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
const PacketHeader DliProtocol::header_[DLI_PACKET_TYPE_MAX] = {
{.headerSize = 0, .dataLengthOffset = 0, .dataLengthSize = 0}, /* 0 */
{.headerSize = 4, .dataLengthOffset = 2, .dataLengthSize = 2}, /* 1 */
{.headerSize = 4, .dataLengthOffset = 2, .dataLengthSize = 2}, /* 2 */
{.headerSize = 4, .dataLengthOffset = 2, .dataLengthSize = 2}, /* 3 */
{.headerSize = 4, .dataLengthOffset = 2, .dataLengthSize = 2}, /* 4 */
};
const PacketHeader &DliProtocol::GetPacketHeaderInfo(DliPacketType packetType)
{
if (packetType >= DLI_PACKET_TYPE_MAX) {
return header_[DLI_PACKET_TYPE_UNKNOWN];
}
return header_[DliProtocol::TypeConvert(packetType)];
}
ssize_t DliProtocol::Read(int fd, uint8_t *data, size_t length)
{
const int bufsize = 256;
char buf[bufsize] = {0};
ssize_t ret = TEMP_FAILURE_RETRY(read(fd, data, length));
if (ret == -1) {
strerror_r(errno, buf, sizeof(buf));
HDF_LOGE("read failed");
ret = 0;
}
return ret;
}
ssize_t DliProtocol::Write(int fd, const uint8_t *data, size_t length)
{
const int bufsize = 256;
char buf[bufsize] = {0};
ssize_t ret = 0;
do {
ret = TEMP_FAILURE_RETRY(write(fd, data, length));
} while (ret == -1 && errno == EAGAIN);
if (ret == -1) {
strerror_r(errno, buf, sizeof(buf));
HDF_LOGE("write failed");
} else if (static_cast<size_t>(ret) != length) {
HDF_LOGE("write data %{public}zd less than %{public}zu.", ret, length);
}
return ret;
}
uint8_t DliProtocol::TypeConvert(DliPacketType packetType)
{
switch (packetType) {
case DLI_PACKET_TYPE_SLE_EVENT:
return static_cast<uint8_t>(DLI_PACKET_TYPE_SLE_EVENT) & 0x0F;
default:
HDF_LOGE("type convert error.");
return 0;
}
}
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_DLI_PROTOCOL_H
#define OHOS_HDI_NEARLINK_DLI_PROTOCOL_H
#include <cstdio>
#include <vector>
#include <functional>
#include "dli_internal.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
class DliProtocol {
public:
using DliDataCallback = std::function<void(const std::vector<uint8_t> &data)>;
DliProtocol() = default;
virtual ~DliProtocol() = default;
virtual ssize_t SendPacket(const std::vector<uint8_t> &packetData) = 0;
const PacketHeader& GetPacketHeaderInfo(DliPacketType packetType);
protected:
static ssize_t Read(int fd, uint8_t *data, size_t length);
static ssize_t Write(int fd, const uint8_t *data, size_t length);
static uint8_t TypeConvert(DliPacketType packetType);
static const PacketHeader header_[DLI_PACKET_TYPE_MAX];
};
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_DLI_PROTOCOL_H */
@@ -0,0 +1,205 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <thread>
#include <unistd.h>
#include <sys/select.h>
#include <sys/syscall.h>
#include "nearlink_hdf_log.h"
#include "sle_hal_constant.h"
#include "dli_watcher.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
DliWatcher::DliWatcher()
{}
DliWatcher::~DliWatcher()
{
Stop();
}
bool DliWatcher::AddFdToWatcher(int fd, DliDataCallback callback)
{
std::lock_guard<std::mutex> lock(fdsMutex_);
fds_[fd] = callback;
ThreadWakeup();
return true;
}
bool DliWatcher::RemoveFdToWatcher(int fd)
{
std::lock_guard<std::mutex> lock(fdsMutex_);
fds_.erase(fd);
ThreadWakeup();
return true;
}
bool DliWatcher::SetTimeout(std::chrono::milliseconds timeout, TimeoutCallback callback)
{
std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(timeout);
std::lock_guard<std::mutex> lock(timeoutMutex_);
timeoutTimer_.tv_sec = seconds.count();
timeoutTimer_.tv_usec = (timeout - seconds).count();
timeoutCallback_ = callback;
ThreadWakeup();
return true;
}
bool DliWatcher::Start()
{
if (running_.exchange(true)) {
return true;
}
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
if (pipe(wakeupPipe_) != 0) {
HDF_LOGE("DliWatcher create pipe failed.");
running_.exchange(false);
return false;
}
}
thread_ = std::thread(std::bind(&DliWatcher::WatcherThread, this));
if (!thread_.joinable()) {
HDF_LOGE("thread is not joinable.");
running_.exchange(false);
return false;
}
int policy = SLE_THREAD_POLICY;
sched_param params = {.sched_priority = SLE_THREAD_PRIORITY};
if (pthread_setschedparam(thread_.native_handle(), policy, &params) != 0) {
HDF_LOGW("pthread_setschedparam failed tid[%{public}lu] policy[%{public}d]", thread_.native_handle(), policy);
}
return true;
}
bool DliWatcher::Stop()
{
if (!running_.exchange(false)) {
return true;
}
ThreadWakeup();
thread_.join();
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
close(wakeupPipe_[0]);
close(wakeupPipe_[1]);
}
HDF_LOGI("DliWatcher stop");
return true;
}
void DliWatcher::CheckFdReady(std::map<int, DliDataCallback>& fds, fd_set &readFds)
{
std::lock_guard<std::mutex> lock(fdsMutex_);
for (auto &&fd : fds_) {
if (FD_ISSET(fd.first, &readFds)) {
fd.second(fd.first);
}
}
}
void DliWatcher::InitReadFds(fd_set &readFds, int &nfds)
{
FD_ZERO(&readFds);
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
FD_SET(wakeupPipe_[0], &readFds);
nfds = wakeupPipe_[0];
}
{
std::lock_guard<std::mutex> lock(fdsMutex_);
for (auto &&fd : fds_) {
FD_SET(fd.first, &readFds);
nfds = std::max(fd.first, nfds);
}
}
}
void DliWatcher::HandleTimeout()
{
TimeoutCallback callback;
{
std::lock_guard<std::mutex> lock(timeoutMutex_);
callback = timeoutCallback_;
}
if (callback) {
callback();
}
}
void DliWatcher::HandleSelectEvent(fd_set &readFds)
{
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
if (FD_ISSET(wakeupPipe_[0], &readFds)) {
uint8_t buff = 0;
TEMP_FAILURE_RETRY(read(wakeupPipe_[0], &buff, sizeof(buff)));
}
}
if (running_) {
CheckFdReady(fds_, readFds);
}
}
void DliWatcher::WatcherThread()
{
fd_set readFds;
int nfds;
timeval *timeout = nullptr;
while (running_) {
InitReadFds(readFds, nfds);
{
std::lock_guard<std::mutex> lock(timeoutMutex_);
if (timeoutTimer_.tv_sec == 0 && timeoutTimer_.tv_usec == 0) {
timeout = nullptr;
} else {
timeout = &timeoutTimer_;
}
}
int ret = select(nfds + 1, &readFds, nullptr, nullptr, timeout);
if (ret < 0) {
continue;
} else if (ret == 0) {
HandleTimeout();
} else {
HandleSelectEvent(readFds);
}
}
HDF_LOGI("DliWatcher thread is stop!");
}
void DliWatcher::ThreadWakeup()
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
uint8_t buff = 0;
ssize_t ret = TEMP_FAILURE_RETRY(write(wakeupPipe_[1], &buff, sizeof(buff)));
if (ret == -1) {
HDF_LOGE("wake up failed err:%{public}s", strerror(errno));
}
}
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_DLI_WATCHER_H
#define OHOS_HDI_NEARLINK_DLI_WATCHER_H
#include <atomic>
#include <ctime>
#include <functional>
#include <map>
#include <mutex>
#include <sys/time.h>
#include <thread>
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
class DliWatcher {
public:
using DliDataCallback = std::function<void(int fd)>;
using TimeoutCallback = std::function<void()>;
DliWatcher();
~DliWatcher();
bool AddFdToWatcher(int fd, DliDataCallback callback);
bool RemoveFdToWatcher(int fd);
bool SetTimeout(std::chrono::milliseconds timeout, TimeoutCallback callback);
bool Start();
bool Stop();
private:
void CheckFdReady(std::map<int, DliDataCallback>& fds, fd_set &readFds);
void InitReadFds(fd_set &readFds, int &nfds);
void HandleTimeout();
void HandleSelectEvent(fd_set &readFds);
void WatcherThread();
void ThreadWakeup();
private:
std::atomic_bool running_ = {false};
std::map<int, DliDataCallback> fds_;
std::mutex fdsMutex_;
std::mutex wakeupPipeMutex_;
int wakeupPipe_[2] = {0};
timeval timeoutTimer_ = {};
TimeoutCallback timeoutCallback_;
std::mutex timeoutMutex_;
std::thread thread_;
};
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_DLI_WATCHER_H */
@@ -0,0 +1,176 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cerrno>
#include <cstring>
#include <dlfcn.h>
#include "nearlink_hdf_log.h"
#include "h4_protocol.h"
#include <unistd.h>
#include "sle_hal_constant.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
constexpr const char *SLE_MAC_LIB = "libnearlink_mac.z.so";
constexpr const char *SET_VIP_PRIO = "SetVipPrio";
constexpr int NEARLINK_HOST_PRIORITY = 10;
thread_local bool H4Protocol::isThreadPromoted = false;
H4Protocol::H4Protocol(
int fd, DliDataCallback onAcbReceive, DliDataCallback onIcbReceive, DliDataCallback onEventReceive)
: dliFd_(fd), onAcbReceive_(onAcbReceive), onIcbReceive_(onIcbReceive), onEventReceive_(onEventReceive)
{}
ssize_t H4Protocol::SendPacket(const std::vector<uint8_t> &packetData)
{
SetRTSchedule();
ssize_t writtenNumber = 0;
ssize_t ret = 0;
do {
ret = Write(dliFd_, packetData.data() + writtenNumber, packetData.size() - writtenNumber);
if (ret > 0) {
writtenNumber += ret;
} else if (ret < 0) {
return ret;
}
} while (static_cast<size_t>(writtenNumber) != packetData.size());
return writtenNumber;
}
static bool ReadLengthCheck(ssize_t readLen, char *buf, int fd)
{
const int bufsize = 256;
if (readLen < 0) {
strerror_r(errno, buf, bufsize);
HDF_LOGE("read fd[%{public}d] fail", fd);
return false;
} else if (readLen == 0) {
HDF_LOGE("read fd[%{public}d] readLen = 0.", fd);
return false;
}
return true;
}
void H4Protocol::ReadData(int fd)
{
SetRTSchedule();
const int bufsize = 256;
char buf[bufsize] = {0};
static uint8_t typeNo{0};
ssize_t readLen = 0;
if (dliPacket_.size() == 0) {
readLen = Read(fd, &packetType_, sizeof(packetType_));
NL_HDF_CHECK_RETURN(readLen > 0, "read fd[%{public}d], readLen[%{public}zd]", fd, readLen);
typeNo = (packetType_) & 0x0F;
if (packetType_ > DLI_PACKET_TYPE_UNKNOWN && packetType_ < DLI_PACKET_TYPE_MAX) {
dliPacket_.resize(header_[typeNo].headerSize);
}
} else if (dliPacket_.size() == header_[typeNo].headerSize) {
readLen = Read(fd, dliPacket_.data() + readLength_, dliPacket_.size() - readLength_);
NL_HDF_CHECK_RETURN(ReadLengthCheck(readLen, buf, fd),
"hciPacket size[%{public}zu], readLength[%{public}u]", dliPacket_.size(), readLength_);
readLength_ += readLen;
if (readLength_ == dliPacket_.size()) {
size_t dataLen = 0;
for (int ii = 0; ii < header_[typeNo].dataLengthSize; ii++) {
dataLen += (dliPacket_[header_[typeNo].dataLengthOffset + ii] << (ii * 0x08));
}
if (dataLen == 0) {
HDF_LOGE("dataLen == 0, proc data error.");
dliPacket_.clear();
readLength_ = 0;
typeNo = 0;
} else {
dliPacket_.resize(dliPacket_.size() + dataLen);
}
}
} else {
readLen = Read(fd, dliPacket_.data() + readLength_, dliPacket_.size() - readLength_);
NL_HDF_CHECK_RETURN(ReadLengthCheck(readLen, buf, fd),
"hciPacket size[%{public}zu], readLength[%{public}u]", dliPacket_.size(), readLength_);
readLength_ += readLen;
if (readLength_ == dliPacket_.size()) {
PacketCallback();
dliPacket_.clear();
readLength_ = 0;
typeNo = 0;
}
}
}
H4Protocol::~H4Protocol() {}
void H4Protocol::PacketCallback()
{
switch (packetType_) {
case DLI_PACKET_TYPE_SLE_ACB_DATA:
if (onAcbReceive_) {
onAcbReceive_(dliPacket_);
}
break;
case DLI_PACKET_TYPE_SLE_ICB_DATA:
if (onIcbReceive_) {
onIcbReceive_(dliPacket_);
}
break;
case DLI_PACKET_TYPE_SLE_EVENT:
if (onEventReceive_) {
onEventReceive_(dliPacket_);
}
break;
default:
HDF_LOGE("PacketCallback type[%{public}d] error.", packetType_);
break;
}
}
bool SetVipPrio(unsigned int vipPrio)
{
void *libMac = dlopen(SLE_MAC_LIB, RTLD_LAZY);
if (libMac == nullptr) {
HDF_LOGI("SetVipPrio no mac lib ready for dlopen");
return false;
}
using GetMacFun = bool (*)(unsigned int);
GetMacFun setVipPrio = reinterpret_cast<GetMacFun>(dlsym(libMac, SET_VIP_PRIO));
if (setVipPrio == nullptr) {
HDF_LOGE("SetVipPrio dlsym error");
dlclose(libMac);
return false;
}
bool result = false;
result = setVipPrio(vipPrio);
dlclose(libMac);
return result;
}
void H4Protocol::SetRTSchedule()
{
if (isThreadPromoted) {
return;
}
SetVipPrio(NEARLINK_HOST_PRIORITY);
pid_t tid = gettid();
struct sched_param rtParams = {.sched_priority = SLE_THREAD_PRIORITY};
int rc = sched_setscheduler(tid, SCHED_FIFO, &rtParams);
isThreadPromoted = (rc != 0) ? false : true;
}
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_DLI_H4_PROTOCOL_H
#define OHOS_HDI_NEARLINK_DLI_H4_PROTOCOL_H
#include "dli_protocol.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
class H4Protocol : public DliProtocol {
public:
H4Protocol(int fd, DliDataCallback onAcbReceive, DliDataCallback onIcbReceive, DliDataCallback onEventReceive);
ssize_t SendPacket(const std::vector<uint8_t> &packetData) override;
void ReadData(int fd);
~H4Protocol() override;
private:
void PacketCallback();
void SetRTSchedule();
private:
int dliFd_ = 0;
DliDataCallback onAcbReceive_;
DliDataCallback onIcbReceive_;
DliDataCallback onEventReceive_;
thread_local static bool isThreadPromoted;
uint8_t packetType_ = 0;
std::vector<uint8_t> dliPacket_;
uint32_t readLength_ = 0;
};
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_DLI_H4_PROTOCOL_H */
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NARLINK_HDF_LOG_H
#define NARLINK_HDF_LOG_H
#include <hdf_log.h>
#ifdef LOG_DOMAIN
#undef LOG_DOMAIN
#endif
#define LOG_DOMAIN 0xD000154
#ifndef HDF_LOG_TAG
#define HDF_LOG_TAG nearlink_hdf
#endif
#ifdef NL_HDF_CHECK_RETURN
#undef NL_HDF_CHECK_RETURN
#endif
#define NL_HDF_CHECK_RETURN(cond, fmt, ...) \
do { \
if (!(cond)) { \
HDF_LOGE(fmt, ##__VA_ARGS__); \
return; \
} \
} while (0)
#endif // NARLINK_HDF_LOG_H
@@ -0,0 +1,152 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SLE_VENDOR_LIB_H
#define SLE_VENDOR_LIB_H
#include <cstdint>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Define DLI channel descriptors array used in SLE_OP_DLI_CHANNEL_OPEN operation.
*/
typedef enum {
DLI_CMD, // DLI Command channel
DLI_EVT, // DLI Event channel
DLI_ACB_OUT, // DLI ACB downstream channel
DLI_ACB_IN, // DLI ACB upstream channel
DLI_MAX_CHANNEL // Total channels
} DliChannelsT;
typedef enum {
SLE_OP_RESULT_SUCCESS,
SLE_OP_RESULT_FAIL,
} SleOpResultT;
/**
* SLE vendor lib cmd.
*/
typedef enum {
/**
* Power on the SLE Controller.
* @return 0 if success.
*/
SLE_OP_POWER_ON,
/**
* Power off the SLE Controller.
* @return 0 if success.
*/
SLE_OP_POWER_OFF,
/**
* Establish dli channels. it will be called after SLE_OP_POWER_ON.
* @param int (*)[DLI_MAX_CHANNEL].
* @return fd count.
*/
SLE_OP_DLI_CHANNEL_OPEN,
/**
* Close all the dli channels which is opened.
*/
SLE_OP_DLI_CHANNEL_CLOSE,
/**
* initialization the SLE Controller. it will be called after SLE_OP_DLI_CHANNEL_OPEN.
* Controller Must call initCb to notify the host once it has been done.
*/
SLE_OP_INIT,
/**
* Get the LPM idle timeout in milliseconds.
* @param (uint_32 *)milliseconds, slec will return the value of lpm timer.
* @return 0 if success.
*/
SLE_OP_GET_LPM_TIMER,
/**
* Enable LPM mode on SLE Controller.
*/
SLE_OP_LPM_ENABLE,
/**
* Disable LPM mode on SLE Controller.
*/
SLE_OP_LPM_DISABLE,
/**
* Wakeup lock the SLEC.
*/
SLE_OP_WAKEUP_LOCK,
/**
* Wakeup unlock the SLEC.
*/
SLE_OP_WAKEUP_UNLOCK,
/**
* transmit event response to vendor lib.
* @param (void *)buf, struct of HC_SLE_HDR.
*/
SLE_OP_EVENT_CALLBACK
} SleOpcodeT;
/**
* initialization callback.
*/
typedef void (*InitCallback)(SleOpResultT result);
/**
* Nearlink Host/Controller VENDOR Interface
*/
typedef struct {
/**
* Set to sizeof(sle_vndor_interface_t)
*/
size_t size;
/**
* Caller will open the interface and pass in the callback routines
* to the implementation of this interface.
*/
int (*init)(void);
/**
* Vendor specific operations
*/
int (*op)(int opcode, void* param);
/**
* Closes the interface
*/
void (*close)(void);
} SleVendorInterfaceT;
typedef struct {
uint16_t event;
uint16_t len;
uint16_t offset;
uint16_t layerSpecific;
uint8_t data[];
} HC_SLE_HDR;
#ifdef __cplusplus
}
#endif
#endif /* SLE_VENDOR_LIB_H */
@@ -0,0 +1,259 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <dlfcn.h>
#include "securec.h"
#include "dli_internal.h"
#include "nearlink_hdf_log.h"
#include "sle_address.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
namespace {
constexpr int ADDRESS_STR_LEN = 17;
constexpr int ADDRESS_SIZE = 6;
constexpr const char *SLE_MAC_LIB = "libnearlink_mac.z.so";
constexpr const char *GET_SLE_MAC_SYMBOL_NAME = "GetConstantMac";
} // namespace
SleAddress::SleAddress()
{
address_.resize(ADDRESS_SIZE);
}
constexpr int START_POS = 6;
constexpr int END_POS = 13;
constexpr int ADDR_BYTE = 18;
std::string SleAddress::GetEncryptAddr(std::string addr)
{
if (addr.empty() || addr.length() != ADDRESS_STR_LEN) {
HDF_LOGE("addr is invalid, length= %{public}zu", addr.length());
return std::string("");
}
std::string tmp = "**:**:**:**:**:**";
std::string out = addr;
for (int i = START_POS; i <= END_POS; i++) {
out[i] = tmp[i];
}
return out;
}
void SleAddress::ParseAddressToString(std::vector<uint8_t> &address, std::string &outString)
{
char temp[ADDR_BYTE] = {0};
int ret = sprintf_s(temp, sizeof(temp), "%02X:%02X:%02X:%02X:%02X:%02X",
address[0], address[1], address[2], address[3], address[4], address[5]);
if (ret == -1) {
HDF_LOGE("ConvertAddr sprintf_s return error, ret -1");
}
outString = temp;
}
int SleAddress::ParseAddressFromString(const std::string &string) const
{
size_t offset = 0;
int bytesIndex = 0;
int readCount = 0;
for (bytesIndex = 0; bytesIndex < ADDRESS_SIZE && offset < string.size(); bytesIndex++) {
readCount = 0;
if (sscanf_s(&string[offset], "%02hhx:%n", &address_[bytesIndex], &readCount) > 0) {
if (readCount == 0 && bytesIndex != ADDRESS_SIZE - 1) {
return bytesIndex;
}
offset += static_cast<uint32_t>(readCount);
} else {
break;
}
}
return bytesIndex;
}
bool SleAddress::GetConstantAddress(char *address, int len)
{
if (address == nullptr || len < ADDRESS_STR_LEN + 1) {
HDF_LOGE("GetConstantAddress buf error");
return false;
}
void *libMac = dlopen(SLE_MAC_LIB, RTLD_LAZY);
if (libMac == nullptr) {
HDF_LOGI("GetConstantAddress no mac lib ready for dlopen");
return false;
}
using GetMacFun = int (*)(char*, int);
GetMacFun getMac = reinterpret_cast<GetMacFun>(dlsym(libMac, GET_SLE_MAC_SYMBOL_NAME));
if (getMac == nullptr) {
HDF_LOGE("GetConstantAddress dlsym error");
dlclose(libMac);
return false;
}
int ret = getMac(address, len);
HDF_LOGI("GetConstantAddress ret: %{public}d", ret);
dlclose(libMac);
return (ret == 0);
}
#ifdef RELOAD_ADDRESS
// system boot less 2min, need ReadSleAddress from NV
bool SleAddress::NeedReloadAddress()
{
const int64_t maxNeedReloadTime = 120000; // 120s
constexpr int64_t msPerSecond = 1000;
constexpr int64_t nsPerMs = 1000000;
struct timespec times = {0};
if (clock_gettime(CLOCK_MONOTONIC, &times) < 0) {
HDF_LOGE("Failed clock_gettime:%{public}s, needReloadAddress:false", strerror(errno));
return false;
}
int64_t relativeTime = ((times.tv_sec * msPerSecond) + (times.tv_nsec / nsPerMs));
HDF_LOGI("relativeTime:%{public}lu", relativeTime);
return relativeTime < maxNeedReloadTime;
}
#endif
std::shared_ptr<SleAddress> SleAddress::UpdateAddressFromNV(const std::string &path)
{
const int bufsize = 256;
char buf[bufsize] = {0};
int newFd = open(path.c_str(), O_RDWR | O_CREAT, 00644);
HDF_LOGI("GetDeviceAddress open newFd %{public}d.", newFd);
char addressStr[ADDRESS_STR_LEN + 1] = {"00:11:22:33:44:55"};
bool readNVSucc = GetConstantAddress(addressStr, ADDRESS_STR_LEN + 1);
bool needUpdateTxt = true;
if (newFd < 0) {
auto ptr = std::make_shared<SleAddress>();
int ret = ptr->ParseAddressFromString(addressStr);
if (ret != ADDRESS_SIZE) {
HDF_LOGE("UpdateAddressFromNV ParseAddressFromString failed, ret:%{public}d.", ret);
return nullptr;
}
return ptr;
}
if (!readNVSucc) {
// read NV fail ,if slemac.txt exsit mac, not update, needUpdateTxt = false
if (read(newFd, addressStr, ADDRESS_STR_LEN) != ADDRESS_STR_LEN) {
auto tmpPtr = GenerateDeviceAddress();
std::string strAddress;
ParseAddressToString(tmpPtr->address_, strAddress);
HDF_LOGI("device mac addr: %{public}s", GetEncryptAddr(strAddress).c_str());
int ret = strcpy_s(addressStr, ADDRESS_STR_LEN + 1, strAddress.c_str());
if (ret != 0) {
HDF_LOGE("ParseAddressToString strcpy_s err!");
}
} else {
HDF_LOGI("newFd %{public}d not update", newFd);
needUpdateTxt = false;
}
}
if (needUpdateTxt) {
int fdRet = write(newFd, addressStr, ADDRESS_STR_LEN);
if (fdRet < 0) {
strerror_r(errno, buf, sizeof(buf));
HDF_LOGE("GetDeviceAddress addr write failed, err:%{public}s.", buf);
}
}
close(newFd);
auto ptr = std::make_shared<SleAddress>();
int result = ptr->ParseAddressFromString(addressStr);
if (result != ADDRESS_SIZE) {
HDF_LOGE("UpdateAddressFromNV ParseAddressFromString failed, result:%{public}d.", result);
return nullptr;
}
return ptr;
}
std::shared_ptr<SleAddress> SleAddress::GetDeviceAddress(const std::string &path)
{
#ifdef RELOAD_ADDRESS
if (NeedReloadAddress()) {
return UpdateAddressFromNV(path);
}
#endif
int addrFd = open(path.c_str(), O_RDONLY);
if (addrFd < 0) {
return UpdateAddressFromNV(path);
}
char addressStr[ADDRESS_STR_LEN + 1] = {0};
if (read(addrFd, addressStr, ADDRESS_STR_LEN) != ADDRESS_STR_LEN) {
HDF_LOGE("read %{public}s failed.", path.c_str());
close(addrFd);
return UpdateAddressFromNV(path);
}
close(addrFd);
auto ptr = std::make_shared<SleAddress>();
int result = ptr->ParseAddressFromString(addressStr);
if (result != ADDRESS_SIZE) {
HDF_LOGE("GetDeviceAddress ParseAddressFromString failed, result:%{public}d.", result);
return UpdateAddressFromNV(path);
}
return ptr;
}
std::shared_ptr<SleAddress> SleAddress::GenerateDeviceAddress(const std::string &prefix)
{
const int bufsize = 256;
char buf[bufsize] = {0};
auto ptr = std::make_shared<SleAddress>();
char addressStr[ADDRESS_STR_LEN + 1] = {"00:11:22:33:44:55"};
ptr->ParseAddressFromString(addressStr);
int prefixCount = ptr->ParseAddressFromString(prefix);
if (prefixCount < ADDRESS_SIZE) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
strerror_r(errno, buf, sizeof(buf));
HDF_LOGE("open /dev/urandom failed err:%{public}s.", buf);
return ptr;
}
if (read(fd, &ptr->address_[prefixCount], ADDRESS_SIZE - prefixCount) != ADDRESS_SIZE - prefixCount) {
strerror_r(errno, buf, sizeof(buf));
HDF_LOGE("read /dev/urandom failed err:%{public}s.", buf);
}
close(fd);
}
return ptr;
}
void SleAddress::ReadAddress(std::vector<uint8_t> &address) const
{
address = address_;
}
void SleAddress::ReadAddress(std::string &address) const
{
address.resize(ADDRESS_STR_LEN + 1);
int offset = 0;
for (int ii = 0; ii < ADDRESS_SIZE; ii++) {
int ret = snprintf_s(
&address[offset], (ADDRESS_STR_LEN + 1) - offset, ADDRESS_STR_LEN - offset, "%02x:", address_[ii]);
if (ret < 0) {
break;
}
offset += ret;
}
}
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_HCI_NEARLINK_ADDRESS_H
#define OHOS_HDI_NEARLINK_HCI_NEARLINK_ADDRESS_H
#include <memory>
#include <string>
#include <vector>
#include "sle_hal_constant.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
class SleAddress {
public:
SleAddress();
~SleAddress() = default;
static std::shared_ptr<SleAddress> GetDeviceAddress(const std::string &path = SLE_DEVICE_ADDRESS_PATH);
static std::shared_ptr<SleAddress> GenerateDeviceAddress(const std::string &prefix = "");
static void ParseAddressToString(std::vector<uint8_t> &address, std::string &outString);
static std::string GetEncryptAddr(std::string addr);
void ReadAddress(std::vector<uint8_t> &address) const;
void ReadAddress(std::string &address) const;
private:
int ParseAddressFromString(const std::string &string) const;
static bool GetConstantAddress(char *address, int len);
#ifdef RELOAD_ADDRESS
static bool NeedReloadAddress();
#endif
static std::shared_ptr<SleAddress> UpdateAddressFromNV(const std::string &path);
private:
std::vector<uint8_t> address_;
};
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_HCI_NEARLINK_ADDRESS_H */
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_HCI_CONSTANT_H
#define OHOS_HDI_NEARLINK_HCI_CONSTANT_H
#include <string>
#include <sched.h>
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
constexpr const char *SLE_VENDOR_NAME = "libnearlink_sle_vendor";
constexpr const char *SLE_CHIP_TYPE = "const.nearlink.slechiptype";
constexpr const char *SLE_VENDOR_INTERFACE_SYMBOL_NAME = "NEARLINK_VENDOR_LIB_INTERFACE";
constexpr const char *SLE_DEVICE_ADDRESS_PATH = "/data/vendor/nearlink/slemac.txt";
constexpr int SLE_THREAD_POLICY = SCHED_RR;
constexpr int SLE_THREAD_PRIORITY = 1;
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_HCI_CONSTANT_H */
@@ -0,0 +1,109 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "thread_util.h"
#include <mutex>
#include "ffrt_inner.h"
#include <hdf_log.h>
#include "nearlink_hdf_log.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
void DoInDliThread(const ThreadUtilFunc &func)
{
ThreadUtil::GetInstance().PostTask(func);
}
struct ThreadUtil::impl {
class TaskQueue {
public:
explicit TaskQueue(const char *name) : queue_(name, ffrt::queue_attr().qos(ffrt::qos_user_interactive)) {}
~TaskQueue() = default;
void PostTask(const ThreadUtilFunc &func);
int GetQueueId(void);
private:
ffrt::queue queue_;
};
impl();
~impl() = default;
std::shared_ptr<TaskQueue> CreateTaskQueue();
std::shared_ptr<TaskQueue> taskQueue;
};
ThreadUtil::impl::impl()
{}
ThreadUtil::ThreadUtil() : pimpl(std::make_unique<impl>())
{}
ThreadUtil::~ThreadUtil()
{}
void ThreadUtil::impl::TaskQueue::PostTask(const ThreadUtilFunc &func)
{
queue_.submit(func);
}
int ThreadUtil::impl::TaskQueue::GetQueueId(void)
{
int id = -1;
auto handle = queue_.submit_h([&id]() {
id = ffrt::get_queue_id();
});
queue_.wait(handle);
return id;
}
void ThreadUtil::PostTask(const ThreadUtilFunc &func)
{
std::shared_ptr<impl::TaskQueue> taskQueue = pimpl->taskQueue;
if (taskQueue != nullptr) {
taskQueue->PostTask(func);
return;
}
// If the thread not found, create it.
taskQueue = pimpl->CreateTaskQueue();
// Execute the first task.
if (taskQueue) {
taskQueue->PostTask(func);
}
}
std::shared_ptr<ThreadUtil::impl::TaskQueue> ThreadUtil::impl::CreateTaskQueue()
{
std::string threadName = "sle_hci";
auto taskQueue_ = std::make_shared<TaskQueue>(threadName.c_str());
int queueId = taskQueue_->GetQueueId();
HDF_LOGI("sle_ffrt_queue: queueId(%{public}d), name(%{public}s)", queueId, threadName.c_str());
taskQueue = taskQueue_;
return taskQueue_;
}
ThreadUtil &ThreadUtil::GetInstance()
{
static ThreadUtil instance;
return instance;
}
} // namespace Nearlink
} // namepsace HDI
} // namespace OHOS
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SLE_THREAD_UTIL_H
#define SLE_THREAD_UTIL_H
#include <functional>
#ifdef RESOURCESCHEDULE_FFRT_ENABLE
#include "ffrt_inner.h"
#include <hdf_log.h>
#include "nearlink_hdf_log.h"
#else
#include <mutex>
#endif // RESOURCESCHEDULE_FFRT_ENABLE
namespace OHOS {
namespace HDI {
namespace Nearlink {
using ThreadUtilFunc = std::function<void(void)>;
/**
* @brief Post the task to the dli thread.
* @param func The task to be executed.
*/
void DoInDliThread(const ThreadUtilFunc &func);
class ThreadUtil {
public:
void PostTask(const ThreadUtilFunc &func);
static ThreadUtil &GetInstance();
private:
ThreadUtil();
~ThreadUtil();
struct impl;
std::unique_ptr<impl> pimpl;
};
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif // SLE_THREAD_UTIL_H
@@ -0,0 +1,461 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <thread>
#include <dlfcn.h>
#include <future>
#include <securec.h>
#include "common_timer_errors.h"
#include "nearlink_hdf_log.h"
#include "sle_address.h"
#include "sle_hal_constant.h"
#include "timer.h"
#include "h4_protocol.h"
#include "vendor_interface.h"
#include "parameters.h"
#include "thread_util.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
namespace V1_1 {
constexpr size_t DLI_PACKET_TYPE_SIZE = 1;
constexpr uint16_t DLI_SET_SLE_ADDR = 0x0405;
/* Define the preamble length for all DLI Commands. This is 2-bytes for opcode and 2 byte for length */
constexpr size_t DLIC_PREAMBLE_SIZE = 4;
/* Device address length */
constexpr int BD_ADDR_LEN = 6;
constexpr int OFFSET_EIGHT_BITS = 8;
constexpr int OFFSET_STATUS = 3;
constexpr size_t SLE_VENDOR_INVALID_DATA_LEN = 0;
constexpr int DLI_INTERFACE_CLOSED = 0;
constexpr int DLI_INTERFACE_OPENING = 1;
constexpr int DLI_INTERFACE_OPENED = 2;
constexpr int SET_ADDR_TIME_OUT_MS = 200;
struct InternalCommand {
uint16_t cb;
uint16_t opcode;
};
struct TaskConfig {
pid_t pid;
unsigned int value;
};
#define SET_VIP_PRIO 25
#define PERF_CTRL_MAGIC 'x'
#define PERF_CTRL_SET_VIP_PRIO \
_IOW(PERF_CTRL_MAGIC, SET_VIP_PRIO, struct TaskConfig)
class VendorInterface::Impl {
public:
bool Initialize(VendorInterface::InitializeCompleteCallback initializeCompleteCallback,
const VendorInterface::ReceiveCallback &receiveCallback);
void CleanUp();
size_t SendPacket(const std::vector<uint8_t> &packet);
void SetSleAddress(uint8_t *localSleAddr);
uint8_t *TransformAddress(uint8_t *addr);
void OnEventReceived(const std::vector<uint8_t> &data);
void OnAcbReceived(const std::vector<uint8_t> &data);
void OnIcbReceived(const std::vector<uint8_t> &data);
void SetSleAddressTimeOut();
public:
std::mutex setAddrFinishedMutex_ {};
bool isSetAddrFinished_ = false; // guarded by setAddrFinishedMutex_
std::condition_variable setAddrConditionVariable_ {};
std::shared_ptr<Dli::DliProtocol> dli_ = nullptr;
private:
void OnInitCallback(SleOpResultT result);
bool WatchDliChannel(const VendorInterface::ReceiveCallback &receiveCallback);
void WatcherTimeout();
void GetFullVendorName(std::string &name);
private:
VendorInterface::InitializeCompleteCallback initializeCompleteCallback_;
ReceiveDataCallback eventDataCallback_ = nullptr;
ReceiveDataCallback acbDataCallback_ = nullptr;
ReceiveDataCallback icbDataCallback_ = nullptr;
void* vendorHandle_ = nullptr;
SleVendorInterfaceT *vendorInterface_ = nullptr;
DliWatcher watcher_;
uint32_t lpmTimer_ = 0;
InternalCommand internalCommand_ = {0, 0};
std::shared_ptr<SleAddress> sleAddress_ = nullptr;
int dliInterfaceState_ = DLI_INTERFACE_CLOSED;
};
VendorInterface::VendorInterface() : impl_(std::make_unique<Impl>())
{
HDF_LOGI("VendorInterface Constructor.");
}
VendorInterface::~VendorInterface()
{
HDF_LOGW("VendorInterface destructor.");
CleanUp();
}
bool VendorInterface::Initialize(InitializeCompleteCallback initializeCompleteCallback,
const ReceiveCallback &receiveCallback)
{
std::promise<bool> promise;
vendorInterfaceWeak_ = shared_from_this();
Nearlink::DoInDliThread([this, &initializeCompleteCallback, cb = receiveCallback, &promise]() {
const ReceiveCallback &safeReceiveCallback = cb;
bool result = impl_->Initialize(initializeCompleteCallback, safeReceiveCallback);
promise.set_value(result);
});
return promise.get_future().get();
}
void VendorInterface::CleanUp()
{
std::promise<void> promise;
Nearlink::DoInDliThread([this, &promise]() {
this->impl_->CleanUp();
promise.set_value();
});
promise.get_future().get();
return;
}
size_t VendorInterface::SendPacket(const std::vector<uint8_t> &packet)
{
std::promise<size_t> promise;
Nearlink::DoInDliThread([this, packet_ = packet, &promise]() {
const std::vector<uint8_t> &safePacket = packet_;
size_t result = this->impl_->SendPacket(safePacket);
promise.set_value(result);
});
return promise.get_future().get();
}
void VendorInterface::OnEventReceived(const std::vector<uint8_t> &data)
{
{
std::lock_guard<std::mutex> lock(impl_->setAddrFinishedMutex_);
if (impl_->isSetAddrFinished_ == false && impl_->dli_ != nullptr) {
size_t opcodeOffset = impl_->dli_->GetPacketHeaderInfo(DLI_PACKET_TYPE_SLE_EVENT).headerSize;
uint16_t opcode = data[opcodeOffset] | (data[opcodeOffset + 1] << OFFSET_EIGHT_BITS);
if (opcode == DLI_SET_SLE_ADDR) {
HDF_LOGI("SetSleAddress received the response");
impl_->isSetAddrFinished_ = true;
impl_->setAddrConditionVariable_.notify_all();
}
}
}
Nearlink::DoInDliThread([weakPtr = vendorInterfaceWeak_, data_ = data]() {
auto vendorInterface = weakPtr.lock();
if (vendorInterface == nullptr) {
HDF_LOGE("VendorInterface::OnEventReceived: vendorInterface is nullptr");
return;
}
vendorInterface->impl_->OnEventReceived(data_);
});
return;
}
void VendorInterface::OnAcbReceived(const std::vector<uint8_t> &data)
{
if (!impl_) {
return;
}
impl_->OnAcbReceived(data);
}
void VendorInterface::OnIcbReceived(const std::vector<uint8_t> &data)
{
Nearlink::DoInDliThread([weakPtr = vendorInterfaceWeak_, icbData = data]() {
auto vendorInterface = weakPtr.lock();
if (vendorInterface == nullptr) {
HDF_LOGE("VendorInterface::OnIcbReceived: vendorInterface is nullptr");
return;
}
vendorInterface->impl_->OnIcbReceived(icbData);
});
return;
}
bool VendorInterface::Impl::WatchDliChannel(const VendorInterface::ReceiveCallback &receiveCallback)
{
HDF_LOGI("VendorInterface %{public}s", __func__);
int channel[DLI_MAX_CHANNEL] = {0};
int channelCount = vendorInterface_->op(SleOpcodeT::SLE_OP_DLI_CHANNEL_OPEN, static_cast<void *>(channel));
if (channelCount < 1 || channelCount > DLI_MAX_CHANNEL) {
HDF_LOGE("VendorInterface vendorInterface_->op SLE_OP_DLI_CHANNEL_OPEN failed ret:%{public}d.", channelCount);
return false;
}
if (channelCount == 1) {
auto h4 = std::make_shared<Dli::H4Protocol>(channel[0],
std::bind(&VendorInterface::OnAcbReceived, VendorInterface::GetInstance(),
std::placeholders::_1),
std::bind(&VendorInterface::OnIcbReceived, VendorInterface::GetInstance(),
std::placeholders::_1),
std::bind(&VendorInterface::OnEventReceived, VendorInterface::GetInstance(),
std::placeholders::_1));
watcher_.AddFdToWatcher(channel[0], std::bind(&Dli::H4Protocol::ReadData, h4, std::placeholders::_1));
dli_ = h4;
} else {
HDF_LOGE("VendorInterface invalid channel count:%{public}d.", channelCount);
return false;
}
return true;
}
uint8_t *VendorInterface::Impl::TransformAddress(uint8_t *addr)
{
HDF_LOGI("VendorInterface %{public}s", __func__);
uint8_t* data = new (std::nothrow) uint8_t[DLI_PACKET_TYPE_SIZE + DLIC_PREAMBLE_SIZE + BD_ADDR_LEN];
if (data == nullptr) {
HDF_LOGE("failed to create data");
return nullptr;
}
uint8_t* p = data;
*(p)++ = static_cast<uint8_t>(DLI_PACKET_TYPE_SLE_CMD);
*(p)++ = static_cast<uint8_t>(DLI_SET_SLE_ADDR);
*(p)++ = static_cast<uint8_t>(DLI_SET_SLE_ADDR >> OFFSET_EIGHT_BITS);
*(p)++ = static_cast<uint8_t>(BD_ADDR_LEN);
*(p)++ = static_cast<uint8_t>(0x00); // length domain is 2 bytes
for (int i = 0; i < BD_ADDR_LEN; i++) {
*(p)++ = static_cast<uint8_t>(addr[BD_ADDR_LEN - 1 - i]);
}
return data;
}
void VendorInterface::Impl::SetSleAddress(uint8_t *localSleAddr)
{
HDF_LOGI("VendorInterface %{public}s", __func__);
if (dli_ == nullptr) {
HDF_LOGE("VendorInterface SetSleAddress dli_ is nullptr");
return;
}
uint8_t *hciAddrPacket = TransformAddress(localSleAddr);
if (hciAddrPacket == nullptr) {
HDF_LOGE("VendorInterface SetSleAddress hciAddrPacket is nullptr");
return;
}
std::vector<uint8_t> hciData(&hciAddrPacket[0],
&hciAddrPacket[DLI_PACKET_TYPE_SIZE + DLIC_PREAMBLE_SIZE + BD_ADDR_LEN]);
internalCommand_.cb = 1;
internalCommand_.opcode = DLI_SET_SLE_ADDR;
dli_->SendPacket(hciData);
delete[] reinterpret_cast<uint8_t*>(hciAddrPacket);
HDF_LOGI("VendorInterface send mac address complete. opcode: 0x%{public}04X, cb: %{public}hu",
internalCommand_.opcode, internalCommand_.cb);
}
void VendorInterface::Impl::GetFullVendorName(std::string &name)
{
std::string sleChipType = OHOS::system::GetParameter(SLE_CHIP_TYPE, "");
HDF_LOGI("slechiptype is %{public}s", sleChipType.c_str());
if (!sleChipType.empty()) {
name.append("_");
name.append(sleChipType.c_str());
}
name.append(".z.so");
HDF_LOGI("sle vendor library name is %{public}s", name.c_str());
}
bool VendorInterface::Impl::Initialize(VendorInterface::InitializeCompleteCallback initializeCompleteCallback,
const VendorInterface::ReceiveCallback &receiveCallback)
{
HDF_LOGI("VendorInterface %{public}s", __func__);
dliInterfaceState_ = DLI_INTERFACE_OPENING;
initializeCompleteCallback_ = initializeCompleteCallback;
eventDataCallback_ = receiveCallback.onEventReceive;
acbDataCallback_ = receiveCallback.onAcbReceive;
icbDataCallback_ = receiveCallback.onIcbReceive;
std::string sleVendorFullName = SLE_VENDOR_NAME;
GetFullVendorName(sleVendorFullName);
vendorHandle_ = dlopen(sleVendorFullName.c_str(), RTLD_NOW);
if (vendorHandle_ == nullptr) {
HDF_LOGE("Vendor dlopen %{public}s failed, error code: %{public}s", sleVendorFullName.c_str(), dlerror());
return false;
}
vendorInterface_ = reinterpret_cast<SleVendorInterfaceT *>(dlsym(vendorHandle_, SLE_VENDOR_INTERFACE_SYMBOL_NAME));
if (vendorInterface_ == nullptr) {
HDF_LOGE("VendorInterface dlsym %{public}s failed.", SLE_VENDOR_INTERFACE_SYMBOL_NAME);
return false;
}
sleAddress_ = SleAddress::GetDeviceAddress();
std::vector<uint8_t> address = { 0, 0, 0, 0, 0, 0 };
if (sleAddress_ != nullptr) {
sleAddress_->ReadAddress(address);
}
vendorInterface_->init();
if (!WatchDliChannel(receiveCallback)) {
HDF_LOGE("VendorInterface WatchDliChannel failed.");
return false;
}
if (!watcher_.Start()) {
HDF_LOGE("VendorInterface watcher start failed.");
return false;
}
if (sleAddress_ != nullptr) {
std::string strAddress;
sleAddress_->ParseAddressToString(address, strAddress);
HDF_LOGI("VendorInterface local addr: %{public}s", sleAddress_->GetEncryptAddr(strAddress).c_str());
}
SetSleAddress(address.data());
std::unique_lock<std::mutex> lock(setAddrFinishedMutex_);
if (!setAddrConditionVariable_.wait_for(lock, std::chrono::milliseconds(SET_ADDR_TIME_OUT_MS),
[this]() -> bool { return isSetAddrFinished_; })) {
Nearlink::DoInDliThread([this]() -> void {
SetSleAddressTimeOut();
});
}
return true;
}
void VendorInterface::Impl::CleanUp()
{
HDF_LOGI("VendorInterface %{public}s", __func__);
if (vendorInterface_ == nullptr) {
HDF_LOGE("VendorInterface::CleanUp, vendorInterface_ is nullptr.");
return;
}
watcher_.Stop();
vendorInterface_->op(SleOpcodeT::SLE_OP_DLI_CHANNEL_CLOSE, nullptr);
vendorInterface_->close();
dli_ = nullptr;
vendorInterface_ = nullptr;
initializeCompleteCallback_ = nullptr;
eventDataCallback_ = nullptr;
acbDataCallback_ = nullptr;
icbDataCallback_ = nullptr;
if ((vendorHandle_ == nullptr) || dlclose(vendorHandle_)) {
HDF_LOGE("VendorInterface vendorHandle_ is wrong.");
}
vendorHandle_ = nullptr;
dliInterfaceState_ = DLI_INTERFACE_CLOSED;
{
std::unique_lock<std::mutex> lock(setAddrFinishedMutex_);
isSetAddrFinished_ = false;
}
}
size_t VendorInterface::Impl::SendPacket(const std::vector<uint8_t> &packet)
{
HDF_LOGI("VendorInterface %{public}s", __func__);
if (vendorInterface_ == nullptr || dli_ == nullptr) {
HDF_LOGE("VendorInterface::SendPacket, vendorInterface_ is nullptr.");
return SLE_VENDOR_INVALID_DATA_LEN;
}
watcher_.SetTimeout(std::chrono::milliseconds(lpmTimer_), std::bind(&Impl::WatcherTimeout, this));
return dli_->SendPacket(packet);
}
// No lock is required through OnEventReceived invoking.
void VendorInterface::Impl::OnInitCallback(SleOpResultT result)
{
HDF_LOGI("VendorInterface %{public}s, ", __func__);
if (initializeCompleteCallback_) {
initializeCompleteCallback_(result == SLE_OP_RESULT_SUCCESS);
initializeCompleteCallback_ = nullptr;
}
uint32_t lpmTimer = 0;
lpmTimer_ = lpmTimer;
watcher_.SetTimeout(std::chrono::milliseconds(lpmTimer), std::bind(&Impl::WatcherTimeout, this));
}
void VendorInterface::Impl::OnEventReceived(const std::vector<uint8_t> &data)
{
HDF_LOGI("VendorInterface %{public}s", __func__);
if (dliInterfaceState_ == DLI_INTERFACE_OPENED) {
eventDataCallback_(data);
return;
}
if (dli_ == nullptr) {
HDF_LOGE("VendorInterface OnEventReceived dli_ is nullptr");
return;
}
size_t opcodeOffset = dli_->GetPacketHeaderInfo(DLI_PACKET_TYPE_SLE_EVENT).headerSize;
if (data.size() <= opcodeOffset + OFFSET_STATUS) {
HDF_LOGE("Invalid data length: %{public}zu", data.size());
return;
}
uint16_t eventOpcode = data[0] | (data[1] << OFFSET_EIGHT_BITS);
uint16_t opcode = data[opcodeOffset] | (data[opcodeOffset + 1] << OFFSET_EIGHT_BITS);
if (internalCommand_.cb != 0 && (internalCommand_.opcode == opcode)) {
internalCommand_.cb = 0;
if (data[opcodeOffset + OFFSET_STATUS] == 0) {
dliInterfaceState_ = DLI_INTERFACE_OPENED;
OnInitCallback(SLE_OP_RESULT_SUCCESS);
} else {
HDF_LOGE("VendorInterface Invalid status: %{public}d", data[opcodeOffset + OFFSET_STATUS]);
OnInitCallback(SLE_OP_RESULT_FAIL);
CleanUp();
}
} else {
HDF_LOGI("recv unexpected eventOpcode:0x%{public}x opcode:0x%{public}x", eventOpcode, opcode);
}
}
void VendorInterface::Impl::OnAcbReceived(const std::vector<uint8_t> &data)
{
if (acbDataCallback_ == nullptr) {
HDF_LOGE("acbDataCallback_ is nullptr");
return;
}
HDF_LOGD("VendorInterface %{public}s", __func__);
acbDataCallback_(data);
}
void VendorInterface::Impl::OnIcbReceived(const std::vector<uint8_t> &data)
{
if (icbDataCallback_ == nullptr) {
HDF_LOGE("icbDataCallback_ is nullptr");
return;
}
HDF_LOGD("VendorInterface %{public}s", __func__);
icbDataCallback_(data);
}
void VendorInterface::Impl::WatcherTimeout()
{
HDF_LOGI("fd wait out of time, no data ready");
}
void VendorInterface::Impl::SetSleAddressTimeOut()
{
HDF_LOGI("SetSleAddressTimeOut, retry SetSleAddress");
std::vector<uint8_t> address = { 0, 0, 0, 0, 0, 0 };
if (sleAddress_ != nullptr) {
sleAddress_->ReadAddress(address);
}
SetSleAddress(address.data());
}
} // namespace V1_1
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_DLI_V1_1_VENDOR_INTERFACE_H
#define OHOS_HDI_NEARLINK_DLI_V1_1_VENDOR_INTERFACE_H
#include <functional>
#include <shared_mutex>
#include <vector>
#include "singleton.h"
#include "dli_internal.h"
#include "dli_protocol.h"
#include "dli_watcher.h"
#include "ohos_sle_vendor_lib.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
namespace V1_1 {
class VendorInterface : public DelayedSingleton<VendorInterface>,
public std::enable_shared_from_this<VendorInterface> {
public:
using InitializeCompleteCallback = std::function<void(bool isSuccess)>;
using ReceiveDataCallback = Dli::DliProtocol::DliDataCallback;
struct ReceiveCallback {
ReceiveDataCallback onAcbReceive;
ReceiveDataCallback onIcbReceive;
ReceiveDataCallback onEventReceive;
};
bool Initialize(InitializeCompleteCallback initializeCompleteCallback, const ReceiveCallback &receiveCallback);
void CleanUp();
size_t SendPacket(const std::vector<uint8_t> &packet);
private:
// callback function
void OnEventReceived(const std::vector<uint8_t> &data);
void OnAcbReceived(const std::vector<uint8_t> &data);
void OnIcbReceived(const std::vector<uint8_t> &data);
private:
class Impl;
std::unique_ptr<Impl> impl_;
std::shared_mutex vendorMutex_;
std::weak_ptr<VendorInterface> vendorInterfaceWeak_;
DECLARE_DELAYED_SINGLETON(VendorInterface);
DISALLOW_COPY_AND_MOVE(VendorInterface);
};
} // namespace V1_1
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_Nearlink_DLI_V1_1_VENDOR_INTERFACE_H */
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_HCI_V1_1_REMOTE_DEATH_RECIPIENT_H
#define OHOS_HDI_NEARLINK_HCI_V1_1_REMOTE_DEATH_RECIPIENT_H
#include <functional>
#include "iremote_object.h"
#include "refbase.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Dli {
namespace V1_1 {
class RemoteDeathRecipient : public IRemoteObject::DeathRecipient {
public:
explicit RemoteDeathRecipient(std::function<void(const wptr<IRemoteObject> &)> callback)
{
callback_ = callback;
}
~RemoteDeathRecipient()
{
callback_ = nullptr;
}
void OnRemoteDied(const wptr<IRemoteObject> &object)
{
if (callback_ != nullptr) {
callback_(object);
}
}
private:
std::function<void(const wptr<IRemoteObject> &)> callback_;
};
} // namespace V1_1
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // OHOS
#endif // OHOS_HDI_NEARLINK_HCI_V1_1_REMOTE_DEATH_RECIPIENT_H
@@ -0,0 +1,125 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <hdf_base.h>
#include <hdf_device_desc.h>
#include <hdf_log.h>
#include <hdf_sbuf_ipc.h>
#include "v1_1/sle_hci_interface_stub.h"
#define HDF_LOG_TAG sle_dli_interface_driver
using namespace OHOS::HDI::Nearlink::Hci::V1_1;
struct HdfSleDliInterfaceHost {
struct IDeviceIoService ioService;
OHOS::sptr<OHOS::IRemoteObject> stub;
HdfSleDliInterfaceHost()
{
ioService.object.objectId = 0;
ioService.Open = nullptr;
ioService.Release = nullptr;
ioService.Dispatch = nullptr;
}
};
static int32_t SleDliInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data,
struct HdfSBuf *reply)
{
auto *hdfSleDliInterfaceHost = CONTAINER_OF(client->device->service, struct HdfSleDliInterfaceHost, ioService);
OHOS::MessageParcel *dataParcel = nullptr;
OHOS::MessageParcel *replyParcel = nullptr;
OHOS::MessageOption option;
if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) {
HDF_LOGE("%{public}s: invalid data sbuf object to dispatch", __func__);
return HDF_ERR_INVALID_PARAM;
}
if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) {
HDF_LOGE("%{public}s: invalid reply sbuf object to dispatch", __func__);
return HDF_ERR_INVALID_PARAM;
}
return hdfSleDliInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option);
}
static int HdfSleDliInterfaceDriverInit(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("%{public}s: driver init start", __func__);
return HDF_SUCCESS;
}
static int HdfSleDliInterfaceDriverBind(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("%{public}s: driver bind start", __func__);
auto *hdfSleDliInterfaceHost = new (std::nothrow) HdfSleDliInterfaceHost;
if (hdfSleDliInterfaceHost == nullptr) {
HDF_LOGE("%{public}s: failed to create create HdfSleDliInterfaceHost object", __func__);
return HDF_FAILURE;
}
hdfSleDliInterfaceHost->ioService.Dispatch = SleDliInterfaceDriverDispatch;
hdfSleDliInterfaceHost->ioService.Open = NULL;
hdfSleDliInterfaceHost->ioService.Release = NULL;
auto serviceImpl = OHOS::HDI::Nearlink::Hci::V1_1::ISleHciInterface::Get(true);
if (serviceImpl == nullptr) {
HDF_LOGE("%{public}s: failed to get of implement service", __func__);
delete hdfSleDliInterfaceHost;
return HDF_FAILURE;
}
hdfSleDliInterfaceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl,
OHOS::HDI::Nearlink::Hci::V1_1::ISleHciInterface::GetDescriptor());
if (hdfSleDliInterfaceHost->stub == nullptr) {
HDF_LOGE("%{public}s: failed to get stub object", __func__);
delete hdfSleDliInterfaceHost;
return HDF_FAILURE;
}
deviceObject->service = &hdfSleDliInterfaceHost->ioService;
return HDF_SUCCESS;
}
static void HdfSleDliInterfaceDriverRelease(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("%{public}s: driver release start", __func__);
if (deviceObject->service == nullptr) {
HDF_LOGE("HdfSleDliInterfaceDriverRelease not initted");
return;
}
auto *hdfSleDliInterfaceHost = CONTAINER_OF(deviceObject->service, struct HdfSleDliInterfaceHost, ioService);
if (hdfSleDliInterfaceHost != nullptr) {
delete hdfSleDliInterfaceHost;
}
}
struct HdfDriverEntry g_sledliinterfaceDriverEntry = {
.moduleVersion = 1,
.moduleName = "nearlink_dli",
.Bind = HdfSleDliInterfaceDriverBind,
.Init = HdfSleDliInterfaceDriverInit,
.Release = HdfSleDliInterfaceDriverRelease,
};
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
HDF_INIT(g_sledliinterfaceDriverEntry);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,178 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sle_hci_interface_impl.h"
#include <unistd.h>
#include <hdf_base.h>
#include "nearlink_hdf_log.h"
#include <iproxy_broker.h>
#include "vendor_interface.h"
#include "sle_hal_constant.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Hci {
namespace V1_1 {
using VendorInterface = OHOS::HDI::Nearlink::Dli::V1_1::VendorInterface;
using DliPacketType = OHOS::HDI::Nearlink::Dli::DliPacketType;
thread_local bool SleHciInterfaceImpl::isThreadPromoted = false;
using namespace OHOS::HDI::Nearlink::Dli;
extern "C" ISleHciInterface *SleHciInterfaceImplGetInstance(void)
{
return new (std::nothrow) SleHciInterfaceImpl();
}
SleHciInterfaceImpl::SleHciInterfaceImpl()
{
remoteDeathRecipient_ =
new RemoteDeathRecipient(std::bind(&SleHciInterfaceImpl::OnRemoteDied, this, std::placeholders::_1));
}
SleHciInterfaceImpl::~SleHciInterfaceImpl()
{
std::lock_guard<std::mutex> lock(dliCallbackMutex_);
if (callbacks_ != nullptr) {
RemoveDliDeathRecipient(callbacks_);
callbacks_ = nullptr;
}
}
int32_t SleHciInterfaceImpl::SleHalInit(const sptr<OHOS::HDI::Nearlink::Hci::V1_0::ISleHciCallback>& callbackObj)
{
HDF_LOGI("SleHciInterfaceImpl %{public}s", __func__);
if (callbackObj == nullptr) {
HDF_LOGE("SleHciInterfaceImpl %{public}s callbackObj null", __func__);
return HDF_FAILURE;
}
VendorInterface::ReceiveCallback callback = {
.onAcbReceive =
[callbackObj](
const std::vector<uint8_t> &packet) { callbackObj->hciPacketReceived(SleType::ACB_DATA, packet); },
.onIcbReceive =
[callbackObj](
const std::vector<uint8_t> &packet) { callbackObj->hciPacketReceived(SleType::ICB_DATA, packet); },
.onEventReceive =
[callbackObj](
const std::vector<uint8_t> &packet) { callbackObj->hciPacketReceived(SleType::HCI_EVENT, packet); },
};
bool result = VendorInterface::GetInstance()->Initialize(
[callbackObj](bool status) {
callbackObj->initializationComplete(status ? SleStatus::SUCCESS : SleStatus::INITIALIZATION_ERROR);
}, callback);
if (result) {
std::lock_guard<std::mutex> lock(dliCallbackMutex_);
callbacks_ = callbackObj;
AddDliDeathRecipient(callbacks_);
}
return result ? HDF_SUCCESS : HDF_FAILURE;
}
int32_t SleHciInterfaceImpl::SleSendHciPacket(const std::vector<uint8_t>& data)
{
HDF_LOGD("SleHciInterfaceImpl %{public}s", __func__);
if (data.empty()) {
return HDF_FAILURE;
}
SetRTSchedule();
size_t result = VendorInterface::GetInstance()->SendPacket(data);
return result ? HDF_SUCCESS : HDF_FAILURE;
}
int32_t SleHciInterfaceImpl::CheckOnBoardState(bool& result)
{
HDF_LOGI("SleHciInterfaceImpl %{public}s", __func__);
result = true;
return HDF_SUCCESS;
}
int32_t SleHciInterfaceImpl::Close()
{
HDF_LOGI("SleHciInterfaceImpl %{public}s", __func__);
{
std::lock_guard<std::mutex> lock(dliCallbackMutex_);
if (callbacks_ != nullptr) {
RemoveDliDeathRecipient(callbacks_);
callbacks_ = nullptr;
}
}
VendorInterface::GetInstance()->CleanUp();
VendorInterface::DestroyInstance();
return HDF_SUCCESS;
}
void SleHciInterfaceImpl::OnRemoteDied(const wptr<IRemoteObject> &object)
{
HDF_LOGI("SleHciInterfaceImpl %{public}s", __func__);
{
std::lock_guard<std::mutex> lock(dliCallbackMutex_);
callbacks_ = nullptr;
}
VendorInterface::GetInstance()->CleanUp();
VendorInterface::DestroyInstance();
}
int32_t SleHciInterfaceImpl::AddDliDeathRecipient(const sptr<ISleHciCallback>& callbackObj)
{
HDF_LOGI("SleHciInterfaceImpl %{public}s", __func__);
const sptr<IRemoteObject>& remote = OHOS::HDI::hdi_objcast<ISleHciCallback>(callbackObj);
if (!remote) {
HDF_LOGE("remote is nullptr");
return HDF_FAILURE;
}
bool result = remote->AddDeathRecipient(remoteDeathRecipient_);
if (!result) {
HDF_LOGE("SleHciInterfaceImpl AddDeathRecipient fail");
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
int32_t SleHciInterfaceImpl::RemoveDliDeathRecipient(const sptr<ISleHciCallback>& callbackObj)
{
HDF_LOGI("SleHciInterfaceImpl %{public}s", __func__);
const sptr<IRemoteObject>& remote = OHOS::HDI::hdi_objcast<ISleHciCallback>(callbackObj);
if (!remote) {
HDF_LOGE("remote is nullptr");
return HDF_FAILURE;
}
bool result = remote->RemoveDeathRecipient(remoteDeathRecipient_);
if (!result) {
HDF_LOGE("SleHciInterfaceImpl RemoveDeathRecipient fail");
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
void SleHciInterfaceImpl::SetRTSchedule()
{
if (isThreadPromoted) {
return;
}
pid_t tid = gettid();
struct sched_param rtParams = {.sched_priority = SLE_THREAD_PRIORITY};
int rc = sched_setscheduler(tid, SCHED_FIFO, &rtParams);
isThreadPromoted = (rc != 0) ? false : true;
}
} // V1_1
} // Hci
} // Nearlink
} // HDI
} // OHOS
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_DLI_V1_1_SLEDLIINTERFACEIMPL_H
#define OHOS_HDI_NEARLINK_DLI_V1_1_SLEDLIINTERFACEIMPL_H
#include "v1_1/isle_hci_interface.h"
#include "v1_0/isle_hci_callback.h"
#include "remote_death_recipient.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Hci {
namespace V1_1 {
using namespace OHOS::HDI::Nearlink::Dli::V1_1;
class SleHciInterfaceImpl : public OHOS::HDI::Nearlink::Hci::V1_1::ISleHciInterface {
public:
SleHciInterfaceImpl();
virtual ~SleHciInterfaceImpl();
int32_t SleHalInit(const sptr<OHOS::HDI::Nearlink::Hci::V1_0::ISleHciCallback>& callbackObj) override;
int32_t SleSendHciPacket(const std::vector<uint8_t>& data) override;
int32_t Close() override;
int32_t CheckOnBoardState(bool& result) override;
private:
void OnRemoteDied(const wptr<IRemoteObject> &object);
sptr<ISleHciCallback> callbacks_ = nullptr;
sptr<RemoteDeathRecipient> remoteDeathRecipient_ = nullptr;
int32_t AddDliDeathRecipient(const sptr<ISleHciCallback>& callbackObj);
int32_t RemoveDliDeathRecipient(const sptr<ISleHciCallback>& callbackObj);
void SetRTSchedule();
std::mutex dliCallbackMutex_;
thread_local static bool isThreadPromoted;
};
} // V1_1
} // Hci
} // Nearlink
} // HDI
} // OHOS
#endif // OHOS_HDI_NEARLINK_DLI_V1_1_SleHciInterfaceImplE_H
+44
View File
@@ -0,0 +1,44 @@
# Copyright (C) 2026 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/test.gni")
group("nearlink_hdi_test") {
testonly = true
deps = [
":NearlinkHdiTest",
]
}
ohos_unittest("NearlinkHdiTest") {
module_out_path = "drivers_peripheral_nearlink/drivers_peripheral_nearlink/"
sources = [
"nearlink_hdi_test.cpp",
]
include_dirs = [
"../dli_service",
]
external_deps = [
"c_utils:utils",
"hdf_core:libhdf_host",
"hdf_core:libhdf_utils",
"hilog:libhilog",
"ipc:ipc_single",
"drivers_interface_nearlink:libnearlink_hci_proxy_1.0",
"drivers_interface_nearlink:libnearlink_hci_proxy_1.1",
]
}
+131
View File
@@ -0,0 +1,131 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <hdf_log.h>
#include <vector>
#include "v1_1/isle_hci_interface.h"
#include "v1_0/isle_hci_callback.h"
using namespace testing::ext;
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace Hci {
namespace V1_1 {
sptr<ISleHciInterface> nearlinkDliInterface = nullptr;
class NearlinkDliHdiTest : public testing::Test {
public:
static void SetUpTestCase(void);
static void TearDownTestCase(void);
void SetUp();
void TearDown();
};
class NearlinkDliCallbacksForTest : public ISleHciCallback {
public:
NearlinkDliCallbacksForTest()
{}
~NearlinkDliCallbacksForTest()
{}
int32_t initializationComplete(SleStatus status)
{
GTEST_LOG_(INFO) << "NeearlinkDliCallbacksForTest::initializationComplete called";
return 0;
}
int32_t hciPacketReceived(uint32_t type, const std::vector<uint8_t> &data)
{
GTEST_LOG_(INFO) << "NeearlinkDliCallbacksForTest::hciPacketReceived called";
return 0;
}
};
void NearlinkDliHdiTest::SetUpTestCase(void)
{
// input testsuit setup stepsetup invoked before all testcases
nearlinkDliInterface = OHOS::HDI::Nearlink::Hci::V1_1::ISleHciInterface::Get(true);
if (nearlinkDliInterface == nullptr) {
HDF_LOGE("nearlinkDliHdiTest nearlinkDliInterface is nullptr\n");
}
}
void NearlinkDliHdiTest::TearDownTestCase(void)
{
// input testsuit teardown stepteardown invoked after all testcases
}
void NearlinkDliHdiTest::SetUp(void)
{
// input testcase setup stepsetup invoked before each testcases
}
void NearlinkDliHdiTest::TearDown(void)
{
// input testcase teardown stepteardown invoked after each testcases
}
/**
* @tc.name: HDI_nearlinkDliInterface_001_SleHalInitTest
* @tc.desc: Test nearlinkDliInterface SleHalInit.
* @tc.type: FUNC
*/
HWTEST_F(NearlinkDliHdiTest, HDI_nearlinkDliInterface_001_SleHalInitTest, TestSize.Level1)
{
sptr<NearlinkDliCallbacksForTest> callback_ = new NearlinkDliCallbacksForTest();
int ret = nearlinkDliInterface->SleHalInit(callback_);
EXPECT_EQ(-1, ret);
}
/**
* @tc.name: HDI_nearlinkDliInterface_002_SleSendHciPacketTest
* @tc.desc: Test nearlinkDliInterface SleSendHciPacket.
* @tc.type: FUNC
*/
HWTEST_F(NearlinkDliHdiTest, HDI_nearlinkDliInterface_002_SleSendHciPacketTest, TestSize.Level1)
{
sptr<NearlinkDliCallbacksForTest> callback_ = new NearlinkDliCallbacksForTest();
int ret = nearlinkDliInterface->SleHalInit(callback_);
EXPECT_EQ(-1, ret);
std::vector<uint8_t> sleHciTestData = {0xA1, 0x07, 0x10, 0x02, 0x00, 0x00, 0x00};
ret = nearlinkDliInterface->SleSendHciPacket(sleHciTestData);
EXPECT_EQ(-1, ret);
}
/**
* @tc.name: HDI_nearlinkDliInterface_003_SleCloseTest
* @tc.desc: Test nearlinkDliInterface SleClose.
* @tc.type: FUNC
*/
HWTEST_F(NearlinkDliHdiTest, HDI_nearlinkDliInterface_003_SleCloseTest, TestSize.Level1)
{
sptr<NearlinkDliCallbacksForTest> callback_ = new NearlinkDliCallbacksForTest();
int ret = nearlinkDliInterface->SleHalInit(callback_);
EXPECT_EQ(-1, ret);
ret = nearlinkDliInterface->Close();
EXPECT_EQ(0, ret);
}
} // V1_1
} // Hci
} // Nearlink
} // HDI
} // OHOS
+17
View File
@@ -0,0 +1,17 @@
# Copyright (C) 2026 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
declare_args() {
drivers_peripheral_nearlink_feature_support_send_disable_hci = false
drivers_peripheral_nearlink_reload_address = false
}
+26
View File
@@ -0,0 +1,26 @@
# Copyright (C) 2026 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/ohos.gni")
if (defined(ohos_lite)) {
group("off_find_entry") {
deps = []
}
} else {
group("off_find_entry") {
deps = [
"hdi_service:hdf_off_find_service",
]
}
}
+158
View File
@@ -0,0 +1,158 @@
# Copyright (C) 2026 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/config/components/hdi/hdi.gni")
import("//out/products_ext/hmos_ext_var.gni")
import("../../drivers_peripheral_nearlink.gni")
ohos_shared_library("liboff_find_interface_service_1.0") {
sanitize = {
cfi = true # Enable/disable control flow integrity detection
boundary_sanitize = true # Enable boundary san detection
cfi_cross_dso = true # Cross-SO CFI Checks
integer_overflow = true # Enable integer overflow detection
ubsan = true # Enable some Ubsan options
debug = false
}
branch_protector_ret = "pac_ret"
cflags = [
"-Os",
"-fdata-sections",
"-ffunction-sections",
"-fno-unwind-tables",
"-fno-asynchronous-unwind-tables",
"-fno-exceptions",
"-fno-rtti",
"-fPIC",
"-fno-common",
"-fstack-protector-strong",
"-fvisibility-inlines-hidden",
"-flto",
]
cflags_cc = cflags
ldflags = [
"-Wl,--as-needed",
"-Wl,--gc-sections",
"-Wl,--no-whole-archive",
]
include_dirs = [
".",
"implement",
]
sources = [
"off_find_interface_impl.cpp",
"implement/h4_protocol.cpp",
"implement/dli_protocol.cpp",
"implement/dli_watcher.cpp",
"implement/vendor_interface.cpp",
"implement/thread_util.cpp",
]
defines = []
if (drivers_peripheral_nearlink_feature_support_send_disable_hci) {
defines += [ "SEND_DISABLE_OFF_FIND_DLI" ]
}
if (is_standard_system) {
external_deps = [
"c_utils:utils",
"drivers_interface_nearlink:off_find_idl_headers",
"hdf_core:libhdf_host",
"hdf_core:libhdf_utils",
"hilog:libhilog",
"ipc:ipc_core",
"ffrt:libffrt",
"init:libbegetutil"
]
} else {
external_deps = [
"hilog:libhilog",
"ipc:ipc_core",
]
}
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_nearlink"
}
ohos_shared_library("liboff_find_hdi_driver") {
sanitize = {
cfi = true # Enable/disable control flow integrity detection
boundary_sanitize = true # Enable boundary san detection
cfi_cross_dso = true # Cross-SO CFI Checks
integer_overflow = true # Enable integer overflow detection
ubsan = true # Enable some Ubsan options
debug = false
}
branch_protector_ret = "pac_ret"
cflags = [
"-Os",
"-fdata-sections",
"-ffunction-sections",
"-fno-unwind-tables",
"-fno-asynchronous-unwind-tables",
"-fno-exceptions",
"-fno-rtti",
"-fPIC",
"-fno-common",
"-fstack-protector-strong",
"-fvisibility-inlines-hidden",
"-flto",
]
cflags_cc = cflags
ldflags = [
"-Wl,--as-needed",
"-Wl,--gc-sections",
"-Wl,--no-whole-archive",
]
include_dirs = [ "." ]
sources = [ "off_find_interface_driver.cpp" ]
if (is_standard_system) {
external_deps = [
"c_utils:utils",
"hdf_core:libhdf_host",
"hdf_core:libhdf_ipc_adapter",
"hdf_core:libhdf_utils",
"hdf_core:libhdi",
"hilog:libhilog",
"ipc:ipc_single",
]
} else {
external_deps = [
"hilog:libhilog",
"ipc:ipc_single",
]
}
external_deps += [ "drivers_interface_nearlink:liboff_find_stub_1.0" ]
shlib_type = "hdi"
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_nearlink"
}
group("hdf_off_find_service") {
deps = [
":liboff_find_hdi_driver",
":liboff_find_interface_service_1.0",
]
}
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_DLI_INTERNAL_H
#define OHOS_HDI_NEARLINK_DLI_INTERNAL_H
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
typedef enum {
DLI_PACKET_TYPE_UNKNOWN = 0,
DLI_PACKET_TYPE_SLE_CMD = 0xA1,
DLI_PACKET_TYPE_SLE_EVENT = 0xA2,
DLI_PACKET_TYPE_MAX,
} DliPacketType;
struct PacketHeader {
uint8_t headerSize;
uint8_t dataLengthOffset;
uint8_t dataLengthSize;
};
// opcode (DLI_SLE_Cmd_Complete_Evt opcode)
const uint16_t SLE_DLI_COMMAND_COMPLETE_EVENT = 0x0002;
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_DLI_INTERNAL_H */
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include "off_find_log.h"
#include "dli_protocol.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
const PacketHeader DliProtocol::header_[DLI_PACKET_TYPE_MAX] = {
{.headerSize = 0, .dataLengthOffset = 0, .dataLengthSize = 0}, /* 0 */
{.headerSize = 4, .dataLengthOffset = 2, .dataLengthSize = 2}, /* 1 */
{.headerSize = 4, .dataLengthOffset = 2, .dataLengthSize = 2}, /* 4 */
};
const PacketHeader &DliProtocol::GetPacketHeaderInfo(DliPacketType packetType)
{
if (packetType >= DLI_PACKET_TYPE_MAX) {
return header_[DLI_PACKET_TYPE_UNKNOWN];
}
return header_[DliProtocol::TypeConvert(packetType)];
}
ssize_t DliProtocol::Read(int fd, uint8_t *data, size_t length)
{
const int bufsize = 256;
char buf[bufsize] = {0};
ssize_t ret = TEMP_FAILURE_RETRY(read(fd, data, length));
if (ret == -1) {
strerror_r(errno, buf, sizeof(buf));
HDF_LOGE("read failed err:%s", buf);
ret = 0;
}
return ret;
}
ssize_t DliProtocol::Write(int fd, const uint8_t *data, size_t length)
{
const int bufsize = 256;
char buf[bufsize] = {0};
ssize_t ret = 0;
do {
ret = TEMP_FAILURE_RETRY(write(fd, data, length));
} while (ret == -1 && errno == EAGAIN);
if (ret == -1) {
strerror_r(errno, buf, sizeof(buf));
HDF_LOGE("write failed err:%s", buf);
} else if (static_cast<size_t>(ret) != length) {
HDF_LOGE("write data %zd less than %zu.", ret, length);
}
return ret;
}
uint8_t DliProtocol::TypeConvert(DliPacketType packetType)
{
switch (packetType) {
case DLI_PACKET_TYPE_SLE_EVENT:
return static_cast<uint8_t>(DLI_PACKET_TYPE_SLE_EVENT) & 0x0F;
default:
HDF_LOGE("type convert error.");
return 0;
}
}
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_DLI_PROTOCOL_H
#define OHOS_HDI_NEARLINK_DLI_PROTOCOL_H
#include <cstdio>
#include <vector>
#include <functional>
#include "dli_internal.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
class DliProtocol {
public:
using DliDataCallback = std::function<void(const std::vector<uint8_t> &data)>;
DliProtocol() = default;
virtual ~DliProtocol() = default;
virtual ssize_t SendPacket(const std::vector<uint8_t> &packetData) = 0;
const PacketHeader& GetPacketHeaderInfo(DliPacketType packetType);
protected:
static ssize_t Read(int fd, uint8_t *data, size_t length);
static ssize_t Write(int fd, const uint8_t *data, size_t length);
static uint8_t TypeConvert(DliPacketType packetType);
static const PacketHeader header_[DLI_PACKET_TYPE_MAX];
};
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_DLI_PROTOCOL_H */
@@ -0,0 +1,186 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <thread>
#include <unistd.h>
#include <sys/select.h>
#include <sys/syscall.h>
#include "off_find_log.h"
#include "off_find_hdi_constant.h"
#include "dli_watcher.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
DliWatcher::DliWatcher()
{}
DliWatcher::~DliWatcher()
{
Stop();
}
bool DliWatcher::AddFdToWatcher(int fd, DliDataCallback callback)
{
std::lock_guard<std::mutex> lock(fdsMutex_);
fds_[fd] = callback;
ThreadWakeup();
return true;
}
bool DliWatcher::RemoveFdToWatcher(int fd)
{
std::lock_guard<std::mutex> lock(fdsMutex_);
fds_.erase(fd);
ThreadWakeup();
return true;
}
bool DliWatcher::SetTimeout(std::chrono::milliseconds timeout, TimeoutCallback callback)
{
std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(timeout);
std::lock_guard<std::mutex> lock(timeoutMutex_);
timeoutTimer_.tv_sec = seconds.count();
timeoutTimer_.tv_usec = (timeout - seconds).count();
timeoutCallback_ = callback;
ThreadWakeup();
return true;
}
bool DliWatcher::Start()
{
if (running_.exchange(true)) {
return true;
}
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
if (pipe(wakeupPipe_) != 0) {
HDF_LOGE("DliWatcher create pipe failed.");
running_.exchange(false);
return false;
}
}
thread_ = std::thread(std::bind(&DliWatcher::WatcherThread, this));
if (!thread_.joinable()) {
HDF_LOGE("thread is not joinable.");
running_.exchange(false);
return false;
}
int policy = SLE_THREAD_POLICY;
sched_param params = {.sched_priority = SLE_THREAD_PRIORITY};
if (pthread_setschedparam(thread_.native_handle(), policy, &params) != 0) {
HDF_LOGW("pthread_setschedparam failed tid[%lu] policy[%d]", thread_.native_handle(), policy);
}
return true;
}
bool DliWatcher::Stop()
{
if (!running_.exchange(false)) {
return true;
}
ThreadWakeup();
thread_.join();
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
close(wakeupPipe_[0]);
close(wakeupPipe_[1]);
}
return true;
}
void DliWatcher::CheckFdReady(std::map<int, DliDataCallback>& fds, fd_set &readFds)
{
std::lock_guard<std::mutex> lock(fdsMutex_);
for (auto &&fd : fds_) {
if (FD_ISSET(fd.first, &readFds)) {
fd.second(fd.first);
}
}
}
void DliWatcher::WatcherThread()
{
fd_set readFds;
int nfds;
timeval *timeout = nullptr;
while (running_) {
FD_ZERO(&readFds);
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
FD_SET(wakeupPipe_[0], &readFds);
nfds = wakeupPipe_[0];
}
{
std::lock_guard<std::mutex> lock(fdsMutex_);
for (auto &&fd : fds_) {
FD_SET(fd.first, &readFds);
nfds = std::max(fd.first, nfds);
}
}
{
std::lock_guard<std::mutex> lock(timeoutMutex_);
if (timeoutTimer_.tv_sec == 0 && timeoutTimer_.tv_usec == 0) {
timeout = nullptr;
} else {
timeout = &timeoutTimer_;
}
}
int ret = select(nfds + 1, &readFds, nullptr, nullptr, timeout);
if (ret < 0) {
continue;
} else if (ret == 0) {
TimeoutCallback callback;
{
std::lock_guard<std::mutex> lock(timeoutMutex_);
callback = timeoutCallback_;
}
if (callback) {
callback();
}
} else {
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
if (FD_ISSET(wakeupPipe_[0], &readFds)) {
uint8_t buff = 0;
TEMP_FAILURE_RETRY(read(wakeupPipe_[0], &buff, sizeof(buff)));
}
}
CheckFdReady(fds_, readFds);
}
}
}
void DliWatcher::ThreadWakeup()
{
std::lock_guard<std::mutex> lock(wakeupPipeMutex_);
uint8_t buff = 0;
TEMP_FAILURE_RETRY(write(wakeupPipe_[1], &buff, sizeof(buff)));
}
} // namespace Dli
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_HCI_WATCHER_H
#define OHOS_HDI_NEARLINK_HCI_WATCHER_H
#include <atomic>
#include <ctime>
#include <functional>
#include <map>
#include <mutex>
#include <sys/time.h>
#include <thread>
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
class DliWatcher {
public:
using DliDataCallback = std::function<void(int fd)>;
using TimeoutCallback = std::function<void()>;
DliWatcher();
~DliWatcher();
bool AddFdToWatcher(int fd, DliDataCallback callback);
bool RemoveFdToWatcher(int fd);
bool SetTimeout(std::chrono::milliseconds timeout, TimeoutCallback callback);
bool Start();
bool Stop();
private:
void CheckFdReady(std::map<int, DliDataCallback>& fds, fd_set &readFds);
void WatcherThread();
void ThreadWakeup();
private:
std::atomic_bool running_ = {false};
std::map<int, DliDataCallback> fds_;
std::mutex fdsMutex_;
std::mutex wakeupPipeMutex_;
int wakeupPipe_[2] = {0};
timeval timeoutTimer_ = {};
TimeoutCallback timeoutCallback_;
std::mutex timeoutMutex_;
std::thread thread_;
};
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_HCI_WATCHER_H */
@@ -0,0 +1,125 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cerrno>
#include <cstring>
#include "off_find_log.h"
#include "h4_protocol.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
H4Protocol::H4Protocol(
int fd, DliDataCallback onEventReceive)
: dliFd_(fd), onEventReceive_(onEventReceive)
{}
ssize_t H4Protocol::SendPacket(const std::vector<uint8_t> &packetData)
{
ssize_t writtenNumber = 0;
ssize_t ret = 0;
do {
ret = Write(dliFd_, packetData.data() + writtenNumber, packetData.size() - writtenNumber);
if (ret > 0) {
writtenNumber += ret;
} else if (ret < 0) {
return ret;
}
} while (static_cast<size_t>(writtenNumber) < packetData.size());
return writtenNumber;
}
static bool ReadLengthCheck(ssize_t readLen, char *buf, int fd)
{
const int bufsize = 256;
if (readLen < 0) {
strerror_r(errno, buf, bufsize);
HDF_LOGE("read fd[%d] err:%s", fd, buf);
return false;
} else if (readLen == 0) {
HDF_LOGE("read fd[%d] readLen = 0.", fd);
return false;
}
return true;
}
void H4Protocol::ReadData(int fd)
{
const int bufsize = 256;
char buf[bufsize] = {0};
static uint8_t typeNo{0};
ssize_t readLen = 0;
if (dliPacket_.size() == 0) {
readLen = Read(fd, &packetType_, sizeof(packetType_));
if (readLen < 0) {
HDF_LOGE("read fd[%d]", fd);
return;
} else if (readLen == 0) {
HDF_LOGE("read fd[%d] readLen = 0.", fd);
return;
}
typeNo = (packetType_) & 0x0F;
if (packetType_ > DLI_PACKET_TYPE_UNKNOWN && packetType_ < DLI_PACKET_TYPE_MAX) {
dliPacket_.resize(header_[typeNo].headerSize);
}
} else if (dliPacket_.size() == header_[typeNo].headerSize) {
readLen = Read(fd, dliPacket_.data() + readLength_, dliPacket_.size() - readLength_);
if (!ReadLengthCheck(readLen, buf, fd)) {
return;
}
readLength_ += readLen;
if (readLength_ == dliPacket_.size()) {
size_t dataLen = 0;
for (int ii = 0; ii < header_[typeNo].dataLengthSize; ii++) {
dataLen += (dliPacket_[header_[typeNo].dataLengthOffset + ii] << (ii * 0x08));
}
dliPacket_.resize(dliPacket_.size() + dataLen);
}
} else {
readLen = Read(fd, dliPacket_.data() + readLength_, dliPacket_.size() - readLength_);
if (!ReadLengthCheck(readLen, buf, fd)) {
return;
}
readLength_ += readLen;
if (readLength_ == dliPacket_.size()) {
PacketCallback();
dliPacket_.clear();
readLength_ = 0;
typeNo = 0;
}
}
}
H4Protocol::~H4Protocol() {}
void H4Protocol::PacketCallback()
{
switch (packetType_) {
case DLI_PACKET_TYPE_SLE_EVENT:
if (onEventReceive_) {
onEventReceive_(dliPacket_);
}
break;
default:
HDF_LOGE("PacketCallback type[%d] error.", packetType_);
break;
}
}
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_HCI_H4_PROTOCOL_H
#define OHOS_HDI_NEARLINK_HCI_H4_PROTOCOL_H
#include "dli_protocol.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
class H4Protocol : public DliProtocol {
public:
H4Protocol(int fd, DliDataCallback onEventReceive);
ssize_t SendPacket(const std::vector<uint8_t> &packetData) override;
void ReadData(int fd);
~H4Protocol() override;
private:
void PacketCallback();
private:
int dliFd_ = 0;
DliDataCallback onEventReceive_;
uint8_t packetType_ = 0;
std::vector<uint8_t> dliPacket_;
uint32_t readLength_ = 0;
};
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_HCI_H4_PROTOCOL_H */
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_OFF_FIND_CONSTANT_H
#define OHOS_HDI_NEARLINK_OFF_FIND_CONSTANT_H
#include <string>
#include <sched.h>
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
constexpr const char *OFF_FIND_VENDOR_NAME = "liboff_find_vendor";
constexpr const char *SLE_OFF_FIND_CHIP_TYPE = "const.nearlink.off_find.slechiptype";
constexpr const char *OFF_FIND_VENDOR_INTERFACE_SYMBOL_NAME = "NEARLINK_OFF_FIND_VENDOR_LIB_INTERFACE";
constexpr const char *OFF_FIND_VENDOR_COMMON_NAME = "liboff_find_vendor_common_ext.z.so";
constexpr const char *OFF_FIND_VENDOR_COMMON_INTERFACE_SYMBOL_NAME = "NEARLINK_OFF_FIND_VENDOR_COMMON_LIB_INTERFACE";
constexpr int SLE_THREAD_POLICY = SCHED_RR;
constexpr int SLE_THREAD_PRIORITY = 1;
/**
* Define OFF FIND MODEL ENABLE OR DISABLE RESULT.
*/
typedef enum {
SUCCESS, // HCI Command channel
INITIALIZATION_ERROR, // HCI Event channel
} OffFindSwitchResult;
typedef enum {
OFF_FIND_SUCCESS,
OFF_FIND_FAIL,
OFF_FIND_SYNC_FAIL,
} OffFindErrorCode;
/**
* Define POWER MODE CMD.
*/
typedef enum {
CLEAR_RESERVED_POWER,
SET_RESERVED_POWER
} PmuPowerMode;
/**
* Define off find capability.
*/
typedef enum {
OF_SLE_ADV_ENABLE = 0x01,
OF_SLE_RING_SUPPORT = 0X02,
} OfSleFeatureFlag;
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_OFF_FIND_CONSTANT_H */
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OFF_FIND_LOG_H
#define OFF_FIND_LOG_H
#include <hdf_log.h>
#ifdef LOG_DOMAIN
#undef LOG_DOMAIN
#endif
#define LOG_DOMAIN 0xD000154
#ifndef HDF_LOG_TAG
#define HDF_LOG_TAG hdf_off_find
#endif
#endif // OFF_FIND_LOG_H
@@ -0,0 +1,114 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OFF_FIND_VENDOR_LIB_H
#define OFF_FIND_VENDOR_LIB_H
#include <cstdint>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Define DLI channel descriptors array used in SLE_OP_DLI_CHANNEL_OPEN operation.
*/
typedef enum {
DLI_CMD, // DLI Command channel
DLI_EVT, // DLI Event channel
DLI_MAX_CHANNEL // Total channels
} DliChannelsT;
/**
* SLE vendor lib cmd.
*/
typedef enum {
/**
* Set Resvered Power for off find mode,it will be called when findnetwork enable.
*/
SLE_OP_RESERVERD_POWER_SET,
/**
* Establish dli channels. it will be called after SLE_OP_POWER_ON.
* @param int (*)[DLI_MAX_CHANNEL].
* @return fd count.
*/
SLE_OP_DLI_CHANNEL_OPEN,
/**
* Close all the dli channels which is opened.
*/
SLE_OP_DLI_CHANNEL_CLOSE,
/**
* Set off find mode.
* @param int *powerTime.
*/
SLE_OP_OFF_FIND_MODE_ENABLE,
/**
* Disable off find mode.
*/
SLE_OP_OFF_FIND_MODE_DISABLE,
/**
* Enable Leakage Prevention Mode mode.
*/
SLE_OP_OFF_FIND_POWER_PROTECT_ENABLE,
} OffFindOpcodeT;
/**
* Nearlink Host/Controller VENDOR Interface
*/
typedef struct {
/**
* Set to sizeof(sle_vndor_interface_t)
*/
size_t size;
/**
* Caller will open the interface and pass in the callback routines
* to the implementation of this interface.
*/
int (*init)(void);
/**
* Vendor specific operations
*/
int (*op)(int opcode, void* param);
/**
* Closes the interface
*/
void (*close)(void);
} OffFindVendorInterfaceT;
typedef struct {
size_t size;
int (*modifyPowerProtect)(int powerTime, int power);
bool (*isNeedWatcherStart)(std::string chipType);
uint32_t (*getRegisterTimerDuration)(std::string chipType);
void (*close)(void);
} OffFindVendorCommInterfaceT;
typedef struct {
uint16_t event;
uint16_t len;
uint16_t offset;
uint16_t layerSpecific;
uint8_t data[];
} HC_SLE_HDR;
#ifdef __cplusplus
}
#endif
#endif /* SLE_VENDOR_LIB_H */
@@ -0,0 +1,109 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "thread_util.h"
#include <mutex>
#include "ffrt_inner.h"
#include <hdf_log.h>
namespace OHOS {
namespace HDI {
namespace Nearlink {
void DoInDliThread(const ThreadUtilFunc &func)
{
ThreadUtil::GetInstance().PostTask(func);
}
struct ThreadUtil::impl {
class TaskQueue {
public:
explicit TaskQueue(const char *name) : queue_(name, ffrt::queue_attr().qos(ffrt::qos_user_interactive)) {}
~TaskQueue() = default;
void PostTask(const ThreadUtilFunc &func);
int GetQueueId(void);
private:
ffrt::queue queue_;
};
impl();
~impl() = default;
std::shared_ptr<TaskQueue> CreateTaskQueue();
std::shared_ptr<TaskQueue> taskQueue;
};
ThreadUtil::impl::impl()
{}
ThreadUtil::ThreadUtil() : pimpl(std::make_unique<impl>())
{}
ThreadUtil::~ThreadUtil()
{}
void ThreadUtil::impl::TaskQueue::PostTask(const ThreadUtilFunc &func)
{
queue_.submit(func);
}
int ThreadUtil::impl::TaskQueue::GetQueueId(void)
{
int id = -1;
auto handle = queue_.submit_h([&id]() {
id = ffrt::get_queue_id();
});
queue_.wait(handle);
return id;
}
void ThreadUtil::PostTask(const ThreadUtilFunc &func)
{
std::lock_guard<ffrt::mutex> lock(taskQueueMutex_);
std::shared_ptr<impl::TaskQueue> taskQueue = pimpl->taskQueue;
if (taskQueue != nullptr) {
taskQueue->PostTask(func);
return;
}
// If the thread not found, create it.
taskQueue = pimpl->CreateTaskQueue();
// Execute the first task.
if (taskQueue) {
taskQueue->PostTask(func);
}
}
std::shared_ptr<ThreadUtil::impl::TaskQueue> ThreadUtil::impl::CreateTaskQueue()
{
std::string threadName = "sle_hci";
auto taskQueue_ = std::make_shared<TaskQueue>(threadName.c_str());
int queueId = taskQueue_->GetQueueId();
HDF_LOGI("sle_ffrt_queue: queueId(%{public}d), name(%{public}s)", queueId, threadName.c_str());
taskQueue = taskQueue_;
return taskQueue_;
}
ThreadUtil &ThreadUtil::GetInstance()
{
static ThreadUtil instance;
return instance;
}
} // namespace Nearlink
} // namepsace HDI
} // namespace OHOS
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SLE_THREAD_UTIL_H
#define SLE_THREAD_UTIL_H
#include <functional>
#include "ffrt_inner.h"
#include <hdf_log.h>
#include "off_find_log.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
using ThreadUtilFunc = std::function<void(void)>;
/**
* @brief Post the task to the dli thread.
* @param func The task to be executed.
*/
void DoInDliThread(const ThreadUtilFunc &func);
class ThreadUtil {
public:
void PostTask(const ThreadUtilFunc &func);
static ThreadUtil &GetInstance();
private:
ThreadUtil();
~ThreadUtil();
struct impl;
std::unique_ptr<impl> pimpl;
ffrt::mutex taskQueueMutex_ {};
};
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif // SLE_THREAD_UTIL_H
@@ -0,0 +1,620 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <chrono>
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <securec.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <thread>
#include "off_find_log.h"
#include "h4_protocol.h"
#include "common_timer_errors.h"
#include "parameters.h"
#include "vendor_interface.h"
#include "thread_util.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
namespace V1_0 {
// disable off find cmd
#ifdef SEND_DISABLE_OFF_FIND_DLI
constexpr uint8_t DLI_DISABLE_OFFFIDN_CMD = 0x16;
constexpr uint8_t DLI_DISABLE_OFFFIDN_PARAM = 0x19;
#endif
// Opcode
constexpr uint16_t DLI_SWITCH_HOST = 0xFC92;
constexpr uint16_t DLI_SET_POWER_OFF_PARAM = 0xFC91;
// Power off parm
constexpr size_t DLI_PACKET_MIN_SIZE = 8;
constexpr size_t DLI_PACKET_HEADER_SIZE = 5;
constexpr size_t DLI_DATA_LEN_SIZE = 3;
constexpr size_t DLI_ADVTIME_SIZE = 4;
constexpr size_t ADV_FLAG_SIZE = 2; // ble and sle adv enable flag 1 on 0 off
// User and device Id len LIMIT
constexpr size_t DEVICE_ID_LEN = 64;
constexpr size_t USER_ID_LEN = 32;
// Advtising data len limit
constexpr size_t ADV_KEY_LEN = 32;
constexpr size_t ADV_CMAC_LEN = 2;
constexpr int OFFSET_EIGHT_BITS = 8;
// battery and correspond pwoerTime
constexpr int BATTERY_ENTRY = 3;
constexpr int BATTERY_HIGH_ENTRY = 10; // 关机七天可找电池阈值
constexpr int DEFAULT_CHIP_STEP = 1; // 默认芯片步长
constexpr int HIGH_POWER_TIME = 168; // 关机七天 7 day = 7*24h
constexpr int UP_POWER_TIME = 48;
constexpr int DOWN_POWER_TIME = 12;
constexpr int AIR_OTA_POWER_TIME = 168; // 隔空升级 7 day = 7*24h
constexpr size_t DEFAULT_ADV_DATA_LEN = 3; // 关机3d下发广播数组长度
constexpr size_t SEVEN_DAYS_ADV_DATA_LEN = 8; // 关机7d下发广播数组长度
// Data len dli position
constexpr int THIRD_BYTE = 3;
constexpr int FOURTH_BYTE = 4;
constexpr int FIFTH_BYTE = 5;
constexpr int SIXTH_BYTE = 6;
constexpr int OFFSET_STATUS = 3;
// Data len limit
constexpr size_t DATA_LEN_BYTE = 2;
constexpr int TIME_OUT_MAX = 3000;
// Timeot ms
constexpr int TIME_OUT_INIT_CHMOD = 500;
typedef struct InternalCommand {
uint16_t cbsh;
uint16_t cbofp;
uint16_t opcode;
} InternalCommand;
InternalCommand g_internalCommand = {0, 0, 0};
constexpr size_t SLE_VENDOR_INVALID_DATA_LEN = 0;
VendorInterface::VendorInterface()
{
timer_ = std::make_unique<OHOS::Utils::Timer>("NearlinkOffFindTimer");
timer_->Setup();
offFindTimerId_ = 0;
}
VendorInterface::~VendorInterface()
{
if (timer_) {
timer_->Shutdown();
}
CleanUp();
}
bool VendorInterface::SetSwitchHostTimer()
{
if (timer_ == nullptr) {
HDF_LOGE("timer_ is nulptr");
return false;
}
auto func = []() {
HDF_LOGI("SetSwitchHost TimeOut");
VendorInterface::GetInstance()->OnInitCallback(OFF_FIND_FAIL);
};
uint32_t timeOut = TIME_OUT_MAX;
if (LoadVendorCommonLibrary() == OFF_FIND_SUCCESS &&
vendorCommInterface_->getRegisterTimerDuration != NULL) {
timeOut = vendorCommInterface_->getRegisterTimerDuration(chipType_);
}
uint32_t ret = timer_->Register(func, timeOut, true); // only support once timer now
if (ret == OHOS::Utils::TIMER_ERR_DEAL_FAILED) {
HDF_LOGE("Register timer failed");
return false;
}
offFindTimerId_ = ret;
HDF_LOGI("offFindTimerId_ set: %{public}u", offFindTimerId_);
return true;
}
void VendorInterface::DliPacketHearderGet(uint16_t paramLen)
{
offFindDli_.push_back(DLI_PACKET_TYPE_SLE_CMD);
offFindDli_.push_back(static_cast<uint8_t>(DLI_SET_POWER_OFF_PARAM));
offFindDli_.push_back(static_cast<uint8_t>((DLI_SET_POWER_OFF_PARAM) >> OFFSET_EIGHT_BITS));
// Total data len in header
offFindDli_.push_back(static_cast<uint8_t>(paramLen - DLI_PACKET_HEADER_SIZE));
offFindDli_.push_back(static_cast<uint8_t>((paramLen - DLI_PACKET_HEADER_SIZE) >> OFFSET_EIGHT_BITS));
// Total data len in data head
offFindDli_.push_back(static_cast<uint8_t>(paramLen - DLI_PACKET_HEADER_SIZE));
offFindDli_.push_back(static_cast<uint8_t>((paramLen - DLI_PACKET_HEADER_SIZE) >> OFFSET_EIGHT_BITS));
}
#ifdef SEND_DISABLE_OFF_FIND_DLI
void VendorInterface::DisableDliPacketHearderGet()
{
// disable offfind cmd
offFindDli_.push_back(DLI_PACKET_TYPE_SLE_CMD);
offFindDli_.push_back(DLI_DISABLE_OFFFIDN_CMD);
offFindDli_.push_back(DLI_DISABLE_OFFFIDN_PARAM);
}
#endif
uint8_t VendorInterface::GetSleCapability(int power)
{
uint8_t sleCapability = power <= BATTERY_ENTRY ? 0 : OF_SLE_ADV_ENABLE;
if (OHOS::system::GetBoolParameter("const.nearlink.support.off_ring", false) && sleCapability != 0) {
sleCapability = sleCapability | OF_SLE_RING_SUPPORT;
}
return sleCapability;
}
bool VendorInterface::PowerOffDliGet(
const std::vector<OffFindParam>& data, const OffFindExtraInfo& info, size_t paramsCnt)
{
HDF_LOGI("data[%{public}zu]udid[%{public}zu]uid[%{public}zu]", data.size(), info.udid.size(), info.uid.size());
if (data.size() != paramsCnt || info.udid.size() > DEVICE_ID_LEN || info.uid.size() > USER_ID_LEN) {
return false;
}
uint8_t sleCapability = GetSleCapability(info.battery);
HDF_LOGI("power[%{public}d] sle[%{public}d]", info.battery, sleCapability);
uint16_t paramLen = DLI_PACKET_HEADER_SIZE + ADV_FLAG_SIZE + DLI_DATA_LEN_SIZE + info.udid.size() + info.uid.size()
+ data.size() * DLI_DATA_LEN_SIZE;
uint16_t dataLen = 0;
offFindDli_.reserve(static_cast<int>(paramLen));
DliPacketHearderGet(paramLen);
offFindDli_.push_back(1); // ble adv flag (default on)
offFindDli_.push_back(sleCapability); // sle capability
offFindDli_.push_back(info.udid.size());
offFindDli_.insert(offFindDli_.end(), info.udid.begin(), info.udid.end());
offFindDli_.push_back(info.uid.size());
offFindDli_.insert(offFindDli_.end(), info.uid.begin(), info.uid.end());
offFindDli_.insert(offFindDli_.end(), data.size());
for (size_t i = 0; i < data.size(); i++) {
HDF_LOGI("advertising key or cmac len [%{public}zu][%{public}zu][%{public}zu]",
data[i].publicKey.size(), data[i].cmac.size(), data[i].aesKey.size());
if (data[i].publicKey.size() > ADV_KEY_LEN || data[i].cmac.size() > ADV_CMAC_LEN ||
data[i].aesKey.size() > ADV_KEY_LEN) {
return false;
}
offFindDli_.push_back(data[i].publicKey.size());
offFindDli_.insert(offFindDli_.end(), data[i].publicKey.begin(), data[i].publicKey.end());
offFindDli_.push_back(data[i].cmac.size());
offFindDli_.insert(offFindDli_.end(), data[i].cmac.begin(), data[i].cmac.end());
offFindDli_.push_back(data[i].aesKey.size());
offFindDli_.insert(offFindDli_.end(), data[i].aesKey.begin(), data[i].aesKey.end());
std::vector<uint8_t> advTime(DLI_ADVTIME_SIZE);
for (size_t j = 0; j < DLI_ADVTIME_SIZE; j++) {
advTime[DLI_ADVTIME_SIZE - j - 1] =
(data[i].advTime >> ((DLI_ADVTIME_SIZE-1 - j) * OFFSET_EIGHT_BITS)) & 0xFF;
}
offFindDli_.insert(offFindDli_.end(), advTime.begin(), advTime.end());
dataLen += data[i].publicKey.size() + data[i].cmac.size() + data[i].aesKey.size() + DLI_ADVTIME_SIZE;
}
offFindDli_[THIRD_BYTE] = static_cast<uint8_t>(paramLen + dataLen + DATA_LEN_BYTE - DLI_PACKET_HEADER_SIZE);
offFindDli_[FOURTH_BYTE] = static_cast<uint8_t>(
(paramLen + dataLen + DATA_LEN_BYTE - DLI_PACKET_HEADER_SIZE) >> OFFSET_EIGHT_BITS);
offFindDli_[FIFTH_BYTE] = static_cast<uint8_t>(paramLen + dataLen - DLI_PACKET_HEADER_SIZE);
offFindDli_[SIXTH_BYTE] = static_cast<uint8_t>(
(paramLen + dataLen - DLI_PACKET_HEADER_SIZE) >> OFFSET_EIGHT_BITS);
return true;
}
void VendorInterface::SwitchHostDliGet(std::vector<uint8_t>& data)
{
data.push_back(DLI_PACKET_TYPE_SLE_CMD);
data.push_back(static_cast<uint8_t>(DLI_SWITCH_HOST));
data.push_back(static_cast<uint8_t>((DLI_SWITCH_HOST) >> OFFSET_EIGHT_BITS));
data.push_back(0x00);
data.push_back(0x00);
g_internalCommand.cbsh = 1;
g_internalCommand.opcode = DLI_SWITCH_HOST;
}
bool VendorInterface::WatchDliChannel()
{
int channel[DLI_MAX_CHANNEL] = {0};
int channelCount = vendorInterface_->op(OffFindOpcodeT::SLE_OP_DLI_CHANNEL_OPEN, static_cast<void *>(channel));
if (channelCount < 1 || channelCount > DLI_MAX_CHANNEL) {
HDF_LOGE("vendorInterface_->op SLE_OP_DLI_CHANNEL_OPEN failed ret:%{public}d.", channelCount);
return false;
}
if (channelCount == 1) {
auto h4 = std::make_shared<OffFind::H4Protocol>(channel[0],
std::bind(&VendorInterface::OnEventReceived, this, std::placeholders::_1));
watcher_.AddFdToWatcher(channel[0], std::bind(&OffFind::H4Protocol::ReadData, h4, std::placeholders::_1));
dli_ = h4;
} else {
HDF_LOGE("invalid channel count:%d.", channelCount);
return false;
}
return true;
}
void VendorInterface::SetReservedPower()
{
if (isInitialized_.load()) {
HDF_LOGE("already Initialized");
return;
}
std::lock_guard<ffrt::recursive_mutex> lock(processMutex_);
if (vendorHandle_ == nullptr || vendorInterface_ == nullptr) {
vendorHandle_ = dlopen(GetFullVendorName(), RTLD_NOW);
if (vendorHandle_ == nullptr) {
HDF_LOGE("VendorInterface dlopen %{public}s failed, error code: %{public}s", offFindVendorName_.c_str(),
dlerror());
return;
}
vendorInterface_ =
reinterpret_cast<OffFindVendorInterfaceT *>(dlsym(vendorHandle_, OFF_FIND_VENDOR_INTERFACE_SYMBOL_NAME));
if (vendorInterface_ == nullptr) {
HDF_LOGE("VendorInterface dlsym %{public}s failed.", OFF_FIND_VENDOR_INTERFACE_SYMBOL_NAME);
return;
}
}
// dev_offfind node init time sometimes later than findnetwork process init when startup
std::this_thread::sleep_for(std::chrono::milliseconds(TIME_OUT_INIT_CHMOD));
vendorInterface_->init();
int powerSetFlag = SET_RESERVED_POWER;
vendorInterface_->op(OffFindOpcodeT::SLE_OP_RESERVERD_POWER_SET, static_cast<void *>(&powerSetFlag));
CleanUp();
}
int VendorInterface::GetPowerTimeParameter(const OffFindExtraInfo& info, size_t paramsCnt)
{
int power = info.battery <= BATTERY_ENTRY ?
OHOS::system::GetIntParameter<int>("const.nearlink.off_find.low_battery_time", DOWN_POWER_TIME)
: OHOS::system::GetIntParameter<int>("const.nearlink.off_find.high_battery_time", UP_POWER_TIME);
if (paramsCnt == SEVEN_DAYS_ADV_DATA_LEN && info.battery > BATTERY_HIGH_ENTRY) {
power = HIGH_POWER_TIME;
}
bool isAirOta = std::all_of(info.uid.begin(), info.uid.end(), [](unsigned char c) {
return c == 0xFF;
});
if (!info.uid.empty() && isAirOta) {
power = AIR_OTA_POWER_TIME;
}
int chipStep = OHOS::system::GetIntParameter<int>("const.nearlink.off_find.chip_step", DEFAULT_CHIP_STEP);
if (chipStep <= 0) {
chipStep = DEFAULT_CHIP_STEP;
}
if (chipStep > 0) {
power /= chipStep;
}
return power;
}
OffFindErrorCode VendorInterface::OpenAndSymDl()
{
if (vendorHandle_ == nullptr || vendorInterface_ == nullptr) {
vendorHandle_ = dlopen(GetFullVendorName(), RTLD_NOW);
if (vendorHandle_ == nullptr) {
HDF_LOGE("dlopen %{public}s failed, error code: %{public}s", offFindVendorName_.c_str(), dlerror());
return OFF_FIND_SYNC_FAIL;
}
vendorInterface_ =
reinterpret_cast<OffFindVendorInterfaceT *>(dlsym(vendorHandle_, OFF_FIND_VENDOR_INTERFACE_SYMBOL_NAME));
if (vendorInterface_ == nullptr) {
HDF_LOGE("VendorInterface dlsym %{public}s failed.", OFF_FIND_VENDOR_INTERFACE_SYMBOL_NAME);
return OFF_FIND_SYNC_FAIL;
}
}
return OFF_FIND_SUCCESS;
}
OffFindErrorCode VendorInterface::LoadVendorCommonLibrary()
{
if (vendorCommHandle_ != nullptr && vendorCommInterface_ != nullptr) {
return OFF_FIND_SUCCESS;
}
vendorCommHandle_ = dlopen(OFF_FIND_VENDOR_COMMON_NAME, RTLD_NOW);
if (vendorCommHandle_ == nullptr) {
HDF_LOGE("dlopen %{public}s failed, errCode: %{public}s", OFF_FIND_VENDOR_COMMON_NAME, dlerror());
return OFF_FIND_SYNC_FAIL;
}
vendorCommInterface_ = reinterpret_cast<OffFindVendorCommInterfaceT *>(dlsym(vendorCommHandle_,
OFF_FIND_VENDOR_COMMON_INTERFACE_SYMBOL_NAME));
if (vendorCommInterface_ == nullptr) {
HDF_LOGE("dlsym failed %{public}s, err: %{public}s", OFF_FIND_VENDOR_COMMON_INTERFACE_SYMBOL_NAME, dlerror());
dlclose(vendorCommHandle_);
return OFF_FIND_SYNC_FAIL;
}
HDF_LOGI("LoadVendorCommonLibrary success %{public}s", OFF_FIND_VENDOR_COMMON_NAME);
return OFF_FIND_SUCCESS;
}
OffFindErrorCode VendorInterface::EnableOffFindMode(InitializeCompleteCallback initializeCompleteCallback,
const std::vector<OffFindParam>& data, const OffFindExtraInfo& info)
{
std::lock_guard<ffrt::recursive_mutex> lock(processMutex_);
size_t paramsCnt =
OHOS::system::GetUintParameter<size_t>("const.findnetwork.shutdown_params.count", DEFAULT_ADV_DATA_LEN);
HDF_LOGI("EnableOffFindMode Begin, %{public}zu params", paramsCnt);
if (!PowerOffDliGet(data, info, paramsCnt)) {
HDF_LOGE("PowerOffDliGet ERROR");
return OFF_FIND_SYNC_FAIL;
}
initializeCompleteCallback_ = initializeCompleteCallback;
if (OpenAndSymDl() != OFF_FIND_SUCCESS || LoadVendorCommonLibrary() != OFF_FIND_SUCCESS) {
return OFF_FIND_SYNC_FAIL;
}
vendorInterface_->init();
if (!WatchDliChannel()) {
HDF_LOGE("WatchDliChannel Init Fail");
return OFF_FIND_SYNC_FAIL;
}
if (vendorCommInterface_->isNeedWatcherStart != NULL &&
vendorCommInterface_->isNeedWatcherStart(chipType_) && !watcher_.Start()) {
HDF_LOGE("watcher start failed.");
return OFF_FIND_SYNC_FAIL;
}
int powerTime = GetPowerTimeParameter(info, paramsCnt);
HDF_LOGI("battery[%{public}d] and powerTime[%{public}d]", info.battery, powerTime);
if (vendorCommInterface_->modifyPowerProtect != NULL &&
vendorCommInterface_->modifyPowerProtect(powerTime, info.battery) != OFF_FIND_SUCCESS) {
HDF_LOGE("modifyPowerProtect failed");
return OFF_FIND_SYNC_FAIL;
}
if (!SetSwitchHostTimer()) {
HDF_LOGE("OffFind Mode SetTimer failed");
return OFF_FIND_SYNC_FAIL;
}
int ret = vendorInterface_->op(OffFindOpcodeT::SLE_OP_OFF_FIND_MODE_ENABLE, static_cast<void *>(&powerTime));
if (ret < 0) {
HDF_LOGE("OffFind Mode Enable failed");
return OFF_FIND_FAIL;
}
g_internalCommand.cbofp = 1;
dliCompleteFlag_ = false;
dli_->SendPacket(offFindDli_);
offFindDli_.clear();
return OFF_FIND_SUCCESS;
}
void VendorInterface::RegisterClearOffFindCallback(ClearIpcCallback clearIpcCallback)
{
clearIpcCallback_ = clearIpcCallback;
}
bool VendorInterface::Initialize(InitializeCompleteCallback initializeCompleteCallback,
const std::vector<OffFindParam>& data, const OffFindExtraInfo& info)
{
if (isInitialized_.load()) {
HDF_LOGE("already Initialized");
return false;
}
isInitialized_.store(true);
OffFindErrorCode ret = OFF_FIND_SUCCESS;
ret = EnableOffFindMode(initializeCompleteCallback, data, info);
if (ret == OFF_FIND_SUCCESS) {
return true;
}
OnInitCallback(ret);
return false;
}
const char* VendorInterface::GetFullVendorName()
{
if (offFindVendorName_ != "") {
return offFindVendorName_.c_str();
}
offFindVendorName_ = OFF_FIND_VENDOR_NAME;
std::string sleChipType = OHOS::system::GetParameter(SLE_OFF_FIND_CHIP_TYPE, "");
HDF_LOGI("sleChipType is %{public}s", sleChipType.c_str());
if (!sleChipType.empty()) {
offFindVendorName_.append("_");
offFindVendorName_.append(sleChipType.c_str());
chipType_.append(sleChipType);
}
offFindVendorName_.append(".z.so");
HDF_LOGI("vendor name is %{public}s", offFindVendorName_.c_str());
return offFindVendorName_.c_str();
}
void VendorInterface::DisableOffFindMode()
{
std::lock_guard<ffrt::recursive_mutex> lock(processMutex_);
if (vendorHandle_ == nullptr || vendorInterface_ == nullptr) {
vendorHandle_ = dlopen(GetFullVendorName(), RTLD_NOW);
if (vendorHandle_ == nullptr) {
HDF_LOGE("VendorInterface dlopen %{public}s failed, error code: %{public}s", offFindVendorName_.c_str(),
dlerror());
return;
}
vendorInterface_ =
reinterpret_cast<OffFindVendorInterfaceT *>(dlsym(vendorHandle_, OFF_FIND_VENDOR_INTERFACE_SYMBOL_NAME));
if (vendorInterface_ == nullptr) {
HDF_LOGE("VendorInterface dlsym %{public}s failed.", OFF_FIND_VENDOR_INTERFACE_SYMBOL_NAME);
return;
}
}
vendorInterface_->init();
#ifdef SEND_DISABLE_OFF_FIND_DLI
if (!WatchDliChannel()) {
HDF_LOGE("WatchDliChannel Init Fail");
}
DisableDliPacketHearderGet();
if (dli_ != nullptr) {
dli_->SendPacket(offFindDli_);
}
offFindDli_.clear();
vendorInterface_->op(OffFindOpcodeT::SLE_OP_DLI_CHANNEL_CLOSE, nullptr);
#endif
int powerSetFlag = CLEAR_RESERVED_POWER;
vendorInterface_->op(OffFindOpcodeT::SLE_OP_RESERVERD_POWER_SET, static_cast<void *>(&powerSetFlag));
isInitialized_.store(false);
vendorInterface_->op(OffFindOpcodeT::SLE_OP_OFF_FIND_MODE_DISABLE, nullptr);
vendorInterface_->close();
CleanUp();
}
void VendorInterface::CleanUp()
{
std::lock_guard<ffrt::recursive_mutex> lock(processMutex_);
if (vendorInterface_ == nullptr) {
HDF_LOGE("VendorInterface::CleanUp, vendorInterface_ is nullptr.");
return;
}
HDF_LOGI("off find clean up");
watcher_.Stop();
dli_ = nullptr;
vendorInterface_ = nullptr;
initializeCompleteCallback_ = nullptr;
eventDataCallback_ = nullptr;
dlclose(vendorHandle_);
vendorCommInterface_ = nullptr;
dlclose(vendorCommHandle_);
}
size_t VendorInterface::SendPacket(const std::vector<uint8_t> &packet)
{
if (vendorInterface_ == nullptr || dli_ == nullptr) {
HDF_LOGE("VendorInterface::SendPacket, vendorInterface_ is nullptr.");
return SLE_VENDOR_INVALID_DATA_LEN;
}
watcher_.SetTimeout(std::chrono::milliseconds(lpmTimer_), std::bind(&VendorInterface::WatcherTimeout, this));
return dli_->SendPacket(packet);
}
void VendorInterface::OnInitCallback(OffFindErrorCode result)
{
HDF_LOGI("OnInitCallback %{public}d", result);
OffFindSwitchResult status = OffFindSwitchResult::INITIALIZATION_ERROR;
if (result != OFF_FIND_SYNC_FAIL) {
if (offFindTimerId_ == 0) {
HDF_LOGE("offFindTimerId_ is 0, no registered timer");
} else {
HDF_LOGI("OnInitCallback offFindTimerId_: %{public}d", offFindTimerId_);
if (timer_ == nullptr) {
HDF_LOGE("timer_ is nulptr");
} else {
timer_->Unregister(offFindTimerId_);
}
}
if (result == OFF_FIND_SUCCESS) {
status = OffFindSwitchResult::SUCCESS;
} else {
vendorInterface_->op(OffFindOpcodeT::SLE_OP_OFF_FIND_MODE_DISABLE, nullptr);
}
}
isInitialized_.store(false);
if (initializeCompleteCallback_) {
initializeCompleteCallback_(status);
initializeCompleteCallback_ = nullptr;
if (clearIpcCallback_) {
clearIpcCallback_();
}
}
CleanUp();
}
bool VendorInterface::EventReceivedHandler(const std::vector<uint8_t> &data)
{
if (data.size() < DLI_PACKET_MIN_SIZE) {
HDF_LOGE("packet len err , [%{public}zu]", data.size());
return false;
}
uint16_t eventOpcode = data[0] | (data[1] << OFFSET_EIGHT_BITS);
// 如果 eventOpcode 不是 SLE_DLI_COMMAND_COMPLETE_EVENT,可能是残留数据不做任何处理,直接返回 true,后续正常的COMPLETE_EVENT会继续返回
if (eventOpcode != SLE_DLI_COMMAND_COMPLETE_EVENT) {
HDF_LOGW("eventOpcode[%{public}04X] is not complete event", eventOpcode);
return true;
}
size_t opcodeOffset = dli_->GetPacketHeaderInfo(DLI_PACKET_TYPE_SLE_EVENT).headerSize;
uint16_t opcode = data[opcodeOffset] + (data[opcodeOffset + 1] << OFFSET_EIGHT_BITS);
HDF_LOGI("opcode[%{public}04X], cbofp[%{public}d], cbsh[%{public}d], status[%{public}d]",
opcode, g_internalCommand.cbofp, g_internalCommand.cbsh, data[opcodeOffset + OFFSET_STATUS]);
if (g_internalCommand.cbofp != 0 && opcode == DLI_SET_POWER_OFF_PARAM) {
if (data[opcodeOffset + OFFSET_STATUS] != 0) {
HDF_LOGE("set param event status[%{public}d] error", data[opcodeOffset + OFFSET_STATUS]);
return false;
}
g_internalCommand.cbofp = 0;
g_internalCommand.cbsh = 1;
g_internalCommand.opcode = DLI_SWITCH_HOST;
std::vector<uint8_t> switchHostDli;
switchHostDli.reserve(DLI_PACKET_HEADER_SIZE);
SwitchHostDliGet(switchHostDli);
dli_->SendPacket(switchHostDli);
return true;
} else if (g_internalCommand.cbsh != 0 && opcode == DLI_SWITCH_HOST) {
if (data[opcodeOffset + OFFSET_STATUS] != 0) {
HDF_LOGE("switch host status[%{public}d] error", data[opcodeOffset + OFFSET_STATUS]);
return false;
}
int ret = vendorInterface_->op(OffFindOpcodeT::SLE_OP_OFF_FIND_POWER_PROTECT_ENABLE, nullptr);
if (ret < 0) {
HDF_LOGE("OffFind power cfg config failed");
return false;
}
dliCompleteFlag_ = true;
g_internalCommand.cbsh = 0;
return true;
} else {
HDF_LOGE("unknown dli");
return false;
}
}
void VendorInterface::OnEventReceived(const std::vector<uint8_t> &data)
{
Nearlink::DoInDliThread([this, acbData = data]() {
this->OnEventReceivedTask(acbData);
});
return;
}
void VendorInterface::OnEventReceivedTask(const std::vector<uint8_t> &data)
{
HDF_LOGI("OnEventReceivedTask");
bool ret = EventReceivedHandler(data);
if (ret && dliCompleteFlag_) {
OnInitCallback(OFF_FIND_SUCCESS);
} else if (!ret) {
OnInitCallback(OFF_FIND_FAIL);
} else {
return ;
}
}
void VendorInterface::WatcherTimeout()
{
HDF_LOGI("fd wait out of time, no data ready");
}
} // namespace V1_0
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,99 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_OFF_FIND_V1_0_VENDOR_INTERFACE_H
#define OHOS_HDI_NEARLINK_OFF_FIND_V1_0_VENDOR_INTERFACE_H
#include <functional>
#include <vector>
#include "timer.h"
#include "singleton.h"
#include "off_find_hdi_constant.h"
#include "dli_internal.h"
#include "dli_protocol.h"
#include "dli_watcher.h"
#include "ffrt_inner.h"
#include "v1_0/off_find_types.h"
#include "ohos_off_find_vendor_lib.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
namespace V1_0 {
class VendorInterface : public DelayedSingleton<VendorInterface> {
public:
using InitializeCompleteCallback = std::function<void(int32_t result)>;
using ClearIpcCallback = std::function<void()>;
using ReceiveDataCallback = DliProtocol::DliDataCallback;
void SetReservedPower();
void RegisterClearOffFindCallback(ClearIpcCallback clearIpcCallback
);
bool Initialize(InitializeCompleteCallback initializeCompleteCallback, const std::vector<OffFindParam>& data,
const OffFindExtraInfo& info);
void DisableOffFindMode();
void CleanUp();
size_t SendPacket(const std::vector<uint8_t> &packet);
void SetSleAddress(uint8_t *localSleAddr);
uint8_t *SetAddress(uint8_t *addr);
private:
OffFindErrorCode EnableOffFindMode(InitializeCompleteCallback initializeCompleteCallback,
const std::vector<OffFindParam>& data, const OffFindExtraInfo& info);
OffFindErrorCode OpenAndSymDl();
OffFindErrorCode LoadVendorCommonLibrary();
bool EventReceivedHandler(const std::vector<uint8_t> &data);
void OnInitCallback(OffFindErrorCode result);
void OnEventReceived(const std::vector<uint8_t> &data);
void OnEventReceivedTask(const std::vector<uint8_t> &data);
bool WatchDliChannel();
void WatcherTimeout();
InitializeCompleteCallback initializeCompleteCallback_;
ClearIpcCallback clearIpcCallback_;
ReceiveDataCallback eventDataCallback_;
void* vendorHandle_ = nullptr;
OffFindVendorInterfaceT *vendorInterface_ = nullptr;
void* vendorCommHandle_ = nullptr;
OffFindVendorCommInterfaceT *vendorCommInterface_ = nullptr;
DliWatcher watcher_;
std::shared_ptr<DliProtocol> dli_ = nullptr;
std::vector<uint8_t> offFindDli_;
uint16_t vendorSentOpcode_ = 0;
uint32_t lpmTimer_ = 0;
std::unique_ptr<OHOS::Utils::Timer> timer_ {nullptr};
uint32_t offFindTimerId_;
std::string offFindVendorName_ = "";
std::string chipType_ = "";
bool dliCompleteFlag_ = false;
void DliPacketHearderGet(uint16_t paramLen);
#ifdef SEND_DISABLE_OFF_FIND_DLI
void DisableDliPacketHearderGet();
#endif
bool SetSwitchHostTimer();
uint8_t GetSleCapability(int power);
bool PowerOffDliGet(const std::vector<OffFindParam>& data, const OffFindExtraInfo& info, size_t paramsCnt);
void SwitchHostDliGet(std::vector<uint8_t>& data);
int GetPowerTimeParameter(const OffFindExtraInfo& info, size_t paramsCnt);
DECLARE_DELAYED_SINGLETON(VendorInterface);
DISALLOW_COPY_AND_MOVE(VendorInterface);
ffrt::recursive_mutex processMutex_;
const char* GetFullVendorName();
std::atomic_bool isInitialized_ = false;
};
} // namespace V1_0
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // namespace OHOS
#endif /* OHOS_HDI_NEARLINK_OFF_FIND_V1_0_VENDOR_INTERFACE_H */
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <hdf_base.h>
#include <hdf_device_desc.h>
#include <hdf_log.h>
#include <hdf_sbuf_ipc.h>
#include "v1_0/off_find_interface_stub.h"
#define HDF_LOG_TAG off_find_interface_driver
using namespace OHOS::HDI::Nearlink::OffFind::V1_0;
struct HdfOffFindInterfaceHost {
struct IDeviceIoService ioService;
OHOS::sptr<OHOS::IRemoteObject> stub;
HdfOffFindInterfaceHost()
{
ioService.object.objectId = 0;
ioService.Open = nullptr;
ioService.Release = nullptr;
ioService.Dispatch = nullptr;
}
};
static int32_t OffFindInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data,
struct HdfSBuf *reply)
{
auto *hdfOffFindInterfaceHost = CONTAINER_OF(client->device->service, struct HdfOffFindInterfaceHost, ioService);
OHOS::MessageParcel *dataParcel = nullptr;
OHOS::MessageParcel *replyParcel = nullptr;
OHOS::MessageOption option;
if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) {
HDF_LOGE("%{public}s: invalid data sbuf object to dispatch", __func__);
return HDF_ERR_INVALID_PARAM;
}
if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) {
HDF_LOGE("%{public}s: invalid reply sbuf object to dispatch", __func__);
return HDF_ERR_INVALID_PARAM;
}
return hdfOffFindInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option);
}
static int HdfOffFindInterfaceDriverInit(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("%{public}s: driver init start", __func__);
return HDF_SUCCESS;
}
static int HdfOffFindInterfaceDriverBind(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("%{public}s: driver bind start", __func__);
auto *hdfOffFindInterfaceHost = new (std::nothrow) HdfOffFindInterfaceHost;
if (hdfOffFindInterfaceHost == nullptr) {
HDF_LOGE("%{public}s: failed to create create HdfOffFindInterfaceHost object", __func__);
return HDF_FAILURE;
}
hdfOffFindInterfaceHost->ioService.Dispatch = OffFindInterfaceDriverDispatch;
hdfOffFindInterfaceHost->ioService.Open = NULL;
hdfOffFindInterfaceHost->ioService.Release = NULL;
auto serviceImpl = OHOS::HDI::Nearlink::OffFind::V1_0::IOffFindInterface::Get(true);
if (serviceImpl == nullptr) {
HDF_LOGE("%{public}s: failed to get of implement service", __func__);
delete hdfOffFindInterfaceHost;
return HDF_FAILURE;
}
hdfOffFindInterfaceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl,
OHOS::HDI::Nearlink::OffFind::V1_0::IOffFindInterface::GetDescriptor());
if (hdfOffFindInterfaceHost->stub == nullptr) {
HDF_LOGE("%{public}s: failed to get stub object", __func__);
delete hdfOffFindInterfaceHost;
return HDF_FAILURE;
}
deviceObject->service = &hdfOffFindInterfaceHost->ioService;
return HDF_SUCCESS;
}
static void HdfOffFindInterfaceDriverRelease(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("%{public}s: driver release start", __func__);
if (deviceObject->service == nullptr) {
return;
}
auto *hdfOffFindInterfaceHost = CONTAINER_OF(deviceObject->service, struct HdfOffFindInterfaceHost, ioService);
if (hdfOffFindInterfaceHost != nullptr) {
delete hdfOffFindInterfaceHost;
}
}
struct HdfDriverEntry g_offfindinterfaceDriverEntry = {
.moduleVersion = 1,
.moduleName = "off_find",
.Bind = HdfOffFindInterfaceDriverBind,
.Init = HdfOffFindInterfaceDriverInit,
.Release = HdfOffFindInterfaceDriverRelease,
};
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
HDF_INIT(g_offfindinterfaceDriverEntry);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,172 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <hdf_base.h>
#include <iproxy_broker.h>
#include "off_find_log.h"
#include "vendor_interface.h"
#include "off_find_interface_impl.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
namespace V1_0 {
using VendorInterface = OHOS::HDI::Nearlink::OffFind::V1_0::VendorInterface;
const std::string OFF_FIND_FFRT_THREAD = "OffFindHdf";
extern "C" IOffFindInterface *OffFindInterfaceImplGetInstance(void)
{
return new (std::nothrow) OffFindInterfaceImpl();
}
OffFindInterfaceImpl::OffFindInterfaceImpl()
{
remoteDeathRecipient_ =
new RemoteDeathRecipient(std::bind(&OffFindInterfaceImpl::OnRemoteDied, this, std::placeholders::_1));
if (eventQueue_ != nullptr) {
HDF_LOGI("eventQueue already init");
} else {
eventQueue_ = std::make_shared<ffrt::queue>(OFF_FIND_FFRT_THREAD.c_str());
HDF_LOGI("Create a new eventQueue, threadName:%{public}s", OFF_FIND_FFRT_THREAD.c_str());
}
}
OffFindInterfaceImpl::~OffFindInterfaceImpl()
{
if (callbacks_ != nullptr) {
RemoveHciDeathRecipient(callbacks_);
callbacks_ = nullptr;
}
if (eventQueue_) {
eventQueue_.reset();
}
}
int32_t OffFindInterfaceImpl::SetReservedPower()
{
HDF_LOGI("OffFindInterfaceImpl::SetReservedPower");
auto func = []() {
HDF_LOGI("asyc SetReservedPower work start");
VendorInterface::GetInstance()->SetReservedPower();
};
if (eventQueue_ == nullptr) {
HDF_LOGE("eventQueue is nullptr!");
return HDF_FAILURE;
}
eventQueue_->submit(func);
return HDF_SUCCESS;
}
int32_t OffFindInterfaceImpl::EnableOffFind(
const sptr<OHOS::HDI::Nearlink::OffFind::V1_0::IOffFindCallback>& callbackObj,
const std::vector<OHOS::HDI::Nearlink::OffFind::V1_0::OffFindParam>& data,
const OHOS::HDI::Nearlink::OffFind::V1_0::OffFindExtraInfo& info)
{
HDF_LOGI("OffFindInterfaceImpl::EnableOffFind");
auto func = [callbackObj, data, info, this]() {
HDF_LOGI("Enter EnableOffFind");
if (callbackObj == nullptr) {
HDF_LOGE("OffFindInterfaceImpl callbackObj null");
return ;
}
VendorInterface::GetInstance()->RegisterClearOffFindCallback(
[this]() {
HDF_LOGI("clear callbacks is success !");
if (callbacks_ != nullptr) {
RemoveHciDeathRecipient(callbacks_);
callbacks_ = nullptr;
}
}
);
bool result = VendorInterface::GetInstance()->Initialize(
[callbackObj](int32_t result) {callbackObj->OnOffFindEnableResult(result); }, data, info);
if (result) {
callbacks_ = callbackObj;
AddHciDeathRecipient(callbacks_);
}
};
if (eventQueue_ == nullptr) {
HDF_LOGE("eventQueue is nullptr!");
return HDF_FAILURE;
}
eventQueue_->submit(func);
return HDF_SUCCESS;
}
int32_t OffFindInterfaceImpl::DisableOffFind()
{
HDF_LOGI("OffFindInterfaceImpl::DisableOffFind");
auto func = []() {
HDF_LOGI("asyc disable work start");
VendorInterface::GetInstance()->DisableOffFindMode();
};
if (eventQueue_ == nullptr) {
HDF_LOGE("eventQueue is nullptr!");
return HDF_FAILURE;
}
eventQueue_->submit(func);
VendorInterface::DestroyInstance();
return HDF_SUCCESS;
}
void OffFindInterfaceImpl::OnRemoteDied(const wptr<IRemoteObject> &object)
{
HDF_LOGI("OffFindInterfaceImpl");
callbacks_ = nullptr;
VendorInterface::GetInstance()->CleanUp();
VendorInterface::DestroyInstance();
}
int32_t OffFindInterfaceImpl::AddHciDeathRecipient(const sptr<IOffFindCallback>& callbackObj)
{
HDF_LOGI("OffFindInterfaceImpl");
const sptr<IRemoteObject>& remote = OHOS::HDI::hdi_objcast<IOffFindCallback>(callbackObj);
if (!remote) {
HDF_LOGE("remote is nullptr");
return HDF_FAILURE;
}
bool result = remote->AddDeathRecipient(remoteDeathRecipient_);
if (!result) {
HDF_LOGE("OffFindInterfaceImpl AddDeathRecipient fail");
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
int32_t OffFindInterfaceImpl::RemoveHciDeathRecipient(const sptr<IOffFindCallback>& callbackObj)
{
HDF_LOGI("OffFindInterfaceImpl");
const sptr<IRemoteObject>& remote = OHOS::HDI::hdi_objcast<IOffFindCallback>(callbackObj);
if (!remote) {
HDF_LOGE("remote is nullptr");
return HDF_FAILURE;
}
bool result = remote->RemoveDeathRecipient(remoteDeathRecipient_);
if (!result) {
HDF_LOGE("OffFindInterfaceImpl RemoveDeathRecipient fail");
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
} // V1_0
} // OffFind
} // Nearlink
} // HDI
} // OHOS
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_OFF_FIND_V1_0_OFF_FINDINTERFACEIMPL_H
#define OHOS_HDI_NEARLINK_OFF_FIND_V1_0_OFF_FINDINTERFACEIMPL_H
#include "v1_0/ioff_find_interface.h"
#include "v1_0/ioff_find_callback.h"
#include "remote_death_recipient.h"
#include "ffrt_inner.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
namespace V1_0 {
class OffFindInterfaceImpl : public OHOS::HDI::Nearlink::OffFind::V1_0::IOffFindInterface {
public:
OffFindInterfaceImpl();
virtual ~OffFindInterfaceImpl();
int32_t SetReservedPower() override;
int32_t EnableOffFind(const sptr<OHOS::HDI::Nearlink::OffFind::V1_0::IOffFindCallback>& callbackObj,
const std::vector<OHOS::HDI::Nearlink::OffFind::V1_0::OffFindParam>& data,
const OHOS::HDI::Nearlink::OffFind::V1_0::OffFindExtraInfo& info) override;
int32_t DisableOffFind() override;
private:
void OnRemoteDied(const wptr<IRemoteObject> &object);
sptr<IOffFindCallback> callbacks_ = nullptr;
sptr<RemoteDeathRecipient> remoteDeathRecipient_ = nullptr;
int32_t AddHciDeathRecipient(const sptr<IOffFindCallback>& callbackObj);
int32_t RemoveHciDeathRecipient(const sptr<IOffFindCallback>& callbackObj);
std::shared_ptr<ffrt::queue> eventQueue_ = nullptr;
};
} // V1_0
} // OffFind
} // Nearlink
} // HDI
} // OHOS
#endif // OHOS_HDI_NEARLINK_OFF_FIND_V1_0_OffFindInterfaceImplE_H
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NEARLINK_OFF_FIND_V1_0_REMOTE_DEATH_RECIPIENT_H
#define OHOS_HDI_NEARLINK_OFF_FIND_V1_0_REMOTE_DEATH_RECIPIENT_H
#include <functional>
#include "iremote_object.h"
#include "refbase.h"
namespace OHOS {
namespace HDI {
namespace Nearlink {
namespace OffFind {
namespace V1_0 {
class RemoteDeathRecipient : public IRemoteObject::DeathRecipient {
public:
explicit RemoteDeathRecipient(std::function<void(const wptr<IRemoteObject> &)> callback)
{
callback_ = callback;
}
~RemoteDeathRecipient()
{
callback_ = nullptr;
}
void OnRemoteDied(const wptr<IRemoteObject> &object)
{
if (callback_ != nullptr) {
callback_(object);
}
}
private:
std::function<void(const wptr<IRemoteObject> &)> callback_;
};
} // namespace V1_0
} // namespace OffFind
} // namespace Nearlink
} // namespace HDI
} // OHOS
#endif // OHOS_HDI_NEARLINK_OFF_FIND_V1_0_REMOTE_DEATH_RECIPIENT_H