exynos9610: Usb: HAL: Update wahoo HAL
[GitHub/moto-9609/android_device_motorola_exynos9610-common.git] / hidl / usb / Usb.cpp
CommitLineData
9550981c
BJS
1/*
2 * Copyright (C) 2017 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 */
89b64b29
BJS
16
17#define LOG_TAG "android.hardware.usb@1.1-service.wahoo"
18
19#include <android-base/logging.h>
9550981c 20#include <assert.h>
80fa0c82 21#include <chrono>
9550981c
BJS
22#include <dirent.h>
23#include <pthread.h>
80fa0c82 24#include <regex>
9550981c
BJS
25#include <stdio.h>
26#include <sys/types.h>
80fa0c82 27#include <thread>
9550981c
BJS
28#include <unistd.h>
29#include <unordered_map>
30
31#include <cutils/uevent.h>
32#include <sys/epoll.h>
33#include <utils/Errors.h>
34#include <utils/StrongPointer.h>
35
36#include "Usb.h"
37
38namespace android {
39namespace hardware {
40namespace usb {
89b64b29 41namespace V1_1 {
9550981c
BJS
42namespace implementation {
43
44// Set by the signal handler to destroy the thread
45volatile bool destroyThread;
46
80fa0c82 47int32_t readFile(const std::string &filename, std::string *contents) {
9550981c
BJS
48 FILE *fp;
49 ssize_t read = 0;
50 char *line = NULL;
51 size_t len = 0;
52
53 fp = fopen(filename.c_str(), "r");
54 if (fp != NULL) {
55 if ((read = getline(&line, &len, fp)) != -1) {
56 char *pos;
57 if ((pos = strchr(line, '\n')) != NULL) *pos = '\0';
58 *contents = line;
59 }
60 free(line);
61 fclose(fp);
62 return 0;
63 } else {
64 ALOGE("fopen failed");
65 }
66
67 return -1;
68}
69
80fa0c82 70std::string appendRoleNodeHelper(const std::string &portName,
9550981c
BJS
71 PortRoleType type) {
72 std::string node("/sys/class/typec/" + portName);
73
74 switch (type) {
75 case PortRoleType::DATA_ROLE:
80fa0c82 76 return node + "/data_role";
9550981c 77 case PortRoleType::POWER_ROLE:
80fa0c82
BJS
78 return node + "/power_role";
79 case PortRoleType::MODE:
80 return node + "/port_type";
9550981c 81 default:
80fa0c82 82 return "";
9550981c
BJS
83 }
84}
85
86std::string convertRoletoString(PortRole role) {
87 if (role.type == PortRoleType::POWER_ROLE) {
88 if (role.role == static_cast<uint32_t>(PortPowerRole::SOURCE))
89 return "source";
90 else if (role.role == static_cast<uint32_t>(PortPowerRole::SINK))
91 return "sink";
92 } else if (role.type == PortRoleType::DATA_ROLE) {
93 if (role.role == static_cast<uint32_t>(PortDataRole::HOST)) return "host";
94 if (role.role == static_cast<uint32_t>(PortDataRole::DEVICE))
95 return "device";
96 } else if (role.type == PortRoleType::MODE) {
80fa0c82
BJS
97 if (role.role == static_cast<uint32_t>(PortMode_1_1::UFP)) return "sink";
98 if (role.role == static_cast<uint32_t>(PortMode_1_1::DFP)) return "source";
9550981c
BJS
99 }
100 return "none";
101}
102
103void extractRole(std::string *roleName) {
104 std::size_t first, last;
105
106 first = roleName->find("[");
107 last = roleName->find("]");
108
109 if (first != std::string::npos && last != std::string::npos) {
110 *roleName = roleName->substr(first + 1, last - first - 1);
111 }
112}
113
80fa0c82
BJS
114void switchToDrp(const std::string &portName) {
115 std::string filename =
116 appendRoleNodeHelper(std::string(portName.c_str()), PortRoleType::MODE);
117 FILE *fp;
118
119 if (filename != "") {
120 fp = fopen(filename.c_str(), "w");
121 if (fp != NULL) {
122 int ret = fputs("dual", fp);
123 fclose(fp);
124 if (ret == EOF)
125 ALOGE("Fatal: Error while switching back to drp");
126 } else {
127 ALOGE("Fatal: Cannot open file to switch back to drp");
128 }
129 } else {
130 ALOGE("Fatal: invalid node type");
131 }
132}
133
134bool switchMode(const hidl_string &portName,
135 const PortRole &newRole, struct Usb *usb) {
136 std::string filename =
137 appendRoleNodeHelper(std::string(portName.c_str()), newRole.type);
138 std::string written;
139 FILE *fp;
140 bool roleSwitch = false;
141
142 if (filename == "") {
143 ALOGE("Fatal: invalid node type");
144 return false;
145 }
146
147 fp = fopen(filename.c_str(), "w");
148 if (fp != NULL) {
149 // Hold the lock here to prevent loosing connected signals
150 // as once the file is written the partner added signal
151 // can arrive anytime.
152 pthread_mutex_lock(&usb->mPartnerLock);
153 usb->mPartnerUp = false;
154 int ret = fputs(convertRoletoString(newRole).c_str(), fp);
155 fclose(fp);
156
157 if (ret != EOF) {
158 struct timespec to;
159 struct timeval tp;
160
161wait_again:
162 gettimeofday(&tp, NULL);
163 to.tv_sec = tp.tv_sec + PORT_TYPE_TIMEOUT;
164 to.tv_nsec = tp.tv_usec * 1000;;
165
166 int err = pthread_cond_timedwait(&usb->mPartnerCV, &usb->mPartnerLock, &to);
167 // There are no uevent signals which implies role swap timed out.
168 if (err == ETIMEDOUT) {
169 ALOGI("uevents wait timedout");
170 // Sanity check.
171 } else if (!usb->mPartnerUp) {
172 goto wait_again;
173 // Role switch succeeded since usb->mPartnerUp is true.
174 } else {
175 roleSwitch = true;
176 }
177 } else {
178 ALOGI("Role switch failed while wrting to file");
179 }
180 pthread_mutex_unlock(&usb->mPartnerLock);
181 }
182
183 if (!roleSwitch)
184 switchToDrp(std::string(portName.c_str()));
185
186 return roleSwitch;
187}
188
189
190
9550981c
BJS
191Return<void> Usb::switchRole(const hidl_string &portName,
192 const PortRole &newRole) {
193 std::string filename =
194 appendRoleNodeHelper(std::string(portName.c_str()), newRole.type);
195 std::string written;
196 FILE *fp;
80fa0c82
BJS
197 bool roleSwitch = false;
198
199 if (filename == "") {
200 ALOGE("Fatal: invalid node type");
201 return Void();
202 }
203
204 pthread_mutex_lock(&mRoleSwitchLock);
9550981c
BJS
205
206 ALOGI("filename write: %s role:%s", filename.c_str(),
207 convertRoletoString(newRole).c_str());
208
80fa0c82
BJS
209 if (newRole.type == PortRoleType::MODE) {
210 roleSwitch = switchMode(portName, newRole, this);
211 } else {
212 fp = fopen(filename.c_str(), "w");
213 if (fp != NULL) {
214 int ret = fputs(convertRoletoString(newRole).c_str(), fp);
215 fclose(fp);
216 if ((ret != EOF) && !readFile(filename, &written)) {
217 extractRole(&written);
218 ALOGI("written: %s", written.c_str());
219 if (written == convertRoletoString(newRole)) {
220 roleSwitch = true;
9550981c 221 } else {
80fa0c82 222 ALOGE("Role switch failed");
9550981c 223 }
9550981c 224 } else {
80fa0c82 225 ALOGE("failed to update the new role");
9550981c
BJS
226 }
227 } else {
80fa0c82 228 ALOGE("fopen failed");
9550981c 229 }
9550981c
BJS
230 }
231
232 pthread_mutex_lock(&mLock);
80fa0c82 233 if (mCallback_1_0 != NULL) {
9550981c 234 Return<void> ret =
80fa0c82
BJS
235 mCallback_1_0->notifyRoleSwitchStatus(portName, newRole,
236 roleSwitch ? Status::SUCCESS : Status::ERROR);
9550981c
BJS
237 if (!ret.isOk())
238 ALOGE("RoleSwitchStatus error %s", ret.description().c_str());
239 } else {
240 ALOGE("Not notifying the userspace. Callback is not set");
241 }
242 pthread_mutex_unlock(&mLock);
80fa0c82 243 pthread_mutex_unlock(&mRoleSwitchLock);
9550981c
BJS
244
245 return Void();
246}
247
80fa0c82 248Status getAccessoryConnected(const std::string &portName, std::string *accessory) {
89b64b29
BJS
249 std::string filename =
250 "/sys/class/typec/" + portName + "-partner/accessory_mode";
251
252 if (readFile(filename, accessory)) {
253 ALOGE("getAccessoryConnected: Failed to open filesystem node: %s",
254 filename.c_str());
255 return Status::ERROR;
256 }
257
258 return Status::SUCCESS;
259}
260
80fa0c82 261Status getCurrentRoleHelper(const std::string &portName, bool connected,
9550981c
BJS
262 PortRoleType type, uint32_t *currentRole) {
263 std::string filename;
264 std::string roleName;
89b64b29 265 std::string accessory;
9550981c
BJS
266
267 // Mode
268
269 if (type == PortRoleType::POWER_ROLE) {
80fa0c82 270 filename = "/sys/class/typec/" + portName + "/power_role";
9550981c
BJS
271 *currentRole = static_cast<uint32_t>(PortPowerRole::NONE);
272 } else if (type == PortRoleType::DATA_ROLE) {
80fa0c82 273 filename = "/sys/class/typec/" + portName + "/data_role";
9550981c
BJS
274 *currentRole = static_cast<uint32_t>(PortDataRole::NONE);
275 } else if (type == PortRoleType::MODE) {
80fa0c82 276 filename = "/sys/class/typec/" + portName + "/data_role";
89b64b29 277 *currentRole = static_cast<uint32_t>(PortMode_1_1::NONE);
9550981c
BJS
278 } else {
279 return Status::ERROR;
280 }
281
282 if (!connected) return Status::SUCCESS;
283
89b64b29
BJS
284 if (type == PortRoleType::MODE) {
285 if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) {
286 return Status::ERROR;
287 }
80fa0c82 288 if (accessory == "analog_audio") {
89b64b29
BJS
289 *currentRole = static_cast<uint32_t>(PortMode_1_1::AUDIO_ACCESSORY);
290 return Status::SUCCESS;
80fa0c82 291 } else if (accessory == "debug") {
89b64b29
BJS
292 *currentRole = static_cast<uint32_t>(PortMode_1_1::DEBUG_ACCESSORY);
293 return Status::SUCCESS;
294 }
295 }
296
9550981c
BJS
297 if (readFile(filename, &roleName)) {
298 ALOGE("getCurrentRole: Failed to open filesystem node: %s",
299 filename.c_str());
300 return Status::ERROR;
301 }
302
303 extractRole(&roleName);
304
305 if (roleName == "source") {
306 *currentRole = static_cast<uint32_t>(PortPowerRole::SOURCE);
307 } else if (roleName == "sink") {
308 *currentRole = static_cast<uint32_t>(PortPowerRole::SINK);
309 } else if (roleName == "host") {
310 if (type == PortRoleType::DATA_ROLE)
311 *currentRole = static_cast<uint32_t>(PortDataRole::HOST);
312 else
89b64b29 313 *currentRole = static_cast<uint32_t>(PortMode_1_1::DFP);
9550981c
BJS
314 } else if (roleName == "device") {
315 if (type == PortRoleType::DATA_ROLE)
316 *currentRole = static_cast<uint32_t>(PortDataRole::DEVICE);
317 else
89b64b29 318 *currentRole = static_cast<uint32_t>(PortMode_1_1::UFP);
9550981c
BJS
319 } else if (roleName != "none") {
320 /* case for none has already been addressed.
321 * so we check if the role isnt none.
322 */
323 return Status::UNRECOGNIZED_ROLE;
324 }
325
326 return Status::SUCCESS;
327}
328
329Status getTypeCPortNamesHelper(std::unordered_map<std::string, bool> *names) {
330 DIR *dp;
331
332 dp = opendir("/sys/class/typec");
333 if (dp != NULL) {
334 int32_t ports = 0;
335 int32_t current = 0;
336 struct dirent *ep;
337
338 while ((ep = readdir(dp))) {
339 if (ep->d_type == DT_LNK) {
340 if (std::string::npos == std::string(ep->d_name).find("-partner")) {
341 std::unordered_map<std::string, bool>::const_iterator portName =
342 names->find(ep->d_name);
343 if (portName == names->end()) {
344 names->insert({ep->d_name, false});
345 }
346 } else {
347 (*names)[std::strtok(ep->d_name, "-")] = true;
348 }
349 }
350 }
351 closedir(dp);
352 return Status::SUCCESS;
353 }
354
355 ALOGE("Failed to open /sys/class/typec");
356 return Status::ERROR;
357}
358
80fa0c82 359bool canSwitchRoleHelper(const std::string &portName, PortRoleType /*type*/) {
9550981c
BJS
360 std::string filename =
361 "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery";
362 std::string supportsPD;
363
364 if (!readFile(filename, &supportsPD)) {
80fa0c82 365 if (supportsPD == "yes") {
9550981c
BJS
366 return true;
367 }
368 }
369
370 return false;
371}
372
80fa0c82
BJS
373/*
374 * Reuse the same method for both V1_0 and V1_1 callback objects.
375 * The caller of this method would reconstruct the V1_0::PortStatus
376 * object if required.
377 */
378Status getPortStatusHelper(hidl_vec<PortStatus_1_1> *currentPortStatus_1_1,
379 bool V1_0) {
9550981c
BJS
380 std::unordered_map<std::string, bool> names;
381 Status result = getTypeCPortNamesHelper(&names);
382 int i = -1;
383
384 if (result == Status::SUCCESS) {
89b64b29 385 currentPortStatus_1_1->resize(names.size());
9550981c
BJS
386 for (std::pair<std::string, bool> port : names) {
387 i++;
388 ALOGI("%s", port.first.c_str());
89b64b29 389 (*currentPortStatus_1_1)[i].status.portName = port.first;
9550981c
BJS
390
391 uint32_t currentRole;
392 if (getCurrentRoleHelper(port.first, port.second,
393 PortRoleType::POWER_ROLE,
394 &currentRole) == Status::SUCCESS) {
89b64b29 395 (*currentPortStatus_1_1)[i].status.currentPowerRole =
9550981c
BJS
396 static_cast<PortPowerRole>(currentRole);
397 } else {
398 ALOGE("Error while retreiving portNames");
399 goto done;
400 }
401
402 if (getCurrentRoleHelper(port.first, port.second, PortRoleType::DATA_ROLE,
403 &currentRole) == Status::SUCCESS) {
89b64b29 404 (*currentPortStatus_1_1)[i].status.currentDataRole =
9550981c
BJS
405 static_cast<PortDataRole>(currentRole);
406 } else {
407 ALOGE("Error while retreiving current port role");
408 goto done;
409 }
410
411 if (getCurrentRoleHelper(port.first, port.second, PortRoleType::MODE,
412 &currentRole) == Status::SUCCESS) {
89b64b29
BJS
413 (*currentPortStatus_1_1)[i].currentMode =
414 static_cast<PortMode_1_1>(currentRole);
80fa0c82
BJS
415 (*currentPortStatus_1_1)[i].status.currentMode =
416 static_cast<V1_0::PortMode>(currentRole);
9550981c
BJS
417 } else {
418 ALOGE("Error while retreiving current data role");
419 goto done;
420 }
421
80fa0c82 422 (*currentPortStatus_1_1)[i].status.canChangeMode = true;
89b64b29 423 (*currentPortStatus_1_1)[i].status.canChangeDataRole =
9550981c
BJS
424 port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE)
425 : false;
89b64b29 426 (*currentPortStatus_1_1)[i].status.canChangePowerRole =
9550981c
BJS
427 port.second
428 ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE)
429 : false;
430
80fa0c82 431 ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d",
89b64b29
BJS
432 port.second, (*currentPortStatus_1_1)[i].status.canChangeMode,
433 (*currentPortStatus_1_1)[i].status.canChangeDataRole,
434 (*currentPortStatus_1_1)[i].status.canChangePowerRole);
9550981c 435
80fa0c82
BJS
436 if (V1_0) {
437 (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::DFP;
438 } else {
439 (*currentPortStatus_1_1)[i].supportedModes = PortMode_1_1::UFP | PortMode_1_1::DFP;
440 (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::NONE;
441 (*currentPortStatus_1_1)[i].status.currentMode = V1_0::PortMode::NONE;
442 }
9550981c
BJS
443 }
444 return Status::SUCCESS;
445 }
446done:
447 return Status::ERROR;
448}
449
450Return<void> Usb::queryPortStatus() {
89b64b29 451 hidl_vec<PortStatus_1_1> currentPortStatus_1_1;
80fa0c82 452 hidl_vec<V1_0::PortStatus> currentPortStatus;
9550981c 453 Status status;
80fa0c82 454 sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(mCallback_1_0);
9550981c
BJS
455
456 pthread_mutex_lock(&mLock);
80fa0c82
BJS
457 if (mCallback_1_0 != NULL) {
458 if (callback_V1_1 != NULL) {
459 status = getPortStatusHelper(&currentPortStatus_1_1, false);
460 } else {
461 status = getPortStatusHelper(&currentPortStatus_1_1, true);
462 currentPortStatus.resize(currentPortStatus_1_1.size());
463 for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++)
464 currentPortStatus[i] = currentPortStatus_1_1[i].status;
465 }
466
467 Return<void> ret;
468
469 if (callback_V1_1 != NULL)
470 ret = callback_V1_1->notifyPortStatusChange_1_1(currentPortStatus_1_1, status);
471 else
472 ret = mCallback_1_0->notifyPortStatusChange(currentPortStatus, status);
473
9550981c 474 if (!ret.isOk())
89b64b29 475 ALOGE("queryPortStatus_1_1 error %s", ret.description().c_str());
9550981c
BJS
476 } else {
477 ALOGI("Notifying userspace skipped. Callback is NULL");
478 }
479 pthread_mutex_unlock(&mLock);
480
481 return Void();
482}
80fa0c82 483
9550981c
BJS
484struct data {
485 int uevent_fd;
89b64b29 486 android::hardware::usb::V1_1::implementation::Usb *usb;
9550981c
BJS
487};
488
489static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
490 char msg[UEVENT_MSG_LEN + 2];
491 char *cp;
492 int n;
493
494 n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
495 if (n <= 0) return;
496 if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
497 return;
498
499 msg[n] = '\0';
500 msg[n + 1] = '\0';
501 cp = msg;
502
503 while (*cp) {
80fa0c82
BJS
504 if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) {
505 ALOGI("partner added");
506 pthread_mutex_lock(&payload->usb->mPartnerLock);
507 payload->usb->mPartnerUp = true;
508 pthread_cond_signal(&payload->usb->mPartnerCV);
509 pthread_mutex_unlock(&payload->usb->mPartnerLock);
510 } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
511 hidl_vec<PortStatus_1_1> currentPortStatus_1_1;
9550981c
BJS
512 ALOGI("uevent received %s", cp);
513 pthread_mutex_lock(&payload->usb->mLock);
80fa0c82
BJS
514 if (payload->usb->mCallback_1_0 != NULL) {
515 sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(payload->usb->mCallback_1_0);
516 Return<void> ret;
517
518 // V1_1 callback
519 if (callback_V1_1 != NULL) {
520 Status status = getPortStatusHelper(&currentPortStatus_1_1, false);
521 ret = callback_V1_1->notifyPortStatusChange_1_1(
522 currentPortStatus_1_1, status);
523 } else { // V1_0 callback
524 Status status = getPortStatusHelper(&currentPortStatus_1_1, true);
525
526 /*
527 * Copying the result from getPortStatusHelper
528 * into V1_0::PortStatus to pass back through
529 * the V1_0 callback object.
530 */
531 hidl_vec<V1_0::PortStatus> currentPortStatus;
532 currentPortStatus.resize(currentPortStatus_1_1.size());
533 for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++)
534 currentPortStatus[i] = currentPortStatus_1_1[i].status;
535
536 ret = payload->usb->mCallback_1_0->notifyPortStatusChange(
537 currentPortStatus, status);
538 }
9550981c
BJS
539 if (!ret.isOk()) ALOGE("error %s", ret.description().c_str());
540 } else {
541 ALOGI("Notifying userspace skipped. Callback is NULL");
542 }
543 pthread_mutex_unlock(&payload->usb->mLock);
80fa0c82
BJS
544
545 //Role switch is not in progress and port is in disconnected state
546 if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) {
547 for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) {
548 DIR *dp = opendir(std::string("/sys/class/typec/"
549 + std::string(currentPortStatus_1_1[i].status.portName.c_str())
550 + "-partner").c_str());
551 if (dp == NULL) {
552 //PortRole role = {.role = static_cast<uint32_t>(PortMode::UFP)};
553 switchToDrp(currentPortStatus_1_1[i].status.portName);
554 } else {
555 closedir(dp);
556 }
557 }
558 pthread_mutex_unlock(&payload->usb->mRoleSwitchLock);
559 }
9550981c
BJS
560 break;
561 }
562 /* advance to after the next \0 */
563 while (*cp++) {}
564 }
565}
566
567void *work(void *param) {
568 int epoll_fd, uevent_fd;
569 struct epoll_event ev;
570 int nevents = 0;
571 struct data payload;
572
573 ALOGE("creating thread");
574
575 uevent_fd = uevent_open_socket(64 * 1024, true);
576
577 if (uevent_fd < 0) {
578 ALOGE("uevent_init: uevent_open_socket failed\n");
579 return NULL;
580 }
581
582 payload.uevent_fd = uevent_fd;
89b64b29 583 payload.usb = (android::hardware::usb::V1_1::implementation::Usb *)param;
9550981c
BJS
584
585 fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
586
587 ev.events = EPOLLIN;
588 ev.data.ptr = (void *)uevent_event;
589
590 epoll_fd = epoll_create(64);
591 if (epoll_fd == -1) {
592 ALOGE("epoll_create failed; errno=%d", errno);
593 goto error;
594 }
595
596 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
597 ALOGE("epoll_ctl failed; errno=%d", errno);
598 goto error;
599 }
600
601 while (!destroyThread) {
602 struct epoll_event events[64];
603
604 nevents = epoll_wait(epoll_fd, events, 64, -1);
605 if (nevents == -1) {
606 if (errno == EINTR) continue;
607 ALOGE("usb epoll_wait failed; errno=%d", errno);
608 break;
609 }
610
611 for (int n = 0; n < nevents; ++n) {
612 if (events[n].data.ptr)
613 (*(void (*)(int, struct data *payload))events[n].data.ptr)(
614 events[n].events, &payload);
615 }
616 }
617
618 ALOGI("exiting worker thread");
619error:
620 close(uevent_fd);
621
622 if (epoll_fd >= 0) close(epoll_fd);
623
624 return NULL;
625}
626
627void sighandler(int sig) {
628 if (sig == SIGUSR1) {
629 destroyThread = true;
630 ALOGI("destroy set");
631 return;
632 }
633 signal(SIGUSR1, sighandler);
634}
635
80fa0c82 636Return<void> Usb::setCallback(const sp<V1_0::IUsbCallback> &callback) {
89b64b29
BJS
637
638 sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(callback);
639
640 if (callback != NULL)
80fa0c82
BJS
641 if (callback_V1_1 == NULL)
642 ALOGI("Registering 1.0 callback");
89b64b29 643
9550981c
BJS
644 pthread_mutex_lock(&mLock);
645 /*
646 * When both the old callback and new callback values are NULL,
647 * there is no need to spin off the worker thread.
648 * When both the values are not NULL, we would already have a
649 * worker thread running, so updating the callback object would
650 * be suffice.
651 */
80fa0c82
BJS
652 if ((mCallback_1_0 == NULL && callback == NULL) ||
653 (mCallback_1_0 != NULL && callback != NULL)) {
654 /*
655 * Always store as V1_0 callback object. Type cast to V1_1
656 * when the callback is actually invoked.
657 */
658 mCallback_1_0 = callback;
9550981c
BJS
659 pthread_mutex_unlock(&mLock);
660 return Void();
661 }
662
80fa0c82 663 mCallback_1_0 = callback;
9550981c
BJS
664 ALOGI("registering callback");
665
666 // Kill the worker thread if the new callback is NULL.
80fa0c82 667 if (mCallback_1_0 == NULL) {
9550981c
BJS
668 pthread_mutex_unlock(&mLock);
669 if (!pthread_kill(mPoll, SIGUSR1)) {
670 pthread_join(mPoll, NULL);
671 ALOGI("pthread destroyed");
672 }
673 return Void();
674 }
675
676 destroyThread = false;
677 signal(SIGUSR1, sighandler);
678
679 /*
680 * Create a background thread if the old callback value is NULL
681 * and being updated with a new value.
682 */
683 if (pthread_create(&mPoll, NULL, work, this)) {
684 ALOGE("pthread creation failed %d", errno);
80fa0c82 685 mCallback_1_0 = NULL;
9550981c
BJS
686 }
687
688 pthread_mutex_unlock(&mLock);
689 return Void();
690}
691
692} // namespace implementation
693} // namespace V1_0
694} // namespace usb
695} // namespace hardware
696} // namespace android