From 041d46ded732f0dd40fb1fb2465bd50ec00375a0 Mon Sep 17 00:00:00 2001 From: Simo Piiroinen Date: Wed, 22 Aug 2018 09:26:57 +0300 Subject: [PATCH] [usb_moded] Explicitly start/stop mtp daemon. Fixes JB#41748 The mtp functionality is started roughly as follows: 1. usb-moded completes whole gadget configuration, and then 2. signals mtp mode activation on D-Bus 3. msyncd sees the mode change and loads mtp plugin 4. mtp plugin writes endpoint configuration, and then 5. scans storage ares to enumerate files exposed via mtp, but 6. does not really respond to ptp/mtp requests until storages are ready The problems are: While the above sequence has worked with with android usb, it does cause timing issues for usb enumeration and longer than expected response times from pc side mtp initiator point of view. Additionally when configfs is used, the gadget configuration can't even be completed before mtp daemon gets a chance to write configuration data to the control endpoint. To remedy the situation the following changes are made: 1. buteo-mtp-qt5-sync-plugin package is removed from the system -> mtp functionality is no longer handled by msyncd process 2. mtp daemon is made to enumerate storage content before writing config data to control endpoint -> when usb enumeration is possible, mtp daemon is already ready to handle commands from initiator 3. usb-moded explicitly starts/stops mtp daemon when it is about to activate/deactivate mtp_mode -> usb stays configured in charging mode until it is ready to handle mtp This patch handles step (3). Step (2) is in buteo-mtp-qt5 >= 0.5.0, and (1) needs to be fixed in device specific package configuration. Signed-off-by: Simo Piiroinen --- rpm/usb-moded.spec | 3 ++ src/usb_moded-configfs.c | 18 ------- src/usb_moded.c | 108 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 18 deletions(-) diff --git a/rpm/usb-moded.spec b/rpm/usb-moded.spec index ff7dbbc..799e307 100644 --- a/rpm/usb-moded.spec +++ b/rpm/usb-moded.spec @@ -24,6 +24,9 @@ Requires: busybox-symlinks-dhcp Requires(post): systemd Requires(postun): systemd Conflicts: dsme < 0.79.0 +Conflicts: buteo-mtp-qt5-sync-plugin +Conflicts: buteo-mtp-qt5 < 0.5.0 +Recommends: buteo-mtp-qt5 %description Usb_moded is a daemon to control the USB states. For this diff --git a/src/usb_moded-configfs.c b/src/usb_moded-configfs.c index 6de953a..a7931d2 100644 --- a/src/usb_moded-configfs.c +++ b/src/usb_moded-configfs.c @@ -635,14 +635,6 @@ configfs_set_function(const char *func) */ func = configfs_map_function(func); - /* HACK: Stop mtp daemon when enabling any other function - * after bootup is finished (assumption being it - * can't be started before init done and we do not - * want to spam bootup journal with warnings. - */ - if( strcmp(func, FUNCTION_MTP) && usbmoded_init_done_p() ) - usbmoded_system("systemctl-user stop buteo-mtp.service"); - if( !configfs_set_udc(false) ) goto EXIT; @@ -652,16 +644,6 @@ configfs_set_function(const char *func) if( !configfs_enable_function(func) ) goto EXIT; - /* HACK: Start mtp daemon when enabling mtp function. - * Then wait "a bit" since udc can't be enabled - * before mtpd has written suitable configuration - * to control endpoint. - */ - if( !strcmp(func, FUNCTION_MTP) ) { - usbmoded_system("systemctl-user start buteo-mtp.service"); - usbmoded_msleep(1500); - } - /* Leave disabled, so that caller can adjust attributes * etc before enabling */ diff --git a/src/usb_moded.c b/src/usb_moded.c index 12b44d0..ad91c3a 100644 --- a/src/usb_moded.c +++ b/src/usb_moded.c @@ -369,6 +369,108 @@ static const modemapping_t modemapping[] = * Functions * ========================================================================= */ +static bool usbmoded_mode_is_mtp_mode(const char *mode) +{ + return mode && !strcmp(mode, "mtp_mode"); +} + +static bool usbmoded_is_mtpd_running(void) +{ + /* ep0 becomes available when /dev/mtp is mounted. + * + * ep1, ep2, ep3 exist while mtp daemon is running, + * has ep0 opened and has written config data to it. + */ + static const char * const lut[] = { + "/dev/mtp/ep0", + "/dev/mtp/ep1", + "/dev/mtp/ep2", + "/dev/mtp/ep3", + 0 + }; + + bool ack = true; + + for( size_t i = 0; lut[i]; ++i ) { + if( access(lut[i], F_OK) == -1 ) { + ack = false; + break; + } + } + + return ack; +} + +static bool +usbmoded_stop_mtpd(void) +{ + bool ack = !usbmoded_is_mtpd_running(); + + if( ack ) { + log_debug("mtp daemon is not running"); + goto EXIT; + } + + int rc = usbmoded_system("systemctl-user stop buteo-mtp.service"); + if( rc != 0 ) { + log_warning("failed to stop mtp daemon; exit code = %d", rc); + goto EXIT; + } + + for( int attempts = 3; ; ) { + if( (ack = !usbmoded_is_mtpd_running()) ) { + log_debug("mtp daemon has stopped"); + break; + } + + if( --attempts <= 0) { + log_warning("failed to stop mtp daemon; giving up"); + break; + } + + log_debug("waiting for mtp daemon to stop"); + usbmoded_msleep(2000); + } +EXIT: + + return ack; +} + +static bool +usbmoded_start_mtpd(void) +{ + bool ack = usbmoded_is_mtpd_running(); + + if( ack ) { + log_debug("mtp daemon is not running"); + goto EXIT; + } + + int rc = usbmoded_system("systemctl-user start buteo-mtp.service"); + if( rc != 0 ) { + log_warning("failed to start mtp daemon; exit code = %d", rc); + goto EXIT; + } + + for( int attempts = 15; ; ) { + if( (ack = usbmoded_is_mtpd_running()) ) { + log_debug("mtp daemon has started"); + break; + } + + if( --attempts <= 0) { + log_warning("failed to start mtp daemon; giving up"); + break; + } + + log_debug("waiting for mtp daemon to start"); + usbmoded_msleep(2000); + } +EXIT: + + return ack; +} + const char *cable_state_repr(cable_state_t state) { static const char * const lut[CABLE_STATE_NUMOF] = { @@ -471,6 +573,9 @@ static void usbmoded_switch_to_mode(const char *mode) log_debug("Cleaning up previous mode"); + if( !usbmoded_mode_is_mtp_mode(mode) ) + usbmoded_stop_mtpd(); + if( usbmoded_get_usb_mode_data() ) { modesetting_leave_dynamic_mode(); usbmoded_set_usb_mode_data(NULL); @@ -507,6 +612,9 @@ static void usbmoded_switch_to_mode(const char *mode) * as they will use the usbmoded_get_usb_mode_data function */ usbmoded_set_usb_mode_data(data); + if( usbmoded_mode_is_mtp_mode(mode) ) + usbmoded_start_mtpd(); + if( !usbmoded_set_usb_module(data->mode_module) ) break;