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