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@1.1-service"
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
38namespace android {
39namespace hardware {
40namespace usb {
41namespace V1_1 {
42namespace implementation {
43
44// Set by the signal handler to destroy the thread
45volatile bool destroyThread;
46
47int32_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
70std::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
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) {
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
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
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
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;
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
248Status 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
261Status 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
329Status getTypeCPortNamesHelper(std::unordered_map<std::string, bool> *names) {
330  DIR *dp;
331
332  dp = opendir("/sys/class/typec");
333  if (dp != NULL) {
334    struct dirent *ep;
335
336    while ((ep = readdir(dp))) {
337      if (ep->d_type == DT_LNK) {
338        if (std::string::npos == std::string(ep->d_name).find("-partner")) {
339          std::unordered_map<std::string, bool>::const_iterator portName =
340              names->find(ep->d_name);
341          if (portName == names->end()) {
342            names->insert({ep->d_name, false});
343          }
344        } else {
345          (*names)[std::strtok(ep->d_name, "-")] = true;
346        }
347      }
348    }
349    closedir(dp);
350    return Status::SUCCESS;
351  }
352
353  ALOGE("Failed to open /sys/class/typec");
354  return Status::ERROR;
355}
356
357bool canSwitchRoleHelper(const std::string &portName, PortRoleType /*type*/) {
358  std::string filename =
359      "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery";
360  std::string supportsPD;
361
362  if (!readFile(filename, &supportsPD)) {
363    if (supportsPD == "yes") {
364      return true;
365    }
366  }
367
368  return false;
369}
370
371/*
372 * Reuse the same method for both V1_0 and V1_1 callback objects.
373 * The caller of this method would reconstruct the V1_0::PortStatus
374 * object if required.
375 */
376Status getPortStatusHelper(hidl_vec<PortStatus_1_1> *currentPortStatus_1_1,
377    bool V1_0) {
378  std::unordered_map<std::string, bool> names;
379  Status result = getTypeCPortNamesHelper(&names);
380  int i = -1;
381
382  if (result == Status::SUCCESS) {
383    currentPortStatus_1_1->resize(names.size());
384    for (std::pair<std::string, bool> port : names) {
385      i++;
386      ALOGI("%s", port.first.c_str());
387      (*currentPortStatus_1_1)[i].status.portName = port.first;
388
389      uint32_t currentRole;
390      if (getCurrentRoleHelper(port.first, port.second,
391                               PortRoleType::POWER_ROLE,
392                               &currentRole) == Status::SUCCESS) {
393        (*currentPortStatus_1_1)[i].status.currentPowerRole =
394            static_cast<PortPowerRole>(currentRole);
395      } else {
396        ALOGE("Error while retreiving portNames");
397        goto done;
398      }
399
400      if (getCurrentRoleHelper(port.first, port.second, PortRoleType::DATA_ROLE,
401                               &currentRole) == Status::SUCCESS) {
402        (*currentPortStatus_1_1)[i].status.currentDataRole =
403            static_cast<PortDataRole>(currentRole);
404      } else {
405        ALOGE("Error while retreiving current port role");
406        goto done;
407      }
408
409      if (getCurrentRoleHelper(port.first, port.second, PortRoleType::MODE,
410                               &currentRole) == Status::SUCCESS) {
411        (*currentPortStatus_1_1)[i].currentMode =
412            static_cast<PortMode_1_1>(currentRole);
413        (*currentPortStatus_1_1)[i].status.currentMode =
414            static_cast<V1_0::PortMode>(currentRole);
415      } else {
416        ALOGE("Error while retreiving current data role");
417        goto done;
418      }
419
420      (*currentPortStatus_1_1)[i].status.canChangeMode = true;
421      (*currentPortStatus_1_1)[i].status.canChangeDataRole =
422          port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE)
423                      : false;
424      (*currentPortStatus_1_1)[i].status.canChangePowerRole =
425          port.second
426              ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE)
427              : false;
428
429      ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d",
430            port.second, (*currentPortStatus_1_1)[i].status.canChangeMode,
431            (*currentPortStatus_1_1)[i].status.canChangeDataRole,
432            (*currentPortStatus_1_1)[i].status.canChangePowerRole);
433
434      if (V1_0) {
435        (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::DFP;
436      } else {
437        (*currentPortStatus_1_1)[i].supportedModes = PortMode_1_1::UFP | PortMode_1_1::DFP;
438        (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::NONE;
439        (*currentPortStatus_1_1)[i].status.currentMode = V1_0::PortMode::NONE;
440      }
441    }
442    return Status::SUCCESS;
443  }
444done:
445  return Status::ERROR;
446}
447
448Return<void> Usb::queryPortStatus() {
449  hidl_vec<PortStatus_1_1> currentPortStatus_1_1;
450  hidl_vec<V1_0::PortStatus> currentPortStatus;
451  Status status;
452  sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(mCallback_1_0);
453
454  pthread_mutex_lock(&mLock);
455  if (mCallback_1_0 != NULL) {
456    if (callback_V1_1 != NULL) {
457      status = getPortStatusHelper(&currentPortStatus_1_1, false);
458    } else {
459      status = getPortStatusHelper(&currentPortStatus_1_1, true);
460      currentPortStatus.resize(currentPortStatus_1_1.size());
461      for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++)
462        currentPortStatus[i] = currentPortStatus_1_1[i].status;
463    }
464
465    Return<void> ret;
466
467    if (callback_V1_1 != NULL)
468      ret = callback_V1_1->notifyPortStatusChange_1_1(currentPortStatus_1_1, status);
469    else
470      ret = mCallback_1_0->notifyPortStatusChange(currentPortStatus, status);
471
472    if (!ret.isOk())
473      ALOGE("queryPortStatus_1_1 error %s", ret.description().c_str());
474  } else {
475    ALOGI("Notifying userspace skipped. Callback is NULL");
476  }
477  pthread_mutex_unlock(&mLock);
478
479  return Void();
480}
481
482struct data {
483  int uevent_fd;
484  android::hardware::usb::V1_1::implementation::Usb *usb;
485};
486
487static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
488  char msg[UEVENT_MSG_LEN + 2];
489  char *cp;
490  int n;
491
492  n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
493  if (n <= 0) return;
494  if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
495    return;
496
497  msg[n] = '\0';
498  msg[n + 1] = '\0';
499  cp = msg;
500
501  while (*cp) {
502    if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) {
503       ALOGI("partner added");
504       pthread_mutex_lock(&payload->usb->mPartnerLock);
505       payload->usb->mPartnerUp = true;
506       pthread_cond_signal(&payload->usb->mPartnerCV);
507       pthread_mutex_unlock(&payload->usb->mPartnerLock);
508    } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
509      hidl_vec<PortStatus_1_1> currentPortStatus_1_1;
510      ALOGI("uevent received %s", cp);
511      pthread_mutex_lock(&payload->usb->mLock);
512      if (payload->usb->mCallback_1_0 != NULL) {
513        sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(payload->usb->mCallback_1_0);
514        Return<void> ret;
515
516        // V1_1 callback
517        if (callback_V1_1 != NULL) {
518          Status status = getPortStatusHelper(&currentPortStatus_1_1, false);
519          ret = callback_V1_1->notifyPortStatusChange_1_1(
520              currentPortStatus_1_1, status);
521        } else { // V1_0 callback
522          Status status = getPortStatusHelper(&currentPortStatus_1_1, true);
523
524          /*
525           * Copying the result from getPortStatusHelper
526           * into V1_0::PortStatus to pass back through
527           * the V1_0 callback object.
528           */
529          hidl_vec<V1_0::PortStatus> currentPortStatus;
530          currentPortStatus.resize(currentPortStatus_1_1.size());
531          for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++)
532            currentPortStatus[i] = currentPortStatus_1_1[i].status;
533
534          ret = payload->usb->mCallback_1_0->notifyPortStatusChange(
535              currentPortStatus, status);
536        }
537        if (!ret.isOk()) ALOGE("error %s", ret.description().c_str());
538      } else {
539        ALOGI("Notifying userspace skipped. Callback is NULL");
540      }
541      pthread_mutex_unlock(&payload->usb->mLock);
542
543      //Role switch is not in progress and port is in disconnected state
544      if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) {
545        for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) {
546          DIR *dp = opendir(std::string("/sys/class/typec/"
547              + std::string(currentPortStatus_1_1[i].status.portName.c_str())
548              + "-partner").c_str());
549          if (dp == NULL) {
550              //PortRole role = {.role = static_cast<uint32_t>(PortMode::UFP)};
551              switchToDrp(currentPortStatus_1_1[i].status.portName);
552          } else {
553              closedir(dp);
554          }
555        }
556        pthread_mutex_unlock(&payload->usb->mRoleSwitchLock);
557      }
558      break;
559    }
560    /* advance to after the next \0 */
561    while (*cp++) {}
562  }
563}
564
565void *work(void *param) {
566  int epoll_fd, uevent_fd;
567  struct epoll_event ev;
568  int nevents = 0;
569  struct data payload;
570
571  ALOGE("creating thread");
572
573  uevent_fd = uevent_open_socket(64 * 1024, true);
574
575  if (uevent_fd < 0) {
576    ALOGE("uevent_init: uevent_open_socket failed\n");
577    return NULL;
578  }
579
580  payload.uevent_fd = uevent_fd;
581  payload.usb = (android::hardware::usb::V1_1::implementation::Usb *)param;
582
583  fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
584
585  ev.events = EPOLLIN;
586  ev.data.ptr = (void *)uevent_event;
587
588  epoll_fd = epoll_create(64);
589  if (epoll_fd == -1) {
590    ALOGE("epoll_create failed; errno=%d", errno);
591    goto error;
592  }
593
594  if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
595    ALOGE("epoll_ctl failed; errno=%d", errno);
596    goto error;
597  }
598
599  while (!destroyThread) {
600    struct epoll_event events[64];
601
602    nevents = epoll_wait(epoll_fd, events, 64, -1);
603    if (nevents == -1) {
604      if (errno == EINTR) continue;
605      ALOGE("usb epoll_wait failed; errno=%d", errno);
606      break;
607    }
608
609    for (int n = 0; n < nevents; ++n) {
610      if (events[n].data.ptr)
611        (*(void (*)(int, struct data *payload))events[n].data.ptr)(
612            events[n].events, &payload);
613    }
614  }
615
616  ALOGI("exiting worker thread");
617error:
618  close(uevent_fd);
619
620  if (epoll_fd >= 0) close(epoll_fd);
621
622  return NULL;
623}
624
625void sighandler(int sig) {
626  if (sig == SIGUSR1) {
627    destroyThread = true;
628    ALOGI("destroy set");
629    return;
630  }
631  signal(SIGUSR1, sighandler);
632}
633
634Return<void> Usb::setCallback(const sp<V1_0::IUsbCallback> &callback) {
635
636  sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(callback);
637
638  if (callback != NULL)
639      if (callback_V1_1 == NULL)
640          ALOGI("Registering 1.0 callback");
641
642  pthread_mutex_lock(&mLock);
643  /*
644   * When both the old callback and new callback values are NULL,
645   * there is no need to spin off the worker thread.
646   * When both the values are not NULL, we would already have a
647   * worker thread running, so updating the callback object would
648   * be suffice.
649   */
650  if ((mCallback_1_0 == NULL && callback == NULL) ||
651      (mCallback_1_0 != NULL && callback != NULL)) {
652    /*
653     * Always store as V1_0 callback object. Type cast to V1_1
654     * when the callback is actually invoked.
655     */
656    mCallback_1_0 = callback;
657    pthread_mutex_unlock(&mLock);
658    return Void();
659  }
660
661  mCallback_1_0 = callback;
662  ALOGI("registering callback");
663
664  // Kill the worker thread if the new callback is NULL.
665  if (mCallback_1_0 == NULL) {
666    pthread_mutex_unlock(&mLock);
667    if (!pthread_kill(mPoll, SIGUSR1)) {
668      pthread_join(mPoll, NULL);
669      ALOGI("pthread destroyed");
670    }
671    return Void();
672  }
673
674  destroyThread = false;
675  signal(SIGUSR1, sighandler);
676
677  /*
678   * Create a background thread if the old callback value is NULL
679   * and being updated with a new value.
680   */
681  if (pthread_create(&mPoll, NULL, work, this)) {
682    ALOGE("pthread creation failed %d", errno);
683    mCallback_1_0 = NULL;
684  }
685
686  pthread_mutex_unlock(&mLock);
687  return Void();
688}
689
690}  // namespace implementation
691}  // namespace V1_0
692}  // namespace usb
693}  // namespace hardware
694}  // namespace android
695