Home | History | Annotate | Download | only in default
      1 /*
      2  * Copyright (C) 2016 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 #include <assert.h>
     17 #include <dirent.h>
     18 #include <iostream>
     19 #include <fstream>
     20 #include <pthread.h>
     21 #include <stdio.h>
     22 #include <sys/types.h>
     23 #include <unistd.h>
     24 
     25 #include <cutils/uevent.h>
     26 #include <sys/epoll.h>
     27 #include <utils/Errors.h>
     28 #include <utils/StrongPointer.h>
     29 
     30 #include "Usb.h"
     31 
     32 namespace android {
     33 namespace hardware {
     34 namespace usb {
     35 namespace V1_0 {
     36 namespace implementation {
     37 
     38 // Set by the signal handler to destroy the thread
     39 volatile bool destroyThread;
     40 
     41 int32_t readFile(std::string filename, std::string& contents) {
     42     std::ifstream file(filename);
     43 
     44     if (file.is_open()) {
     45         getline(file, contents);
     46         file.close();
     47         return 0;
     48     }
     49     return -1;
     50 }
     51 
     52 std::string appendRoleNodeHelper(const std::string portName, PortRoleType type) {
     53     std::string node("/sys/class/dual_role_usb/" + portName);
     54 
     55     switch(type) {
     56         case PortRoleType::DATA_ROLE:
     57             return node + "/data_role";
     58         case PortRoleType::POWER_ROLE:
     59             return node + "/power_role";
     60         default:
     61             return node + "/mode";
     62     }
     63 }
     64 
     65 std::string convertRoletoString(PortRole role) {
     66     if (role.type == PortRoleType::POWER_ROLE) {
     67         if (role.role == static_cast<uint32_t> (PortPowerRole::SOURCE))
     68             return "source";
     69         else if (role.role ==  static_cast<uint32_t> (PortPowerRole::SINK))
     70             return "sink";
     71     } else if (role.type == PortRoleType::DATA_ROLE) {
     72         if (role.role == static_cast<uint32_t> (PortDataRole::HOST))
     73             return "host";
     74         if (role.role == static_cast<uint32_t> (PortDataRole::DEVICE))
     75             return "device";
     76     } else if (role.type == PortRoleType::MODE) {
     77         if (role.role == static_cast<uint32_t> (PortMode::UFP))
     78             return "ufp";
     79         if (role.role == static_cast<uint32_t> (PortMode::DFP))
     80             return "dfp";
     81     }
     82     return "none";
     83 }
     84 
     85 Return<void> Usb::switchRole(const hidl_string& portName,
     86         const PortRole& newRole) {
     87     std::string filename = appendRoleNodeHelper(std::string(portName.c_str()),
     88         newRole.type);
     89     std::ofstream file(filename);
     90     std::string written;
     91 
     92     ALOGI("filename write: %s role:%d", filename.c_str(), newRole.role);
     93 
     94     if (file.is_open()) {
     95         file << convertRoletoString(newRole).c_str();
     96         file.close();
     97         if (!readFile(filename, written)) {
     98             ALOGI("written: %s", written.c_str());
     99             if (written == convertRoletoString(newRole)) {
    100                 ALOGI("Role switch successfull");
    101                 Return<void> ret =
    102                     mCallback->notifyRoleSwitchStatus(portName, newRole,
    103                     Status::SUCCESS);
    104                 if (!ret.isOk())
    105                     ALOGE("RoleSwitchStatus error %s",
    106                         ret.description().c_str());
    107             }
    108         }
    109     }
    110 
    111     Return<void> ret = mCallback->notifyRoleSwitchStatus(portName, newRole, Status::ERROR);
    112     if (!ret.isOk())
    113         ALOGE("RoleSwitchStatus error %s", ret.description().c_str());
    114 
    115     return Void();
    116 }
    117 
    118 Status getCurrentRoleHelper(std::string portName,
    119         PortRoleType type, uint32_t &currentRole)  {
    120     std::string filename;
    121     std::string roleName;
    122 
    123     if (type == PortRoleType::POWER_ROLE) {
    124         filename = "/sys/class/dual_role_usb/" +
    125             portName + "/power_role";
    126         currentRole = static_cast<uint32_t>(PortPowerRole::NONE);
    127     } else if (type == PortRoleType::DATA_ROLE) {
    128         filename = "/sys/class/dual_role_usb/" +
    129             portName + "/data_role";
    130         currentRole = static_cast<uint32_t> (PortDataRole::NONE);
    131     } else if (type == PortRoleType::MODE) {
    132         filename = "/sys/class/dual_role_usb/" +
    133             portName + "/mode";
    134         currentRole = static_cast<uint32_t> (PortMode::NONE);
    135     }
    136 
    137     if (readFile(filename, roleName)) {
    138         ALOGE("getCurrentRole: Failed to open filesystem node");
    139         return Status::ERROR;
    140     }
    141 
    142     if (roleName == "dfp")
    143         currentRole = static_cast<uint32_t> (PortMode::DFP);
    144     else if (roleName == "ufp")
    145         currentRole = static_cast<uint32_t> (PortMode::UFP);
    146     else if (roleName == "source")
    147         currentRole = static_cast<uint32_t> (PortPowerRole::SOURCE);
    148     else if (roleName == "sink")
    149         currentRole = static_cast<uint32_t> (PortPowerRole::SINK);
    150     else if (roleName == "host")
    151         currentRole = static_cast<uint32_t> (PortDataRole::HOST);
    152     else if (roleName == "device")
    153         currentRole = static_cast<uint32_t> (PortDataRole::DEVICE);
    154     else if (roleName != "none") {
    155          /* case for none has already been addressed.
    156           * so we check if the role isnt none.
    157           */
    158         return Status::UNRECOGNIZED_ROLE;
    159     }
    160     return Status::SUCCESS;
    161 }
    162 
    163 Status getTypeCPortNamesHelper(std::vector<std::string>& names) {
    164     DIR *dp;
    165 
    166     dp = opendir("/sys/class/dual_role_usb");
    167     if (dp != NULL)
    168     {
    169 rescan:
    170         int32_t ports = 0;
    171         int32_t current = 0;
    172         struct dirent *ep;
    173 
    174         while ((ep = readdir (dp))) {
    175             if (ep->d_type == DT_LNK) {
    176                 ports++;
    177             }
    178         }
    179 
    180         if (ports == 0) {
    181             closedir(dp);
    182             return Status::SUCCESS;
    183         }
    184 
    185         names.resize(ports);
    186         rewinddir(dp);
    187 
    188         while ((ep = readdir (dp))) {
    189             if (ep->d_type == DT_LNK) {
    190                 /* Check to see if new ports were added since the first pass. */
    191                 if (current >= ports) {
    192                     rewinddir(dp);
    193                     goto rescan;
    194                 }
    195                 names[current++] = ep->d_name;
    196             }
    197         }
    198 
    199         closedir (dp);
    200         return Status::SUCCESS;
    201     }
    202 
    203     ALOGE("Failed to open /sys/class/dual_role_usb");
    204     return Status::ERROR;
    205 }
    206 
    207 bool canSwitchRoleHelper(const std::string portName, PortRoleType type)  {
    208     std::string filename = appendRoleNodeHelper(portName, type);
    209     std::ofstream file(filename);
    210 
    211     if (file.is_open()) {
    212         file.close();
    213         return true;
    214     }
    215     return false;
    216 }
    217 
    218 Status getPortModeHelper(const std::string portName, PortMode& portMode)  {
    219     std::string filename = "/sys/class/dual_role_usb/" +
    220     std::string(portName.c_str()) + "/supported_modes";
    221     std::string modes;
    222 
    223     if (readFile(filename, modes)) {
    224         ALOGE("getSupportedRoles: Failed to open filesystem node");
    225         return Status::ERROR;
    226     }
    227 
    228     if (modes == "ufp dfp")
    229         portMode = PortMode::DRP;
    230     else  if (modes == "ufp")
    231         portMode = PortMode::UFP;
    232     else if  (modes == "dfp")
    233         portMode = PortMode::DFP;
    234     else
    235         return Status::UNRECOGNIZED_ROLE;
    236 
    237         return Status::SUCCESS;
    238 }
    239 
    240 Status getPortStatusHelper (hidl_vec<PortStatus>& currentPortStatus) {
    241     std::vector<std::string> names;
    242     Status result = getTypeCPortNamesHelper(names);
    243 
    244     if (result == Status::SUCCESS) {
    245         currentPortStatus.resize(names.size());
    246         for(std::vector<std::string>::size_type i = 0; i < names.size(); i++) {
    247             ALOGI("%s", names[i].c_str());
    248             currentPortStatus[i].portName = names[i];
    249 
    250             uint32_t currentRole;
    251             if (getCurrentRoleHelper(names[i], PortRoleType::POWER_ROLE,
    252                     currentRole) == Status::SUCCESS) {
    253                 currentPortStatus[i].currentPowerRole =
    254                 static_cast<PortPowerRole> (currentRole);
    255             } else {
    256                 ALOGE("Error while retreiving portNames");
    257                 goto done;
    258             }
    259 
    260             if (getCurrentRoleHelper(names[i],
    261                     PortRoleType::DATA_ROLE, currentRole) == Status::SUCCESS) {
    262                 currentPortStatus[i].currentDataRole =
    263                         static_cast<PortDataRole> (currentRole);
    264             } else {
    265                 ALOGE("Error while retreiving current port role");
    266                 goto done;
    267             }
    268 
    269             if (getCurrentRoleHelper(names[i], PortRoleType::MODE,
    270                     currentRole) == Status::SUCCESS) {
    271                 currentPortStatus[i].currentMode =
    272                     static_cast<PortMode> (currentRole);
    273             } else {
    274                 ALOGE("Error while retreiving current data role");
    275                 goto done;
    276             }
    277 
    278             currentPortStatus[i].canChangeMode =
    279                 canSwitchRoleHelper(names[i], PortRoleType::MODE);
    280             currentPortStatus[i].canChangeDataRole =
    281                 canSwitchRoleHelper(names[i], PortRoleType::DATA_ROLE);
    282             currentPortStatus[i].canChangePowerRole =
    283                 canSwitchRoleHelper(names[i], PortRoleType::POWER_ROLE);
    284 
    285             ALOGI("canChangeMode: %d canChagedata: %d canChangePower:%d",
    286                 currentPortStatus[i].canChangeMode,
    287                 currentPortStatus[i].canChangeDataRole,
    288                 currentPortStatus[i].canChangePowerRole);
    289 
    290             if (getPortModeHelper(names[i], currentPortStatus[i].supportedModes)
    291                   != Status::SUCCESS) {
    292                 ALOGE("Error while retrieving port modes");
    293                 goto done;
    294             }
    295         }
    296         return Status::SUCCESS;
    297     }
    298 done:
    299     return Status::ERROR;
    300 }
    301 
    302 Return<void> Usb::queryPortStatus() {
    303     hidl_vec<PortStatus> currentPortStatus;
    304     Status status;
    305 
    306     status = getPortStatusHelper(currentPortStatus);
    307     Return<void> ret = mCallback->notifyPortStatusChange(currentPortStatus,
    308        status);
    309     if (!ret.isOk())
    310         ALOGE("queryPortStatus error %s", ret.description().c_str());
    311 
    312     return Void();
    313 }
    314 struct data {
    315     int uevent_fd;
    316     android::hardware::usb::V1_0::implementation::Usb *usb;
    317 };
    318 
    319 static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
    320     char msg[UEVENT_MSG_LEN + 2];
    321     char *cp;
    322     int n;
    323 
    324     n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
    325     if (n <= 0)
    326         return;
    327     if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
    328         return;
    329 
    330     msg[n] = '\0';
    331     msg[n + 1] = '\0';
    332     cp = msg;
    333 
    334     while (*cp) {
    335         if (!strcmp(cp, "SUBSYSTEM=dual_role_usb")) {
    336             ALOGE("uevent received %s", cp);
    337             if (payload->usb->mCallback != NULL) {
    338                 hidl_vec<PortStatus> currentPortStatus;
    339                 Status status = getPortStatusHelper(currentPortStatus);
    340                 Return<void> ret =
    341                     payload->usb->mCallback->notifyPortStatusChange(currentPortStatus, status);
    342                 if (!ret.isOk())
    343                     ALOGE("error %s", ret.description().c_str());
    344             }
    345             break;
    346         }
    347         /* advance to after the next \0 */
    348         while (*cp++);
    349     }
    350 }
    351 
    352 void* work(void* param) {
    353     int epoll_fd, uevent_fd;
    354     struct epoll_event ev;
    355     int nevents = 0;
    356     struct data payload;
    357 
    358     ALOGE("creating thread");
    359 
    360     uevent_fd = uevent_open_socket(64*1024, true);
    361 
    362     if (uevent_fd < 0) {
    363         ALOGE("uevent_init: uevent_open_socket failed\n");
    364         return NULL;
    365     }
    366 
    367     payload.uevent_fd = uevent_fd;
    368     payload.usb = (android::hardware::usb::V1_0::implementation::Usb *)param;
    369 
    370     fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
    371 
    372     ev.events = EPOLLIN;
    373     ev.data.ptr = (void *)uevent_event;
    374 
    375     epoll_fd = epoll_create(64);
    376     if (epoll_fd == -1) {
    377         ALOGE("epoll_create failed; errno=%d", errno);
    378         goto error;
    379     }
    380 
    381     if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
    382         ALOGE("epoll_ctl failed; errno=%d", errno);
    383         goto error;
    384     }
    385 
    386     while (!destroyThread) {
    387         struct epoll_event events[64];
    388 
    389         nevents = epoll_wait(epoll_fd, events, 64, -1);
    390         if (nevents == -1) {
    391             if (errno == EINTR)
    392                 continue;
    393             ALOGE("usb epoll_wait failed; errno=%d", errno);
    394             break;
    395         }
    396 
    397         for (int n = 0; n < nevents; ++n) {
    398             if (events[n].data.ptr)
    399                 (*(void (*)(int, struct data *payload))events[n].data.ptr)
    400                     (events[n].events, &payload);
    401         }
    402     }
    403 
    404     ALOGI("exiting worker thread");
    405 error:
    406     close(uevent_fd);
    407 
    408     if (epoll_fd >= 0)
    409         close(epoll_fd);
    410 
    411     return NULL;
    412 }
    413 
    414 void sighandler(int sig)
    415 {
    416     if (sig == SIGUSR1) {
    417         destroyThread = true;
    418         ALOGI("destroy set");
    419         return;
    420     }
    421     signal(SIGUSR1, sighandler);
    422 }
    423 
    424 Return<void> Usb::setCallback(const sp<IUsbCallback>& callback) {
    425 
    426     pthread_mutex_lock(&mLock);
    427     if ((mCallback == NULL && callback == NULL) ||
    428             (mCallback != NULL && callback != NULL)) {
    429         mCallback = callback;
    430         pthread_mutex_unlock(&mLock);
    431         return Void();
    432     }
    433 
    434     mCallback = callback;
    435     ALOGI("registering callback");
    436 
    437     if (mCallback == NULL) {
    438         if  (!pthread_kill(mPoll, SIGUSR1)) {
    439             pthread_join(mPoll, NULL);
    440             ALOGI("pthread destroyed");
    441         }
    442         pthread_mutex_unlock(&mLock);
    443         return Void();
    444     }
    445 
    446     destroyThread = false;
    447     signal(SIGUSR1, sighandler);
    448 
    449     if (pthread_create(&mPoll, NULL, work, this)) {
    450         ALOGE("pthread creation failed %d", errno);
    451         mCallback = NULL;
    452     }
    453     pthread_mutex_unlock(&mLock);
    454     return Void();
    455 }
    456 
    457 // Protects *usb assignment
    458 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    459 Usb *usb;
    460 
    461 Usb::Usb() {
    462     pthread_mutex_lock(&lock);
    463     // Make this a singleton class
    464     assert(usb == NULL);
    465     usb = this;
    466     pthread_mutex_unlock(&lock);
    467 }
    468 
    469 }  // namespace implementation
    470 }  // namespace V1_0
    471 }  // namespace usb
    472 }  // namespace hardware
    473 }  // namespace android
    474