blob: 174e3c5357a738cacfb61c88989d4c686c435b32 [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 */
16#include <dirent.h>
17#include <iostream>
18#include <fstream>
19#include <pthread.h>
20#include <stdio.h>
21#include <sys/types.h>
22#include <unistd.h>
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
37int32_t readFile(std::string filename, std::string& contents) {
38 std::ifstream file(filename);
39
40 if (file.is_open()) {
41 getline(file, contents);
42 file.close();
43 return 0;
44 }
45 return -1;
46}
47
48std::string appendRoleNodeHelper(const std::string portName, PortRoleType type) {
49 std::string node("/sys/class/dual_role_usb/" + portName);
50
51 switch(type) {
52 case PortRoleType::DATA_ROLE:
53 return node + "/data_role";
54 case PortRoleType::POWER_ROLE:
55 return node + "/power_role";
56 default:
57 return node + "/mode";
58 }
59}
60
61std::string convertRoletoString(PortRole role) {
62 if (role.type == PortRoleType::POWER_ROLE) {
63 if (role.role == static_cast<uint32_t> (PortPowerRole::SOURCE))
64 return "source";
65 else if (role.role == static_cast<uint32_t> (PortPowerRole::SINK))
66 return "sink";
67 } else if (role.type == PortRoleType::DATA_ROLE) {
68 if (role.role == static_cast<uint32_t> (PortDataRole::HOST))
69 return "host";
70 if (role.role == static_cast<uint32_t> (PortDataRole::DEVICE))
71 return "device";
72 } else if (role.type == PortRoleType::MODE) {
73 if (role.role == static_cast<uint32_t> (PortMode::UFP))
74 return "ufp";
75 if (role.role == static_cast<uint32_t> (PortMode::DFP))
76 return "dfp";
77 }
78 return "none";
79}
80
81Return<void> Usb::switchRole(const hidl_string& portName,
82 const PortRole& newRole) {
83 std::string filename = appendRoleNodeHelper(std::string(portName.c_str()),
84 newRole.type);
85 std::ofstream file(filename);
86 std::string written;
87
88 ALOGI("filename write: %s role:%d", filename.c_str(), newRole.role);
89
90 if (file.is_open()) {
91 file << convertRoletoString(newRole).c_str();
92 file.close();
93 if (!readFile(filename, written)) {
94 ALOGI("written: %s", written.c_str());
95 if (written == convertRoletoString(newRole)) {
96 ALOGI("Role switch successfull");
97 Return<void> ret =
98 mCallback->notifyRoleSwitchStatus(portName, newRole,
99 Status::SUCCESS);
100 if (!ret.isOk())
101 ALOGE("RoleSwitchStatus error %s",
102 ret.description().c_str());
103 }
104 }
105 }
106
107 Return<void> ret = mCallback->notifyRoleSwitchStatus(portName, newRole, Status::ERROR);
108 if (!ret.isOk())
109 ALOGE("RoleSwitchStatus error %s", ret.description().c_str());
110
111 return Void();
112}
113
114Status getCurrentRoleHelper(std::string portName,
115 PortRoleType type, uint32_t &currentRole) {
116 std::string filename;
117 std::string roleName;
118
119 if (type == PortRoleType::POWER_ROLE) {
120 filename = "/sys/class/dual_role_usb/" +
121 portName + "/power_role";
122 currentRole = static_cast<uint32_t>(PortPowerRole::NONE);
123 } else if (type == PortRoleType::DATA_ROLE) {
124 filename = "/sys/class/dual_role_usb/" +
125 portName + "/data_role";
126 currentRole = static_cast<uint32_t> (PortDataRole::NONE);
127 } else if (type == PortRoleType::MODE) {
128 filename = "/sys/class/dual_role_usb/" +
129 portName + "/mode";
130 currentRole = static_cast<uint32_t> (PortMode::NONE);
131 }
132
133 if (readFile(filename, roleName)) {
134 ALOGE("getCurrentRole: Failed to open filesystem node");
135 return Status::ERROR;
136 }
137
138 if (roleName == "dfp")
139 currentRole = static_cast<uint32_t> (PortMode::DFP);
140 else if (roleName == "ufp")
141 currentRole = static_cast<uint32_t> (PortMode::UFP);
142 else if (roleName == "source")
143 currentRole = static_cast<uint32_t> (PortPowerRole::SOURCE);
144 else if (roleName == "sink")
145 currentRole = static_cast<uint32_t> (PortPowerRole::SINK);
146 else if (roleName == "host")
147 currentRole = static_cast<uint32_t> (PortDataRole::HOST);
148 else if (roleName == "device")
149 currentRole = static_cast<uint32_t> (PortDataRole::DEVICE);
150 else if (roleName != "none") {
151 /* case for none has already been addressed.
152 * so we check if the role isnt none.
153 */
154 return Status::UNRECOGNIZED_ROLE;
155 }
156 return Status::SUCCESS;
157}
158
159Status getTypeCPortNamesHelper(std::vector<std::string>& names) {
160 DIR *dp;
161
162 dp = opendir("/sys/class/dual_role_usb");
163 if (dp != NULL)
164 {
165rescan:
166 int32_t ports = 0;
167 int32_t current = 0;
168 struct dirent *ep;
169
170 while ((ep = readdir (dp))) {
171 if (ep->d_type == DT_LNK) {
172 ports++;
173 }
174 }
Badhri Jagan Sridharanb31cbe92017-02-24 05:49:29 -0800175
176 if (ports == 0) {
177 closedir(dp);
178 return Status::SUCCESS;
179 }
180
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800181 names.resize(ports);
182 rewinddir(dp);
183
184 while ((ep = readdir (dp))) {
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800185 if (ep->d_type == DT_LNK) {
Badhri Jagan Sridharanb31cbe92017-02-24 05:49:29 -0800186 /* Check to see if new ports were added since the first pass. */
187 if (current >= ports) {
188 rewinddir(dp);
189 goto rescan;
190 }
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800191 names[current++] = ep->d_name;
192 }
193 }
194
195 closedir (dp);
196 return Status::SUCCESS;
197 }
198
199 ALOGE("Failed to open /sys/class/dual_role_usb");
200 return Status::ERROR;
201}
202
203bool canSwitchRoleHelper(const std::string portName, PortRoleType type) {
204 std::string filename = appendRoleNodeHelper(portName, type);
205 std::ofstream file(filename);
206
207 if (file.is_open()) {
208 file.close();
209 return true;
210 }
211 return false;
212}
213
214Status getPortModeHelper(const std::string portName, PortMode& portMode) {
215 std::string filename = "/sys/class/dual_role_usb/" +
216 std::string(portName.c_str()) + "/supported_modes";
217 std::string modes;
218
219 if (readFile(filename, modes)) {
220 ALOGE("getSupportedRoles: Failed to open filesystem node");
221 return Status::ERROR;
222 }
223
224 if (modes == "ufp dfp")
225 portMode = PortMode::DRP;
226 else if (modes == "ufp")
227 portMode = PortMode::UFP;
228 else if (modes == "dfp")
229 portMode = PortMode::DFP;
230 else
231 return Status::UNRECOGNIZED_ROLE;
232
233 return Status::SUCCESS;
234}
235
236Status getPortStatusHelper (hidl_vec<PortStatus>& currentPortStatus) {
237 std::vector<std::string> names;
238 Status result = getTypeCPortNamesHelper(names);
239
240 if (result == Status::SUCCESS) {
241 currentPortStatus.resize(names.size());
Badhri Jagan Sridharanb31cbe92017-02-24 05:49:29 -0800242 for(std::vector<std::string>::size_type i = 0; i < names.size(); i++) {
Badhri Jagan Sridharan81ea7482017-01-20 06:05:58 -0800243 ALOGI("%s", names[i].c_str());
244 currentPortStatus[i].portName = names[i];
245
246 uint32_t currentRole;
247 if (getCurrentRoleHelper(names[i], PortRoleType::POWER_ROLE,
248 currentRole) == Status::SUCCESS) {
249 currentPortStatus[i].currentPowerRole =
250 static_cast<PortPowerRole> (currentRole);
251 } else {
252 ALOGE("Error while retreiving portNames");
253 goto done;
254 }
255
256 if (getCurrentRoleHelper(names[i],
257 PortRoleType::DATA_ROLE, currentRole) == Status::SUCCESS) {
258 currentPortStatus[i].currentDataRole =
259 static_cast<PortDataRole> (currentRole);
260 } else {
261 ALOGE("Error while retreiving current port role");
262 goto done;
263 }
264
265 if (getCurrentRoleHelper(names[i], PortRoleType::MODE,
266 currentRole) == Status::SUCCESS) {
267 currentPortStatus[i].currentMode =
268 static_cast<PortMode> (currentRole);
269 } else {
270 ALOGE("Error while retreiving current data role");
271 goto done;
272 }
273
274 currentPortStatus[i].canChangeMode =
275 canSwitchRoleHelper(names[i], PortRoleType::MODE);
276 currentPortStatus[i].canChangeDataRole =
277 canSwitchRoleHelper(names[i], PortRoleType::DATA_ROLE);
278 currentPortStatus[i].canChangePowerRole =
279 canSwitchRoleHelper(names[i], PortRoleType::POWER_ROLE);
280
281 ALOGI("canChangeMode: %d canChagedata: %d canChangePower:%d",
282 currentPortStatus[i].canChangeMode,
283 currentPortStatus[i].canChangeDataRole,
284 currentPortStatus[i].canChangePowerRole);
285
286 if (getPortModeHelper(names[i], currentPortStatus[i].supportedModes)
287 != Status::SUCCESS) {
288 ALOGE("Error while retrieving port modes");
289 goto done;
290 }
291 }
292 return Status::SUCCESS;
293 }
294done:
295 return Status::ERROR;
296}
297
298Return<void> Usb::queryPortStatus() {
299 hidl_vec<PortStatus> currentPortStatus;
300 Status status;
301
302 status = getPortStatusHelper(currentPortStatus);
303 Return<void> ret = mCallback->notifyPortStatusChange(currentPortStatus,
304 status);
305 if (!ret.isOk())
306 ALOGE("queryPortStatus error %s", ret.description().c_str());
307
308 return Void();
309}
310struct data {
311 int uevent_fd;
312 android::hardware::usb::V1_0::implementation::Usb *usb;
313};
314
315static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
316 char msg[UEVENT_MSG_LEN + 2];
317 char *cp;
318 int n;
319
320 n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
321 if (n <= 0)
322 return;
323 if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
324 return;
325
326 msg[n] = '\0';
327 msg[n + 1] = '\0';
328 cp = msg;
329
330 while (*cp) {
331 if (!strcmp(cp, "SUBSYSTEM=dual_role_usb")) {
332 ALOGE("uevent received %s", cp);
333 if (payload->usb->mCallback != NULL) {
334 hidl_vec<PortStatus> currentPortStatus;
335 Status status = getPortStatusHelper(currentPortStatus);
336 Return<void> ret =
337 payload->usb->mCallback->notifyPortStatusChange(currentPortStatus, status);
338 if (!ret.isOk())
339 ALOGE("error %s", ret.description().c_str());
340 }
341 break;
342 }
343 /* advance to after the next \0 */
344 while (*cp++);
345 }
346}
347
348void* work(void* param) {
349 int epoll_fd, uevent_fd;
350 struct epoll_event ev;
351 int nevents = 0;
352 struct data payload;
353
354 ALOGE("creating thread");
355
356 uevent_fd = uevent_open_socket(64*1024, true);
357
358 if (uevent_fd < 0) {
359 ALOGE("uevent_init: uevent_open_socket failed\n");
360 return NULL;
361 }
362
363 payload.uevent_fd = uevent_fd;
364 payload.usb = (android::hardware::usb::V1_0::implementation::Usb *)param;
365
366 fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
367
368 ev.events = EPOLLIN;
369 ev.data.ptr = (void *)uevent_event;
370
371 epoll_fd = epoll_create(64);
372 if (epoll_fd == -1) {
373 ALOGE("epoll_create failed; errno=%d", errno);
374 goto error;
375 }
376
377 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
378 ALOGE("epoll_ctl failed; errno=%d", errno);
379 goto error;
380 }
381
382 while (1) {
383 struct epoll_event events[64];
384
385 nevents = epoll_wait(epoll_fd, events, 64, -1);
386 if (nevents == -1) {
387 if (errno == EINTR)
388 continue;
389 ALOGE("usb epoll_wait failed; errno=%d", errno);
390 break;
391 }
392
393 for (int n = 0; n < nevents; ++n) {
394 if (events[n].data.ptr)
395 (*(void (*)(int, struct data *payload))events[n].data.ptr)
396 (events[n].events, &payload);
397 }
398 }
399
400error:
401 close(uevent_fd);
402
403 if (epoll_fd >= 0)
404 close(epoll_fd);
405
406 return NULL;
407}
408
409
410Return<void> Usb::setCallback(const sp<IUsbCallback>& callback) {
411
412 if (mCallback != NULL) {
413 ALOGE("Callback already registered");
414 return Void();
415 }
416
417 mCallback = callback;
418 ALOGI("registering callback");
419
420 if (pthread_create(&mPoll, NULL, work, this)) {
421 ALOGE("pthread creation failed %d", errno);
422 mCallback = NULL;
423 return Void();
424 }
425
426 return Void();
427}
428
429} // namespace implementation
430} // namespace V1_0
431} // namespace usb
432} // namespace hardware
433} // namespace android