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
32namespace android {
33namespace hardware {
34namespace usb {
35namespace V1_0 {
36namespace implementation {
37
38// Set by the signal handler to destroy the thread
39volatile bool destroyThread;
40
41int32_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
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
207bool 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
218Status 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
240Status 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    }
298done:
299    return Status::ERROR;
300}
301
302Return<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}
314struct data {
315    int uevent_fd;
316    android::hardware::usb::V1_0::implementation::Usb *usb;
317};
318
319static 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
352void* 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");
405error:
406    close(uevent_fd);
407
408    if (epoll_fd >= 0)
409        close(epoll_fd);
410
411    return NULL;
412}
413
414void sighandler(int sig)
415{
416    if (sig == SIGUSR1) {
417        destroyThread = true;
418        ALOGI("destroy set");
419        return;
420    }
421    signal(SIGUSR1, sighandler);
422}
423
424Return<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
458pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
459Usb *usb;
460
461Usb::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