#include <android-base/logging.h>
#include <assert.h>
+#include <chrono>
#include <dirent.h>
#include <pthread.h>
+#include <regex>
#include <stdio.h>
#include <sys/types.h>
+#include <thread>
#include <unistd.h>
#include <unordered_map>
// Set by the signal handler to destroy the thread
volatile bool destroyThread;
-int32_t readFile(std::string filename, std::string *contents) {
+int32_t readFile(const std::string &filename, std::string *contents) {
FILE *fp;
ssize_t read = 0;
char *line = NULL;
return -1;
}
-std::string appendRoleNodeHelper(const std::string portName,
+std::string appendRoleNodeHelper(const std::string &portName,
PortRoleType type) {
std::string node("/sys/class/typec/" + portName);
switch (type) {
case PortRoleType::DATA_ROLE:
- return node + "/current_data_role";
+ return node + "/data_role";
case PortRoleType::POWER_ROLE:
- return node + "/current_power_role";
+ return node + "/power_role";
+ case PortRoleType::MODE:
+ return node + "/port_type";
default:
- return node + "/mode";
+ return "";
}
}
if (role.role == static_cast<uint32_t>(PortDataRole::DEVICE))
return "device";
} else if (role.type == PortRoleType::MODE) {
- if (role.role == static_cast<uint32_t>(PortMode_1_1::UFP)) return "ufp";
- if (role.role == static_cast<uint32_t>(PortMode_1_1::DFP)) return "dfp";
+ if (role.role == static_cast<uint32_t>(PortMode_1_1::UFP)) return "sink";
+ if (role.role == static_cast<uint32_t>(PortMode_1_1::DFP)) return "source";
}
return "none";
}
}
}
+void switchToDrp(const std::string &portName) {
+ std::string filename =
+ appendRoleNodeHelper(std::string(portName.c_str()), PortRoleType::MODE);
+ FILE *fp;
+
+ if (filename != "") {
+ fp = fopen(filename.c_str(), "w");
+ if (fp != NULL) {
+ int ret = fputs("dual", fp);
+ fclose(fp);
+ if (ret == EOF)
+ ALOGE("Fatal: Error while switching back to drp");
+ } else {
+ ALOGE("Fatal: Cannot open file to switch back to drp");
+ }
+ } else {
+ ALOGE("Fatal: invalid node type");
+ }
+}
+
+bool switchMode(const hidl_string &portName,
+ const PortRole &newRole, struct Usb *usb) {
+ std::string filename =
+ appendRoleNodeHelper(std::string(portName.c_str()), newRole.type);
+ std::string written;
+ FILE *fp;
+ bool roleSwitch = false;
+
+ if (filename == "") {
+ ALOGE("Fatal: invalid node type");
+ return false;
+ }
+
+ fp = fopen(filename.c_str(), "w");
+ if (fp != NULL) {
+ // Hold the lock here to prevent loosing connected signals
+ // as once the file is written the partner added signal
+ // can arrive anytime.
+ pthread_mutex_lock(&usb->mPartnerLock);
+ usb->mPartnerUp = false;
+ int ret = fputs(convertRoletoString(newRole).c_str(), fp);
+ fclose(fp);
+
+ if (ret != EOF) {
+ struct timespec to;
+ struct timeval tp;
+
+wait_again:
+ gettimeofday(&tp, NULL);
+ to.tv_sec = tp.tv_sec + PORT_TYPE_TIMEOUT;
+ to.tv_nsec = tp.tv_usec * 1000;;
+
+ int err = pthread_cond_timedwait(&usb->mPartnerCV, &usb->mPartnerLock, &to);
+ // There are no uevent signals which implies role swap timed out.
+ if (err == ETIMEDOUT) {
+ ALOGI("uevents wait timedout");
+ // Sanity check.
+ } else if (!usb->mPartnerUp) {
+ goto wait_again;
+ // Role switch succeeded since usb->mPartnerUp is true.
+ } else {
+ roleSwitch = true;
+ }
+ } else {
+ ALOGI("Role switch failed while wrting to file");
+ }
+ pthread_mutex_unlock(&usb->mPartnerLock);
+ }
+
+ if (!roleSwitch)
+ switchToDrp(std::string(portName.c_str()));
+
+ return roleSwitch;
+}
+
+
+
Return<void> Usb::switchRole(const hidl_string &portName,
const PortRole &newRole) {
std::string filename =
appendRoleNodeHelper(std::string(portName.c_str()), newRole.type);
std::string written;
FILE *fp;
+ bool roleSwitch = false;
+
+ if (filename == "") {
+ ALOGE("Fatal: invalid node type");
+ return Void();
+ }
+
+ pthread_mutex_lock(&mRoleSwitchLock);
ALOGI("filename write: %s role:%s", filename.c_str(),
convertRoletoString(newRole).c_str());
- fp = fopen(filename.c_str(), "w");
- if (fp != NULL) {
- int ret = fputs(convertRoletoString(newRole).c_str(), fp);
- fclose(fp);
- if ((ret != EOF) && !readFile(filename, &written)) {
- extractRole(&written);
- ALOGI("written: %s", written.c_str());
- if (written == convertRoletoString(newRole)) {
- pthread_mutex_lock(&mLock);
- if (mCallback != NULL) {
- Return<void> ret = mCallback->notifyRoleSwitchStatus(
- portName, newRole, Status::SUCCESS);
- if (!ret.isOk())
- ALOGE("RoleSwitch transaction error %s", ret.description().c_str());
+ if (newRole.type == PortRoleType::MODE) {
+ roleSwitch = switchMode(portName, newRole, this);
+ } else {
+ fp = fopen(filename.c_str(), "w");
+ if (fp != NULL) {
+ int ret = fputs(convertRoletoString(newRole).c_str(), fp);
+ fclose(fp);
+ if ((ret != EOF) && !readFile(filename, &written)) {
+ extractRole(&written);
+ ALOGI("written: %s", written.c_str());
+ if (written == convertRoletoString(newRole)) {
+ roleSwitch = true;
} else {
- ALOGE("Not notifying the userspace. Callback is not set");
+ ALOGE("Role switch failed");
}
- pthread_mutex_unlock(&mLock);
- return Void();
} else {
- ALOGE("Role switch failed");
+ ALOGE("failed to update the new role");
}
} else {
- ALOGE("failed to update the new role");
+ ALOGE("fopen failed");
}
- } else {
- ALOGE("fopen failed");
}
pthread_mutex_lock(&mLock);
- if (mCallback != NULL) {
+ if (mCallback_1_0 != NULL) {
Return<void> ret =
- mCallback->notifyRoleSwitchStatus(portName, newRole, Status::ERROR);
+ mCallback_1_0->notifyRoleSwitchStatus(portName, newRole,
+ roleSwitch ? Status::SUCCESS : Status::ERROR);
if (!ret.isOk())
ALOGE("RoleSwitchStatus error %s", ret.description().c_str());
} else {
ALOGE("Not notifying the userspace. Callback is not set");
}
pthread_mutex_unlock(&mLock);
+ pthread_mutex_unlock(&mRoleSwitchLock);
return Void();
}
-Status getAccessoryConnected(std::string portName, std::string *accessory) {
+Status getAccessoryConnected(const std::string &portName, std::string *accessory) {
std::string filename =
"/sys/class/typec/" + portName + "-partner/accessory_mode";
return Status::SUCCESS;
}
-Status getCurrentRoleHelper(std::string portName, bool connected,
+Status getCurrentRoleHelper(const std::string &portName, bool connected,
PortRoleType type, uint32_t *currentRole) {
std::string filename;
std::string roleName;
// Mode
if (type == PortRoleType::POWER_ROLE) {
- filename = "/sys/class/typec/" + portName + "/current_power_role";
+ filename = "/sys/class/typec/" + portName + "/power_role";
*currentRole = static_cast<uint32_t>(PortPowerRole::NONE);
} else if (type == PortRoleType::DATA_ROLE) {
- filename = "/sys/class/typec/" + portName + "/current_data_role";
+ filename = "/sys/class/typec/" + portName + "/data_role";
*currentRole = static_cast<uint32_t>(PortDataRole::NONE);
} else if (type == PortRoleType::MODE) {
- filename = "/sys/class/typec/" + portName + "/current_data_role";
+ filename = "/sys/class/typec/" + portName + "/data_role";
*currentRole = static_cast<uint32_t>(PortMode_1_1::NONE);
} else {
return Status::ERROR;
if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) {
return Status::ERROR;
}
- if (accessory == "Audio Adapter Accessory Mode") {
+ if (accessory == "analog_audio") {
*currentRole = static_cast<uint32_t>(PortMode_1_1::AUDIO_ACCESSORY);
return Status::SUCCESS;
- } else if (accessory == "Debug Accessory Mode") {
+ } else if (accessory == "debug") {
*currentRole = static_cast<uint32_t>(PortMode_1_1::DEBUG_ACCESSORY);
return Status::SUCCESS;
}
return Status::ERROR;
}
-bool canSwitchRoleHelper(const std::string portName, PortRoleType /*type*/) {
+bool canSwitchRoleHelper(const std::string &portName, PortRoleType /*type*/) {
std::string filename =
"/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery";
std::string supportsPD;
if (!readFile(filename, &supportsPD)) {
- if (supportsPD == "1") {
+ if (supportsPD == "yes") {
return true;
}
}
return false;
}
-Status getPortStatus_1_1Helper(hidl_vec<PortStatus_1_1> *currentPortStatus_1_1) {
+/*
+ * Reuse the same method for both V1_0 and V1_1 callback objects.
+ * The caller of this method would reconstruct the V1_0::PortStatus
+ * object if required.
+ */
+Status getPortStatusHelper(hidl_vec<PortStatus_1_1> *currentPortStatus_1_1,
+ bool V1_0) {
std::unordered_map<std::string, bool> names;
Status result = getTypeCPortNamesHelper(&names);
int i = -1;
¤tRole) == Status::SUCCESS) {
(*currentPortStatus_1_1)[i].currentMode =
static_cast<PortMode_1_1>(currentRole);
+ (*currentPortStatus_1_1)[i].status.currentMode =
+ static_cast<V1_0::PortMode>(currentRole);
} else {
ALOGE("Error while retreiving current data role");
goto done;
}
- (*currentPortStatus_1_1)[i].status.canChangeMode = false;
+ (*currentPortStatus_1_1)[i].status.canChangeMode = true;
(*currentPortStatus_1_1)[i].status.canChangeDataRole =
port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE)
: false;
? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE)
: false;
- ALOGI("connected:%d canChangeMode: %d canChagedata: %d canChangePower:%d",
+ ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d",
port.second, (*currentPortStatus_1_1)[i].status.canChangeMode,
(*currentPortStatus_1_1)[i].status.canChangeDataRole,
(*currentPortStatus_1_1)[i].status.canChangePowerRole);
- (*currentPortStatus_1_1)[i].supportedModes = PortMode_1_1::UFP | PortMode_1_1::DFP;
-
- (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::NONE;
- (*currentPortStatus_1_1)[i].status.currentMode = V1_0::PortMode::NONE;
+ if (V1_0) {
+ (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::DFP;
+ } else {
+ (*currentPortStatus_1_1)[i].supportedModes = PortMode_1_1::UFP | PortMode_1_1::DFP;
+ (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::NONE;
+ (*currentPortStatus_1_1)[i].status.currentMode = V1_0::PortMode::NONE;
+ }
}
return Status::SUCCESS;
}
Return<void> Usb::queryPortStatus() {
hidl_vec<PortStatus_1_1> currentPortStatus_1_1;
+ hidl_vec<V1_0::PortStatus> currentPortStatus;
Status status;
-
- status = getPortStatus_1_1Helper(¤tPortStatus_1_1);
+ sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(mCallback_1_0);
pthread_mutex_lock(&mLock);
- if (mCallback != NULL) {
- Return<void> ret =
- mCallback->notifyPortStatusChange_1_1(currentPortStatus_1_1, status);
+ if (mCallback_1_0 != NULL) {
+ if (callback_V1_1 != NULL) {
+ status = getPortStatusHelper(¤tPortStatus_1_1, false);
+ } else {
+ status = getPortStatusHelper(¤tPortStatus_1_1, true);
+ currentPortStatus.resize(currentPortStatus_1_1.size());
+ for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++)
+ currentPortStatus[i] = currentPortStatus_1_1[i].status;
+ }
+
+ Return<void> ret;
+
+ if (callback_V1_1 != NULL)
+ ret = callback_V1_1->notifyPortStatusChange_1_1(currentPortStatus_1_1, status);
+ else
+ ret = mCallback_1_0->notifyPortStatusChange(currentPortStatus, status);
+
if (!ret.isOk())
ALOGE("queryPortStatus_1_1 error %s", ret.description().c_str());
} else {
return Void();
}
+
struct data {
int uevent_fd;
android::hardware::usb::V1_1::implementation::Usb *usb;
cp = msg;
while (*cp) {
- if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
+ if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) {
+ ALOGI("partner added");
+ pthread_mutex_lock(&payload->usb->mPartnerLock);
+ payload->usb->mPartnerUp = true;
+ pthread_cond_signal(&payload->usb->mPartnerCV);
+ pthread_mutex_unlock(&payload->usb->mPartnerLock);
+ } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
+ hidl_vec<PortStatus_1_1> currentPortStatus_1_1;
ALOGI("uevent received %s", cp);
pthread_mutex_lock(&payload->usb->mLock);
- if (payload->usb->mCallback != NULL) {
- hidl_vec<PortStatus_1_1> currentPortStatus_1_1;
- Status status = getPortStatus_1_1Helper(¤tPortStatus_1_1);
- Return<void> ret = payload->usb->mCallback->notifyPortStatusChange_1_1(
- currentPortStatus_1_1, status);
+ if (payload->usb->mCallback_1_0 != NULL) {
+ sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(payload->usb->mCallback_1_0);
+ Return<void> ret;
+
+ // V1_1 callback
+ if (callback_V1_1 != NULL) {
+ Status status = getPortStatusHelper(¤tPortStatus_1_1, false);
+ ret = callback_V1_1->notifyPortStatusChange_1_1(
+ currentPortStatus_1_1, status);
+ } else { // V1_0 callback
+ Status status = getPortStatusHelper(¤tPortStatus_1_1, true);
+
+ /*
+ * Copying the result from getPortStatusHelper
+ * into V1_0::PortStatus to pass back through
+ * the V1_0 callback object.
+ */
+ hidl_vec<V1_0::PortStatus> currentPortStatus;
+ currentPortStatus.resize(currentPortStatus_1_1.size());
+ for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++)
+ currentPortStatus[i] = currentPortStatus_1_1[i].status;
+
+ ret = payload->usb->mCallback_1_0->notifyPortStatusChange(
+ currentPortStatus, status);
+ }
if (!ret.isOk()) ALOGE("error %s", ret.description().c_str());
} else {
ALOGI("Notifying userspace skipped. Callback is NULL");
}
pthread_mutex_unlock(&payload->usb->mLock);
+
+ //Role switch is not in progress and port is in disconnected state
+ if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) {
+ for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) {
+ DIR *dp = opendir(std::string("/sys/class/typec/"
+ + std::string(currentPortStatus_1_1[i].status.portName.c_str())
+ + "-partner").c_str());
+ if (dp == NULL) {
+ //PortRole role = {.role = static_cast<uint32_t>(PortMode::UFP)};
+ switchToDrp(currentPortStatus_1_1[i].status.portName);
+ } else {
+ closedir(dp);
+ }
+ }
+ pthread_mutex_unlock(&payload->usb->mRoleSwitchLock);
+ }
break;
}
/* advance to after the next \0 */
signal(SIGUSR1, sighandler);
}
-Return<void> Usb::setCallback(const sp<::android::hardware::usb::V1_0::IUsbCallback> &callback) {
+Return<void> Usb::setCallback(const sp<V1_0::IUsbCallback> &callback) {
sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(callback);
if (callback != NULL)
- CHECK(callback_V1_1 != NULL);
+ if (callback_V1_1 == NULL)
+ ALOGI("Registering 1.0 callback");
pthread_mutex_lock(&mLock);
/*
* worker thread running, so updating the callback object would
* be suffice.
*/
- if ((mCallback == NULL && callback == NULL) ||
- (mCallback != NULL && callback != NULL)) {
- mCallback = callback_V1_1;
+ if ((mCallback_1_0 == NULL && callback == NULL) ||
+ (mCallback_1_0 != NULL && callback != NULL)) {
+ /*
+ * Always store as V1_0 callback object. Type cast to V1_1
+ * when the callback is actually invoked.
+ */
+ mCallback_1_0 = callback;
pthread_mutex_unlock(&mLock);
return Void();
}
- mCallback = callback_V1_1;
+ mCallback_1_0 = callback;
ALOGI("registering callback");
// Kill the worker thread if the new callback is NULL.
- if (mCallback == NULL) {
+ if (mCallback_1_0 == NULL) {
pthread_mutex_unlock(&mLock);
if (!pthread_kill(mPoll, SIGUSR1)) {
pthread_join(mPoll, NULL);
*/
if (pthread_create(&mPoll, NULL, work, this)) {
ALOGE("pthread creation failed %d", errno);
- mCallback = NULL;
+ mCallback_1_0 = NULL;
}
pthread_mutex_unlock(&mLock);