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 "android.hardware.usb (at) 1.1-service.wahoo" 18 19 #include <android-base/logging.h> 20 #include <assert.h> 21 #include <chrono> 22 #include <dirent.h> 23 #include <pthread.h> 24 #include <regex> 25 #include <stdio.h> 26 #include <sys/types.h> 27 #include <thread> 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 38 namespace android { 39 namespace hardware { 40 namespace usb { 41 namespace V1_1 { 42 namespace implementation { 43 44 // Set by the signal handler to destroy the thread 45 volatile bool destroyThread; 46 47 int32_t readFile(const std::string &filename, std::string *contents) { 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 70 std::string appendRoleNodeHelper(const std::string &portName, 71 PortRoleType type) { 72 std::string node("/sys/class/typec/" + portName); 73 74 switch (type) { 75 case PortRoleType::DATA_ROLE: 76 return node + "/data_role"; 77 case PortRoleType::POWER_ROLE: 78 return node + "/power_role"; 79 case PortRoleType::MODE: 80 return node + "/port_type"; 81 default: 82 return ""; 83 } 84 } 85 86 std::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) { 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"; 99 } 100 return "none"; 101 } 102 103 void 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 114 void 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 134 bool 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 161 wait_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 191 Return<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; 197 bool roleSwitch = false; 198 199 if (filename == "") { 200 ALOGE("Fatal: invalid node type"); 201 return Void(); 202 } 203 204 pthread_mutex_lock(&mRoleSwitchLock); 205 206 ALOGI("filename write: %s role:%s", filename.c_str(), 207 convertRoletoString(newRole).c_str()); 208 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; 221 } else { 222 ALOGE("Role switch failed"); 223 } 224 } else { 225 ALOGE("failed to update the new role"); 226 } 227 } else { 228 ALOGE("fopen failed"); 229 } 230 } 231 232 pthread_mutex_lock(&mLock); 233 if (mCallback_1_0 != NULL) { 234 Return<void> ret = 235 mCallback_1_0->notifyRoleSwitchStatus(portName, newRole, 236 roleSwitch ? Status::SUCCESS : Status::ERROR); 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); 243 pthread_mutex_unlock(&mRoleSwitchLock); 244 245 return Void(); 246 } 247 248 Status getAccessoryConnected(const std::string &portName, std::string *accessory) { 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 261 Status getCurrentRoleHelper(const std::string &portName, bool connected, 262 PortRoleType type, uint32_t *currentRole) { 263 std::string filename; 264 std::string roleName; 265 std::string accessory; 266 267 // Mode 268 269 if (type == PortRoleType::POWER_ROLE) { 270 filename = "/sys/class/typec/" + portName + "/power_role"; 271 *currentRole = static_cast<uint32_t>(PortPowerRole::NONE); 272 } else if (type == PortRoleType::DATA_ROLE) { 273 filename = "/sys/class/typec/" + portName + "/data_role"; 274 *currentRole = static_cast<uint32_t>(PortDataRole::NONE); 275 } else if (type == PortRoleType::MODE) { 276 filename = "/sys/class/typec/" + portName + "/data_role"; 277 *currentRole = static_cast<uint32_t>(PortMode_1_1::NONE); 278 } else { 279 return Status::ERROR; 280 } 281 282 if (!connected) return Status::SUCCESS; 283 284 if (type == PortRoleType::MODE) { 285 if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) { 286 return Status::ERROR; 287 } 288 if (accessory == "analog_audio") { 289 *currentRole = static_cast<uint32_t>(PortMode_1_1::AUDIO_ACCESSORY); 290 return Status::SUCCESS; 291 } else if (accessory == "debug") { 292 *currentRole = static_cast<uint32_t>(PortMode_1_1::DEBUG_ACCESSORY); 293 return Status::SUCCESS; 294 } 295 } 296 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 313 *currentRole = static_cast<uint32_t>(PortMode_1_1::DFP); 314 } else if (roleName == "device") { 315 if (type == PortRoleType::DATA_ROLE) 316 *currentRole = static_cast<uint32_t>(PortDataRole::DEVICE); 317 else 318 *currentRole = static_cast<uint32_t>(PortMode_1_1::UFP); 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 329 Status 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 359 bool canSwitchRoleHelper(const std::string &portName, PortRoleType /*type*/) { 360 std::string filename = 361 "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery"; 362 std::string supportsPD; 363 364 if (!readFile(filename, &supportsPD)) { 365 if (supportsPD == "yes") { 366 return true; 367 } 368 } 369 370 return false; 371 } 372 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 */ 378 Status getPortStatusHelper(hidl_vec<PortStatus_1_1> *currentPortStatus_1_1, 379 bool V1_0) { 380 std::unordered_map<std::string, bool> names; 381 Status result = getTypeCPortNamesHelper(&names); 382 int i = -1; 383 384 if (result == Status::SUCCESS) { 385 currentPortStatus_1_1->resize(names.size()); 386 for (std::pair<std::string, bool> port : names) { 387 i++; 388 ALOGI("%s", port.first.c_str()); 389 (*currentPortStatus_1_1)[i].status.portName = port.first; 390 391 uint32_t currentRole; 392 if (getCurrentRoleHelper(port.first, port.second, 393 PortRoleType::POWER_ROLE, 394 ¤tRole) == Status::SUCCESS) { 395 (*currentPortStatus_1_1)[i].status.currentPowerRole = 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 ¤tRole) == Status::SUCCESS) { 404 (*currentPortStatus_1_1)[i].status.currentDataRole = 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 ¤tRole) == Status::SUCCESS) { 413 (*currentPortStatus_1_1)[i].currentMode = 414 static_cast<PortMode_1_1>(currentRole); 415 (*currentPortStatus_1_1)[i].status.currentMode = 416 static_cast<V1_0::PortMode>(currentRole); 417 } else { 418 ALOGE("Error while retreiving current data role"); 419 goto done; 420 } 421 422 (*currentPortStatus_1_1)[i].status.canChangeMode = true; 423 (*currentPortStatus_1_1)[i].status.canChangeDataRole = 424 port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE) 425 : false; 426 (*currentPortStatus_1_1)[i].status.canChangePowerRole = 427 port.second 428 ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE) 429 : false; 430 431 ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d", 432 port.second, (*currentPortStatus_1_1)[i].status.canChangeMode, 433 (*currentPortStatus_1_1)[i].status.canChangeDataRole, 434 (*currentPortStatus_1_1)[i].status.canChangePowerRole); 435 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 } 443 } 444 return Status::SUCCESS; 445 } 446 done: 447 return Status::ERROR; 448 } 449 450 Return<void> Usb::queryPortStatus() { 451 hidl_vec<PortStatus_1_1> currentPortStatus_1_1; 452 hidl_vec<V1_0::PortStatus> currentPortStatus; 453 Status status; 454 sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(mCallback_1_0); 455 456 pthread_mutex_lock(&mLock); 457 if (mCallback_1_0 != NULL) { 458 if (callback_V1_1 != NULL) { 459 status = getPortStatusHelper(¤tPortStatus_1_1, false); 460 } else { 461 status = getPortStatusHelper(¤tPortStatus_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 474 if (!ret.isOk()) 475 ALOGE("queryPortStatus_1_1 error %s", ret.description().c_str()); 476 } else { 477 ALOGI("Notifying userspace skipped. Callback is NULL"); 478 } 479 pthread_mutex_unlock(&mLock); 480 481 return Void(); 482 } 483 484 struct data { 485 int uevent_fd; 486 android::hardware::usb::V1_1::implementation::Usb *usb; 487 }; 488 489 static 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) { 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; 512 ALOGI("uevent received %s", cp); 513 pthread_mutex_lock(&payload->usb->mLock); 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(¤tPortStatus_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(¤tPortStatus_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 } 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); 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 } 560 break; 561 } 562 /* advance to after the next \0 */ 563 while (*cp++) {} 564 } 565 } 566 567 void *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; 583 payload.usb = (android::hardware::usb::V1_1::implementation::Usb *)param; 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"); 619 error: 620 close(uevent_fd); 621 622 if (epoll_fd >= 0) close(epoll_fd); 623 624 return NULL; 625 } 626 627 void sighandler(int sig) { 628 if (sig == SIGUSR1) { 629 destroyThread = true; 630 ALOGI("destroy set"); 631 return; 632 } 633 signal(SIGUSR1, sighandler); 634 } 635 636 Return<void> Usb::setCallback(const sp<V1_0::IUsbCallback> &callback) { 637 638 sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(callback); 639 640 if (callback != NULL) 641 if (callback_V1_1 == NULL) 642 ALOGI("Registering 1.0 callback"); 643 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 */ 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; 659 pthread_mutex_unlock(&mLock); 660 return Void(); 661 } 662 663 mCallback_1_0 = callback; 664 ALOGI("registering callback"); 665 666 // Kill the worker thread if the new callback is NULL. 667 if (mCallback_1_0 == NULL) { 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); 685 mCallback_1_0 = NULL; 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 697