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