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, &params) != 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