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 32namespace android { 33namespace hardware { 34namespace usb { 35namespace V1_0 { 36namespace implementation { 37 38// Set by the signal handler to destroy the thread 39volatile bool destroyThread; 40 41int32_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 52std::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 65std::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 85Return<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 118Status getCurrentRoleHelper(std::string portName, 119 PortRoleType type, uint32_t ¤tRole) { 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 163Status getTypeCPortNamesHelper(std::vector<std::string>& names) { 164 DIR *dp; 165 166 dp = opendir("/sys/class/dual_role_usb"); 167 if (dp != NULL) 168 { 169rescan: 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 207bool 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 218Status 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 240Status 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 } 298done: 299 return Status::ERROR; 300} 301 302Return<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} 314struct data { 315 int uevent_fd; 316 android::hardware::usb::V1_0::implementation::Usb *usb; 317}; 318 319static 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 352void* 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"); 405error: 406 close(uevent_fd); 407 408 if (epoll_fd >= 0) 409 close(epoll_fd); 410 411 return NULL; 412} 413 414void sighandler(int sig) 415{ 416 if (sig == SIGUSR1) { 417 destroyThread = true; 418 ALOGI("destroy set"); 419 return; 420 } 421 signal(SIGUSR1, sighandler); 422} 423 424Return<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 458pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 459Usb *usb; 460 461Usb::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