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