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 namespace OHOS {
25 namespace HDI {
26 namespace Bluetooth {
27 namespace Hci {
HciWatcher()28 HciWatcher::HciWatcher()
29 {}
30
~HciWatcher()31 HciWatcher::~HciWatcher()
32 {
33 Stop();
34 }
35
AddFdToWatcher(int fd,HciDataCallback callback)36 bool HciWatcher::AddFdToWatcher(int fd, HciDataCallback callback)
37 {
38 std::lock_guard<std::mutex> lock(fdsMutex_);
39 fds_[fd] = callback;
40 ThreadWakeup();
41 return true;
42 }
43
RemoveFdToWatcher(int fd)44 bool HciWatcher::RemoveFdToWatcher(int fd)
45 {
46 std::lock_guard<std::mutex> lock(fdsMutex_);
47 fds_.erase(fd);
48 ThreadWakeup();
49 return true;
50 }
51
SetTimeout(std::chrono::milliseconds timeout,TimeoutCallback callback)52 bool HciWatcher::SetTimeout(std::chrono::milliseconds timeout, TimeoutCallback callback)
53 {
54 std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(timeout);
55 std::lock_guard<std::mutex> lock(timeoutMutex_);
56 timeoutTimer_.tv_sec = seconds.count();
57 timeoutTimer_.tv_usec = (timeout - seconds).count();
58 timeoutCallback_ = callback;
59 ThreadWakeup();
60 return true;
61 }
62
Start()63 bool HciWatcher::Start()
64 {
65 if (running_.exchange(true)) {
66 return true;
67 }
68
69 if (pipe(wakeupPipe_) != 0) {
70 HDF_LOGE("HciWatcher create pipe failed.");
71 running_.exchange(false);
72 return false;
73 }
74
75 thread_ = std::thread(std::bind(&HciWatcher::WatcherThread, this));
76 if (!thread_.joinable()) {
77 HDF_LOGE("thread is not joinable.");
78 running_.exchange(false);
79 return false;
80 }
81
82 int policy = BT_THREAD_POLICY;
83 sched_param params = {.sched_priority = BT_THREAD_PRIORITY};
84 if (pthread_setschedparam(thread_.native_handle(), policy, ¶ms) != 0) {
85 HDF_LOGW("pthread_setschedparam failed tid[%lu] policy[%d]", thread_.native_handle(), policy);
86 }
87
88 return true;
89 }
90
Stop()91 bool HciWatcher::Stop()
92 {
93 if (!running_.exchange(false)) {
94 return true;
95 }
96
97 ThreadWakeup();
98 thread_.join();
99
100 close(wakeupPipe_[0]);
101 close(wakeupPipe_[1]);
102
103 return true;
104 }
105
WatcherThread()106 void HciWatcher::WatcherThread()
107 {
108 fd_set readFds;
109 int nfds;
110 timeval *timeout = nullptr;
111
112 while (running_) {
113 FD_ZERO(&readFds);
114 FD_SET(wakeupPipe_[0], &readFds);
115 nfds = wakeupPipe_[0];
116 {
117 std::lock_guard<std::mutex> lock(fdsMutex_);
118 for (auto &&fd : fds_) {
119 FD_SET(fd.first, &readFds);
120 nfds = std::max(fd.first, nfds);
121 }
122 }
123
124 {
125 std::lock_guard<std::mutex> lock(timeoutMutex_);
126 if (timeoutTimer_.tv_sec == 0 && timeoutTimer_.tv_usec == 0) {
127 timeout = nullptr;
128 } else {
129 timeout = &timeoutTimer_;
130 }
131 }
132
133 int ret = select(nfds + 1, &readFds, nullptr, nullptr, timeout);
134 if (ret < 0) {
135 continue;
136 } else if (ret == 0) {
137 TimeoutCallback callback;
138 {
139 std::lock_guard<std::mutex> lock(timeoutMutex_);
140 callback = timeoutCallback_;
141 }
142 if (callback) {
143 callback();
144 }
145 } else {
146 if (FD_ISSET(wakeupPipe_[0], &readFds)) {
147 uint8_t buff;
148 TEMP_FAILURE_RETRY(read(wakeupPipe_[0], &buff, sizeof(buff)));
149 }
150 std::lock_guard<std::mutex> lock(fdsMutex_);
151 for (auto &&fd : fds_) {
152 if (FD_ISSET(fd.first, &readFds)) {
153 fd.second(fd.first);
154 }
155 }
156 }
157 }
158 }
159
ThreadWakeup()160 void HciWatcher::ThreadWakeup()
161 {
162 uint8_t buff = 0;
163 TEMP_FAILURE_RETRY(write(wakeupPipe_[1], &buff, sizeof(buff)));
164 }
165 } // namespace Hci
166 } // namespace Bluetooth
167 } // namespace HDI
168 } // namespace OHOS