2021-04-12 13:08:57 +02:00
/*
* Copyright ( C ) 2017 The Android Open Source Project
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
# define LOG_TAG "android.hardware.usb@1.1-service.rosemary"
# 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>
# include <cutils/uevent.h>
# include <sys/epoll.h>
# include <utils/Errors.h>
# include <utils/StrongPointer.h>
# include "Usb.h"
namespace android {
namespace hardware {
namespace usb {
namespace V1_1 {
namespace implementation {
// Set by the signal handler to destroy the thread
volatile bool destroyThread ;
static int32_t readFile ( const std : : string & filename , std : : string * contents ) {
FILE * fp ;
ssize_t read = 0 ;
char * line = NULL ;
size_t len = 0 ;
fp = fopen ( filename . c_str ( ) , " r " ) ;
if ( fp ! = NULL ) {
if ( ( read = getline ( & line , & len , fp ) ) ! = - 1 ) {
char * pos ;
if ( ( pos = strchr ( line , ' \n ' ) ) ! = NULL ) * pos = ' \0 ' ;
* contents = line ;
}
free ( line ) ;
fclose ( fp ) ;
return 0 ;
} else {
ALOGE ( " fopen failed in readFile %s, errno=%d " , filename . c_str ( ) , errno ) ;
}
return - 1 ;
}
static int32_t writeFile ( const std : : string & filename ,
2021-04-12 22:04:12 +02:00
const std : : string & contents ) { // Might remove later
2021-04-12 13:08:57 +02:00
FILE * fp ;
int ret ;
fp = fopen ( filename . c_str ( ) , " w " ) ;
if ( fp ! = NULL ) {
ret = fputs ( contents . c_str ( ) , fp ) ;
fclose ( fp ) ;
if ( ret = = EOF ) {
ALOGE ( " fputs failed in writeFile %s " , filename . c_str ( ) ) ;
return - 1 ;
}
return 0 ;
} else {
ALOGE ( " fopen failed in writeFile %s, errno=%d " , filename . c_str ( ) , errno ) ;
}
return - 1 ;
}
std : : string appendRoleNodeHelper ( const std : : string & portName ,
PortRoleType type ) {
2021-04-12 22:04:12 +02:00
std : : string node ( " /sys/class/dual_role_usb/ " + portName ) ; // We have /sys/class/dual_role_usb, not /sys/class/typec
2021-04-12 13:08:57 +02:00
switch ( type ) {
case PortRoleType : : DATA_ROLE :
return node + " /data_role " ;
case PortRoleType : : POWER_ROLE :
return node + " /power_role " ;
case PortRoleType : : MODE :
2021-04-12 22:04:12 +02:00
return node + " /mode " ; // We have a separate file for mode
2021-04-12 13:08:57 +02:00
default :
2021-04-12 22:04:12 +02:00
return node + " /mode " ; // Disassembly shows no separate case for non-matches and MODE case could be removed, leaving just default, but let's keep it like that for now in case we need to change something.
2021-04-12 13:08:57 +02:00
}
}
std : : string convertRoletoString ( PortRole role ) {
if ( role . type = = PortRoleType : : POWER_ROLE ) {
2021-04-12 22:04:12 +02:00
if ( role . role = = static_cast < uint32_t > ( PortPowerRole : : SOURCE ) ) return " source " ;
else if ( role . role = = static_cast < uint32_t > ( PortPowerRole : : SINK ) ) return " sink " ;
2021-04-12 13:08:57 +02:00
} else if ( role . type = = PortRoleType : : DATA_ROLE ) {
if ( role . role = = static_cast < uint32_t > ( PortDataRole : : HOST ) ) return " host " ;
2021-04-12 22:04:12 +02:00
if ( role . role = = static_cast < uint32_t > ( PortDataRole : : DEVICE ) ) return " device " ;
2021-04-12 13:08:57 +02:00
} else if ( role . type = = PortRoleType : : MODE ) {
2021-04-12 22:04:12 +02:00
if ( role . role = = static_cast < uint32_t > ( PortMode_1_1 : : UFP ) ) return " ufp " ; // We probably call these 'ufp' and 'dfp', as seen in switchToDrp for example
if ( role . role = = static_cast < uint32_t > ( PortMode_1_1 : : DFP ) ) return " dfp " ;
2021-04-12 13:08:57 +02:00
}
return " none " ;
}
2021-04-12 22:04:12 +02:00
void extractRole ( std : : string * roleName ) { // Might remove later.
2021-04-12 13:08:57 +02:00
std : : size_t first , last ;
first = roleName - > find ( " [ " ) ;
last = roleName - > find ( " ] " ) ;
if ( first ! = std : : string : : npos & & last ! = std : : string : : npos ) {
* roleName = roleName - > substr ( first + 1 , last - first - 1 ) ;
}
}
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 ) {
2021-04-12 22:04:12 +02:00
int ret = fputs ( " dfp " , fp ) ; // Change dual -> dfp. Based on disassembly of stock HAL.
2021-04-12 13:08:57 +02:00
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 " ) ;
}
}
Usb : : Usb ( )
: mLock ( PTHREAD_MUTEX_INITIALIZER ) ,
mRoleSwitchLock ( PTHREAD_MUTEX_INITIALIZER ) ,
mPartnerLock ( PTHREAD_MUTEX_INITIALIZER ) ,
mPartnerUp ( false ) {
pthread_condattr_t attr ;
if ( pthread_condattr_init ( & attr ) ) {
ALOGE ( " pthread_condattr_init failed: %s " , strerror ( errno ) ) ;
abort ( ) ;
}
if ( pthread_condattr_setclock ( & attr , CLOCK_MONOTONIC ) ) {
ALOGE ( " pthread_condattr_setclock failed: %s " , strerror ( errno ) ) ;
abort ( ) ;
}
if ( pthread_cond_init ( & mPartnerCV , & attr ) ) {
ALOGE ( " pthread_cond_init failed: %s " , strerror ( errno ) ) ;
abort ( ) ;
}
if ( pthread_condattr_destroy ( & attr ) ) {
ALOGE ( " pthread_condattr_destroy failed: %s " , strerror ( errno ) ) ;
abort ( ) ;
}
}
Return < void > Usb : : switchRole ( const hidl_string & portName ,
const V1_0 : : 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 ) ;
2021-04-12 22:04:12 +02:00
/*
It looks like we have some type of loop in the stock HAL .
The loop starts at 0x4860 and ends at 0x4D4C in android . hardware . usb @ 1.1 - service - mediatek from V12 .0 .1 .0 . QJOEUXM
We break out of the loop only if the value at W24 register is equal to zero . Before loop start , we can see that W24 is set to 0x14 = 20.
We can also see that before deciding to break out or restart , W24 is decremented by 1 and there is an usleep call .
The amount of useconds to sleep is stored in register W19 . This register is set to 0xC350 = 50000 before the loop .
We can conclude that the program attempts to write the role 20 times and waits 50000 useconds before trying again .
*/
for ( int i = 0 ; i < 20 ; + + i ) {
ALOGI ( " filename write: %s role:%s " , filename . c_str ( ) ,
convertRoletoString ( newRole ) . c_str ( ) ) ;
// We don't need to check role type and use a separate way of setting mode, as we simply have a "mode" file for doing that
2021-04-12 13:08:57 +02:00
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 ) ) {
2021-04-12 22:04:12 +02:00
// extractRole(&written);
2021-04-12 13:08:57 +02:00
ALOGI ( " written: %s " , written . c_str ( ) ) ;
if ( written = = convertRoletoString ( newRole ) ) {
roleSwitch = true ;
2021-04-12 22:04:12 +02:00
break ; // Role switched, stop retrying.
2021-04-12 13:08:57 +02:00
} else {
ALOGE ( " Role switch failed " ) ;
}
} else {
ALOGE ( " failed to update the new role " ) ;
}
} else {
ALOGE ( " fopen failed " ) ;
}
2021-04-12 22:04:12 +02:00
usleep ( 50000 ) ;
2021-04-12 13:08:57 +02:00
}
pthread_mutex_lock ( & mLock ) ;
if ( mCallback_1_0 ! = NULL ) {
Return < void > ret =
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 getCurrentRoleHelper ( const std : : string & portName , bool connected ,
PortRoleType type , uint32_t * currentRole ) {
std : : string filename ;
std : : string roleName ;
2021-04-12 22:04:12 +02:00
// Accessory stuff removed - stock HAL doesn't have that
2021-04-12 13:08:57 +02:00
// Mode
if ( type = = PortRoleType : : POWER_ROLE ) {
2021-04-12 22:04:12 +02:00
filename = " /sys/class/dual_role_usb/ " + portName + " /power_role " ;
2021-04-12 13:08:57 +02:00
* currentRole = static_cast < uint32_t > ( PortPowerRole : : NONE ) ;
} else if ( type = = PortRoleType : : DATA_ROLE ) {
2021-04-12 22:04:12 +02:00
filename = " /sys/class/dual_role_usb/ " + portName + " /data_role " ;
2021-04-12 13:08:57 +02:00
* currentRole = static_cast < uint32_t > ( PortDataRole : : NONE ) ;
} else if ( type = = PortRoleType : : MODE ) {
2021-04-12 22:04:12 +02:00
filename = " /sys/class/dual_role_usb/ " + portName + " /mode " ; // We have a separate file for setting mode.
2021-04-12 13:08:57 +02:00
* currentRole = static_cast < uint32_t > ( PortMode_1_1 : : NONE ) ;
} else {
return Status : : ERROR ;
}
if ( ! connected ) return Status : : SUCCESS ;
if ( readFile ( filename , & roleName ) ) {
ALOGE ( " getCurrentRole: Failed to open filesystem node: %s " ,
filename . c_str ( ) ) ;
return Status : : ERROR ;
}
2021-04-12 22:04:12 +02:00
// extractRole(&roleName); // Probably don't need to extract. The role names on our device are just single words
2021-04-12 13:08:57 +02:00
2021-04-12 22:04:12 +02:00
// We have simple role names and don't need to make a distinction based on role type
2021-04-12 13:08:57 +02:00
if ( roleName = = " source " ) {
2021-04-12 22:04:12 +02:00
* currentRole = static_cast < uint32_t > ( PortPowerRole : : SOURCE ) ;
2021-04-12 13:08:57 +02:00
} else if ( roleName = = " sink " ) {
2021-04-12 22:04:12 +02:00
* currentRole = static_cast < uint32_t > ( PortPowerRole : : SINK ) ;
2021-04-12 13:08:57 +02:00
} else if ( roleName = = " host " ) {
* currentRole = static_cast < uint32_t > ( PortDataRole : : HOST ) ;
2021-04-12 22:04:12 +02:00
} else if ( roleName = = " dfp " ) {
2021-04-12 13:08:57 +02:00
* currentRole = static_cast < uint32_t > ( PortMode_1_1 : : DFP ) ;
} else if ( roleName = = " device " ) {
* currentRole = static_cast < uint32_t > ( PortDataRole : : DEVICE ) ;
2021-04-12 22:04:12 +02:00
} else if ( roleName = = " ufp " ) {
2021-04-12 13:08:57 +02:00
* currentRole = static_cast < uint32_t > ( PortMode_1_1 : : UFP ) ;
} else if ( roleName ! = " none " ) {
/* case for none has already been addressed.
* so we check if the role isnt none .
*/
return Status : : UNRECOGNIZED_ROLE ;
}
return Status : : SUCCESS ;
}
Status getTypeCPortNamesHelper ( std : : unordered_map < std : : string , bool > * names ) {
DIR * dp ;
2021-04-12 22:04:12 +02:00
dp = opendir ( " /sys/class/dual_role_usb " ) ;
2021-04-12 13:08:57 +02:00
if ( dp ! = NULL ) {
struct dirent * ep ;
while ( ( ep = readdir ( dp ) ) ) {
if ( ep - > d_type = = DT_LNK ) {
2021-04-12 22:04:12 +02:00
// We don't have -partner suffixes
2021-04-12 13:08:57 +02:00
std : : unordered_map < std : : string , bool > : : const_iterator portName =
names - > find ( ep - > d_name ) ;
if ( portName = = names - > end ( ) ) {
2021-04-12 22:04:12 +02:00
names - > insert ( { ep - > d_name , true } ) ; // True by default - otherwise getPortStatusHelper will never say if role can be switched
2021-04-12 13:08:57 +02:00
}
}
}
closedir ( dp ) ;
return Status : : SUCCESS ;
}
2021-04-12 22:04:12 +02:00
ALOGE ( " Failed to open /sys/class/dual_role_usb " ) ;
2021-04-12 13:08:57 +02:00
return Status : : ERROR ;
}
bool canSwitchRoleHelper ( const std : : string & portName , PortRoleType /*type*/ ) {
2021-04-12 22:04:12 +02:00
std : : string filename = " /sys/class/tcpc/type_c_port0/pe_ready " ;
2021-04-12 13:08:57 +02:00
std : : string supportsPD ;
if ( ! readFile ( filename , & supportsPD ) ) {
if ( supportsPD = = " yes " ) {
return true ;
}
}
return false ;
}
/*
* 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 ;
if ( result = = Status : : SUCCESS ) {
currentPortStatus_1_1 - > resize ( names . size ( ) ) ;
for ( const std : : pair < std : : string , bool > & port : names ) {
i + + ;
ALOGI ( " %s " , port . first . c_str ( ) ) ;
( * currentPortStatus_1_1 ) [ i ] . status . portName = port . first ;
uint32_t currentRole ;
if ( getCurrentRoleHelper ( port . first , port . second ,
PortRoleType : : POWER_ROLE ,
& currentRole ) = = Status : : SUCCESS ) {
( * currentPortStatus_1_1 ) [ i ] . status . currentPowerRole =
static_cast < PortPowerRole > ( currentRole ) ;
} else {
ALOGE ( " Error while retreiving portNames " ) ;
goto done ;
}
if ( getCurrentRoleHelper ( port . first , port . second , PortRoleType : : DATA_ROLE ,
& currentRole ) = = Status : : SUCCESS ) {
2021-04-19 01:40:45 +02:00
/* HACK: Our device has broken roles: they appear to be permanently set
to NONE and don ' t respond to configfs writes . This causes Android to
not see the USB port as connected , breaking the USB settings .
To get USB preferences to work , we have to spoof some roles . */
if ( port . second = = true & & currentRole = = static_cast < uint32_t > ( PortDataRole : : NONE ) ) {
currentRole = static_cast < uint32_t > ( PortDataRole : : DEVICE ) ;
}
2021-04-12 13:08:57 +02:00
( * currentPortStatus_1_1 ) [ i ] . status . currentDataRole =
static_cast < PortDataRole > ( currentRole ) ;
} else {
ALOGE ( " Error while retreiving current port role " ) ;
goto done ;
}
if ( getCurrentRoleHelper ( port . first , port . second , PortRoleType : : MODE ,
& currentRole ) = = Status : : SUCCESS ) {
2021-04-19 01:40:45 +02:00
// HACK: see above
if ( port . second = = true & & currentRole = = static_cast < uint32_t > ( PortMode_1_1 : : NONE ) ) {
currentRole = static_cast < uint32_t > ( PortMode_1_1 : : UFP ) ;
}
2021-04-12 13:08:57 +02:00
( * 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 ;
}
2021-04-12 22:04:12 +02:00
// Treat mode change like any type of role change - we have the "mode" file in our port directory.
( * currentPortStatus_1_1 ) [ i ] . status . canChangeMode =
port . second ? canSwitchRoleHelper ( port . first , PortRoleType : : MODE )
: false ;
2021-04-12 13:08:57 +02:00
( * currentPortStatus_1_1 ) [ i ] . status . canChangeDataRole =
port . second ? canSwitchRoleHelper ( port . first , PortRoleType : : DATA_ROLE )
: false ;
( * currentPortStatus_1_1 ) [ i ] . status . canChangePowerRole =
2021-04-12 22:04:12 +02:00
port . second ? canSwitchRoleHelper ( port . first , PortRoleType : : POWER_ROLE )
: false ;
2021-04-12 13:08:57 +02:00
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 ) ;
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 ;
}
done :
return Status : : ERROR ;
}
Return < void > Usb : : queryPortStatus ( ) {
hidl_vec < PortStatus_1_1 > currentPortStatus_1_1 ;
hidl_vec < V1_0 : : PortStatus > currentPortStatus ;
Status status ;
sp < IUsbCallback > callback_V1_1 = IUsbCallback : : castFrom ( mCallback_1_0 ) ;
pthread_mutex_lock ( & mLock ) ;
if ( mCallback_1_0 ! = NULL ) {
if ( callback_V1_1 ! = NULL ) {
status = getPortStatusHelper ( & currentPortStatus_1_1 , false ) ;
} else {
status = getPortStatusHelper ( & currentPortStatus_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 {
ALOGI ( " Notifying userspace skipped. Callback is NULL " ) ;
}
pthread_mutex_unlock ( & mLock ) ;
return Void ( ) ;
}
struct data {
int uevent_fd ;
android : : hardware : : usb : : V1_1 : : implementation : : Usb * usb ;
} ;
static void uevent_event ( uint32_t /*epevents*/ , struct data * payload ) {
char msg [ UEVENT_MSG_LEN + 2 ] ;
char * cp ;
int n ;
n = uevent_kernel_multicast_recv ( payload - > uevent_fd , msg , UEVENT_MSG_LEN ) ;
if ( n < = 0 ) return ;
if ( n > = UEVENT_MSG_LEN ) /* overflow -- discard */
return ;
msg [ n ] = ' \0 ' ;
msg [ n + 1 ] = ' \0 ' ;
cp = msg ;
while ( * cp ) {
2021-04-12 22:04:12 +02:00
// Removed regex matching for -partner, as we don't seem to have that in the stock HAL and in /sys/class/dual_role_usb.
if ( ! strncmp ( cp , " SUBSYSTEM=dual_role_usb " , strlen ( " SUBSYSTEM=dual_role_usb " ) ) ) { // Stock HAL looks for dual_role_usb, and it checks the SUBSYSTEM instead of DEVNAME. 0x62D4 in android.hardware.usb@1.1-service-mediatek from V12.0.1.0.QJOEUXM
2021-04-12 13:08:57 +02:00
hidl_vec < PortStatus_1_1 > currentPortStatus_1_1 ;
ALOGI ( " uevent received %s " , cp ) ;
pthread_mutex_lock ( & payload - > usb - > mLock ) ;
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 ( & currentPortStatus_1_1 , false ) ;
ret = callback_V1_1 - > notifyPortStatusChange_1_1 (
currentPortStatus_1_1 , status ) ;
} else { // V1_0 callback
Status status = getPortStatusHelper ( & currentPortStatus_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 + + ) {
2021-04-12 22:04:12 +02:00
DIR * dp = opendir ( std : : string ( " /sys/class/dual_role_usb/ "
+ std : : string ( currentPortStatus_1_1 [ i ] . status . portName . c_str ( ) ) ) . c_str ( ) ) ; // Stock HAL did not append -partner. Based on disassembly - 0x65E0 in android.hardware.usb@1.1-service-mediatek from V12.0.1.0.QJOEUXM
2021-04-12 13:08:57 +02:00
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 ;
2021-04-12 22:04:12 +02:00
} // Removed autosuspend regex match stuff - we don't even have the path that regex was looking for on a live device, and there are no mentions of auto suspend stuff in strings from the stock HAL whatsoever. The functions related to auto suspend afterwards have been nuked too.
2021-04-12 13:08:57 +02:00
/* advance to after the next \0 */
while ( * cp + + ) { }
}
}
void * work ( void * param ) {
int epoll_fd , uevent_fd ;
struct epoll_event ev ;
int nevents = 0 ;
struct data payload ;
ALOGE ( " creating thread " ) ;
uevent_fd = uevent_open_socket ( 64 * 1024 , true ) ;
if ( uevent_fd < 0 ) {
ALOGE ( " uevent_init: uevent_open_socket failed \n " ) ;
return NULL ;
}
payload . uevent_fd = uevent_fd ;
payload . usb = ( android : : hardware : : usb : : V1_1 : : implementation : : Usb * ) param ;
fcntl ( uevent_fd , F_SETFL , O_NONBLOCK ) ;
ev . events = EPOLLIN ;
ev . data . ptr = ( void * ) uevent_event ;
epoll_fd = epoll_create ( 64 ) ;
if ( epoll_fd = = - 1 ) {
ALOGE ( " epoll_create failed; errno=%d " , errno ) ;
goto error ;
}
if ( epoll_ctl ( epoll_fd , EPOLL_CTL_ADD , uevent_fd , & ev ) = = - 1 ) {
ALOGE ( " epoll_ctl failed; errno=%d " , errno ) ;
goto error ;
}
while ( ! destroyThread ) {
struct epoll_event events [ 64 ] ;
nevents = epoll_wait ( epoll_fd , events , 64 , - 1 ) ;
if ( nevents = = - 1 ) {
if ( errno = = EINTR ) continue ;
ALOGE ( " usb epoll_wait failed; errno=%d " , errno ) ;
break ;
}
for ( int n = 0 ; n < nevents ; + + n ) {
if ( events [ n ] . data . ptr )
( * ( void ( * ) ( int , struct data * payload ) ) events [ n ] . data . ptr ) (
events [ n ] . events , & payload ) ;
}
}
ALOGI ( " exiting worker thread " ) ;
error :
close ( uevent_fd ) ;
if ( epoll_fd > = 0 ) close ( epoll_fd ) ;
return NULL ;
}
void sighandler ( int sig ) {
if ( sig = = SIGUSR1 ) {
destroyThread = true ;
ALOGI ( " destroy set " ) ;
return ;
}
signal ( SIGUSR1 , sighandler ) ;
}
Return < void > Usb : : setCallback ( const sp < V1_0 : : IUsbCallback > & callback ) {
sp < IUsbCallback > callback_V1_1 = IUsbCallback : : castFrom ( callback ) ;
if ( callback ! = NULL )
if ( callback_V1_1 = = NULL )
ALOGI ( " Registering 1.0 callback " ) ;
pthread_mutex_lock ( & mLock ) ;
/*
* When both the old callback and new callback values are NULL ,
* there is no need to spin off the worker thread .
* When both the values are not NULL , we would already have a
* worker thread running , so updating the callback object would
* be suffice .
*/
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_1_0 = callback ;
ALOGI ( " registering callback " ) ;
// Kill the worker thread if the new callback is NULL.
if ( mCallback_1_0 = = NULL ) {
pthread_mutex_unlock ( & mLock ) ;
if ( ! pthread_kill ( mPoll , SIGUSR1 ) ) {
pthread_join ( mPoll , NULL ) ;
ALOGI ( " pthread destroyed " ) ;
}
return Void ( ) ;
}
destroyThread = false ;
signal ( SIGUSR1 , sighandler ) ;
/*
* Create a background thread if the old callback value is NULL
* and being updated with a new value .
*/
if ( pthread_create ( & mPoll , NULL , work , this ) ) {
ALOGE ( " pthread creation failed %d " , errno ) ;
mCallback_1_0 = NULL ;
}
pthread_mutex_unlock ( & mLock ) ;
return Void ( ) ;
}
} // namespace implementation
} // namespace V1_0
} // namespace usb
} // namespace hardware
} // namespace android