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
operator <<(std::ostream & os,const hub_app_name_t & appId)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
dumpBuffer(const char * pfx,const hub_app_name_t & appId,uint32_t evtId,const void * data,size_t len,int status)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
rwrite(int fd,const void * buf,int len)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
rread(int fd,void * buf,int len)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
init_inotify(pollfd * pfd)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
discard_inotify_evt(pollfd & pfd)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
wait_on_dev_lock(pollfd & pfd)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
doSendToDevice(const hub_app_name_t * name,const void * data,uint32_t len)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
doSendToApp(const hub_app_name_t * name,uint32_t typ,const void * data,uint32_t len)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
run(void * data)179 void* NanoHub::run(void *data)
180 {
181 NanoHub *self = static_cast<NanoHub*>(data);
182 return self->doRun();
183 }
184
doRun()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
openHub()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
closeHub(void)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
doSubscribeMessages(uint32_t hub_id,context_hub_callback * cbk,void * cookie)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
doSendToNanohub(uint32_t hub_id,const hub_message_t * msg)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
hal_get_hubs(context_hub_module_t *,const context_hub_t ** list)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