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 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 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 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 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 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 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 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 149 NanoHub::NanoHub() { 150 reset(); 151 } 152 153 NanoHub::~NanoHub() { 154 if (mMsgCbkFunc) { 155 ALOGD("Shutting down"); 156 closeHub(); 157 } 158 } 159 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 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 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 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 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 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 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 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 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