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 
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,const void * data,size_t len,int status)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 
rwrite(int fd,const void * buf,int len)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 
rread(int fd,void * buf,int len)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 
init_inotify(pollfd * pfd)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 
discard_inotify_evt(pollfd & pfd)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 
wait_on_dev_lock(pollfd & pfd)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 
NanoHub()149 NanoHub::NanoHub() {
150     reset();
151 }
152 
~NanoHub()153 NanoHub::~NanoHub() {
154     if (mMsgCbkFunc) {
155         ALOGD("Shutting down");
156         closeHub();
157     }
158 }
159 
doSendToDevice(const hub_app_name_t name,const void * data,uint32_t len,uint32_t messageType)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 
doSendToApp(HubMessage && msg)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 
runAppTx()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 
runDeviceRx()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 
openHub()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 
closeHub(void)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 
doSubscribeMessages(uint32_t hub_id,context_hub_callback * cbk,void * cookie)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 
doSendToNanohub(uint32_t hub_id,const hub_message_t * msg)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 
hal_get_hubs(context_hub_module_t *,const context_hub_t ** list)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