Commit 64350689 authored by spiiroin's avatar spiiroin

[usb_moded] Refactor entering/leaving usb mode. JB#41748

Split mode switching to three layers: Tracking cable connection state,
choosing target mode, and performing required actions to activate
the chosen mode.

Try to minimize places where layer borders are crossed - which should
work also as an enabler for moving blocking actions to separate thread
later on.

Arrange logic in such manner that usb gadget is always configured as
being able to serve some function - fallback being the dummy mass_storage
function used for charging.

Deprecate command line options that serve no useful purpose after the
changes.
Signed-off-by: spiiroin's avatarSimo Piiroinen <simo.piiroinen@jollamobile.com>
parent 18efbfcc
......@@ -62,7 +62,7 @@ static gboolean appsync_enumerate_usb_cb (gpointer data);
static void appsync_start_enumerate_usb_timer (void);
static void appsync_cancel_enumerate_usb_timer(void);
static void appsync_enumerate_usb (void);
static void appsync_stop_apps (int post);
void appsync_stop_apps (int post);
int appsync_stop (gboolean force);
/* ========================================================================= *
......@@ -456,7 +456,7 @@ static void appsync_enumerate_usb(void)
#endif /* APP_SYNC_DBUS */
}
static void appsync_stop_apps(int post)
void appsync_stop_apps(int post)
{
GList *iter = 0;
......
......@@ -80,5 +80,6 @@ int appsync_activate_sync (const char *mode);
int appsync_activate_sync_post(const char *mode);
int appsync_mark_active (const gchar *name, int post);
int appsync_stop (gboolean force);
void appsync_stop_apps (int post);
#endif /* USB_MODED_APPSYNC_H_ */
......@@ -591,7 +591,6 @@ set_config_result_t config_set_mode_whitelist(const char *whitelist)
else if (strcmp(current_mode, MODE_CHARGING_FALLBACK) && strcmp(current_mode, MODE_ASK) && usbmoded_valid_mode(current_mode)) {
/* Invalid mode that is not MODE_ASK or MODE_CHARGING_FALLBACK
* -> switch to MODE_CHARGING_FALLBACK */
modesetting_cleanup(usbmoded_get_usb_module());
usbmoded_set_usb_mode(MODE_CHARGING_FALLBACK);
}
......
......@@ -283,7 +283,6 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB
/* do not change mode if the mode requested is the one already set */
if(strcmp(use, usbmoded_get_usb_mode()) != 0)
{
modesetting_cleanup(usbmoded_get_usb_module());
usbmoded_set_usb_mode(use);
}
if((reply = dbus_message_new_method_return(msg)))
......
......@@ -61,13 +61,12 @@ void modesetting_verify_values (void);
static char *modesetting_strip (char *str);
static char *modesetting_read_from_file (const char *path, size_t maxsize);
int modesetting_write_to_file_real (const char *file, int line, const char *func, const char *path, const char *text);
static gboolean modesetting_network_retry (gpointer data);
static gboolean modesetting_network_retry_cb (gpointer data);
static int modesetting_set_mass_storage_mode (struct mode_list_elem *data);
static int modesetting_unset_mass_storage_mode (struct mode_list_elem *data);
static void modesetting_report_mass_storage_blocker(const char *mountpoint, int try);
int modesetting_set_dynamic_mode (void);
static void modesetting_unset_dynamic_mode (void);
int modesetting_cleanup (const char *module);
bool modesetting_set_dynamic_mode (void);
void modesetting_unset_dynamic_mode (void);
void modesetting_init (void);
void modesetting_quit (void);
......@@ -76,7 +75,7 @@ void modesetting_quit (void);
* ========================================================================= */
static GHashTable *tracked_values = 0;
static guint delayed_network = 0;
static guint modesetting_network_retry_id = 0;
/* ========================================================================= *
* Functions
......@@ -265,9 +264,9 @@ cleanup:
return err;
}
static gboolean modesetting_network_retry(gpointer data)
static gboolean modesetting_network_retry_cb(gpointer data)
{
delayed_network = 0;
modesetting_network_retry_id = 0;
network_up(data);
return(FALSE);
}
......@@ -368,11 +367,11 @@ umount: command = g_strconcat("mount | grep ", mountpath, NULL);
}
else
{
write_to_file(ANDROID0_ENABLE, "0");
android_set_enabled(false);
write_to_file(ANDROID0_FUNCTIONS, "mass_storage");
//write_to_file("/sys/class/android_usb/f_mass_storage/lun/nofua", fua);
write_to_file("/sys/class/android_usb/f_mass_storage/lun/file", mount);
write_to_file(ANDROID0_ENABLE, "1");
android_set_enabled(true);
}
}
......@@ -445,7 +444,7 @@ static int modesetting_unset_mass_storage_mode(struct mode_list_elem *data)
{
log_debug("Disable android mass storage\n");
write_to_file("/sys/class/android_usb/f_mass_storage/lun/file", "0");
write_to_file(ANDROID0_ENABLE, "0");
android_set_enabled(false);
}
}
else
......@@ -499,81 +498,98 @@ static void modesetting_report_mass_storage_blocker(const char *mountpoint, int
}
int modesetting_set_dynamic_mode(void)
bool modesetting_set_dynamic_mode(void)
{
bool ack = false;
struct mode_list_elem *data;
int ret = 1;
int network = 1;
data = usbmoded_get_usb_mode_data();
log_debug("DYNAMIC MODE: SETUP");
if(!data)
return(ret);
/* - - - - - - - - - - - - - - - - - - - *
* Is a dynamic mode?
* - - - - - - - - - - - - - - - - - - - */
if(data->mass_storage)
{
return modesetting_set_mass_storage_mode(data);
if( !(data = usbmoded_get_usb_mode_data()) ) {
log_debug("No dynamic mode data to setup");
goto EXIT;
}
log_debug("data->mass_storage = %d", data->mass_storage);
log_debug("data->connman_tethering = %d", data->connman_tethering);
log_debug("data->appsync = %d", data->appsync);
log_debug("data->network = %d", data->network);
/* - - - - - - - - - - - - - - - - - - - *
* Is a mass storage dynamic mode?
* - - - - - - - - - - - - - - - - - - - */
if( data->mass_storage ) {
log_debug("Dynamic mode is mass storage");
ack = modesetting_set_mass_storage_mode(data) == 0;
goto EXIT;
}
/* - - - - - - - - - - - - - - - - - - - *
* Start pre-enum app sync
* - - - - - - - - - - - - - - - - - - - */
#ifdef APP_SYNC
if(data->appsync)
if(appsync_activate_sync(data->mode_name)) /* returns 1 on error */
{
if( data->appsync ) {
log_debug("Dynamic mode is appsync: do pre actions");
if( appsync_activate_sync(data->mode_name) != 0 ) {
log_debug("Appsync failure");
return(ret);
goto EXIT;
}
}
#endif
/* - - - - - - - - - - - - - - - - - - - *
* Configure gadget
* - - - - - - - - - - - - - - - - - - - */
if( configfs_in_use() ) {
/* Configfs based gadget configuration */
char *id = config_get_android_vendor_id();
configfs_set_udc(false);
configfs_set_function(data->sysfs_value);
configfs_set_productid(data->idProduct);
char *id = config_get_android_vendor_id();
configfs_set_vendorid(data->idVendorOverride ?: id);
configfs_set_function(data->sysfs_value);
ret = configfs_set_udc(true) ? 0 : -1;
free(id);
if( !configfs_set_udc(true) )
goto EXIT;
}
else if( android_in_use() ) {
/* Android USB based gadget configuration */
/* make sure things are disabled before changing functionality */
write_to_file(data->softconnect_path, data->softconnect_disconnect);
/* set functionality first, then enable */
if(data->android_extra_sysfs_value)
ret = write_to_file(data->android_extra_sysfs_path, data->android_extra_sysfs_value);
else
ret = 0;
/* ??? */
write_to_file(data->android_extra_sysfs_path2, data->android_extra_sysfs_value2);
/* only works for android since the idProduct is a module parameter */
android_set_function(data->sysfs_value);
android_set_productid(data->idProduct);
/* only works for android since the idProduct is a module parameter */
android_set_vendorid(data->idVendorOverride);
write_to_file(data->sysfs_path, data->sysfs_value);
/* enable the device */
if( ret == 0 )
ret = write_to_file(data->softconnect_path, data->softconnect);
char *id = config_get_android_vendor_id();
android_set_vendorid(data->idVendorOverride ?: id);
free(id);
write_to_file(data->android_extra_sysfs_path, data->android_extra_sysfs_value);
write_to_file(data->android_extra_sysfs_path2, data->android_extra_sysfs_value2);
if( !android_set_enabled(true) )
goto EXIT;
}
else if( modules_in_use() ) {
/* Assume relevant module has already been successfully loaded
* from somewhere else.
*/
ret = 0;
// nop
}
else {
log_crit("no backend is selected, can't set dynamic mode");
ret = 1;
goto EXIT;
}
/* - - - - - - - - - - - - - - - - - - - *
* Setup network
* - - - - - - - - - - - - - - - - - - - */
/* functionality should be enabled, so we can enable the network now */
if(data->network)
{
log_debug("Dynamic mode is network");
#ifdef DEBIAN
char command[256];
......@@ -590,9 +606,9 @@ int modesetting_set_dynamic_mode(void)
if(network != 0 && data->network)
{
log_debug("Retry setting up the network later\n");
if(delayed_network)
g_source_remove(delayed_network);
delayed_network = g_timeout_add_seconds(3, modesetting_network_retry, data);
if(modesetting_network_retry_id)
g_source_remove(modesetting_network_retry_id);
modesetting_network_retry_id = g_timeout_add_seconds(3, modesetting_network_retry_cb, data);
}
/* Needs to be called before application post synching so
......@@ -600,76 +616,122 @@ int modesetting_set_dynamic_mode(void)
if(data->nat || data->dhcp_server)
network_set_up_dhcpd(data);
/* - - - - - - - - - - - - - - - - - - - *
* Start post-enum app sync
* - - - - - - - - - - - - - - - - - - - */
/* no need to execute the post sync if there was an error setting the mode */
if(data->appsync && !ret)
if(data->appsync )
{
log_debug("Dynamic mode is appsync: do post actions");
/* let's sleep for a bit (350ms) to allow interfaces to settle before running postsync */
usbmoded_msleep(350);
appsync_activate_sync_post(data->mode_name);
}
/* - - - - - - - - - - - - - - - - - - - *
* Start tethering
* - - - - - - - - - - - - - - - - - - - */
#ifdef CONNMAN
if(data->connman_tethering)
if( data->connman_tethering ) {
log_debug("Dynamic mode is tethering");
connman_set_tethering(data->connman_tethering, TRUE);
}
#endif
if(ret)
ack = true;
EXIT:
if( !ack )
umdbus_send_error_signal(MODE_SETTING_FAILED);
return(ret);
return ack;
}
static void modesetting_unset_dynamic_mode(void)
void modesetting_unset_dynamic_mode(void)
{
log_debug("DYNAMIC MODE: CLEANUP");
struct mode_list_elem *data;
data = usbmoded_get_usb_mode_data();
if(delayed_network)
/* - - - - - - - - - - - - - - - - - - - *
* Do not leave timers behind
* - - - - - - - - - - - - - - - - - - - */
if(modesetting_network_retry_id)
{
g_source_remove(delayed_network);
delayed_network = 0;
g_source_remove(modesetting_network_retry_id);
modesetting_network_retry_id = 0;
}
/* the modelist could be empty */
if(!data)
return;
/* - - - - - - - - - - - - - - - - - - - *
* Is a dynamic mode?
* - - - - - - - - - - - - - - - - - - - */
if( !data ) {
log_debug("No dynamic mode data to cleanup");
goto EXIT;
}
if(!strcmp(data->mode_name, MODE_MASS_STORAGE))
{
log_debug("data->mass_storage = %d", data->mass_storage);
log_debug("data->connman_tethering = %d", data->connman_tethering);
log_debug("data->appsync = %d", data->appsync);
log_debug("data->network = %d", data->network);
/* - - - - - - - - - - - - - - - - - - - *
* Is a mass storage dynamic mode?
* - - - - - - - - - - - - - - - - - - - */
if( data->mass_storage ) {
log_debug("Dynamic mode is mass storage");
modesetting_unset_mass_storage_mode(data);
return;
goto EXIT;
}
/* - - - - - - - - - - - - - - - - - - - *
* Stop tethering
* - - - - - - - - - - - - - - - - - - - */
#ifdef CONNMAN
if(data->connman_tethering)
if( data->connman_tethering ) {
log_debug("Dynamic mode was tethering");
connman_set_tethering(data->connman_tethering, FALSE);
}
#endif
if(data->network)
{
/* - - - - - - - - - - - - - - - - - - - *
* Stop post-enum app sync
* - - - - - - - - - - - - - - - - - - - */
if(data->appsync ) {
log_debug("Dynamic mode was appsync: undo post actions");
/* Just stop post enum appsync apps */
appsync_stop_apps(1);
}
/* - - - - - - - - - - - - - - - - - - - *
* Teardown network
* - - - - - - - - - - - - - - - - - - - */
if( data->network ) {
log_debug("Dynamic mode was network");
network_down(data);
}
/* - - - - - - - - - - - - - - - - - - - *
* Configure gadget
* - - - - - - - - - - - - - - - - - - - */
if( configfs_in_use() ) {
/* Leave as is. We will reprogram wnen mode is
* set, not when it is unset.
*/
}
else if( android_in_use() ) {
/* disconnect before changing functionality */
write_to_file(data->softconnect_path, data->softconnect_disconnect);
write_to_file(data->sysfs_path, data->sysfs_reset_value);
/* restore vendorid if the mode had an override */
if(data->idVendorOverride)
{
char *id = config_get_android_vendor_id();
android_set_vendorid(id);
g_free(id);
}
/* enable after the changes have been made */
write_to_file(data->softconnect_path, data->softconnect);
/* Leave as is. We will reprogram wnen mode is
* set, not when it is unset.
*/
}
else if( modules_in_use() ) {
/* Assume unloading happens somewhere else */
......@@ -677,42 +739,21 @@ static void modesetting_unset_dynamic_mode(void)
else {
log_crit("no backend is selected, can't unset dynamic mode");
}
}
/** clean up mode changes or extra actions to perform after a mode change
* @param module Name of module currently in use
* @return 0 on success, non-zero on failure
*
*/
int modesetting_cleanup(const char *module)
{
log_debug("Cleaning up mode\n");
if(!module)
{
log_warning("No module found to unload. Skipping cleanup\n");
return 0;
}
/* - - - - - - - - - - - - - - - - - - - *
* Stop pre-enum app sync
* - - - - - - - - - - - - - - - - - - - */
#ifdef APP_SYNC
/* Stop applications started due to entering this mode */
appsync_stop(FALSE);
#endif /* APP_SYNC */
if(!strcmp(module, MODULE_MASS_STORAGE)|| !strcmp(module, MODULE_FILE_STORAGE))
{
/* no clean-up needs to be done when we come from charging mode. We need
* to check since we use fake mass-storage for charging */
if(!strcmp(MODE_CHARGING, usbmoded_get_usb_mode()) || !strcmp(MODE_CHARGING_FALLBACK, usbmoded_get_usb_mode()))
return 0;
modesetting_unset_mass_storage_mode(NULL);
if( data->appsync ) {
log_debug("Dynamic mode was appsync: undo all actions");
/* Do full appsync cleanup */
appsync_stop(false);
}
#endif
else if(usbmoded_get_usb_mode_data())
modesetting_unset_dynamic_mode();
return(0);
EXIT:
return;
}
/** Allocate modesetting related dynamic resouces
......
......@@ -31,6 +31,8 @@
# include "usb_moded-dyn-config.h"
# include <stdbool.h>
/* ========================================================================= *
* Prototypes
* ========================================================================= */
......@@ -39,8 +41,8 @@
void modesetting_verify_values (void);
int modesetting_write_to_file_real(const char *file, int line, const char *func, const char *path, const char *text);
int modesetting_set_dynamic_mode (void);
int modesetting_cleanup (const char *module);
bool modesetting_set_dynamic_mode (void);
void modesetting_unset_dynamic_mode(void);
void modesetting_init (void);
void modesetting_quit (void);
......
......@@ -56,19 +56,22 @@ bool modules_in_use (void);
void modules_quit (void);
int modules_load_module (const char *module);
int modules_unload_module (const char *module);
static int modules_module_is_loaded (const char *module);
const char *modules_get_loaded_module (void);
int modules_cleanup_module (const char *module);
static int modules_prepare_for_module_switch(int force);
void modules_check_module_state (const char *module_name);
/* ========================================================================= *
* Data
* ========================================================================= */
/** Availability of kernel module based gadget configuration functionality
*
* -1 = not checked yet
* 0 = not available
* 1 = chosen as means of gadget configuration for usb-moded
*/
static int modules_probed = -1;
/* kmod context - initialized at start in usbmoded_init by ctx_init()
* and cleaned up by ctx_cleanup() functions */
static struct kmod_ctx *ctx = 0;
static struct kmod_ctx *modules_ctx = 0;
/* ========================================================================= *
* Functions
......@@ -81,7 +84,7 @@ static bool modules_have_module(const char *module)
bool ack = false;
struct kmod_list *list = 0;
if( kmod_module_new_from_lookup(ctx, module, &list) < 0 )
if( kmod_module_new_from_lookup(modules_ctx, module, &list) < 0 )
goto EXIT;
if( list == 0 )
......@@ -98,8 +101,6 @@ EXIT:
}
static int modules_probed = -1;
bool modules_in_use(void)
{
if( modules_probed < 0 )
......@@ -140,12 +141,12 @@ bool modules_init(void)
{
bool ack = false;
if( !ctx ) {
if( !(ctx = kmod_new(NULL, NULL)) )
if( !modules_ctx ) {
if( !(modules_ctx = kmod_new(NULL, NULL)) )
goto EXIT;
}
if( kmod_load_resources(ctx) < 0 )
if( kmod_load_resources(modules_ctx) < 0 )
goto EXIT;
if( !modules_probe() )
......@@ -159,8 +160,8 @@ EXIT:
/* kmod module cleanup */
void modules_quit(void)
{
if( ctx )
kmod_unref(ctx), ctx = 0;
if( modules_ctx )
kmod_unref(modules_ctx), modules_ctx = 0;
}
/** load module
......@@ -206,14 +207,14 @@ int modules_load_module(const char *module)
g_strfreev(strings);
}
ret = kmod_module_new_from_name(ctx, load, &mod);
ret = kmod_module_new_from_name(modules_ctx, load, &mod);
/* since kmod_module_new_from_name does not check if the module
* exists we test it's path in case we deal with the mass-storage one */
if(!strcmp(module, MODULE_MASS_STORAGE) &&
(kmod_module_get_path(mod) == NULL))
{
log_debug("Fallback on older g_file_storage\n");
ret = kmod_module_new_from_name(ctx, MODULE_FILE_STORAGE, &mod);
ret = kmod_module_new_from_name(modules_ctx, MODULE_FILE_STORAGE, &mod);
}
if(!charging_args)
......@@ -253,182 +254,9 @@ int modules_unload_module(const char *module)
return -1;
}
kmod_module_new_from_name(ctx, module, &mod);
kmod_module_new_from_name(modules_ctx, module, &mod);
ret = kmod_module_remove_module(mod, KMOD_REMOVE_NOWAIT);
kmod_module_unref(mod);
return(ret);
}
/** Check which state a module is in
*
* @return 1 if loaded, 0 when not loaded
*/
static int modules_module_is_loaded(const char *module)
{
int ret = 0;
struct kmod_module *mod;
kmod_module_new_from_name(ctx, module, &mod);
ret = kmod_module_get_initstate(mod);
kmod_module_unref(mod);
if( ret == KMOD_MODULE_LIVE)
return 1;
return 0;
}
/** find which module is loaded
*
* @return The name of the loaded module, or NULL if no modules are loaded.
*/
const char * modules_get_loaded_module(void)
{
if( !modules_in_use() ) {
log_warning("get loaded module - without module support");
return 0;
}
if(modules_module_is_loaded("g_ether"))
return(MODULE_DEVELOPER);
if(modules_module_is_loaded("g_ncm"))
return("g_ncm");
if(modules_module_is_loaded("g_ffs"))
return(MODULE_MTP);
if(modules_module_is_loaded("g_mass_storage"))
return(MODULE_MASS_STORAGE);
if(modules_module_is_loaded("g_file_storage"))
return(MODULE_FILE_STORAGE);
if(modules_module_is_loaded(usbmoded_get_usb_module()))
return(usbmoded_get_usb_module());
/* no module loaded */
return(0);
}
/** clean up for modules when usb gets disconnected
*
* @param module The name of the module to unload
* @return 0 on success, non-zero on failure
*
*/
int modules_cleanup_module(const char *module)
{
int retry = 0, failure;
if(!strcmp(module, MODULE_NONE))
goto END;
if( !modules_in_use() ) {
log_warning("cleanup module %s - without module support", module);
goto END;
}
/* wait a bit for all components listening on dbus to clean up their act
* usbmoded_sleep(2); */
/* check if things were not reconnected in that timespan
* if(usbmoded_get_connection_state())
* return(0);
*/
failure = modules_unload_module(module);
/* if we have MODULE_MASS_STORAGE it might be MODULE_FILE_STORAGE might
* be loaded. So check and unload that one if unloading fails first time */
if(failure && !strcmp(MODULE_MASS_STORAGE, module))
failure = modules_unload_module(MODULE_FILE_STORAGE);
/* if it still failed it might be the mode has not been cleaned-up correctly,
* so we clean up the mode to be sure */
if(failure)
{
modesetting_cleanup(modules_get_loaded_module());
}
while(failure)
{
/* module did not get unloaded. We will wait a bit and try again */
usbmoded_sleep(1);
/* send the REALLY disconnect message */
umdbus_send_state_signal(USB_REALLY_DISCONNECT);
failure = modules_unload_module(module);
log_debug("unloading failure = %d\n", failure);
if(!failure)
break;
if(!modules_get_loaded_module())
goto END;
retry++;
if(retry == 2)
break;
}
if(!failure)
log_info("Module %s unloaded successfully\n", module);
else
{
log_err("Module %s did not unload! Failing and going to undefined.\n", module);
return(1);
}
END:
return(0);
}
/** try to unload modules to support switching
*
*
* @param force force unloading with a nasty clean-up on TRUE, or just try unloading when FALSE
* @return 0 on success, 1 on failure, 2 if hard clean-up failed
*/
static int modules_prepare_for_module_switch (int force)
{
if( !modules_in_use() ) {
log_warning("prep for module switch force=%d - without module support", force);
return 0;
}
const char *unload;
int ret = 1;
unload = modules_get_loaded_module();
if(unload)
{
if(force)
ret = modules_cleanup_module(unload);
else
ret = modules_unload_module(unload);
}
if(ret && force)
return(2);
return ret;
}
/** check for loaded modules and clean-up if they are not for the chosen mode
*
* @param module_name module name to check for
*
*/
void modules_check_module_state(const char *module_name)
{
if( !modules_in_use() ) {
log_warning("check module %s state - without module support", module_name);
return;
}
const char *module;
module = modules_get_loaded_module();