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