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