exynos9610: USB HAL: initial type-c hal
[GitHub/moto-9609/android_device_motorola_exynos9610-common.git] / hidl / usb / Usb.cpp
CommitLineData
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 */
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
31namespace android {
32namespace hardware {
33namespace usb {
34namespace V1_0 {
35namespace implementation {
36
37// Set by the signal handler to destroy the thread
38volatile bool destroyThread;
39
40int32_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
63std::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 + "/current_data_role";
70 case PortRoleType::POWER_ROLE:
71 return node + "/current_power_role";
72 default:
73 return node + "/mode";
74 }
75}
76
77std::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
94void 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
105Return<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
158Status 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 + "/current_power_role";
167 *currentRole = static_cast<uint32_t>(PortPowerRole::NONE);
168 } else if (type == PortRoleType::DATA_ROLE) {
169 filename = "/sys/class/typec/" + portName + "/current_data_role";
170 *currentRole = static_cast<uint32_t>(PortDataRole::NONE);
171 } else if (type == PortRoleType::MODE) {
172 filename = "/sys/class/typec/" + portName + "/current_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
212Status 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
242bool 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 == "1") {
249 return true;
250 }
251 }
252
253 return false;
254}
255
256Status getPortStatusHelper(hidl_vec<PortStatus> *currentPortStatus) {
257 std::unordered_map<std::string, bool> names;
258 Status result = getTypeCPortNamesHelper(&names);
259 int i = -1;
260
261 if (result == Status::SUCCESS) {
262 currentPortStatus->resize(names.size());
263 for (std::pair<std::string, bool> port : names) {
264 i++;
265 ALOGI("%s", port.first.c_str());
266 (*currentPortStatus)[i].portName = port.first;
267
268 uint32_t currentRole;
269 if (getCurrentRoleHelper(port.first, port.second,
270 PortRoleType::POWER_ROLE,
271 &currentRole) == Status::SUCCESS) {
272 (*currentPortStatus)[i].currentPowerRole =
273 static_cast<PortPowerRole>(currentRole);
274 } else {
275 ALOGE("Error while retreiving portNames");
276 goto done;
277 }
278
279 if (getCurrentRoleHelper(port.first, port.second, PortRoleType::DATA_ROLE,
280 &currentRole) == Status::SUCCESS) {
281 (*currentPortStatus)[i].currentDataRole =
282 static_cast<PortDataRole>(currentRole);
283 } else {
284 ALOGE("Error while retreiving current port role");
285 goto done;
286 }
287
288 if (getCurrentRoleHelper(port.first, port.second, PortRoleType::MODE,
289 &currentRole) == Status::SUCCESS) {
290 (*currentPortStatus)[i].currentMode =
291 static_cast<PortMode>(currentRole);
292 } else {
293 ALOGE("Error while retreiving current data role");
294 goto done;
295 }
296
297 (*currentPortStatus)[i].canChangeMode = false;
298 (*currentPortStatus)[i].canChangeDataRole =
299 port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE)
300 : false;
301 (*currentPortStatus)[i].canChangePowerRole =
302 port.second
303 ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE)
304 : false;
305
306 ALOGI("connected:%d canChangeMode: %d canChagedata: %d canChangePower:%d",
307 port.second, (*currentPortStatus)[i].canChangeMode,
308 (*currentPortStatus)[i].canChangeDataRole,
309 (*currentPortStatus)[i].canChangePowerRole);
310
311 (*currentPortStatus)[i].supportedModes = PortMode::DRP;
312 }
313 return Status::SUCCESS;
314 }
315done:
316 return Status::ERROR;
317}
318
319Return<void> Usb::queryPortStatus() {
320 hidl_vec<PortStatus> currentPortStatus;
321 Status status;
322
323 status = getPortStatusHelper(&currentPortStatus);
324
325 pthread_mutex_lock(&mLock);
326 if (mCallback != NULL) {
327 Return<void> ret =
328 mCallback->notifyPortStatusChange(currentPortStatus, status);
329 if (!ret.isOk())
330 ALOGE("queryPortStatus error %s", ret.description().c_str());
331 } else {
332 ALOGI("Notifying userspace skipped. Callback is NULL");
333 }
334 pthread_mutex_unlock(&mLock);
335
336 return Void();
337}
338struct data {
339 int uevent_fd;
340 android::hardware::usb::V1_0::implementation::Usb *usb;
341};
342
343static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
344 char msg[UEVENT_MSG_LEN + 2];
345 char *cp;
346 int n;
347
348 n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
349 if (n <= 0) return;
350 if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
351 return;
352
353 msg[n] = '\0';
354 msg[n + 1] = '\0';
355 cp = msg;
356
357 while (*cp) {
358 if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
359 ALOGI("uevent received %s", cp);
360 pthread_mutex_lock(&payload->usb->mLock);
361 if (payload->usb->mCallback != NULL) {
362 hidl_vec<PortStatus> currentPortStatus;
363 Status status = getPortStatusHelper(&currentPortStatus);
364 Return<void> ret = payload->usb->mCallback->notifyPortStatusChange(
365 currentPortStatus, status);
366 if (!ret.isOk()) ALOGE("error %s", ret.description().c_str());
367 } else {
368 ALOGI("Notifying userspace skipped. Callback is NULL");
369 }
370 pthread_mutex_unlock(&payload->usb->mLock);
371 break;
372 }
373 /* advance to after the next \0 */
374 while (*cp++) {}
375 }
376}
377
378void *work(void *param) {
379 int epoll_fd, uevent_fd;
380 struct epoll_event ev;
381 int nevents = 0;
382 struct data payload;
383
384 ALOGE("creating thread");
385
386 uevent_fd = uevent_open_socket(64 * 1024, true);
387
388 if (uevent_fd < 0) {
389 ALOGE("uevent_init: uevent_open_socket failed\n");
390 return NULL;
391 }
392
393 payload.uevent_fd = uevent_fd;
394 payload.usb = (android::hardware::usb::V1_0::implementation::Usb *)param;
395
396 fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
397
398 ev.events = EPOLLIN;
399 ev.data.ptr = (void *)uevent_event;
400
401 epoll_fd = epoll_create(64);
402 if (epoll_fd == -1) {
403 ALOGE("epoll_create failed; errno=%d", errno);
404 goto error;
405 }
406
407 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
408 ALOGE("epoll_ctl failed; errno=%d", errno);
409 goto error;
410 }
411
412 while (!destroyThread) {
413 struct epoll_event events[64];
414
415 nevents = epoll_wait(epoll_fd, events, 64, -1);
416 if (nevents == -1) {
417 if (errno == EINTR) continue;
418 ALOGE("usb epoll_wait failed; errno=%d", errno);
419 break;
420 }
421
422 for (int n = 0; n < nevents; ++n) {
423 if (events[n].data.ptr)
424 (*(void (*)(int, struct data *payload))events[n].data.ptr)(
425 events[n].events, &payload);
426 }
427 }
428
429 ALOGI("exiting worker thread");
430error:
431 close(uevent_fd);
432
433 if (epoll_fd >= 0) close(epoll_fd);
434
435 return NULL;
436}
437
438void sighandler(int sig) {
439 if (sig == SIGUSR1) {
440 destroyThread = true;
441 ALOGI("destroy set");
442 return;
443 }
444 signal(SIGUSR1, sighandler);
445}
446
447Return<void> Usb::setCallback(const sp<IUsbCallback> &callback) {
448 pthread_mutex_lock(&mLock);
449 /*
450 * When both the old callback and new callback values are NULL,
451 * there is no need to spin off the worker thread.
452 * When both the values are not NULL, we would already have a
453 * worker thread running, so updating the callback object would
454 * be suffice.
455 */
456 if ((mCallback == NULL && callback == NULL) ||
457 (mCallback != NULL && callback != NULL)) {
458 mCallback = callback;
459 pthread_mutex_unlock(&mLock);
460 return Void();
461 }
462
463 mCallback = callback;
464 ALOGI("registering callback");
465
466 // Kill the worker thread if the new callback is NULL.
467 if (mCallback == NULL) {
468 pthread_mutex_unlock(&mLock);
469 if (!pthread_kill(mPoll, SIGUSR1)) {
470 pthread_join(mPoll, NULL);
471 ALOGI("pthread destroyed");
472 }
473 return Void();
474 }
475
476 destroyThread = false;
477 signal(SIGUSR1, sighandler);
478
479 /*
480 * Create a background thread if the old callback value is NULL
481 * and being updated with a new value.
482 */
483 if (pthread_create(&mPoll, NULL, work, this)) {
484 ALOGE("pthread creation failed %d", errno);
485 mCallback = NULL;
486 }
487
488 pthread_mutex_unlock(&mLock);
489 return Void();
490}
491
492} // namespace implementation
493} // namespace V1_0
494} // namespace usb
495} // namespace hardware
496} // namespace android