1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "NanohubHAL"
18
19 #include <fcntl.h>
20 #include <poll.h>
21 #include <unistd.h>
22 #include <sys/inotify.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25
26 #include <hardware/context_hub.h>
27 #include <hardware/hardware.h>
28
29 #include <utils/Log.h>
30 #include <cutils/properties.h>
31
32 #include <nanohub/nanohub.h>
33
34 #include <cinttypes>
35 #include <iomanip>
36 #include <sstream>
37
38 #include "nanohub_perdevice.h"
39 #include "system_comms.h"
40 #include "nanohubhal.h"
41
42 #define NANOHUB_LOCK_DIR "/data/vendor/sensor/nanohub_lock"
43 #define NANOHUB_LOCK_FILE NANOHUB_LOCK_DIR "/lock"
44 #define NANOHUB_LOCK_DIR_PERMS (S_IRUSR | S_IWUSR | S_IXUSR)
45
46 namespace android {
47
48 namespace nanohub {
49
operator <<(std::ostream & os,const hub_app_name_t & appId)50 inline std::ostream &operator << (std::ostream &os, const hub_app_name_t &appId)
51 {
52 char vendor[6];
53 __be64 beAppId = htobe64(appId.id);
54 uint32_t seqId = appId.id & NANOAPP_VENDOR_ALL_APPS;
55
56 std::ios::fmtflags f(os.flags());
57 memcpy(vendor, (void*)&beAppId, sizeof(vendor) - 1);
58 vendor[sizeof(vendor) - 1] = 0;
59 if (strlen(vendor) == 5)
60 os << vendor << ", " << std::hex << std::setw(6) << seqId;
61 else
62 os << "#" << std::hex << appId.id;
63 os.flags(f);
64
65 return os;
66 }
67
dumpBuffer(const char * pfx,const hub_app_name_t & appId,uint32_t evtId,const void * data,size_t len,int status)68 void dumpBuffer(const char *pfx, const hub_app_name_t &appId, uint32_t evtId, const void *data, size_t len, int status)
69 {
70 std::ostringstream os;
71 const uint8_t *p = static_cast<const uint8_t *>(data);
72 os << pfx << ": [ID=" << appId << "; SZ=" << std::dec << len;
73 if (evtId)
74 os << "; EVT=" << std::hex << evtId;
75 os << "]:" << std::hex;
76 for (size_t i = 0; i < len; ++i) {
77 os << " " << std::setfill('0') << std::setw(2) << (unsigned int)p[i];
78 }
79 if (status) {
80 os << "; status=" << status << " [" << std::setfill('0') << std::setw(8) << status << "]";
81 }
82 ALOGI("%s", os.str().c_str());
83 }
84
rwrite(int fd,const void * buf,int len)85 static int rwrite(int fd, const void *buf, int len)
86 {
87 int ret;
88
89 do {
90 ret = write(fd, buf, len);
91 } while (ret < 0 && errno == EINTR);
92
93 if (ret != len) {
94 return errno ? -errno : -EIO;
95 }
96
97 return 0;
98 }
99
rread(int fd,void * buf,int len)100 static int rread(int fd, void *buf, int len)
101 {
102 int ret;
103
104 do {
105 ret = read(fd, buf, len);
106 } while (ret < 0 && errno == EINTR);
107
108 return ret;
109 }
110
init_inotify(pollfd * pfd)111 static bool init_inotify(pollfd *pfd) {
112 bool success = false;
113
114 mkdir(NANOHUB_LOCK_DIR, NANOHUB_LOCK_DIR_PERMS);
115 pfd->fd = inotify_init1(IN_NONBLOCK);
116 if (pfd->fd < 0) {
117 ALOGE("Couldn't initialize inotify: %s", strerror(errno));
118 } else if (inotify_add_watch(pfd->fd, NANOHUB_LOCK_DIR, IN_CREATE | IN_DELETE) < 0) {
119 ALOGE("Couldn't add inotify watch: %s", strerror(errno));
120 close(pfd->fd);
121 } else {
122 pfd->events = POLLIN;
123 success = true;
124 }
125
126 return success;
127 }
128
discard_inotify_evt(pollfd & pfd)129 static void discard_inotify_evt(pollfd &pfd) {
130 if ((pfd.revents & POLLIN)) {
131 char buf[sizeof(inotify_event) + NAME_MAX + 1];
132 int ret = read(pfd.fd, buf, sizeof(buf));
133 ALOGD("Discarded %d bytes of inotify data", ret);
134 }
135 }
136
wait_on_dev_lock(pollfd & pfd)137 static void wait_on_dev_lock(pollfd &pfd) {
138 // While the lock file exists, poll on the inotify fd (with timeout)
139 discard_inotify_evt(pfd);
140 while (access(NANOHUB_LOCK_FILE, F_OK) == 0) {
141 ALOGW("Nanohub is locked; blocking read thread");
142 int ret = poll(&pfd, 1, 5000);
143 if (ret > 0) {
144 discard_inotify_evt(pfd);
145 }
146 }
147 }
148
NanoHub()149 NanoHub::NanoHub() {
150 reset();
151 }
152
~NanoHub()153 NanoHub::~NanoHub() {
154 if (mMsgCbkFunc) {
155 ALOGD("Shutting down");
156 closeHub();
157 }
158 }
159
doSendToDevice(const hub_app_name_t name,const void * data,uint32_t len,uint32_t messageType)160 int NanoHub::doSendToDevice(const hub_app_name_t name, const void *data, uint32_t len, uint32_t messageType)
161 {
162 if (len > MAX_RX_PACKET) {
163 return -EINVAL;
164 }
165
166 // transmit message to FW in CHRE format
167 nano_message_chre msg = {
168 .hdr = {
169 .eventId = APP_FROM_HOST_CHRE_EVENT_ID,
170 .appId = name.id,
171 .len = static_cast<uint8_t>(len),
172 .appEventId = messageType,
173 },
174 };
175
176 memcpy(&msg.data[0], data, len);
177
178 return rwrite(mFd, &msg, len + sizeof(msg.hdr));
179 }
180
doSendToApp(HubMessage && msg)181 void NanoHub::doSendToApp(HubMessage &&msg)
182 {
183 std::unique_lock<std::mutex> lk(mAppTxLock);
184 mAppTxQueue.push_back((HubMessage &&)msg);
185 lk.unlock();
186 mAppTxCond.notify_all();
187 }
188
runAppTx()189 void* NanoHub::runAppTx()
190 {
191 std::unique_lock<std::mutex> lk(mAppTxLock);
192 while(true) {
193 mAppTxCond.wait(lk, [this] { return !mAppTxQueue.empty() || mAppQuit; });
194 if (mAppQuit) {
195 break;
196 }
197 HubMessage &m = mAppTxQueue.front();
198 lk.unlock();
199 mMsgCbkFunc(0, &m, mMsgCbkData);
200 lk.lock();
201 mAppTxQueue.pop_front();
202 };
203 return NULL;
204 }
205
runDeviceRx()206 void* NanoHub::runDeviceRx()
207 {
208 enum {
209 IDX_NANOHUB,
210 IDX_CLOSE_PIPE,
211 IDX_INOTIFY
212 };
213 pollfd myFds[3] = {
214 [IDX_NANOHUB] = { .fd = mFd, .events = POLLIN, },
215 [IDX_CLOSE_PIPE] = { .fd = mThreadClosingPipe[0], .events = POLLIN, },
216 };
217 pollfd &inotifyFd = myFds[IDX_INOTIFY];
218 bool hasInotify = false;
219 int numPollFds = 2;
220
221 if (init_inotify(&inotifyFd)) {
222 numPollFds++;
223 hasInotify = true;
224 }
225
226 setDebugFlags(property_get_int32("persist.nanohub.debug", 0));
227
228 while (1) {
229 int ret = poll(myFds, numPollFds, -1);
230 if (ret <= 0) {
231 ALOGD("poll returned with an error: %s", strerror(errno));
232 continue;
233 }
234
235 if (hasInotify) {
236 wait_on_dev_lock(inotifyFd);
237 }
238
239 if (myFds[IDX_NANOHUB].revents & POLLIN) { // we have data
240
241 nano_message msg;
242
243 ret = rread(mFd, &msg, sizeof(msg));
244 if (ret <= 0) {
245 ALOGE("read failed with %d", ret);
246 break;
247 }
248 if (ret < (int)sizeof(msg.hdr)) {
249 ALOGE("Only read %d bytes", ret);
250 break;
251 }
252
253 uint32_t len = msg.hdr.len;
254
255 if (len > sizeof(msg.data)) {
256 ALOGE("malformed packet with len %" PRIu32, len);
257 break;
258 }
259
260 // receive message from FW in legacy format
261 if (ret != (int)(sizeof(msg.hdr) + len)) {
262 ALOGE("Expected %zu bytes, read %d bytes", sizeof(msg.hdr) + len, ret);
263 break;
264 }
265
266 ret = SystemComm::handleRx(&msg);
267 if (ret < 0) {
268 ALOGE("SystemComm::handleRx() returned %d", ret);
269 } else if (ret) {
270 hub_app_name_t app_name = { .id = msg.hdr.appId };
271 if (messageTracingEnabled()) {
272 dumpBuffer("DEV -> APP", app_name, msg.hdr.eventId, &msg.data[0], msg.hdr.len);
273 }
274 doSendToApp(HubMessage(&app_name, msg.hdr.eventId, &msg.data[0], msg.hdr.len));
275 }
276 }
277
278 if (myFds[IDX_CLOSE_PIPE].revents & POLLIN) { // we have been asked to die
279 ALOGD("thread exiting");
280 break;
281 }
282 }
283
284 close(mFd);
285 return NULL;
286 }
287
openHub()288 int NanoHub::openHub()
289 {
290 int ret = 0;
291
292 mFd = open(get_devnode_path(), O_RDWR);
293 if (mFd < 0) {
294 ALOGE("cannot find hub devnode '%s'", get_devnode_path());
295 ret = -errno;
296 goto fail_open;
297 }
298
299 if (pipe(mThreadClosingPipe)) {
300 ALOGE("failed to create signal pipe");
301 ret = -errno;
302 goto fail_pipe;
303 }
304
305 mPollThread = std::thread([this] { runDeviceRx(); });
306 mAppThread = std::thread([this] { runAppTx(); });
307 return 0;
308
309 fail_pipe:
310 close(mFd);
311
312 fail_open:
313 return ret;
314 }
315
closeHub(void)316 int NanoHub::closeHub(void)
317 {
318 char zero = 0;
319
320 // stop mPollThread
321 while(write(mThreadClosingPipe[1], &zero, 1) != 1) {
322 continue;
323 }
324
325 // stop mAppThread
326 {
327 std::unique_lock<std::mutex> lk(mAppTxLock);
328 mAppQuit = true;
329 lk.unlock();
330 mAppTxCond.notify_all();
331 }
332
333 //wait
334 if (mPollThread.joinable()) {
335 mPollThread.join();
336 }
337
338 //wait
339 if (mAppThread.joinable()) {
340 mAppThread.join();
341 }
342 //cleanup
343 ::close(mThreadClosingPipe[0]);
344 ::close(mThreadClosingPipe[1]);
345 ::close(mFd);
346
347 reset();
348
349 return 0;
350 }
351
doSubscribeMessages(uint32_t hub_id,context_hub_callback * cbk,void * cookie)352 int NanoHub::doSubscribeMessages(uint32_t hub_id, context_hub_callback *cbk, void *cookie)
353 {
354 if (hub_id) {
355 return -ENODEV;
356 }
357
358 std::lock_guard<std::mutex> _l(mLock);
359 int ret = 0;
360
361 if (!mMsgCbkFunc && !cbk) { //we're off and staying off - do nothing
362
363 ALOGD("staying off");
364 } else if (cbk && mMsgCbkFunc) { //new callback but staying on
365
366 ALOGD("staying on");
367 } else if (mMsgCbkFunc) { //we were on but turning off
368
369 ALOGD("turning off");
370
371 ret = closeHub();
372 } else if (cbk) { //we're turning on
373
374 ALOGD("turning on");
375 ret = openHub();
376 }
377
378 mMsgCbkFunc = cbk;
379 mMsgCbkData = cookie;
380
381 return ret;
382 }
383
doSendToNanohub(uint32_t hub_id,const hub_message_t * msg)384 int NanoHub::doSendToNanohub(uint32_t hub_id, const hub_message_t *msg)
385 {
386 if (hub_id) {
387 return -ENODEV;
388 }
389
390 int ret = 0;
391 std::lock_guard<std::mutex> _l(mLock);
392
393 if (!mMsgCbkFunc) {
394 ALOGW("refusing to send a message when nobody around to get a reply!");
395 ret = -EIO;
396 } else {
397 if (!msg || !msg->message) {
398 ALOGW("not sending invalid message 1");
399 ret = -EINVAL;
400 } else if (get_hub_info()->os_app_name == msg->app_name) {
401 //messages to the "system" app are special - hal handles them
402 if (messageTracingEnabled()) {
403 dumpBuffer("APP -> HAL", msg->app_name, msg->message_type, msg->message, msg->message_len);
404 }
405 ret = SystemComm::handleTx(msg);
406 } else if (msg->message_len > MAX_RX_PACKET) {
407 ALOGW("not sending invalid message 2");
408 ret = -EINVAL;
409 } else {
410 if (messageTracingEnabled()) {
411 dumpBuffer("APP -> DEV", msg->app_name, msg->message_type, msg->message, msg->message_len);
412 }
413 ret = doSendToDevice(msg->app_name, msg->message, msg->message_len, msg->message_type);
414 }
415 }
416
417 return ret;
418 }
419
hal_get_hubs(context_hub_module_t *,const context_hub_t ** list)420 static int hal_get_hubs(context_hub_module_t*, const context_hub_t ** list)
421 {
422 *list = get_hub_info();
423
424 return 1; /* we have one hub */
425 }
426
427 }; // namespace nanohub
428
429 }; // namespace android
430
431 context_hub_module_t HAL_MODULE_INFO_SYM = {
432 .common = {
433 .tag = HARDWARE_MODULE_TAG,
434 .module_api_version = CONTEXT_HUB_DEVICE_API_VERSION_1_0,
435 .hal_api_version = HARDWARE_HAL_API_VERSION,
436 .id = CONTEXT_HUB_MODULE_ID,
437 .name = "Nanohub HAL",
438 .author = "Google",
439 },
440
441 .get_hubs = android::nanohub::hal_get_hubs,
442 .subscribe_messages = android::nanohub::NanoHub::subscribeMessages,
443 .send_message = android::nanohub::NanoHub::sendToNanohub,
444 };
445