1 /*
2 * Copyright (C) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "hci_watcher.h"
17 #include <thread>
18 #include <unistd.h>
19 #include <sys/select.h>
20 #include <sys/syscall.h>
21 #include <hdf_log.h>
22 #include "bt_hal_constant.h"
23
24 #ifdef LOG_DOMAIN
25 #undef LOG_DOMAIN
26 #endif
27 #define LOG_DOMAIN 0xD000105
28
29 namespace OHOS {
30 namespace HDI {
31 namespace Bluetooth {
32 namespace Hci {
HciWatcher()33 HciWatcher::HciWatcher()
34 {}
35
~HciWatcher()36 HciWatcher::~HciWatcher()
37 {
38 Stop();
39 }
40
AddFdToWatcher(int fd,HciDataCallback callback)41 bool HciWatcher::AddFdToWatcher(int fd, HciDataCallback callback)
42 {
43 std::lock_guard<std::mutex> lock(fdsMutex_);
44 fds_[fd] = callback;
45 ThreadWakeup();
46 return true;
47 }
48
RemoveFdToWatcher(int fd)49 bool HciWatcher::RemoveFdToWatcher(int fd)
50 {
51 std::lock_guard<std::mutex> lock(fdsMutex_);
52 fds_.erase(fd);
53 ThreadWakeup();
54 return true;
55 }
56
SetTimeout(std::chrono::milliseconds timeout,TimeoutCallback callback)57 bool HciWatcher::SetTimeout(std::chrono::milliseconds timeout, TimeoutCallback callback)
58 {
59 std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(timeout);
60 std::lock_guard<std::mutex> lock(timeoutMutex_);
61 timeoutTimer_.tv_sec = seconds.count();
62 timeoutTimer_.tv_usec = (timeout - seconds).count();
63 timeoutCallback_ = callback;
64 ThreadWakeup();
65 return true;
66 }
67
Start()68 bool HciWatcher::Start()
69 {
70 if (running_.exchange(true)) {
71 return true;
72 }
73
74 if (pipe(wakeupPipe_) != 0) {
75 HDF_LOGE("HciWatcher create pipe failed.");
76 running_.exchange(false);
77 return false;
78 }
79
80 thread_ = std::thread(std::bind(&HciWatcher::WatcherThread, this));
81 if (!thread_.joinable()) {
82 HDF_LOGE("thread is not joinable.");
83 running_.exchange(false);
84 return false;
85 }
86
87 int policy = BT_THREAD_POLICY;
88 sched_param params = {.sched_priority = BT_THREAD_PRIORITY};
89 if (pthread_setschedparam(thread_.native_handle(), policy, ¶ms) != 0) {
90 HDF_LOGW("pthread_setschedparam failed tid[%lu] policy[%d]", thread_.native_handle(), policy);
91 }
92
93 return true;
94 }
95
Stop()96 bool HciWatcher::Stop()
97 {
98 if (!running_.exchange(false)) {
99 return true;
100 }
101
102 ThreadWakeup();
103 thread_.join();
104
105 close(wakeupPipe_[0]);
106 close(wakeupPipe_[1]);
107
108 return true;
109 }
110
111 #define FD_SETSIZE 1024
112
WatcherThread()113 void HciWatcher::WatcherThread()
114 {
115 fd_set readFds;
116 int nfds;
117 timeval *timeout = nullptr;
118
119 while (running_) {
120 FD_ZERO(&readFds);
121 if (wakeupPipe_[0] > FD_SETSIZE) {
122 HDF_LOGW("fd[%d]", wakeupPipe_[0]);
123 return;
124 }
125 FD_SET(wakeupPipe_[0], &readFds);
126 nfds = wakeupPipe_[0];
127 {
128 std::lock_guard<std::mutex> lock(fdsMutex_);
129 for (auto &&fd : fds_) {
130 FD_SET(fd.first, &readFds);
131 nfds = std::max(fd.first, nfds);
132 }
133 }
134
135 {
136 std::lock_guard<std::mutex> lock(timeoutMutex_);
137 if (timeoutTimer_.tv_sec == 0 && timeoutTimer_.tv_usec == 0) {
138 timeout = nullptr;
139 } else {
140 timeout = &timeoutTimer_;
141 }
142 }
143
144 int ret = select(nfds + 1, &readFds, nullptr, nullptr, timeout);
145 if (ret < 0) {
146 continue;
147 } else if (ret == 0) {
148 TimeoutCallback callback;
149 {
150 std::lock_guard<std::mutex> lock(timeoutMutex_);
151 callback = timeoutCallback_;
152 }
153 if (callback) {
154 callback();
155 }
156 } else {
157 if (FD_ISSET(wakeupPipe_[0], &readFds)) {
158 uint8_t buff;
159 TEMP_FAILURE_RETRY(read(wakeupPipe_[0], &buff, sizeof(buff)));
160 }
161 std::lock_guard<std::mutex> lock(fdsMutex_);
162 for (auto &&fd : fds_) {
163 if (FD_ISSET(fd.first, &readFds)) {
164 fd.second(fd.first);
165 }
166 }
167 }
168 }
169 }
170
ThreadWakeup()171 void HciWatcher::ThreadWakeup()
172 {
173 uint8_t buff = 0;
174 TEMP_FAILURE_RETRY(write(wakeupPipe_[1], &buff, sizeof(buff)));
175 }
176 } // namespace Hci
177 } // namespace Bluetooth
178 } // namespace HDI
179 } // namespace OHOS