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 <cutils/properties.h>
30 #include <log/log.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,uint16_t endpoint,const void * data,size_t len,int status)68 void dumpBuffer(const char *pfx, const hub_app_name_t &appId, uint32_t evtId, uint16_t endpoint, 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     if (endpoint)
76         os << "; EPT=" << std::hex << endpoint;
77     os << "]:" << std::hex;
78     for (size_t i = 0; i < len; ++i) {
79         os << " "  << std::setfill('0') << std::setw(2) << (unsigned int)p[i];
80     }
81     if (status) {
82         os << "; status=" << status << " [" << std::setfill('0') << std::setw(8) << status << "]";
83     }
84     ALOGI("%s", os.str().c_str());
85 }
86 
rwrite(int fd,const void * buf,int len)87 static int rwrite(int fd, const void *buf, int len)
88 {
89     int ret;
90 
91     do {
92         ret = write(fd, buf, len);
93     } while (ret < 0 && errno == EINTR);
94 
95     if (ret != len) {
96         return errno ? -errno : -EIO;
97     }
98 
99     return 0;
100 }
101 
rread(int fd,void * buf,int len)102 static int rread(int fd, void *buf, int len)
103 {
104     int ret;
105 
106     do {
107         ret = read(fd, buf, len);
108     } while (ret < 0 && errno == EINTR);
109 
110     return ret;
111 }
112 
init_inotify(pollfd * pfd)113 static bool init_inotify(pollfd *pfd) {
114     bool success = false;
115 
116     mkdir(NANOHUB_LOCK_DIR, NANOHUB_LOCK_DIR_PERMS);
117     pfd->fd = inotify_init1(IN_NONBLOCK);
118     if (pfd->fd < 0) {
119         ALOGE("Couldn't initialize inotify: %s", strerror(errno));
120     } else if (inotify_add_watch(pfd->fd, NANOHUB_LOCK_DIR, IN_CREATE | IN_DELETE) < 0) {
121         ALOGE("Couldn't add inotify watch: %s", strerror(errno));
122         close(pfd->fd);
123     } else {
124         pfd->events = POLLIN;
125         success = true;
126     }
127 
128     return success;
129 }
130 
discard_inotify_evt(pollfd & pfd)131 static void discard_inotify_evt(pollfd &pfd) {
132     if ((pfd.revents & POLLIN)) {
133         char buf[sizeof(inotify_event) + NAME_MAX + 1];
134         int ret = read(pfd.fd, buf, sizeof(buf));
135         ALOGD("Discarded %d bytes of inotify data", ret);
136     }
137 }
138 
wait_on_dev_lock(pollfd & pfd)139 static void wait_on_dev_lock(pollfd &pfd) {
140     // While the lock file exists, poll on the inotify fd (with timeout)
141     discard_inotify_evt(pfd);
142     while (access(NANOHUB_LOCK_FILE, F_OK) == 0) {
143         ALOGW("Nanohub is locked; blocking read thread");
144         int ret = TEMP_FAILURE_RETRY(poll(&pfd, 1, 5000));
145         if (ret < 0) {
146             ALOGE("poll returned with an error: %s", strerror(errno));
147             break;
148         } else if (ret > 0) {
149             discard_inotify_evt(pfd);
150         }
151     }
152 }
153 
NanoHub()154 NanoHub::NanoHub() {
155     reset();
156 }
157 
~NanoHub()158 NanoHub::~NanoHub() {
159     if (mMsgCbkFunc) {
160         ALOGD("Shutting down");
161         closeHub();
162     }
163 }
164 
doSendToDevice(const hub_app_name_t name,const void * data,uint32_t len,uint32_t messageType,uint16_t endpoint)165 int NanoHub::doSendToDevice(const hub_app_name_t name, const void *data, uint32_t len, uint32_t messageType, uint16_t endpoint)
166 {
167     if (len > MAX_RX_PACKET) {
168         return -EINVAL;
169     }
170 
171     // transmit message to FW in CHRE format
172     nano_message_chre msg = {
173         .hdr = {
174             .eventId = APP_FROM_HOST_CHRE_EVENT_ID,
175             .appId = name.id,
176             .len = static_cast<uint8_t>(len),
177             .appEventId = messageType,
178             .endpoint = endpoint,
179         },
180     };
181 
182     memcpy(&msg.data[0], data, len);
183 
184     return rwrite(mFd, &msg, len + sizeof(msg.hdr));
185 }
186 
doSendToApp(HubMessage && msg)187 void NanoHub::doSendToApp(HubMessage &&msg)
188 {
189     std::unique_lock<std::mutex> lk(mAppTxLock);
190     mAppTxQueue.push_back((HubMessage &&)msg);
191     lk.unlock();
192     mAppTxCond.notify_all();
193 }
194 
doDumpAppInfo(std::string & result)195 void NanoHub::doDumpAppInfo(std::string &result)
196 {
197     SystemComm::dumpAppInfo(result);
198 }
199 
runAppTx()200 void* NanoHub::runAppTx()
201 {
202     std::unique_lock<std::mutex> lk(mAppTxLock);
203     while(true) {
204         mAppTxCond.wait(lk, [this] { return !mAppTxQueue.empty() || mAppQuit; });
205         if (mAppQuit) {
206             break;
207         }
208         HubMessage &m = mAppTxQueue.front();
209         lk.unlock();
210         mMsgCbkFunc(0, m, mMsgCbkData);
211         lk.lock();
212         mAppTxQueue.pop_front();
213     };
214     return NULL;
215 }
216 
runDeviceRx()217 void* NanoHub::runDeviceRx()
218 {
219     enum {
220         IDX_NANOHUB,
221         IDX_CLOSE_PIPE,
222         IDX_INOTIFY
223     };
224     pollfd myFds[3] = {
225         [IDX_NANOHUB] = { .fd = mFd, .events = POLLIN, },
226         [IDX_CLOSE_PIPE] = { .fd = mThreadClosingPipe[0], .events = POLLIN, },
227     };
228     pollfd &inotifyFd = myFds[IDX_INOTIFY];
229     bool hasInotify = false;
230     int numPollFds = 2;
231 
232     if (init_inotify(&inotifyFd)) {
233         numPollFds++;
234         hasInotify = true;
235     }
236 
237     setDebugFlags(property_get_int32("persist.nanohub.debug", 0));
238 
239     while (1) {
240         int ret = TEMP_FAILURE_RETRY(poll(myFds, numPollFds, -1));
241         if (ret == 0)
242             continue;
243         else if (ret < 0) {
244             ALOGE("poll returned with an error: %s", strerror(errno));
245             break;
246         }
247 
248         if (hasInotify) {
249             wait_on_dev_lock(inotifyFd);
250         }
251 
252         if (myFds[IDX_NANOHUB].revents & POLLIN) { // we have data
253 
254             nano_message msg;
255 
256             ret = rread(mFd, &msg, sizeof(msg));
257             if (ret <= 0) {
258                 ALOGE("read failed with %d", ret);
259                 break;
260             }
261             if (ret < (int)sizeof(msg.raw.hdr)) {
262                 ALOGE("Only read %d bytes", ret);
263                 break;
264             }
265 
266             uint32_t len = msg.raw.hdr.len;
267 
268             if (len > MAX_RX_PACKET) {
269                 ALOGE("malformed packet with len %" PRIu32, len);
270                 break;
271             }
272 
273             // receive message from FW in legacy format
274             if (ret == (int)(sizeof(msg.raw.hdr) + len)) {
275                 ret = SystemComm::handleRx(&msg.raw);
276                 if (ret > 0) {
277                     hub_app_name_t app_name = { .id = msg.raw.hdr.appId };
278                     if (messageTracingEnabled()) {
279                         dumpBuffer("(RAW) DEV -> APP", app_name, msg.raw.hdr.eventId, 0, &msg.raw.data[0], len);
280                     }
281                     doSendToApp(HubMessage(&app_name, msg.raw.hdr.eventId, ENDPOINT_BROADCAST, &msg.raw.data[0], len));
282                 }
283             // receive message from FW in chre format
284             } else if (ret == (int)(sizeof(msg.chre.hdr) + len)) {
285                 ret = SystemComm::handleRx(&msg.chre);
286                 if (ret > 0) {
287                     hub_app_name_t app_name = { .id = msg.chre.hdr.appId };
288                     if (messageTracingEnabled()) {
289                         dumpBuffer("(CHRE) DEV -> APP", app_name, msg.chre.hdr.appEventId, msg.chre.hdr.endpoint, &msg.chre.data[0], len);
290                     }
291                     doSendToApp(HubMessage(&app_name, msg.chre.hdr.appEventId, msg.chre.hdr.endpoint, &msg.chre.data[0], len));
292                 }
293             } else {
294                 ALOGE("Expected (%zu|%zu) bytes, read %d bytes", sizeof(msg.raw.hdr) + len, sizeof(msg.chre.hdr) + len, ret);
295                 break;
296             }
297 
298             if (ret < 0)
299                 ALOGE("SystemComm::handleRx() returned %d", ret);
300         }
301 
302         if (myFds[IDX_CLOSE_PIPE].revents & POLLIN) { // we have been asked to die
303             ALOGD("thread exiting");
304             break;
305         }
306     }
307 
308     close(mFd);
309     return NULL;
310 }
311 
openHub()312 int NanoHub::openHub()
313 {
314     int ret = 0;
315 
316     mFd = open(get_devnode_path(), O_RDWR);
317     if (mFd < 0) {
318         ALOGE("cannot find hub devnode '%s'", get_devnode_path());
319         ret = -errno;
320         goto fail_open;
321     }
322 
323     if (pipe(mThreadClosingPipe)) {
324         ALOGE("failed to create signal pipe");
325         ret = -errno;
326         goto fail_pipe;
327     }
328 
329     mPollThread = std::thread([this] { runDeviceRx(); });
330     mAppThread = std::thread([this] { runAppTx(); });
331     return 0;
332 
333 fail_pipe:
334     close(mFd);
335 
336 fail_open:
337     return ret;
338 }
339 
closeHub(void)340 int NanoHub::closeHub(void)
341 {
342     char zero = 0;
343 
344     // stop mPollThread
345     while(write(mThreadClosingPipe[1], &zero, 1) != 1) {
346         continue;
347     }
348 
349     // stop mAppThread
350     {
351         std::unique_lock<std::mutex> lk(mAppTxLock);
352         mAppQuit = true;
353         lk.unlock();
354         mAppTxCond.notify_all();
355     }
356 
357     //wait
358     if (mPollThread.joinable()) {
359         mPollThread.join();
360     }
361 
362     //wait
363     if (mAppThread.joinable()) {
364         mAppThread.join();
365     }
366     //cleanup
367     ::close(mThreadClosingPipe[0]);
368     ::close(mThreadClosingPipe[1]);
369     ::close(mFd);
370 
371     reset();
372 
373     return 0;
374 }
375 
doSubscribeMessages(uint32_t hub_id,Contexthub_callback * cbk,void * cookie)376 int NanoHub::doSubscribeMessages(uint32_t hub_id, Contexthub_callback *cbk, void *cookie)
377 {
378     if (hub_id) {
379         return -ENODEV;
380     }
381 
382     std::lock_guard<std::mutex> _l(mLock);
383     int ret = 0;
384 
385     if (!mMsgCbkFunc && !cbk) { //we're off and staying off - do nothing
386 
387         ALOGD("staying off");
388     } else if (cbk && mMsgCbkFunc) { //new callback but staying on
389 
390         ALOGD("staying on");
391     } else if (mMsgCbkFunc) {     //we were on but turning off
392 
393         ALOGD("turning off");
394 
395         ret = closeHub();
396     } else if (cbk) {             //we're turning on
397 
398         ALOGD("turning on");
399         ret = openHub();
400     }
401 
402     mMsgCbkFunc = cbk;
403     mMsgCbkData = cookie;
404 
405     return ret;
406 }
407 
doSendToNanohub(uint32_t hub_id,const hub_message_t * msg,uint32_t transaction_id,uint16_t endpoint)408 int NanoHub::doSendToNanohub(uint32_t hub_id, const hub_message_t *msg, uint32_t transaction_id, uint16_t endpoint)
409 {
410     if (hub_id) {
411         return -ENODEV;
412     }
413 
414     int ret = 0;
415     std::lock_guard<std::mutex> _l(mLock);
416 
417     if (!mMsgCbkFunc) {
418         ALOGW("refusing to send a message when nobody around to get a reply!");
419         ret = -EIO;
420     } else {
421         if (!msg || !msg->message) {
422             ALOGW("not sending invalid message 1");
423             ret = -EINVAL;
424         } else if (get_hub_info()->os_app_name == msg->app_name) {
425             //messages to the "system" app are special - hal handles them
426             if (messageTracingEnabled()) {
427                 dumpBuffer("APP -> HAL", msg->app_name, msg->message_type, 0, msg->message, msg->message_len);
428             }
429             ret = SystemComm::handleTx(msg, transaction_id);
430         } else if (msg->message_len > MAX_RX_PACKET) {
431             ALOGW("not sending invalid message 2");
432             ret = -EINVAL;
433         } else {
434             if (messageTracingEnabled()) {
435                 dumpBuffer("APP -> DEV", msg->app_name, msg->message_type, endpoint, msg->message, msg->message_len);
436             }
437             ret = doSendToDevice(msg->app_name, msg->message, msg->message_len, msg->message_type, endpoint);
438         }
439     }
440 
441     return ret;
442 }
443 
444 }; // namespace nanohub
445 
446 }; // namespace android
447