blob: f46ff665342fcc657243c056b8f0309318ad8577 [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 }
175 names.resize(ports);
176 rewinddir(dp);
177
178 while ((ep = readdir (dp))) {
179 /* Check to see if new ports were added since the first pass. */
180 if (current >= ports) {
181 rewinddir(dp);
182 goto rescan;
183 }
184
185 if (ep->d_type == DT_LNK) {
186 names[current++] = ep->d_name;
187 }
188 }
189
190 closedir (dp);
191 return Status::SUCCESS;
192 }
193
194 ALOGE("Failed to open /sys/class/dual_role_usb");
195 return Status::ERROR;
196}
197
198bool canSwitchRoleHelper(const std::string portName, PortRoleType type) {
199 std::string filename = appendRoleNodeHelper(portName, type);
200 std::ofstream file(filename);
201
202 if (file.is_open()) {
203 file.close();
204 return true;
205 }
206 return false;
207}
208
209Status getPortModeHelper(const std::string portName, PortMode& portMode) {
210 std::string filename = "/sys/class/dual_role_usb/" +
211 std::string(portName.c_str()) + "/supported_modes";
212 std::string modes;
213
214 if (readFile(filename, modes)) {
215 ALOGE("getSupportedRoles: Failed to open filesystem node");
216 return Status::ERROR;
217 }
218
219 if (modes == "ufp dfp")
220 portMode = PortMode::DRP;
221 else if (modes == "ufp")
222 portMode = PortMode::UFP;
223 else if (modes == "dfp")
224 portMode = PortMode::DFP;
225 else
226 return Status::UNRECOGNIZED_ROLE;
227
228 return Status::SUCCESS;
229}
230
231Status getPortStatusHelper (hidl_vec<PortStatus>& currentPortStatus) {
232 std::vector<std::string> names;
233 Status result = getTypeCPortNamesHelper(names);
234
235 if (result == Status::SUCCESS) {
236 currentPortStatus.resize(names.size());
237 for(std::vector<std::string>::size_type i = 0; i != names.size(); i++) {
238 ALOGI("%s", names[i].c_str());
239 currentPortStatus[i].portName = names[i];
240
241 uint32_t currentRole;
242 if (getCurrentRoleHelper(names[i], PortRoleType::POWER_ROLE,
243 currentRole) == Status::SUCCESS) {
244 currentPortStatus[i].currentPowerRole =
245 static_cast<PortPowerRole> (currentRole);
246 } else {
247 ALOGE("Error while retreiving portNames");
248 goto done;
249 }
250
251 if (getCurrentRoleHelper(names[i],
252 PortRoleType::DATA_ROLE, currentRole) == Status::SUCCESS) {
253 currentPortStatus[i].currentDataRole =
254 static_cast<PortDataRole> (currentRole);
255 } else {
256 ALOGE("Error while retreiving current port role");
257 goto done;
258 }
259
260 if (getCurrentRoleHelper(names[i], PortRoleType::MODE,
261 currentRole) == Status::SUCCESS) {
262 currentPortStatus[i].currentMode =
263 static_cast<PortMode> (currentRole);
264 } else {
265 ALOGE("Error while retreiving current data role");
266 goto done;
267 }
268
269 currentPortStatus[i].canChangeMode =
270 canSwitchRoleHelper(names[i], PortRoleType::MODE);
271 currentPortStatus[i].canChangeDataRole =
272 canSwitchRoleHelper(names[i], PortRoleType::DATA_ROLE);
273 currentPortStatus[i].canChangePowerRole =
274 canSwitchRoleHelper(names[i], PortRoleType::POWER_ROLE);
275
276 ALOGI("canChangeMode: %d canChagedata: %d canChangePower:%d",
277 currentPortStatus[i].canChangeMode,
278 currentPortStatus[i].canChangeDataRole,
279 currentPortStatus[i].canChangePowerRole);
280
281 if (getPortModeHelper(names[i], currentPortStatus[i].supportedModes)
282 != Status::SUCCESS) {
283 ALOGE("Error while retrieving port modes");
284 goto done;
285 }
286 }
287 return Status::SUCCESS;
288 }
289done:
290 return Status::ERROR;
291}
292
293Return<void> Usb::queryPortStatus() {
294 hidl_vec<PortStatus> currentPortStatus;
295 Status status;
296
297 status = getPortStatusHelper(currentPortStatus);
298 Return<void> ret = mCallback->notifyPortStatusChange(currentPortStatus,
299 status);
300 if (!ret.isOk())
301 ALOGE("queryPortStatus error %s", ret.description().c_str());
302
303 return Void();
304}
305struct data {
306 int uevent_fd;
307 android::hardware::usb::V1_0::implementation::Usb *usb;
308};
309
310static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
311 char msg[UEVENT_MSG_LEN + 2];
312 char *cp;
313 int n;
314
315 n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
316 if (n <= 0)
317 return;
318 if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
319 return;
320
321 msg[n] = '\0';
322 msg[n + 1] = '\0';
323 cp = msg;
324
325 while (*cp) {
326 if (!strcmp(cp, "SUBSYSTEM=dual_role_usb")) {
327 ALOGE("uevent received %s", cp);
328 if (payload->usb->mCallback != NULL) {
329 hidl_vec<PortStatus> currentPortStatus;
330 Status status = getPortStatusHelper(currentPortStatus);
331 Return<void> ret =
332 payload->usb->mCallback->notifyPortStatusChange(currentPortStatus, status);
333 if (!ret.isOk())
334 ALOGE("error %s", ret.description().c_str());
335 }
336 break;
337 }
338 /* advance to after the next \0 */
339 while (*cp++);
340 }
341}
342
343void* work(void* param) {
344 int epoll_fd, uevent_fd;
345 struct epoll_event ev;
346 int nevents = 0;
347 struct data payload;
348
349 ALOGE("creating thread");
350
351 uevent_fd = uevent_open_socket(64*1024, true);
352
353 if (uevent_fd < 0) {
354 ALOGE("uevent_init: uevent_open_socket failed\n");
355 return NULL;
356 }
357
358 payload.uevent_fd = uevent_fd;
359 payload.usb = (android::hardware::usb::V1_0::implementation::Usb *)param;
360
361 fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
362
363 ev.events = EPOLLIN;
364 ev.data.ptr = (void *)uevent_event;
365
366 epoll_fd = epoll_create(64);
367 if (epoll_fd == -1) {
368 ALOGE("epoll_create failed; errno=%d", errno);
369 goto error;
370 }
371
372 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
373 ALOGE("epoll_ctl failed; errno=%d", errno);
374 goto error;
375 }
376
377 while (1) {
378 struct epoll_event events[64];
379
380 nevents = epoll_wait(epoll_fd, events, 64, -1);
381 if (nevents == -1) {
382 if (errno == EINTR)
383 continue;
384 ALOGE("usb epoll_wait failed; errno=%d", errno);
385 break;
386 }
387
388 for (int n = 0; n < nevents; ++n) {
389 if (events[n].data.ptr)
390 (*(void (*)(int, struct data *payload))events[n].data.ptr)
391 (events[n].events, &payload);
392 }
393 }
394
395error:
396 close(uevent_fd);
397
398 if (epoll_fd >= 0)
399 close(epoll_fd);
400
401 return NULL;
402}
403
404
405Return<void> Usb::setCallback(const sp<IUsbCallback>& callback) {
406
407 if (mCallback != NULL) {
408 ALOGE("Callback already registered");
409 return Void();
410 }
411
412 mCallback = callback;
413 ALOGI("registering callback");
414
415 if (pthread_create(&mPoll, NULL, work, this)) {
416 ALOGE("pthread creation failed %d", errno);
417 mCallback = NULL;
418 return Void();
419 }
420
421 return Void();
422}
423
424} // namespace implementation
425} // namespace V1_0
426} // namespace usb
427} // namespace hardware
428} // namespace android