blob: cdf0d797cb03c0afa67edebdb90dc7ded0a1d68b [file] [log] [blame]
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -08001/*
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 */
Badhri Jagan Sridharan6e718f92017-01-30 18:41:32 -080016#include <assert.h>
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -080017#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
32namespace android {
33namespace hardware {
34namespace usb {
35namespace V1_0 {
36namespace implementation {
37
Badhri Jagan Sridharan6e718f92017-01-30 18:41:32 -080038// Set by the signal handler to destroy the thread
39volatile bool destroyThread;
40
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -080041int32_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
52std::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
65std::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
85Return<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
118Status getCurrentRoleHelper(std::string portName,
119 PortRoleType type, uint32_t &currentRole) {
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
163Status getTypeCPortNamesHelper(std::vector<std::string>& names) {
164 DIR *dp;
165
166 dp = opendir("/sys/class/dual_role_usb");
167 if (dp != NULL)
168 {
169rescan:
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 names.resize(ports);
180 rewinddir(dp);
181
182 while ((ep = readdir (dp))) {
183 /* Check to see if new ports were added since the first pass. */
184 if (current >= ports) {
185 rewinddir(dp);
186 goto rescan;
187 }
188
189 if (ep->d_type == DT_LNK) {
190 names[current++] = ep->d_name;
191 }
192 }
193
194 closedir (dp);
195 return Status::SUCCESS;
196 }
197
198 ALOGE("Failed to open /sys/class/dual_role_usb");
199 return Status::ERROR;
200}
201
202bool canSwitchRoleHelper(const std::string portName, PortRoleType type) {
203 std::string filename = appendRoleNodeHelper(portName, type);
204 std::ofstream file(filename);
205
206 if (file.is_open()) {
207 file.close();
208 return true;
209 }
210 return false;
211}
212
213Status getPortModeHelper(const std::string portName, PortMode& portMode) {
214 std::string filename = "/sys/class/dual_role_usb/" +
215 std::string(portName.c_str()) + "/supported_modes";
216 std::string modes;
217
218 if (readFile(filename, modes)) {
219 ALOGE("getSupportedRoles: Failed to open filesystem node");
220 return Status::ERROR;
221 }
222
223 if (modes == "ufp dfp")
224 portMode = PortMode::DRP;
225 else if (modes == "ufp")
226 portMode = PortMode::UFP;
227 else if (modes == "dfp")
228 portMode = PortMode::DFP;
229 else
230 return Status::UNRECOGNIZED_ROLE;
231
232 return Status::SUCCESS;
233}
234
235Status getPortStatusHelper (hidl_vec<PortStatus>& currentPortStatus) {
236 std::vector<std::string> names;
237 Status result = getTypeCPortNamesHelper(names);
238
239 if (result == Status::SUCCESS) {
240 currentPortStatus.resize(names.size());
241 for(std::vector<std::string>::size_type i = 0; i != names.size(); i++) {
242 ALOGI("%s", names[i].c_str());
243 currentPortStatus[i].portName = names[i];
244
245 uint32_t currentRole;
246 if (getCurrentRoleHelper(names[i], PortRoleType::POWER_ROLE,
247 currentRole) == Status::SUCCESS) {
248 currentPortStatus[i].currentPowerRole =
249 static_cast<PortPowerRole> (currentRole);
250 } else {
251 ALOGE("Error while retreiving portNames");
252 goto done;
253 }
254
255 if (getCurrentRoleHelper(names[i],
256 PortRoleType::DATA_ROLE, currentRole) == Status::SUCCESS) {
257 currentPortStatus[i].currentDataRole =
258 static_cast<PortDataRole> (currentRole);
259 } else {
260 ALOGE("Error while retreiving current port role");
261 goto done;
262 }
263
264 if (getCurrentRoleHelper(names[i], PortRoleType::MODE,
265 currentRole) == Status::SUCCESS) {
266 currentPortStatus[i].currentMode =
267 static_cast<PortMode> (currentRole);
268 } else {
269 ALOGE("Error while retreiving current data role");
270 goto done;
271 }
272
273 currentPortStatus[i].canChangeMode =
274 canSwitchRoleHelper(names[i], PortRoleType::MODE);
275 currentPortStatus[i].canChangeDataRole =
276 canSwitchRoleHelper(names[i], PortRoleType::DATA_ROLE);
277 currentPortStatus[i].canChangePowerRole =
278 canSwitchRoleHelper(names[i], PortRoleType::POWER_ROLE);
279
280 ALOGI("canChangeMode: %d canChagedata: %d canChangePower:%d",
281 currentPortStatus[i].canChangeMode,
282 currentPortStatus[i].canChangeDataRole,
283 currentPortStatus[i].canChangePowerRole);
284
285 if (getPortModeHelper(names[i], currentPortStatus[i].supportedModes)
286 != Status::SUCCESS) {
287 ALOGE("Error while retrieving port modes");
288 goto done;
289 }
290 }
291 return Status::SUCCESS;
292 }
293done:
294 return Status::ERROR;
295}
296
297Return<void> Usb::queryPortStatus() {
298 hidl_vec<PortStatus> currentPortStatus;
299 Status status;
300
301 status = getPortStatusHelper(currentPortStatus);
302 Return<void> ret = mCallback->notifyPortStatusChange(currentPortStatus,
303 status);
304 if (!ret.isOk())
305 ALOGE("queryPortStatus error %s", ret.description().c_str());
306
307 return Void();
308}
309struct data {
310 int uevent_fd;
311 android::hardware::usb::V1_0::implementation::Usb *usb;
312};
313
314static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
315 char msg[UEVENT_MSG_LEN + 2];
316 char *cp;
317 int n;
318
319 n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
320 if (n <= 0)
321 return;
322 if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
323 return;
324
325 msg[n] = '\0';
326 msg[n + 1] = '\0';
327 cp = msg;
328
329 while (*cp) {
330 if (!strcmp(cp, "SUBSYSTEM=dual_role_usb")) {
331 ALOGE("uevent received %s", cp);
332 if (payload->usb->mCallback != NULL) {
333 hidl_vec<PortStatus> currentPortStatus;
334 Status status = getPortStatusHelper(currentPortStatus);
335 Return<void> ret =
336 payload->usb->mCallback->notifyPortStatusChange(currentPortStatus, status);
337 if (!ret.isOk())
338 ALOGE("error %s", ret.description().c_str());
339 }
340 break;
341 }
342 /* advance to after the next \0 */
343 while (*cp++);
344 }
345}
346
347void* work(void* param) {
348 int epoll_fd, uevent_fd;
349 struct epoll_event ev;
350 int nevents = 0;
351 struct data payload;
352
353 ALOGE("creating thread");
354
355 uevent_fd = uevent_open_socket(64*1024, true);
356
357 if (uevent_fd < 0) {
358 ALOGE("uevent_init: uevent_open_socket failed\n");
359 return NULL;
360 }
361
362 payload.uevent_fd = uevent_fd;
363 payload.usb = (android::hardware::usb::V1_0::implementation::Usb *)param;
364
365 fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
366
367 ev.events = EPOLLIN;
368 ev.data.ptr = (void *)uevent_event;
369
370 epoll_fd = epoll_create(64);
371 if (epoll_fd == -1) {
372 ALOGE("epoll_create failed; errno=%d", errno);
373 goto error;
374 }
375
376 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
377 ALOGE("epoll_ctl failed; errno=%d", errno);
378 goto error;
379 }
380
Badhri Jagan Sridharan6e718f92017-01-30 18:41:32 -0800381 while (!destroyThread) {
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800382 struct epoll_event events[64];
383
384 nevents = epoll_wait(epoll_fd, events, 64, -1);
385 if (nevents == -1) {
386 if (errno == EINTR)
387 continue;
388 ALOGE("usb epoll_wait failed; errno=%d", errno);
389 break;
390 }
391
392 for (int n = 0; n < nevents; ++n) {
393 if (events[n].data.ptr)
394 (*(void (*)(int, struct data *payload))events[n].data.ptr)
395 (events[n].events, &payload);
396 }
397 }
398
Badhri Jagan Sridharan6e718f92017-01-30 18:41:32 -0800399 ALOGI("exiting worker thread");
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800400error:
401 close(uevent_fd);
402
403 if (epoll_fd >= 0)
404 close(epoll_fd);
405
406 return NULL;
407}
408
Badhri Jagan Sridharan6e718f92017-01-30 18:41:32 -0800409void sighandler(int sig)
410{
411 if (sig == SIGUSR1) {
412 destroyThread = true;
413 ALOGI("destroy set");
414 return;
415 }
416 signal(SIGUSR1, sighandler);
417}
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800418
419Return<void> Usb::setCallback(const sp<IUsbCallback>& callback) {
420
Badhri Jagan Sridharan6e718f92017-01-30 18:41:32 -0800421 pthread_mutex_lock(&mLock);
422 if ((mCallback == NULL && callback == NULL) ||
423 (mCallback != NULL && callback != NULL)) {
424 mCallback = callback;
425 pthread_mutex_unlock(&mLock);
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800426 return Void();
427 }
428
429 mCallback = callback;
430 ALOGI("registering callback");
431
Badhri Jagan Sridharan6e718f92017-01-30 18:41:32 -0800432 if (mCallback == NULL) {
433 if (!pthread_kill(mPoll, SIGUSR1)) {
434 pthread_join(mPoll, NULL);
435 ALOGI("pthread destroyed");
436 }
437 pthread_mutex_unlock(&mLock);
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800438 return Void();
439 }
440
Badhri Jagan Sridharan6e718f92017-01-30 18:41:32 -0800441 destroyThread = false;
442 signal(SIGUSR1, sighandler);
443
444 if (pthread_create(&mPoll, NULL, work, this)) {
445 ALOGE("pthread creation failed %d", errno);
446 mCallback = NULL;
447 }
448 pthread_mutex_unlock(&mLock);
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800449 return Void();
450}
451
Badhri Jagan Sridharan6e718f92017-01-30 18:41:32 -0800452// Protects *usb assignment
453pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
454Usb *usb;
455
456Usb::Usb() {
457 pthread_mutex_lock(&lock);
458 // Make this a singleton class
459 assert(usb == NULL);
460 usb = this;
461 pthread_mutex_unlock(&lock);
462}
463
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800464} // namespace implementation
465} // namespace V1_0
466} // namespace usb
467} // namespace hardware
468} // namespace android