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