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