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