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