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 &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 
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