From 57d404a3d0422fbd2ff850f5e614f0735f1777d8 Mon Sep 17 00:00:00 2001 From: Simo Piiroinen Date: Thu, 23 Aug 2018 16:14:47 +0300 Subject: [PATCH] [usb-moded] Move synchronous mode switch to worker thread. Fixes JB#42757 Mode switching in usb-moded is done via synchronous ipc / otherwise blocking operations - which makes usb-moded mainloop unable to process for example udev events / dbus requests from clients during the swich and causes potentially stale events to be seen after the mode switch. Separate mode selection from mode activation so that the latter is executed from worker thread. Distribute existing code in manner that makes the selection vs activation, or mainloop / worker thread separation more apparent: - control.c: Mode selection logic - worker.c: Mode activation logic And move miscellaneous functions that have accumulated into the main usb-moded module to: - common.c: Generic helper functions - sigpipe.c: Asynchronous signal trapping Rename all moved functions so that they still have prefix indicating the containing module. Refuse synchronous sleeps in order to terminate ongoing mode switch if udev events / other inputs invalidate the target of transition. Signed-off-by: Simo Piiroinen --- .depend | 162 ++++ Makefile.custom | 13 + src/Makefile.am | 11 +- src/usb_moded-common.c | 505 +++++++++++ src/usb_moded-common.h | 61 ++ src/usb_moded-config-private.h | 2 + src/usb_moded-config.c | 29 +- src/usb_moded-configfs.c | 3 +- src/usb_moded-control.c | 340 ++++++++ src/usb_moded-control.h | 45 + src/usb_moded-dbus.c | 16 +- src/usb_moded-devicelock.c | 3 +- src/usb_moded-dsme.c | 3 +- src/usb_moded-modesetting.c | 24 +- src/usb_moded-network.c | 39 +- src/usb_moded-sigpipe.c | 209 +++++ src/usb_moded-sigpipe.h | 31 + src/usb_moded-trigger.c | 3 +- src/usb_moded-udev.c | 12 +- src/usb_moded-worker.c | 830 ++++++++++++++++++ src/usb_moded-worker.h | 52 ++ src/usb_moded.c | 1485 +++----------------------------- src/usb_moded.h | 77 +- 23 files changed, 2448 insertions(+), 1507 deletions(-) create mode 100644 src/usb_moded-common.c create mode 100644 src/usb_moded-common.h create mode 100644 src/usb_moded-control.c create mode 100644 src/usb_moded-control.h create mode 100644 src/usb_moded-sigpipe.c create mode 100644 src/usb_moded-sigpipe.h create mode 100644 src/usb_moded-worker.c create mode 100644 src/usb_moded-worker.h diff --git a/.depend b/.depend index 013c8d4..c5f8561 100644 --- a/.depend +++ b/.depend @@ -72,11 +72,39 @@ src/usb_moded-appsync.pic.o:\ src/usb_moded-modesetting.h\ src/usb_moded-systemd.h\ +src/usb_moded-common.o:\ + src/usb_moded-common.c\ + config-static.h\ + src/usb_moded-common.h\ + src/usb_moded-config-private.h\ + src/usb_moded-config.h\ + src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ + src/usb_moded-dyn-config.h\ + src/usb_moded-log.h\ + src/usb_moded-modes.h\ + src/usb_moded.h\ + +src/usb_moded-common.pic.o:\ + src/usb_moded-common.c\ + config-static.h\ + src/usb_moded-common.h\ + src/usb_moded-config-private.h\ + src/usb_moded-config.h\ + src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ + src/usb_moded-dyn-config.h\ + src/usb_moded-log.h\ + src/usb_moded-modes.h\ + src/usb_moded.h\ + src/usb_moded-config.o:\ src/usb_moded-config.c\ config-static.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ src/usb_moded-dbus.h\ src/usb_moded-dyn-config.h\ @@ -84,13 +112,16 @@ src/usb_moded-config.o:\ src/usb_moded-modes.h\ src/usb_moded-modesetting.h\ src/usb_moded-ssu.h\ + src/usb_moded-worker.h\ src/usb_moded.h\ src/usb_moded-config.pic.o:\ src/usb_moded-config.c\ config-static.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ src/usb_moded-dbus.h\ src/usb_moded-dyn-config.h\ @@ -98,12 +129,14 @@ src/usb_moded-config.pic.o:\ src/usb_moded-modes.h\ src/usb_moded-modesetting.h\ src/usb_moded-ssu.h\ + src/usb_moded-worker.h\ src/usb_moded.h\ src/usb_moded-configfs.o:\ src/usb_moded-configfs.c\ config-static.h\ src/usb_moded-android.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ src/usb_moded-configfs.h\ @@ -117,6 +150,7 @@ src/usb_moded-configfs.pic.o:\ src/usb_moded-configfs.c\ config-static.h\ src/usb_moded-android.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ src/usb_moded-configfs.h\ @@ -126,11 +160,45 @@ src/usb_moded-configfs.pic.o:\ src/usb_moded-modesetting.h\ src/usb_moded.h\ +src/usb_moded-control.o:\ + src/usb_moded-control.c\ + config-static.h\ + src/usb_moded-common.h\ + src/usb_moded-config-private.h\ + src/usb_moded-config.h\ + src/usb_moded-control.h\ + src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ + src/usb_moded-dyn-config.h\ + src/usb_moded-log.h\ + src/usb_moded-modes.h\ + src/usb_moded-worker.h\ + src/usb_moded.h\ + +src/usb_moded-control.pic.o:\ + src/usb_moded-control.c\ + config-static.h\ + src/usb_moded-common.h\ + src/usb_moded-config-private.h\ + src/usb_moded-config.h\ + src/usb_moded-control.h\ + src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ + src/usb_moded-dyn-config.h\ + src/usb_moded-log.h\ + src/usb_moded-modes.h\ + src/usb_moded-worker.h\ + src/usb_moded.h\ + src/usb_moded-dbus.o:\ src/usb_moded-dbus.c\ config-static.h\ + src/usb_moded-appsync-dbus-private.h\ + src/usb_moded-appsync-dbus.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ src/usb_moded-dbus.h\ src/usb_moded-dyn-config.h\ @@ -143,8 +211,12 @@ src/usb_moded-dbus.o:\ src/usb_moded-dbus.pic.o:\ src/usb_moded-dbus.c\ config-static.h\ + src/usb_moded-appsync-dbus-private.h\ + src/usb_moded-appsync-dbus.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ src/usb_moded-dbus.h\ src/usb_moded-dyn-config.h\ @@ -157,7 +229,10 @@ src/usb_moded-dbus.pic.o:\ src/usb_moded-devicelock.o:\ src/usb_moded-devicelock.c\ config-static.h\ + src/usb_moded-common.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ src/usb_moded-devicelock.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ @@ -167,7 +242,10 @@ src/usb_moded-devicelock.o:\ src/usb_moded-devicelock.pic.o:\ src/usb_moded-devicelock.c\ config-static.h\ + src/usb_moded-common.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ src/usb_moded-devicelock.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ @@ -177,7 +255,10 @@ src/usb_moded-devicelock.pic.o:\ src/usb_moded-dsme.o:\ src/usb_moded-dsme.c\ config-static.h\ + src/usb_moded-common.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ src/usb_moded-dsme.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ @@ -187,7 +268,10 @@ src/usb_moded-dsme.o:\ src/usb_moded-dsme.pic.o:\ src/usb_moded-dsme.c\ config-static.h\ + src/usb_moded-common.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ src/usb_moded-dsme.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ @@ -227,6 +311,7 @@ src/usb_moded-modesetting.o:\ config-static.h\ src/usb_moded-android.h\ src/usb_moded-appsync.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ src/usb_moded-configfs.h\ @@ -238,6 +323,7 @@ src/usb_moded-modesetting.o:\ src/usb_moded-modesetting.h\ src/usb_moded-modules.h\ src/usb_moded-network.h\ + src/usb_moded-worker.h\ src/usb_moded.h\ src/usb_moded-modesetting.pic.o:\ @@ -245,6 +331,7 @@ src/usb_moded-modesetting.pic.o:\ config-static.h\ src/usb_moded-android.h\ src/usb_moded-appsync.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ src/usb_moded-configfs.h\ @@ -256,6 +343,7 @@ src/usb_moded-modesetting.pic.o:\ src/usb_moded-modesetting.h\ src/usb_moded-modules.h\ src/usb_moded-network.h\ + src/usb_moded-worker.h\ src/usb_moded.h\ src/usb_moded-modules.o:\ @@ -287,23 +375,45 @@ src/usb_moded-modules.pic.o:\ src/usb_moded-network.o:\ src/usb_moded-network.c\ config-static.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ src/usb_moded-modesetting.h\ src/usb_moded-network.h\ + src/usb_moded-worker.h\ src/usb_moded.h\ src/usb_moded-network.pic.o:\ src/usb_moded-network.c\ config-static.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ src/usb_moded-modesetting.h\ src/usb_moded-network.h\ + src/usb_moded-worker.h\ + src/usb_moded.h\ + +src/usb_moded-sigpipe.o:\ + src/usb_moded-sigpipe.c\ + config-static.h\ + src/usb_moded-dyn-config.h\ + src/usb_moded-log.h\ + src/usb_moded-sigpipe.h\ + src/usb_moded.h\ + +src/usb_moded-sigpipe.pic.o:\ + src/usb_moded-sigpipe.c\ + config-static.h\ + src/usb_moded-dyn-config.h\ + src/usb_moded-log.h\ + src/usb_moded-sigpipe.h\ src/usb_moded.h\ src/usb_moded-ssu.o:\ @@ -339,8 +449,10 @@ src/usb_moded-systemd.pic.o:\ src/usb_moded-trigger.o:\ src/usb_moded-trigger.c\ config-static.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ src/usb_moded-devicelock.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ @@ -352,8 +464,10 @@ src/usb_moded-trigger.o:\ src/usb_moded-trigger.pic.o:\ src/usb_moded-trigger.c\ config-static.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ src/usb_moded-devicelock.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ @@ -365,8 +479,12 @@ src/usb_moded-trigger.pic.o:\ src/usb_moded-udev.o:\ src/usb_moded-udev.c\ config-static.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ + src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ src/usb_moded-modes.h\ @@ -376,8 +494,12 @@ src/usb_moded-udev.o:\ src/usb_moded-udev.pic.o:\ src/usb_moded-udev.c\ config-static.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ + src/usb_moded-control.h\ + src/usb_moded-dbus-private.h\ + src/usb_moded-dbus.h\ src/usb_moded-dyn-config.h\ src/usb_moded-log.h\ src/usb_moded-modes.h\ @@ -386,20 +508,54 @@ src/usb_moded-udev.pic.o:\ src/usb_moded-util.o:\ src/usb_moded-util.c\ + src/usb_moded-dbus-private.h\ src/usb_moded-dbus.h\ src/usb_moded-util.pic.o:\ src/usb_moded-util.c\ + src/usb_moded-dbus-private.h\ src/usb_moded-dbus.h\ +src/usb_moded-worker.o:\ + src/usb_moded-worker.c\ + config-static.h\ + src/usb_moded-android.h\ + src/usb_moded-common.h\ + src/usb_moded-configfs.h\ + src/usb_moded-control.h\ + src/usb_moded-dyn-config.h\ + src/usb_moded-log.h\ + src/usb_moded-modes.h\ + src/usb_moded-modesetting.h\ + src/usb_moded-modules.h\ + src/usb_moded-worker.h\ + src/usb_moded.h\ + +src/usb_moded-worker.pic.o:\ + src/usb_moded-worker.c\ + config-static.h\ + src/usb_moded-android.h\ + src/usb_moded-common.h\ + src/usb_moded-configfs.h\ + src/usb_moded-control.h\ + src/usb_moded-dyn-config.h\ + src/usb_moded-log.h\ + src/usb_moded-modes.h\ + src/usb_moded-modesetting.h\ + src/usb_moded-modules.h\ + src/usb_moded-worker.h\ + src/usb_moded.h\ + src/usb_moded.o:\ src/usb_moded.c\ config-static.h\ src/usb_moded-android.h\ src/usb_moded-appsync.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ src/usb_moded-configfs.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ src/usb_moded-dbus.h\ src/usb_moded-devicelock.h\ @@ -411,9 +567,11 @@ src/usb_moded.o:\ src/usb_moded-modesetting.h\ src/usb_moded-modules.h\ src/usb_moded-network.h\ + src/usb_moded-sigpipe.h\ src/usb_moded-systemd.h\ src/usb_moded-trigger.h\ src/usb_moded-udev.h\ + src/usb_moded-worker.h\ src/usb_moded.h\ src/usb_moded.pic.o:\ @@ -421,9 +579,11 @@ src/usb_moded.pic.o:\ config-static.h\ src/usb_moded-android.h\ src/usb_moded-appsync.h\ + src/usb_moded-common.h\ src/usb_moded-config-private.h\ src/usb_moded-config.h\ src/usb_moded-configfs.h\ + src/usb_moded-control.h\ src/usb_moded-dbus-private.h\ src/usb_moded-dbus.h\ src/usb_moded-devicelock.h\ @@ -435,9 +595,11 @@ src/usb_moded.pic.o:\ src/usb_moded-modesetting.h\ src/usb_moded-modules.h\ src/usb_moded-network.h\ + src/usb_moded-sigpipe.h\ src/usb_moded-systemd.h\ src/usb_moded-trigger.h\ src/usb_moded-udev.h\ + src/usb_moded-worker.h\ src/usb_moded.h\ utils/udev-search.o:\ diff --git a/Makefile.custom b/Makefile.custom index 2660fc4..d8eae83 100644 --- a/Makefile.custom +++ b/Makefile.custom @@ -169,10 +169,13 @@ LDLIBS += $(PKG_LDLIBS) # ---------------------------------------------------------------------------- usb_moded-OBJS += src/usb_moded.o + usb_moded-OBJS += src/usb_moded-android.o usb_moded-OBJS += src/usb_moded-appsync.o +usb_moded-OBJS += src/usb_moded-common.o usb_moded-OBJS += src/usb_moded-config.o usb_moded-OBJS += src/usb_moded-configfs.o +usb_moded-OBJS += src/usb_moded-control.o usb_moded-OBJS += src/usb_moded-dbus.o usb_moded-OBJS += src/usb_moded-devicelock.o usb_moded-OBJS += src/usb_moded-dsme.o @@ -182,10 +185,12 @@ usb_moded-OBJS += src/usb_moded-mac.o usb_moded-OBJS += src/usb_moded-modesetting.o usb_moded-OBJS += src/usb_moded-modules.o usb_moded-OBJS += src/usb_moded-network.o +usb_moded-OBJS += src/usb_moded-sigpipe.o usb_moded-OBJS += src/usb_moded-ssu.o usb_moded-OBJS += src/usb_moded-systemd.o usb_moded-OBJS += src/usb_moded-trigger.o usb_moded-OBJS += src/usb_moded-udev.o +usb_moded-OBJS += src/usb_moded-worker.o usb_moded : $(usb_moded-OBJS) $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) @@ -217,8 +222,10 @@ usb_moded_util : $(usb_moded_util-OBJS) CLEAN_SOURCES += src/usb_moded-android.c CLEAN_SOURCES += src/usb_moded-appsync-dbus.c CLEAN_SOURCES += src/usb_moded-appsync.c +CLEAN_SOURCES += src/usb_moded-common.c CLEAN_SOURCES += src/usb_moded-config.c CLEAN_SOURCES += src/usb_moded-configfs.c +CLEAN_SOURCES += src/usb_moded-control.c CLEAN_SOURCES += src/usb_moded-dbus.c CLEAN_SOURCES += src/usb_moded-devicelock.c CLEAN_SOURCES += src/usb_moded-dsme.c @@ -228,11 +235,13 @@ CLEAN_SOURCES += src/usb_moded-mac.c CLEAN_SOURCES += src/usb_moded-modesetting.c CLEAN_SOURCES += src/usb_moded-modules.c CLEAN_SOURCES += src/usb_moded-network.c +CLEAN_SOURCES += src/usb_moded-sigpipe.c CLEAN_SOURCES += src/usb_moded-ssu.c CLEAN_SOURCES += src/usb_moded-systemd.c CLEAN_SOURCES += src/usb_moded-trigger.c CLEAN_SOURCES += src/usb_moded-udev.c CLEAN_SOURCES += src/usb_moded-util.c +CLEAN_SOURCES += src/usb_moded-worker.c CLEAN_SOURCES += src/usb_moded.c CLEAN_SOURCES += utils/udev-search.c @@ -241,8 +250,10 @@ CLEAN_HEADERS += src/usb_moded-appsync-dbus-private.h CLEAN_HEADERS += src/usb_moded-appsync-dbus.h CLEAN_HEADERS += src/usb_moded-appsync.h CLEAN_HEADERS += src/usb_moded-config-private.h +CLEAN_HEADERS += src/usb_moded-common.h CLEAN_HEADERS += src/usb_moded-config.h CLEAN_HEADERS += src/usb_moded-configfs.h +CLEAN_HEADERS += src/usb_moded-control.h CLEAN_HEADERS += src/usb_moded-dbus-private.h CLEAN_HEADERS += src/usb_moded-dbus.h CLEAN_HEADERS += src/usb_moded-devicelock.h @@ -254,10 +265,12 @@ CLEAN_HEADERS += src/usb_moded-modes.h CLEAN_HEADERS += src/usb_moded-modesetting.h CLEAN_HEADERS += src/usb_moded-modules.h CLEAN_HEADERS += src/usb_moded-network.h +CLEAN_HEADERS += src/usb_moded-sigpipe.h CLEAN_HEADERS += src/usb_moded-ssu.h CLEAN_HEADERS += src/usb_moded-systemd.h CLEAN_HEADERS += src/usb_moded-trigger.h CLEAN_HEADERS += src/usb_moded-udev.h +CLEAN_HEADERS += src/usb_moded-worker.h CLEAN_HEADERS += src/usb_moded.h # Files with whitespace issues diff --git a/src/Makefile.am b/src/Makefile.am index 7023607..451a6ce 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,5 @@ USB_MODED_CFLAGS += -D_GNU_SOURCE +USB_MODED_LIBS += -lpthread sbin_PROGRAMS = usb_moded \ usb_moded_util @@ -24,6 +25,8 @@ usb_moded_SOURCES = \ usb_moded-modules.h \ usb_moded-log.h \ usb_moded-log.c \ + usb_moded-common.c \ + usb_moded-common.h \ usb_moded-config.c \ usb_moded-config.h \ usb_moded-network.c \ @@ -39,8 +42,14 @@ usb_moded_SOURCES = \ usb_moded-modules.c \ usb_moded-configfs.c \ usb_moded-configfs.h \ + usb_moded-worker.h \ + usb_moded-worker.c \ usb_moded-android.h \ - usb_moded-android.c + usb_moded-android.c \ + usb_moded-sigpipe.h \ + usb_moded-sigpipe.c \ + usb_moded-control.h \ + usb_moded-control.c if USE_MER_SSU usb_moded_SOURCES += \ diff --git a/src/usb_moded-common.c b/src/usb_moded-common.c new file mode 100644 index 0000000..e963809 --- /dev/null +++ b/src/usb_moded-common.c @@ -0,0 +1,505 @@ +#include "usb_moded-common.h" + +#include "usb_moded.h" +#include "usb_moded-config-private.h" +#include "usb_moded-dbus-private.h" +#include "usb_moded-log.h" +#include "usb_moded-modes.h" + +/* ========================================================================= * + * Types + * ========================================================================= */ + +/** Mapping usb mode from internal to hardware/broadcast use */ +typedef struct modemapping_t +{ + /** Any valid usb mode */ + const char *internal_mode; + + /** Mode to use for usb configuration, or NULL = internal */ + const char *hardware_mode; + + /** Mode to use for D-Bus broadcast, or NULL = internal */ + const char *external_mode; +} modemapping_t; + +/* ========================================================================= * + * Prototypes + * ========================================================================= */ + +/* -- cable -- */ + +const char *cable_state_repr(cable_state_t state); + +/* -- common -- */ + +const char *common_map_mode_to_hardware (const char *internal_mode); +const char *common_map_mode_to_external (const char *internal_mode); +void common_send_supported_modes_signal (void); +void common_send_available_modes_signal (void); +void common_send_hidden_modes_signal (void); +void common_send_whitelisted_modes_signal(void); +static void common_write_to_sysfs_file (const char *path, const char *text); +void common_acquire_wakelock (const char *wakelock_name); +void common_release_wakelock (const char *wakelock_name); +int common_system_ (const char *file, int line, const char *func, const char *command); +FILE *common_popen_ (const char *file, int line, const char *func, const char *command, const char *type); +void common_usleep_ (const char *file, int line, const char *func, useconds_t usec); +static bool common_mode_in_list (const char *mode, char *const *modes); +int common_valid_mode (const char *mode); +gchar *common_get_mode_list (mode_list_type_t type); + +/* ========================================================================= * + * Functions + * ========================================================================= */ + +/* ------------------------------------------------------------------------- * + * CABLE_STATE + * ------------------------------------------------------------------------- */ + +const char *cable_state_repr(cable_state_t state) +{ + static const char * const lut[CABLE_STATE_NUMOF] = { + [CABLE_STATE_UNKNOWN] = "unknown", + [CABLE_STATE_DISCONNECTED] = "disconnected", + [CABLE_STATE_CHARGER_CONNECTED] = "charger_connected", + [CABLE_STATE_PC_CONNECTED] = "pc_connected", + }; + return lut[state]; +} + +/* ------------------------------------------------------------------------- * + * MODE_MAPPING + * ------------------------------------------------------------------------- */ + +static const modemapping_t common_modemapping[] = +{ + { + .internal_mode = MODE_UNDEFINED, + .hardware_mode = MODE_CHARGING, + .external_mode = 0, + }, + { + .internal_mode = MODE_ASK, + .hardware_mode = MODE_CHARGING, + .external_mode = 0, + }, + { + .internal_mode = MODE_MASS_STORAGE, + .hardware_mode = 0, + .external_mode = 0, + }, + { + .internal_mode = MODE_DEVELOPER, + .hardware_mode = 0, + .external_mode = 0, + }, + { + .internal_mode = MODE_MTP, + .hardware_mode = 0, + .external_mode = 0, + }, + { + .internal_mode = MODE_HOST, + .hardware_mode = 0, + .external_mode = 0, + }, + { + .internal_mode = MODE_CONNECTION_SHARING, + .hardware_mode = 0, + .external_mode = 0, + }, + { + .internal_mode = MODE_DIAG, + .hardware_mode = 0, + .external_mode = 0, + }, + { + .internal_mode = MODE_ADB, + .hardware_mode = 0, + .external_mode = 0, + }, + { + .internal_mode = MODE_PC_SUITE, + .hardware_mode = 0, + .external_mode = 0, + }, + { + .internal_mode = MODE_CHARGING, + .hardware_mode = MODE_CHARGING, + .external_mode = 0, + }, + { + .internal_mode = MODE_CHARGING_FALLBACK, + .hardware_mode = MODE_CHARGING, + .external_mode = MODE_ASK, + }, + { + .internal_mode = MODE_CHARGER, + .hardware_mode = MODE_CHARGING, + .external_mode = 0, + }, + // sentinel + { + .internal_mode = 0, + .hardware_mode = 0, + .external_mode = 0, + } +}; + +const char * +common_map_mode_to_hardware(const char *internal_mode) +{ + const char *hardware_mode = 0; + + for( size_t i = 0; common_modemapping[i].internal_mode; ++i ) { + if( strcmp(common_modemapping[i].internal_mode, internal_mode) ) + continue; + hardware_mode = common_modemapping[i].hardware_mode; + break; + } + return hardware_mode ?: internal_mode; +} + +const char * +common_map_mode_to_external(const char *internal_mode) +{ + const char *external_mode = 0; + + for( size_t i = 0; common_modemapping[i].internal_mode; ++i ) { + if( strcmp(common_modemapping[i].internal_mode, internal_mode) ) + continue; + external_mode = common_modemapping[i].external_mode; + break; + } + return external_mode ?: internal_mode; +} + +/* ------------------------------------------------------------------------- * + * DBUS_NOTIFICATIONS + * ------------------------------------------------------------------------- */ + +/** Send supported modes signal + */ +void common_send_supported_modes_signal(void) +{ + gchar *mode_list = common_get_mode_list(SUPPORTED_MODES_LIST); + umdbus_send_supported_modes_signal(mode_list); + g_free(mode_list); +} + +/** Send available modes signal + */ +void common_send_available_modes_signal(void) +{ + gchar *mode_list = common_get_mode_list(AVAILABLE_MODES_LIST); + umdbus_send_available_modes_signal(mode_list); + g_free(mode_list); +} + +/** Send hidden modes signal + */ +void common_send_hidden_modes_signal(void) +{ + gchar *mode_list = config_get_hidden_modes(); + if(mode_list) { + // TODO: cleared list not signaled? + umdbus_send_hidden_modes_signal(mode_list); + g_free(mode_list); + } +} + +/** Send whitelisted modes signal + */ +void common_send_whitelisted_modes_signal(void) +{ + gchar *mode_list = config_get_mode_whitelist(); + if(mode_list) { + // TODO: cleared list not signaled? + umdbus_send_whitelisted_modes_signal(mode_list); + g_free(mode_list); + } +} + +/* ------------------------------------------------------------------------- * + * SYSFS_IO + * ------------------------------------------------------------------------- */ + +/** Write string to already existing sysfs file + * + * Note: Attempts to write to nonexisting files are silently ignored. + * + * @param path Where to write + * @param text What to write + */ +static void common_write_to_sysfs_file(const char *path, const char *text) +{ + int fd = -1; + + if (!path || !text) + goto EXIT; + + if ((fd = open(path, O_WRONLY)) == -1) { + if (errno != ENOENT) { + log_warning("%s: open for writing failed: %m", path); + } + goto EXIT; + } + + if (write(fd, text, strlen(text)) == -1) { + log_warning("%s: write failed : %m", path); + goto EXIT; + } +EXIT: + if (fd != -1) + close(fd); +} + +/* ------------------------------------------------------------------------- * + * WAKELOCKS + * ------------------------------------------------------------------------- */ + +/** Acquire wakelock via sysfs + * + * Wakelock must be released via common_release_wakelock(). + * + * Automatically terminating wakelock is used, so that we + * do not block suspend indefinately in case usb_moded + * gets stuck or crashes. + * + * Note: The name should be unique within the system. + * + * @param wakelock_name Wake lock to be acquired + */ +void common_acquire_wakelock(const char *wakelock_name) +{ + char buff[256]; + snprintf(buff, sizeof buff, "%s %lld", + wakelock_name, + USB_MODED_SUSPEND_DELAY_MAXIMUM_MS * 1000000LL); + common_write_to_sysfs_file("/sys/power/wake_lock", buff); + +#if VERBOSE_WAKELOCKING + log_debug("common_acquire_wakelock %s", wakelock_name); +#endif +} + +/** Release wakelock via sysfs + * + * @param wakelock_name Wake lock to be released + */ +void common_release_wakelock(const char *wakelock_name) +{ +#if VERBOSE_WAKELOCKING + log_debug("common_release_wakelock %s", wakelock_name); +#endif + + common_write_to_sysfs_file("/sys/power/wake_unlock", wakelock_name); +} + +/* ------------------------------------------------------------------------- * + * BLOCKING_OPERATION + * ------------------------------------------------------------------------- */ + +/** Wrapper to give visibility to blocking system() calls usb-moded is making + */ +int +common_system_(const char *file, int line, const char *func, + const char *command) +{ + log_debug("EXEC %s; from %s:%d: %s()", + command, file, line, func); + + int rc = system(command); + + if( rc != 0 ) + log_warning("EXEC %s; exit code is %d", command, rc); + + return rc; +} + +/** Wrapper to give visibility subprocesses usb-moded is invoking via popen() + */ +FILE * +common_popen_(const char *file, int line, const char *func, + const char *command, const char *type) +{ + log_debug("EXEC %s; from %s:%d: %s()", + command, file, line, func); + + return popen(command, type); +} + +/** Wrapper to give visibility to blocking sleeps usb-moded is making + */ +void +common_usleep_(const char *file, int line, const char *func, + useconds_t usec) +{ + struct timespec ts = { + .tv_sec = (usec / 1000000), + .tv_nsec = (usec % 1000000) * 1000 + }; + + long ms = (ts.tv_nsec + 1000000 - 1) / 1000000; + + if( !ms ) { + log_debug("SLEEP %ld seconds; from %s:%d: %s()", + (long)ts.tv_sec, file, line, func); + } + else if( ts.tv_sec ) { + log_debug("SLEEP %ld.%03ld seconds; from %s:%d: %s()", + (long)ts.tv_sec, ms, file, line, func); + } + else { + log_debug("SLEEP %ld milliseconds; from %s:%d: %s()", + ms, file, line, func); + } + + do { + if( worker_bailing_out() ) { + log_warning("SLEEP %ld milliseconds - ignored; from %s:%d: %s()", + ms, file, line, func); + break; + } + + } while( nanosleep(&ts, &ts) == -1 && errno != EINTR ); +} + +/* ------------------------------------------------------------------------- * + * MISC + * ------------------------------------------------------------------------- */ + +/* check if a mode is in a list */ +static bool common_mode_in_list(const char *mode, char * const *modes) +{ + int i; + + if (!modes) + return false; + + for(i = 0; modes[i] != NULL; i++) + { + if(!strcmp(modes[i], mode)) + return true; + } + return false; +} + +/** check if a given usb_mode exists + * + * @param mode The mode to look for + * @return 0 if mode exists, 1 if it does not exist + * + */ +int common_valid_mode(const char *mode) +{ + int valid = 1; + /* MODE_ASK, MODE_CHARGER and MODE_CHARGING_FALLBACK are not modes that are settable seen their special 'internal' status + * so we only check the modes that are announed outside. Only exception is the built in MODE_CHARGING */ + if(!strcmp(MODE_CHARGING, mode)) + valid = 0; + else + { + char *whitelist; + gchar **whitelist_split = NULL; + + whitelist = config_get_mode_whitelist(); + if (whitelist) + { + whitelist_split = g_strsplit(whitelist, ",", 0); + g_free(whitelist); + } + + /* check dynamic modes */ + if(usbmoded_modelist) + { + GList *iter; + + for( iter = usbmoded_modelist; iter; iter = g_list_next(iter) ) + { + struct mode_list_elem *data = iter->data; + if(!strcmp(mode, data->mode_name)) + { + if (!whitelist_split || common_mode_in_list(data->mode_name, whitelist_split)) + valid = 0; + break; + } + } + + g_strfreev(whitelist_split); + } + } + return valid; + +} + +/** make a list of all available usb modes + * + * @param type The type of list to return. Supported or available. + * @return a comma-separated list of modes (MODE_ASK not included as it is not a real mode) + * + */ +gchar *common_get_mode_list(mode_list_type_t type) +{ + GString *modelist_str; + + modelist_str = g_string_new(NULL); + + if(!usbmoded_diag_mode) + { + /* check dynamic modes */ + if(usbmoded_modelist) + { + GList *iter; + char *hidden_modes_list, *whitelist; + gchar **hidden_mode_split = NULL, **whitelist_split = NULL; + + hidden_modes_list = config_get_hidden_modes(); + if(hidden_modes_list) + { + hidden_mode_split = g_strsplit(hidden_modes_list, ",", 0); + g_free(hidden_modes_list); + } + + if (type == AVAILABLE_MODES_LIST) + { + whitelist = config_get_mode_whitelist(); + if (whitelist) + { + whitelist_split = g_strsplit(whitelist, ",", 0); + g_free(whitelist); + } + } + + for( iter = usbmoded_modelist; iter; iter = g_list_next(iter) ) + { + struct mode_list_elem *data = iter->data; + + /* skip items in the hidden list */ + if (common_mode_in_list(data->mode_name, hidden_mode_split)) + continue; + + /* if there is a whitelist skip items not in the list */ + if (whitelist_split && !common_mode_in_list(data->mode_name, whitelist_split)) + continue; + + modelist_str = g_string_append(modelist_str, data->mode_name); + modelist_str = g_string_append(modelist_str, ", "); + } + + g_strfreev(hidden_mode_split); + g_strfreev(whitelist_split); + } + + /* end with charging mode */ + g_string_append(modelist_str, MODE_CHARGING); + return g_string_free(modelist_str, false); + } + else + { + /* diag mode. there is only one active mode */ + g_string_append(modelist_str, MODE_DIAG); + return g_string_free(modelist_str, false); + } +} diff --git a/src/usb_moded-common.h b/src/usb_moded-common.h new file mode 100644 index 0000000..81c48b8 --- /dev/null +++ b/src/usb_moded-common.h @@ -0,0 +1,61 @@ +#ifndef USB_MODED_COMMON_H_ +# define USB_MODED_COMMON_H_ + +# include + +/* ========================================================================= * + * Types + * ========================================================================= */ + +/** Mode list types + */ +typedef enum mode_list_type_t { + /** All configured modes */ + SUPPORTED_MODES_LIST, + /** Configured modes that can be activated */ + AVAILABLE_MODES_LIST +} mode_list_type_t; + +typedef enum { + CABLE_STATE_UNKNOWN, + CABLE_STATE_DISCONNECTED, + CABLE_STATE_CHARGER_CONNECTED, + CABLE_STATE_PC_CONNECTED, + CABLE_STATE_NUMOF +} cable_state_t; + +/* ========================================================================= * + * Functions + * ========================================================================= */ + +/* -- cable -- */ + +const char *cable_state_repr(cable_state_t state); + +/* -- common -- */ + +const char *common_map_mode_to_hardware (const char *internal_mode); +const char *common_map_mode_to_external (const char *internal_mode); +void common_send_supported_modes_signal (void); +void common_send_available_modes_signal (void); +void common_send_hidden_modes_signal (void); +void common_send_whitelisted_modes_signal(void); +void common_acquire_wakelock (const char *wakelock_name); +void common_release_wakelock (const char *wakelock_name); +int common_system_ (const char *file, int line, const char *func, const char *command); +FILE *common_popen_ (const char *file, int line, const char *func, const char *command, const char *type); +void common_usleep_ (const char *file, int line, const char *func, useconds_t usec); +int common_valid_mode (const char *mode); +gchar *common_get_mode_list (mode_list_type_t type); + +/* ========================================================================= * + * Macros + * ========================================================================= */ + +# define common_system(command) common_system_(__FILE__,__LINE__,__FUNCTION__,(command)) +# define common_popen(command, type) common_popen_(__FILE__,__LINE__,__FUNCTION__,(command),(type)) +# define common_usleep(usec) common_usleep_(__FILE__,__LINE__,__FUNCTION__,(usec)) +# define common_msleep(msec) common_usleep_(__FILE__,__LINE__,__FUNCTION__,(msec)*1000) +# define common_sleep(sec) common_usleep_(__FILE__,__LINE__,__FUNCTION__,(sec)*1000000) + +#endif /* USB_MODED_COMMON_H_ */ diff --git a/src/usb_moded-config-private.h b/src/usb_moded-config-private.h index 6c682f5..89b33d3 100644 --- a/src/usb_moded-config-private.h +++ b/src/usb_moded-config-private.h @@ -36,6 +36,8 @@ # include "usb_moded-config.h" +# include + /* ========================================================================= * * Prototypes * ========================================================================= */ diff --git a/src/usb_moded-config.c b/src/usb_moded-config.c index 4f97338..5a75bf7 100644 --- a/src/usb_moded-config.c +++ b/src/usb_moded-config.c @@ -50,6 +50,9 @@ #include "usb_moded-modes.h" #include "usb_moded-modesetting.h" #include "usb_moded-dbus-private.h" +#include "usb_moded-worker.h" +#include "usb_moded-control.h" +#include "usb_moded-common.h" #ifdef USE_MER_SSU # include "usb_moded-ssu.h" @@ -463,7 +466,7 @@ set_config_result_t config_set_config_setting(const char *entry, const char *key set_config_result_t config_set_mode_setting(const char *mode) { - if (strcmp(mode, MODE_ASK) && usbmoded_valid_mode(mode)) + if (strcmp(mode, MODE_ASK) && common_valid_mode(mode)) return SET_CONFIG_ERROR; return (config_set_config_setting(MODE_SETTING_ENTRY, MODE_SETTING_KEY, mode)); } @@ -540,9 +543,9 @@ set_config_result_t config_set_hide_mode_setting(const char *mode) } if(ret == SET_CONFIG_UPDATED) { - usbmoded_send_hidden_modes_signal(); - usbmoded_send_supported_modes_signal(); - usbmoded_send_available_modes_signal(); + common_send_hidden_modes_signal(); + common_send_supported_modes_signal(); + common_send_available_modes_signal(); } g_free(hidden_modes); @@ -561,9 +564,9 @@ set_config_result_t config_set_unhide_mode_setting(const char *mode) } if(ret == SET_CONFIG_UPDATED) { - usbmoded_send_hidden_modes_signal(); - usbmoded_send_supported_modes_signal(); - usbmoded_send_available_modes_signal(); + common_send_hidden_modes_signal(); + common_send_supported_modes_signal(); + common_send_available_modes_signal(); } g_free(hidden_modes); @@ -580,22 +583,22 @@ set_config_result_t config_set_mode_whitelist(const char *whitelist) const char *current_mode; mode_setting = config_get_mode_setting(); - if (strcmp(mode_setting, MODE_ASK) && usbmoded_valid_mode(mode_setting)) + if (strcmp(mode_setting, MODE_ASK) && common_valid_mode(mode_setting)) config_set_mode_setting(MODE_ASK); g_free(mode_setting); - current_mode = usbmoded_get_usb_mode(); + current_mode = control_get_usb_mode(); if (!strcmp(current_mode, MODE_UNDEFINED)) { /* Disconnected -> do nothing */ } - else if (strcmp(current_mode, MODE_CHARGING_FALLBACK) && strcmp(current_mode, MODE_ASK) && usbmoded_valid_mode(current_mode)) { + else if (strcmp(current_mode, MODE_CHARGING_FALLBACK) && strcmp(current_mode, MODE_ASK) && common_valid_mode(current_mode)) { /* Invalid mode that is not MODE_ASK or MODE_CHARGING_FALLBACK * -> switch to MODE_CHARGING_FALLBACK */ - usbmoded_set_usb_mode(MODE_CHARGING_FALLBACK); + control_set_usb_mode(MODE_CHARGING_FALLBACK); } umdbus_send_whitelisted_modes_signal(whitelist); - usbmoded_send_available_modes_signal(); + common_send_available_modes_signal(); } return ret; @@ -690,7 +693,7 @@ char * config_get_network_setting(const char *config) goto end; /* no interface override specified, let's use the one * from the mode config */ - data = usbmoded_get_usb_mode_data(); + data = worker_get_usb_mode_data(); if(data) { if(data->network_interface) diff --git a/src/usb_moded-configfs.c b/src/usb_moded-configfs.c index b798c73..ef7d563 100644 --- a/src/usb_moded-configfs.c +++ b/src/usb_moded-configfs.c @@ -36,6 +36,7 @@ #include "usb_moded-modesetting.h" #include "usb_moded-config-private.h" #include "usb_moded-mac.h" +#include "usb_moded-common.h" /* ========================================================================= * * Constants @@ -598,7 +599,7 @@ configfs_init_values(void) /* Prep: mtp_mode */ configfs_register_function(FUNCTION_MTP); if( access("/dev/mtp/ep0", F_OK) == -1 ) { - usbmoded_system("/bin/mount -o uid=100000,gid=100000 -t functionfs mtp /dev/mtp"); + common_system("/bin/mount -o uid=100000,gid=100000 -t functionfs mtp /dev/mtp"); } /* Prep: developer_mode */ diff --git a/src/usb_moded-control.c b/src/usb_moded-control.c new file mode 100644 index 0000000..b7c1ef4 --- /dev/null +++ b/src/usb_moded-control.c @@ -0,0 +1,340 @@ +/** + * @file usb_moded-control.c + * + * Copyright (C) 2013-2018 Jolla. All rights reserved. + * + * @author: Philippe De Swert + * @author: Simo Piiroinen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Lesser GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "usb_moded-control.h" + +#include "usb_moded.h" + +#include "usb_moded-config-private.h" +#include "usb_moded-dbus-private.h" +#include "usb_moded-log.h" +#include "usb_moded-modes.h" +#include "usb_moded-worker.h" +#include "usb_moded-common.h" + +/* ========================================================================= * + * Prototypes + * ========================================================================= */ + +/* -- usbmoded -- */ + +void control_rethink_usb_charging_fallback(void); +const char *control_get_external_mode (void); +static void control_set_external_mode (const char *mode); +void control_clear_external_mode (void); +static void control_update_external_mode (void); +const char *control_get_usb_mode (void); +void control_clear_internal_mode (void); +void control_set_usb_mode (const char *mode); +void control_mode_switched (const char *override); +void control_select_usb_mode (void); +void control_set_cable_state (cable_state_t cable_state); +cable_state_t control_get_cable_state (void); +void control_clear_cable_state (void); +bool control_get_connection_state (void); + +/* ========================================================================= * + * Data + * ========================================================================= */ + +/* The external mode; + * + * What was the last mode signaled over D-Bus. + */ +char *control_external_mode = NULL; + +/** The logical mode name + * + * Full set of valid modes can occur here + */ +char *control_internal_mode = NULL; + +/** Connection status + * + * Access only via: + * - control_set_cable_state() + * - control_get_connection_state() + */ +cable_state_t control_cable_state = CABLE_STATE_UNKNOWN; + +/* ========================================================================= * + * Functions + * ========================================================================= */ + +/** Check if we can/should leave charging fallback mode + * + * Called when device lock status, or device status (dsme) + * changes. + */ +void +control_rethink_usb_charging_fallback(void) +{ + /* Cable must be connected to a pc */ + if( control_get_cable_state() != CABLE_STATE_PC_CONNECTED ) + goto EXIT; + + /* Switching can happen only from MODE_UNDEFINED + * or MODE_CHARGING_FALLBACK */ + const char *usb_mode = control_get_usb_mode(); + + if( strcmp(usb_mode, MODE_UNDEFINED) && + strcmp(usb_mode, MODE_CHARGING_FALLBACK) ) + goto EXIT; + + if( !usbmoded_can_export() ) { + log_notice("exporting data not allowed; stay in %s", usb_mode); + goto EXIT; + } + + log_debug("attempt to leave %s", usb_mode); + control_select_usb_mode(); + +EXIT: + return; +} + +const char *control_get_external_mode(void) +{ + return control_external_mode ?: MODE_UNDEFINED; +} + +static void control_set_external_mode(const char *mode) +{ + gchar *previous = control_external_mode; + if( !g_strcmp0(previous, mode) ) + goto EXIT; + + log_debug("external_mode: %s -> %s", + previous, mode); + + control_external_mode = g_strdup(mode); + g_free(previous); + + // DO THE DBUS BROADCAST + + if( !strcmp(control_external_mode, MODE_ASK) ) { + /* send signal, mode will be set when the dialog service calls + * the set_mode method call. */ + umdbus_send_state_signal(USB_CONNECTED_DIALOG_SHOW); + } + + umdbus_send_state_signal(control_external_mode); + +EXIT: + return; +} + +void control_clear_external_mode(void) +{ + g_free(control_external_mode), + control_external_mode = 0; +} + +static void control_update_external_mode(void) +{ + const char *internal_mode = control_get_usb_mode(); + const char *external_mode = common_map_mode_to_external(internal_mode); + + control_set_external_mode(external_mode); +} + +/** get the usb mode + * + * @return the currently set mode + * + */ +const char * control_get_usb_mode(void) +{ + return control_internal_mode; +} + +void control_clear_internal_mode(void) +{ + g_free(control_internal_mode), + control_internal_mode = 0; +} + +/** set the usb mode + * + * @param mode The requested USB mode + */ +void control_set_usb_mode(const char *mode) +{ + gchar *previous = control_internal_mode; + if( !g_strcmp0(previous, mode) ) + goto EXIT; + + log_debug("internal_mode: %s -> %s", + previous, mode); + + control_internal_mode = g_strdup(mode); + g_free(previous); + + /* Invalidate current mode for the duration of mode transition */ + control_set_external_mode(MODE_BUSY); + + /* Propagate down to gadget config */ + worker_request_hardware_mode(control_internal_mode); + +EXIT: + return; +} + +/* Worker thread has finished mode switch + * + * @param mode The activated USB mode + */ +void control_mode_switched(const char *mode) +{ + /* Update state data - without retriggering the worker thread + */ + if( g_strcmp0(control_internal_mode, mode) ) { + log_debug("internal_mode: %s -> %s", + control_internal_mode, mode); + g_free(control_internal_mode), + control_internal_mode = g_strdup(mode); + } + + /* Propagate up to D-Bus */ + control_update_external_mode(); + + return; +} + +/** set the chosen usb state + * + * gauge what mode to enter and then call control_set_usb_mode() + * + */ +void control_select_usb_mode(void) +{ + char *mode_to_set = 0; + + if( usbmoded_rescue_mode ) { + log_debug("Entering rescue mode!\n"); + control_set_usb_mode(MODE_DEVELOPER); + goto EXIT; + } + + if( usbmoded_diag_mode ) { + log_debug("Entering diagnostic mode!\n"); + if( usbmoded_modelist ) { + /* XXX 1st entry is just assumed to be diag mode??? */ + GList *iter = usbmoded_modelist; + struct mode_list_elem *data = iter->data; + control_set_usb_mode(data->mode_name); + } + goto EXIT; + } + + mode_to_set = config_get_mode_setting(); + + /* If there is only one allowed mode, use it without + * going through ask-mode */ + if( !strcmp(MODE_ASK, mode_to_set) ) { + // FIXME free() vs g_free() conflict + gchar *available = common_get_mode_list(AVAILABLE_MODES_LIST); + if( *available && !strchr(available, ',') ) { + free(mode_to_set), mode_to_set = available, available = 0; + } + g_free(available); + } + + if( mode_to_set && usbmoded_can_export() ) { + control_set_usb_mode(mode_to_set); + } + else { + /* config is corrupted or we do not have a mode configured, fallback to charging + * We also fall back here in case the device is locked and we do not + * export the system contents. Or if we are in acting dead mode. + */ + control_set_usb_mode(MODE_CHARGING_FALLBACK); + } +EXIT: + free(mode_to_set); +} + +/** set the usb connection status + * + * @param cable_state CABLE_STATE_DISCONNECTED, ... + */ +void control_set_cable_state(cable_state_t cable_state) +{ + cable_state_t prev = control_cable_state; + control_cable_state = cable_state; + + if( control_cable_state == prev ) + goto EXIT; + + log_debug("control_cable_state: %s -> %s", + cable_state_repr(prev), + cable_state_repr(control_cable_state)); + + switch( control_cable_state ) { + default: + case CABLE_STATE_DISCONNECTED: + control_set_usb_mode(MODE_UNDEFINED); + break; + case CABLE_STATE_CHARGER_CONNECTED: + control_set_usb_mode(MODE_CHARGER); + break; + case CABLE_STATE_PC_CONNECTED: + control_select_usb_mode(); + break; + } + +EXIT: + return; +} + +/** get the usb connection status + * + * @return CABLE_STATE_DISCONNECTED, ... + */ +cable_state_t control_get_cable_state(void) +{ + return control_cable_state; +} + +void control_clear_cable_state(void) +{ + control_cable_state = CABLE_STATE_UNKNOWN; +} + +/** Get if the cable (pc or charger) is connected or not + * + * @ return true if connected, false if disconnected + */ +bool control_get_connection_state(void) +{ + bool connected = false; + switch( control_get_cable_state() ) { + case CABLE_STATE_CHARGER_CONNECTED: + case CABLE_STATE_PC_CONNECTED: + connected = true; + break; + default: + break; + } + return connected; +} diff --git a/src/usb_moded-control.h b/src/usb_moded-control.h new file mode 100644 index 0000000..0ad22e3 --- /dev/null +++ b/src/usb_moded-control.h @@ -0,0 +1,45 @@ +/** + * @file usb_moded-control.h + * + * Copyright (C) 2013-2018 Jolla. All rights reserved. + * + * @author: Philippe De Swert + * @author: Simo Piiroinen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Lesser GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef USB_MODED_CONTROL_H_ +# define USB_MODED_CONTROL_H_ + +# include "usb_moded.h" +# include "usb_moded-common.h" + +/* -- usbmoded -- */ + +void control_rethink_usb_charging_fallback(void); +const char *control_get_external_mode (void); +void control_clear_external_mode (void); +const char *control_get_usb_mode (void); +void control_clear_internal_mode (void); +void control_set_usb_mode (const char *mode); +void control_mode_switched (const char *override); +void control_select_usb_mode (void); +void control_set_cable_state (cable_state_t cable_state); +cable_state_t control_get_cable_state (void); +void control_clear_cable_state (void); +bool control_get_connection_state (void); + +#endif /* USB_MODED_CONTROL_H_ */ diff --git a/src/usb_moded-dbus.c b/src/usb_moded-dbus.c index 6a06107..1ef14a3 100644 --- a/src/usb_moded-dbus.c +++ b/src/usb_moded-dbus.c @@ -46,6 +46,8 @@ #include "usb_moded-config-private.h" #include "usb_moded-network.h" #include "usb_moded-log.h" +#include "usb_moded-control.h" +#include "usb_moded-common.h" /* ========================================================================= * * Constants @@ -254,7 +256,7 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB if(!strcmp(member, USB_MODE_STATE_REQUEST)) { - const char *mode = usbmoded_get_external_mode(); + const char *mode = control_get_external_mode(); /* To the outside we want to keep CHARGING and CHARGING_FALLBACK the same */ if(!strcmp(MODE_CHARGING_FALLBACK, mode)) @@ -272,17 +274,17 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB else { /* check if usb is connected, since it makes no sense to change mode if it isn't */ - if( usbmoded_get_cable_state() != CABLE_STATE_PC_CONNECTED ) { + if( control_get_cable_state() != CABLE_STATE_PC_CONNECTED ) { log_warning("USB not connected, not changing mode!\n"); goto error_reply; } /* check if the mode exists */ - if(usbmoded_valid_mode(use)) + if(common_valid_mode(use)) goto error_reply; /* do not change mode if the mode requested is the one already set */ - if(strcmp(use, usbmoded_get_usb_mode()) != 0) + if(strcmp(use, control_get_usb_mode()) != 0) { - usbmoded_set_usb_mode(use); + control_set_usb_mode(use); } if((reply = dbus_message_new_method_return(msg))) dbus_message_append_args (reply, DBUS_TYPE_STRING, &use, DBUS_TYPE_INVALID); @@ -434,7 +436,7 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB } else if(!strcmp(member, USB_MODE_LIST)) { - gchar *mode_list = usbmoded_get_mode_list(SUPPORTED_MODES_LIST); + gchar *mode_list = common_get_mode_list(SUPPORTED_MODES_LIST); if((reply = dbus_message_new_method_return(msg))) dbus_message_append_args (reply, DBUS_TYPE_STRING, (const char *) &mode_list, DBUS_TYPE_INVALID); @@ -442,7 +444,7 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB } else if(!strcmp(member, USB_MODE_AVAILABLE_MODES_GET)) { - gchar *mode_list = usbmoded_get_mode_list(AVAILABLE_MODES_LIST); + gchar *mode_list = common_get_mode_list(AVAILABLE_MODES_LIST); if((reply = dbus_message_new_method_return(msg))) dbus_message_append_args (reply, DBUS_TYPE_STRING, (const char *) &mode_list, DBUS_TYPE_INVALID); diff --git a/src/usb_moded-devicelock.c b/src/usb_moded-devicelock.c index bc90a9f..c9be76e 100644 --- a/src/usb_moded-devicelock.c +++ b/src/usb_moded-devicelock.c @@ -42,6 +42,7 @@ #include "usb_moded.h" #include "usb_moded-modes.h" #include "usb_moded-dbus-private.h" +#include "usb_moded-control.h" /* ========================================================================= * * Types @@ -147,7 +148,7 @@ static void devicelock_state_changed(devicelock_state_t state) devicelock_state_repr(state)); device_lock_state = state; - usbmoded_rethink_usb_charging_fallback(); + control_rethink_usb_charging_fallback(); EXIT: return; diff --git a/src/usb_moded-dsme.c b/src/usb_moded-dsme.c index 2cef64a..d9547fe 100644 --- a/src/usb_moded-dsme.c +++ b/src/usb_moded-dsme.c @@ -31,6 +31,7 @@ #include "usb_moded-modesetting.h" #include "usb_moded-dbus-private.h" #include "usb_moded-log.h" +#include "usb_moded-control.h" #include #include @@ -200,7 +201,7 @@ dsme_state_update(dsme_state_t state) dsme_user_state = user_state; log_debug("in user state: %s", dsme_user_state ? "true" : "false"); - usbmoded_rethink_usb_charging_fallback(); + control_rethink_usb_charging_fallback(); } /* Handle entry to / exit from SHUTDOWN / REBOOT state */ diff --git a/src/usb_moded-modesetting.c b/src/usb_moded-modesetting.c index 8d96b0d..4e85da0 100644 --- a/src/usb_moded-modesetting.c +++ b/src/usb_moded-modesetting.c @@ -49,6 +49,8 @@ #include "usb_moded-network.h" #include "usb_moded-android.h" #include "usb_moded-configfs.h" +#include "usb_moded-worker.h" +#include "usb_moded-common.h" /* ========================================================================= * * Prototypes @@ -301,21 +303,21 @@ bool modesetting_is_mounted(const char *mountpoint) { char cmd[256]; snprintf(cmd, sizeof cmd, "/bin/mountpoint -q '%s'", mountpoint); - return usbmoded_system(cmd) == 0; + return common_system(cmd) == 0; } bool modesetting_mount(const char *mountpoint) { char cmd[256]; snprintf(cmd, sizeof cmd, "/bin/mount '%s'", mountpoint); - return usbmoded_system(cmd) == 0; + return common_system(cmd) == 0; } bool modesetting_unmount(const char *mountpoint) { char cmd[256]; snprintf(cmd, sizeof cmd, "/bin/umount '%s'", mountpoint); - return usbmoded_system(cmd) == 0; + return common_system(cmd) == 0; } gchar *modesetting_mountdev(const char *mountpoint) @@ -466,7 +468,7 @@ static bool modesetting_enter_mass_storage_mode(struct mode_list_elem *data) log_warning("failed to unmount %s - wait a bit", mountpnt); modesetting_report_mass_storage_blocker(mountpnt, 1); - usbmoded_sleep(1); + common_sleep(1); } } @@ -510,12 +512,12 @@ static bool modesetting_enter_mass_storage_mode(struct mode_list_elem *data) modules_unload_module(MODULE_MASS_STORAGE); snprintf(tmp, sizeof tmp, "modprobe %s luns=%zd \n", MODULE_MASS_STORAGE, count); log_debug("usb-load command = %s \n", tmp); - if( usbmoded_system(tmp) != 0 ) + if( common_system(tmp) != 0 ) goto EXIT; } /* activate mounts after sleeping 1s to be sure enumeration happened and autoplay will work in windows*/ - usbmoded_sleep(1); + common_sleep(1); for( size_t i = 0 ; i < count; ++i ) { const gchar *mountdev = info[i].si_mountdevice; @@ -653,7 +655,7 @@ static void modesetting_report_mass_storage_blocker(const char *mountpoint, int lsof_command = g_strconcat("lsof ", mountpoint, NULL); - if( (stream = usbmoded_popen(lsof_command, "r")) ) + if( (stream = common_popen(lsof_command, "r")) ) { char *text = 0; size_t size = 0; @@ -693,7 +695,7 @@ bool modesetting_enter_dynamic_mode(void) * Is a dynamic mode? * - - - - - - - - - - - - - - - - - - - */ - if( !(data = usbmoded_get_usb_mode_data()) ) { + if( !(data = worker_get_usb_mode_data()) ) { log_debug("No dynamic mode data to setup"); goto EXIT; } @@ -776,7 +778,7 @@ bool modesetting_enter_dynamic_mode(void) char command[256]; g_snprintf(command, 256, "ifdown %s ; ifup %s", data->network_interface, data->network_interface); - usbmoded_system(command); + common_system(command); #else network_down(data); network = network_up(data); @@ -807,7 +809,7 @@ bool modesetting_enter_dynamic_mode(void) { 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); + common_msleep(350); appsync_activate_sync_post(data->mode_name); } @@ -836,7 +838,7 @@ void modesetting_leave_dynamic_mode(void) struct mode_list_elem *data; - data = usbmoded_get_usb_mode_data(); + data = worker_get_usb_mode_data(); /* - - - - - - - - - - - - - - - - - - - * * Do not leave timers behind diff --git a/src/usb_moded-network.c b/src/usb_moded-network.c index 14d7a4b..76af80a 100644 --- a/src/usb_moded-network.c +++ b/src/usb_moded-network.c @@ -43,6 +43,9 @@ #include "usb_moded-config-private.h" #include "usb_moded-log.h" #include "usb_moded-modesetting.h" +#include "usb_moded-worker.h" +#include "usb_moded-control.h" +#include "usb_moded-common.h" #if CONNMAN || OFONO # include @@ -202,13 +205,13 @@ static int network_set_usb_ip_forward(struct mode_list_elem *data, struct ipforw } write_to_file("/proc/sys/net/ipv4/ip_forward", "1"); snprintf(command, 128, "/sbin/iptables -t nat -A POSTROUTING -o %s -j MASQUERADE", nat_interface); - usbmoded_system(command); + common_system(command); snprintf(command, 128, "/sbin/iptables -A FORWARD -i %s -o %s -m state --state RELATED,ESTABLISHED -j ACCEPT", nat_interface, interface); - usbmoded_system(command); + common_system(command); snprintf(command, 128, "/sbin/iptables -A FORWARD -i %s -o %s -j ACCEPT", interface, nat_interface); - usbmoded_system(command); + common_system(command); free(interface); free(nat_interface); @@ -225,7 +228,7 @@ static void network_clean_usb_ip_forward(void) connman_reset_state(); #endif write_to_file("/proc/sys/net/ipv4/ip_forward", "0"); - usbmoded_system("/sbin/iptables -F FORWARD"); + common_system("/sbin/iptables -F FORWARD"); } #ifdef OFONO @@ -537,7 +540,7 @@ gboolean connman_set_tethering(const char *path, gboolean on) { if (i>0) { - usbmoded_msleep(200); + common_msleep(200); } if (connman_try_set_tethering(connection, path, on)) { @@ -754,7 +757,7 @@ static int connman_set_cellular_online(DBusConnection *dbus_conn_connman, const /* we don't care for the reply, which is empty anyway if all goes well */ ret = !dbus_connection_send(dbus_conn_connman, msg, NULL); /* sleep for the connection to come up */ - usbmoded_sleep(5); + common_sleep(5); /* make sure the message is sent before cleaning up and closing the connection */ dbus_connection_flush(dbus_conn_connman); dbus_message_unref(msg); @@ -825,10 +828,10 @@ static int connman_wifi_power_control(DBusConnection *dbus_conn_connman, int on) /* /net/connman/technology/wifi net.connman.Technology.SetProperty string:Powered variant:boolean:false */ if(wifistatus && !on) - usbmoded_system("/bin/dbus-send --print-reply --type=method_call --system --dest=net.connman /net/connman/technology/wifi net.connman.Technology.SetProperty string:Powered variant:boolean:false"); + common_system("/bin/dbus-send --print-reply --type=method_call --system --dest=net.connman /net/connman/technology/wifi net.connman.Technology.SetProperty string:Powered variant:boolean:false"); if(wifistatus && on) /* turn on wifi after tethering is over and wifi was on before */ - usbmoded_system("/bin/dbus-send --print-reply --type=method_call --system --dest=net.connman /net/connman/technology/wifi net.connman.Technology.SetProperty string:Powered variant:boolean:true"); + common_system("/bin/dbus-send --print-reply --type=method_call --system --dest=net.connman /net/connman/technology/wifi net.connman.Technology.SetProperty string:Powered variant:boolean:true"); return(0); } @@ -1015,11 +1018,11 @@ int network_up(struct mode_list_elem *data) const char *service = NULL; /* make sure connman will recognize the gadget interface NEEDED? */ - //usbmoded_system("/bin/dbus-send --print-reply --type=method_call --system --dest=net.connman /net/connman/technology/gadget net.connman.Technology.SetProperty string:Powered variant:boolean:true"); - //usbmoded_system("/sbin/ifconfig rndis0 up"); + //common_system("/bin/dbus-send --print-reply --type=method_call --system --dest=net.connman /net/connman/technology/gadget net.connman.Technology.SetProperty string:Powered variant:boolean:true"); + //common_system("/sbin/ifconfig rndis0 up"); log_debug("waiting for connman to pick up interface\n"); - usbmoded_sleep(1); + common_sleep(1); dbus_error_init(&error); if( (dbus_conn_connman = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == 0 ) @@ -1132,25 +1135,25 @@ int network_up(struct mode_list_elem *data) if(!strcmp(ip, "dhcp")) { sprintf(command, "dhclient -d %s\n", interface); - ret = usbmoded_system(command); + ret = common_system(command); if(ret != 0) { sprintf(command, "udhcpc -i %s\n", interface); - usbmoded_system(command); + common_system(command); } } else { sprintf(command, "ifconfig %s %s %s\n", interface, ip, netmask); - usbmoded_system(command); + common_system(command); } /* TODO: Check first if there is a gateway set */ if(gateway) { sprintf(command, "route add default gw %s\n", gateway); - usbmoded_system(command); + common_system(command); } free(interface); @@ -1222,7 +1225,7 @@ int network_down(struct mode_list_elem *data) return(0); sprintf(command, "ifconfig %s down\n", interface); - usbmoded_system(command); + common_system(command); /* dhcp client shutdown happens on disconnect automatically */ if(data->nat) @@ -1240,8 +1243,8 @@ int network_down(struct mode_list_elem *data) */ int network_update(void) { - if( usbmoded_get_cable_state() == CABLE_STATE_PC_CONNECTED ) { - struct mode_list_elem *data = usbmoded_get_usb_mode_data(); + if( control_get_cable_state() == CABLE_STATE_PC_CONNECTED ) { + struct mode_list_elem *data = worker_get_usb_mode_data(); if( data && data->network ) { network_down(data); network_up(data); diff --git a/src/usb_moded-sigpipe.c b/src/usb_moded-sigpipe.c new file mode 100644 index 0000000..8800d6d --- /dev/null +++ b/src/usb_moded-sigpipe.c @@ -0,0 +1,209 @@ +/** + * @file usb_moded-sigpipe.c + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * Copyright (C) 2012-2018 Jolla. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Lesser GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "usb_moded-sigpipe.h" + +#include "usb_moded.h" +#include "usb_moded-log.h" + +#include +#include +#include +#include + +#include + +/* ========================================================================= * + * Prototypes + * ========================================================================= */ + +/* -- sigpipe -- */ + +static gboolean sigpipe_read_signal_cb(GIOChannel *channel, GIOCondition condition, gpointer data); +static void sigpipe_trap_signal_cb(int sig); +static bool sigpipe_crate_pipe (void); +static void sigpipe_trap_signals (void); +bool sigpipe_init (void); + +/* ========================================================================= * + * Data + * ========================================================================= */ + +/** Pipe fd for transferring signals to mainloop context */ +static int sigpipe_fd = -1; + +/** Glib io watch callback for reading signals from signal pipe + * + * @param channel glib io channel + * @param condition wakeup reason + * @param data user data (unused) + * + * @return TRUE to keep the iowatch, or FALSE to disable it + */ +static gboolean +sigpipe_read_signal_cb(GIOChannel *channel, + GIOCondition condition, + gpointer data) +{ + gboolean keep_watch = FALSE; + + int fd, rc, sig; + + (void)data; + + /* Should never happen, but we must disable the io watch + * if the pipe fd still goes into unexpected state ... */ + if( condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL) ) + goto EXIT; + + if( (fd = g_io_channel_unix_get_fd(channel)) == -1 ) + goto EXIT; + + /* If the actual read fails, terminate with core dump */ + rc = TEMP_FAILURE_RETRY(read(fd, &sig, sizeof sig)); + if( rc != (int)sizeof sig ) + abort(); + + /* handle the signal */ + usbmoded_handle_signal(sig); + + keep_watch = TRUE; + +EXIT: + if( !keep_watch ) + log_crit("disabled signal handler io watch\n"); + + return keep_watch; +} + +/** Async signal handler for writing signals to signal pipe + * + * @param sig the signal number to pass to mainloop via pipe + */ +static void +sigpipe_trap_signal_cb(int sig) +{ + /* NOTE: This function *MUST* be kept async-signal-safe! */ + + static volatile int exit_tries = 0; + + int rc; + + /* Restore signal handler */ + signal(sig, sigpipe_trap_signal_cb); + + switch( sig ) + { + case SIGINT: + case SIGQUIT: + case SIGTERM: + /* If we receive multiple signals that should have + * caused the process to exit, assume that mainloop + * is stuck and terminate with core dump. */ + if( ++exit_tries >= 2 ) + abort(); + break; + + default: + break; + } + + /* Transfer the signal to mainloop via pipe ... */ + rc = TEMP_FAILURE_RETRY(write(sigpipe_fd, &sig, sizeof sig)); + + /* ... or terminate with core dump in case of failures */ + if( rc != (int)sizeof sig ) + abort(); +} + +/** Create a pipe and io watch for handling signal from glib mainloop + * + * @return true on success, or false in case of errors + */ +static bool +sigpipe_crate_pipe(void) +{ + bool res = false; + GIOChannel *chn = 0; + int pfd[2] = { -1, -1 }; + + if( pipe2(pfd, O_CLOEXEC) == -1 ) + goto EXIT; + + if( (chn = g_io_channel_unix_new(pfd[0])) == 0 ) + goto EXIT; + + if( !g_io_add_watch(chn, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + sigpipe_read_signal_cb, 0) ) + goto EXIT; + + g_io_channel_set_close_on_unref(chn, true), pfd[0] = -1; + sigpipe_fd = pfd[1], pfd[1] = -1; + + res = true; + +EXIT: + if( chn ) g_io_channel_unref(chn); + if( pfd[0] != -1 ) close(pfd[0]); + if( pfd[1] != -1 ) close(pfd[1]); + + return res; +} + +/** Install async signal handlers + */ +static void +sigpipe_trap_signals(void) +{ + static const int sig[] = + { + SIGINT, + SIGQUIT, + SIGTERM, + SIGHUP, + -1 + }; + + for( size_t i = 0; sig[i] != -1; ++i ) + { + signal(sig[i], sigpipe_trap_signal_cb); + } +} + +/** Initialize signal trapping + * + * @return true on success, or false in case of errors + */ +bool +sigpipe_init(void) +{ + bool success = false; + + if( !sigpipe_crate_pipe() ) + goto EXIT; + + sigpipe_trap_signals(); + + success = true; + +EXIT: + return success; +} diff --git a/src/usb_moded-sigpipe.h b/src/usb_moded-sigpipe.h new file mode 100644 index 0000000..e16488b --- /dev/null +++ b/src/usb_moded-sigpipe.h @@ -0,0 +1,31 @@ +/** + * @file usb_moded-sigpipe.c + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * Copyright (C) 2012-2018 Jolla. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Lesser GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef USB_MODED_SIGPIPE_H_ +# define USB_MODED_SIGPIPE_H_ + +#include + +/* -- sigpipe -- */ + +bool sigpipe_init(void); + +#endif /* USB_MODED_SIGPIPE_H_ */ diff --git a/src/usb_moded-trigger.c b/src/usb_moded-trigger.c index 830b590..05acfae 100644 --- a/src/usb_moded-trigger.c +++ b/src/usb_moded-trigger.c @@ -42,6 +42,7 @@ #include "usb_moded-udev.h" #include "usb_moded-modesetting.h" #include "usb_moded-trigger.h" +#include "usb_moded-control.h" #if defined MEEGOLOCK # include "usb_moded-devicelock.h" #endif /* MEEGOLOCK */ @@ -257,7 +258,7 @@ static void trigger_parse_udev_properties(struct udev_device *dev) goto EXIT; } - usbmoded_set_usb_mode(trigger_mode); + control_set_usb_mode(trigger_mode); EXIT: free(trigger_value); diff --git a/src/usb_moded-udev.c b/src/usb_moded-udev.c index dbe8f39..fe88622 100644 --- a/src/usb_moded-udev.c +++ b/src/usb_moded-udev.c @@ -45,6 +45,8 @@ #include "usb_moded.h" #include "usb_moded-modes.h" #include "usb_moded-dbus-private.h" +#include "usb_moded-control.h" +#include "usb_moded-common.h" /* ========================================================================= * * Prototypes @@ -215,7 +217,7 @@ static void umudev_cable_state_changed(void) } /* Then act on usb mode */ - usbmoded_set_cable_state(umudev_cable_state_active); + control_set_cable_state(umudev_cable_state_active); } static void umudev_cable_state_from_udev(cable_state_t curr) @@ -286,8 +288,8 @@ static gboolean umudev_io_input_cb(GIOChannel *iochannel, GIOCondition cond, gpo gboolean continue_watching = TRUE; - /* No code paths are allowed to bypass the usbmoded_release_wakelock() call below */ - usbmoded_acquire_wakelock(USB_MODED_WAKELOCK_PROCESS_INPUT); + /* No code paths are allowed to bypass the common_release_wakelock() call below */ + common_acquire_wakelock(USB_MODED_WAKELOCK_PROCESS_INPUT); if( cond & G_IO_IN ) { @@ -325,7 +327,7 @@ static gboolean umudev_io_input_cb(GIOChannel *iochannel, GIOCondition cond, gpo log_crit("udev io watch disabled"); } - usbmoded_release_wakelock(USB_MODED_WAKELOCK_PROCESS_INPUT); + common_release_wakelock(USB_MODED_WAKELOCK_PROCESS_INPUT); return continue_watching; } @@ -365,7 +367,7 @@ static void umudev_parse_properties(struct udev_device *dev, bool initial) /* Transition period = Connection status derived from udev * events disagrees with usb-moded side bookkeeping. */ - if( connected != usbmoded_get_connection_state() ) { + if( connected != control_get_connection_state() ) { /* Enable udev property diagnostic logging */ warnings = true; /* Block suspend briefly */ diff --git a/src/usb_moded-worker.c b/src/usb_moded-worker.c new file mode 100644 index 0000000..849e5c3 --- /dev/null +++ b/src/usb_moded-worker.c @@ -0,0 +1,830 @@ +/** + * @file usb_moded-worker.c + * + * Copyright (C) 2013-2018 Jolla. All rights reserved. + * + * @author: Philippe De Swert + * @author: Simo Piiroinen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Lesser GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "usb_moded-worker.h" + +#include "usb_moded.h" +#include "usb_moded-android.h" +#include "usb_moded-configfs.h" +#include "usb_moded-log.h" +#include "usb_moded-modes.h" +#include "usb_moded-modesetting.h" +#include "usb_moded-modules.h" +#include "usb_moded-control.h" +#include "usb_moded-common.h" + +#include + +#include +#include + +/* ========================================================================= * + * Prototypes + * ========================================================================= */ + +/* -- worker -- */ + +static bool worker_thread_p (void); +bool worker_bailing_out (void); +static bool worker_mode_is_mtp_mode (const char *mode); +static bool worker_is_mtpd_running (void); +static bool worker_stop_mtpd (void); +static bool worker_start_mtpd (void); +static bool worker_switch_to_charging (void); +const char *worker_get_usb_module (void); +bool worker_set_usb_module (const char *module); +void worker_clear_usb_module (void); +struct mode_list_elem *worker_get_usb_mode_data (void); +void worker_set_usb_mode_data (struct mode_list_elem *data); +static const char *worker_get_activated_mode_locked(void); +static bool worker_set_activated_mode_locked(const char *mode); +static const char *worker_get_requested_mode_locked(void); +static bool worker_set_requested_mode_locked(const char *mode); +void worker_request_hardware_mode (const char *mode); +void worker_clear_hardware_mode (void); +static void worker_execute (void); +void worker_switch_to_mode (const char *mode); +static guint worker_add_iowatch (int fd, bool close_on_unref, GIOCondition cnd, GIOFunc io_cb, gpointer aptr); +static void *worker_thread_cb (void *aptr); +static gboolean worker_notify_cb (GIOChannel *chn, GIOCondition cnd, gpointer data); +static bool worker_start_thread (void); +static void worker_stop_thread (void); +static void worker_delete_eventfd (void); +static bool worker_create_eventfd (void); +bool worker_init (void); +void worker_quit (void); +void worker_wakeup (void); +static void worker_notify (void); + +/* ========================================================================= * + * Data + * ========================================================================= */ + +static pthread_t worker_thread_id = 0; + +static pthread_mutex_t worker_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int worker_bailout = 0; + +#define WORKER_LOCKED_ENTER do {\ + if( pthread_mutex_lock(&worker_mutex) != 0 ) { \ + log_crit("WORKER LOCK FAILED");\ + _exit(EXIT_FAILURE);\ + }\ +}while(0) + +#define WORKER_LOCKED_LEAVE do {\ + if( pthread_mutex_unlock(&worker_mutex) != 0 ) { \ + log_crit("WORKER UNLOCK FAILED");\ + _exit(EXIT_FAILURE);\ + }\ +}while(0) + +/* ========================================================================= * + * Functions + * ========================================================================= */ + +static bool +worker_thread_p(void) +{ + return worker_thread_id && worker_thread_id == pthread_self(); +} + +bool +worker_bailing_out(void) +{ + // ref: see common_usleep_() + return worker_thread_p() && worker_bailout > 0; +} + +/* ------------------------------------------------------------------------- * + * MTP_DAEMON + * ------------------------------------------------------------------------- */ + +static bool worker_mode_is_mtp_mode(const char *mode) +{ + return mode && !strcmp(mode, "mtp_mode"); +} + +static bool worker_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 +worker_stop_mtpd(void) +{ + bool ack = !worker_is_mtpd_running(); + + if( ack ) { + log_debug("mtp daemon is not running"); + goto EXIT; + } + + int rc = common_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 = !worker_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"); + common_msleep(2000); + } +EXIT: + + return ack; +} + +static bool +worker_start_mtpd(void) +{ + bool ack = worker_is_mtpd_running(); + + if( ack ) { + log_debug("mtp daemon is not running"); + goto EXIT; + } + + int rc = common_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 = worker_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"); + common_msleep(2000); + } +EXIT: + + return ack; +} + +static bool worker_switch_to_charging(void) +{ + bool ack = true; + + if( android_set_charging_mode() ) + goto SUCCESS; + + if( configfs_set_charging_mode() ) + goto SUCCESS; + + if( modules_in_use() ) { + if( worker_set_usb_module(MODULE_MASS_STORAGE) ) + goto SUCCESS; + worker_set_usb_module(MODULE_NONE); + } + + log_err("switch to charging mode failed"); + + ack = false; +SUCCESS: + return ack; +} + +/* ------------------------------------------------------------------------- * + * KERNEL_MODULE + * ------------------------------------------------------------------------- */ + +/** The module name for the specific mode */ +char *usbmoded_module = NULL; + +/** get the supposedly loaded module + * + * @return The name of the loaded module + * + */ +const char * worker_get_usb_module(void) +{ + return usbmoded_module ?: MODULE_NONE; +} + +/** set the loaded module + * + * @param module The module name for the requested mode + * + */ +bool worker_set_usb_module(const char *module) +{ + bool ack = false; + + if( !module ) + module = MODULE_NONE; + + const char *current = worker_get_usb_module(); + + log_debug("current module: %s -> %s", current, module); + + if( !g_strcmp0(current, module) ) + goto SUCCESS; + + if( modules_unload_module(current) != 0 ) + goto EXIT; + + free(usbmoded_module), usbmoded_module = 0; + + if( modules_load_module(module) != 0 ) + goto EXIT; + + if( g_strcmp0(module, MODULE_NONE) ) + usbmoded_module = strdup(module); + +SUCCESS: + ack = true; +EXIT: + return ack; +} + +void worker_clear_usb_module(void) +{ + free(usbmoded_module), usbmoded_module = 0; +} + +/* ------------------------------------------------------------------------- * + * MODE_DATA + * ------------------------------------------------------------------------- */ + +/** Contains the mode data */ +struct mode_list_elem *usbmoded_data = NULL; + +/** get the usb mode data + * + * @return a pointer to the usb mode data + * + */ +struct mode_list_elem * worker_get_usb_mode_data(void) +{ + return usbmoded_data; +} + +/** set the mode_list_elem data + * + * @param data mode_list_element pointer + * + */ +void worker_set_usb_mode_data(struct mode_list_elem *data) +{ + usbmoded_data = data; +} + +/* ------------------------------------------------------------------------- * + * HARDWARE_MODE + * ------------------------------------------------------------------------- */ + +/* The hardware mode name + * + * How the usb hardware has been configured. + * + * For example internal_mode=MODE_ASK gets + * mapped to hardware_mode=MODE_CHARGING */ +gchar *worker_requested_mode = NULL; + +gchar *worker_activated_mode = NULL; + +static const char * +worker_get_activated_mode_locked(void) +{ + return worker_activated_mode ?: MODE_UNDEFINED; +} + +static bool +worker_set_activated_mode_locked(const char *mode) +{ + bool changed = false; + const char *prev = worker_get_activated_mode_locked(); + + if( !g_strcmp0(prev, mode) ) + goto EXIT; + + log_debug("activated_mode: %s -> %s", prev, mode); + g_free(worker_activated_mode), + worker_activated_mode = g_strdup(mode); + changed = true; + +EXIT: + return changed; +} + +static const char * +worker_get_requested_mode_locked(void) +{ + return worker_requested_mode ?: MODE_UNDEFINED; +} + +static bool +worker_set_requested_mode_locked(const char *mode) +{ + bool changed = false; + const char *prev = worker_get_requested_mode_locked(); + + if( !g_strcmp0(prev, mode) ) + goto EXIT; + + log_debug("requested_mode: %s -> %s", prev, mode); + g_free(worker_requested_mode), + worker_requested_mode = g_strdup(mode); + changed = true; + +EXIT: + return changed; +} + +void worker_request_hardware_mode(const char *mode) +{ + WORKER_LOCKED_ENTER; + + if( !worker_set_requested_mode_locked(mode) ) + goto EXIT; + + worker_wakeup(); + +EXIT: + WORKER_LOCKED_LEAVE; + return; +} + +void worker_clear_hardware_mode(void) +{ + WORKER_LOCKED_ENTER; + g_free(worker_requested_mode), worker_requested_mode = 0; + WORKER_LOCKED_LEAVE; +} + +static void +worker_execute(void) +{ + WORKER_LOCKED_ENTER; + + const char *activated = worker_get_activated_mode_locked(); + const char *requested = worker_get_requested_mode_locked(); + const char *activate = common_map_mode_to_hardware(requested); + + log_debug("activated = %s", activated); + log_debug("requested = %s", requested); + log_debug("activate = %s", activate); + + bool changed = g_strcmp0(activated, activate) != 0; + + WORKER_LOCKED_LEAVE; + + if( changed ) + worker_switch_to_mode(activate); + else + worker_notify(); + + return; +} + +/* ------------------------------------------------------------------------- * + * MODE_SWITCH + * ------------------------------------------------------------------------- */ + +void +worker_switch_to_mode(const char *mode) +{ + const char *override = 0; + + /* set return to 1 to be sure to error out if no matching mode is found either */ + + log_debug("Cleaning up previous mode"); + + if( !worker_mode_is_mtp_mode(mode) ) + worker_stop_mtpd(); + + if( worker_get_usb_mode_data() ) { + modesetting_leave_dynamic_mode(); + worker_set_usb_mode_data(NULL); + } + + log_debug("Setting %s\n", mode); + + /* Mode mapping should mean we only see MODE_CHARGING here, but just + * in case redirect fixed charging related things to charging ... */ + + if( !strcmp(mode, MODE_CHARGING) || + !strcmp(mode, MODE_CHARGING_FALLBACK) || + !strcmp(mode, MODE_CHARGER) || + !strcmp(mode, MODE_UNDEFINED) || + !strcmp(mode, MODE_ASK)) { + goto CHARGE; + } + + if( !usbmoded_can_export() ) { + log_warning("Policy does not allow mode: %s", mode); + goto FAILED; + } + + /* go through all the dynamic modes if the modelist exists*/ + for( GList *iter = usbmoded_modelist; iter; iter = g_list_next(iter) ) + { + struct mode_list_elem *data = iter->data; + if( strcmp(mode, data->mode_name) ) + continue; + + log_debug("Matching mode %s found.\n", mode); + + /* set data before calling any of the dynamic mode functions + * as they will use the worker_get_usb_mode_data function */ + worker_set_usb_mode_data(data); + + if( worker_mode_is_mtp_mode(mode) ) { + if( !worker_start_mtpd() ) + goto FAILED; + } + + if( !worker_set_usb_module(data->mode_module) ) + goto FAILED; + + if( !modesetting_enter_dynamic_mode() ) + goto FAILED; + + goto SUCCESS; + } + + log_warning("Matching mode %s was not found.", mode); + +FAILED: + override = MODE_CHARGING; + log_warning("mode setting failed, try %s", override); + worker_set_usb_mode_data(NULL); + +CHARGE: + if( worker_switch_to_charging() ) + goto SUCCESS; + + log_crit("failed to activate charging, all bets are off"); + + /* FIXME: double check this error path */ + + /* If we get here then usb_module loading failed, + * no mode matched, and charging setup failed too. + */ + + override = MODE_UNDEFINED; + log_warning("mode setting failed, fallback to %s", override); + worker_set_usb_module(MODULE_NONE); + +SUCCESS: + + WORKER_LOCKED_ENTER; + if( override ) { + worker_set_activated_mode_locked(override); + worker_set_requested_mode_locked(override); + } + else { + worker_set_activated_mode_locked(mode); + } + WORKER_LOCKED_LEAVE; + + worker_notify(); + + return; +} + +/* ------------------------------------------------------------------------- * + * WORKER_THREAD + * ------------------------------------------------------------------------- */ + +/** eventfd descriptor for waking up worker thread after adding new jobs */ +static int worker_req_evfd = -1; + +/** eventfd descriptor for waking up main thread after executing jobs */ +static int worker_rsp_evfd = -1; + +/** I/O watch identifier for worker_rsp_evfd */ +static guint worker_rsp_wid = 0; + +static guint +worker_add_iowatch(int fd, bool close_on_unref, + GIOCondition cnd, GIOFunc io_cb, gpointer aptr) +{ + guint wid = 0; + GIOChannel *chn = 0; + + if( !(chn = g_io_channel_unix_new(fd)) ) + goto cleanup; + + g_io_channel_set_close_on_unref(chn, close_on_unref); + + cnd |= G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + if( !(wid = g_io_add_watch(chn, cnd, io_cb, aptr)) ) + goto cleanup; + +cleanup: + if( chn != 0 ) g_io_channel_unref(chn); + + return wid; + +} + +static void *worker_thread_cb(void *aptr) +{ + /* Async cancellation, but disabled */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); + + /* Leave INT/TERM signal processing up to the main thread */ + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss, SIGINT); + sigaddset(&ss, SIGTERM); + pthread_sigmask(SIG_BLOCK, &ss, 0); + + /* Loop until explicitly canceled */ + for( ;; ) { + /* Async cancellation point at wait() */ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); + uint64_t cnt = 0; + int rc = read(worker_req_evfd, &cnt, sizeof cnt); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0); + + if( rc == -1 ) { + if( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK ) + continue; + log_err("read: %m"); + goto EXIT; + } + + if( rc != sizeof cnt ) + continue; + + if( cnt > 0 ) { + --worker_bailout; + log_debug("worker_bailout -> %d", worker_bailout); + worker_execute(); + } + + } +EXIT: + return 0; +} + +static gboolean +worker_notify_cb(GIOChannel *chn, GIOCondition cnd, gpointer data) +{ + (void)data; + + gboolean keep_going = FALSE; + + if( !worker_rsp_wid ) + goto cleanup_nak; + + int fd = g_io_channel_unix_get_fd(chn); + + if( fd < 0 ) + goto cleanup_nak; + + if( cnd & ~G_IO_IN ) + goto cleanup_nak; + + if( !(cnd & G_IO_IN) ) + goto cleanup_ack; + + uint64_t cnt = 0; + + int rc = read(fd, &cnt, sizeof cnt); + + if( rc == 0 ) { + log_err("unexpected eof"); + goto cleanup_nak; + } + + if( rc == -1 ) { + if( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK ) + goto cleanup_ack; + + log_err("read error: %m"); + goto cleanup_nak; + } + + if( rc != sizeof cnt ) + goto cleanup_nak; + + { + WORKER_LOCKED_ENTER; + const char *mode = worker_get_requested_mode_locked(); + gchar *work = g_strdup(mode); + WORKER_LOCKED_LEAVE; + + control_mode_switched(work); + g_free(work); + } + +cleanup_ack: + keep_going = TRUE; + +cleanup_nak: + + if( !keep_going ) { + worker_rsp_wid = 0; + log_crit("worker notifications disabled"); + } + + return keep_going; +} + +static bool +worker_start_thread(void) +{ + bool ack = false; + int err = pthread_create(&worker_thread_id, 0, worker_thread_cb, 0); + if( err ) { + worker_thread_id = 0; + log_err("failed to start worker thread"); + } + else { + ack = true; + log_debug("worker thread started"); + } + + return ack; +} + +static void +worker_stop_thread(void) +{ + if( !worker_thread_id ) + goto EXIT; + + log_debug("stopping worker thread"); + int err = pthread_cancel(worker_thread_id); + if( err ) { + log_err("failed to cancel worker thread"); + } + else { + log_debug("waiting for worker thread to exit ..."); + void *ret = 0; + struct timespec tmo = { 0, 0}; + clock_gettime(CLOCK_REALTIME, &tmo); + tmo.tv_sec += 3; + err = pthread_timedjoin_np(worker_thread_id, &ret, &tmo); + if( err ) { + log_err("worker thread did not exit"); + } + else { + log_debug("worker thread terminated"); + worker_thread_id = 0; + } + } + + if( worker_thread_id ) { + /* Orderly exit is not safe, just die */ + _exit(EXIT_FAILURE); + } + +EXIT: + return; +} + +static void +worker_delete_eventfd(void) +{ + if( worker_req_evfd != -1 ) + close(worker_req_evfd), worker_req_evfd = -1; + + if( worker_rsp_wid ) + g_source_remove(worker_rsp_wid), worker_rsp_wid = 0; + + if( worker_rsp_evfd != -1 ) + close(worker_rsp_evfd), worker_req_evfd = -1; +} + +static bool +worker_create_eventfd(void) +{ + bool ack = false; + + /* Setup notify pipeline */ + + if( (worker_rsp_evfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) == -1 ) + goto EXIT; + + worker_rsp_wid = worker_add_iowatch(worker_rsp_evfd, false, G_IO_IN, + worker_notify_cb, 0); + if( !worker_rsp_wid ) + goto EXIT; + + /* Setup request pipeline */ + + if( (worker_req_evfd = eventfd(0, EFD_CLOEXEC)) == -1 ) + goto EXIT; + + ack = true; + +EXIT: + + return ack; +} + +bool +worker_init(void) +{ + bool ack = false; + + if( !worker_create_eventfd() ) + goto EXIT; + + if( !worker_start_thread() ) + goto EXIT; + + ack = true; + +EXIT: + if( !ack ) + worker_quit(); + + return ack; +} + +void +worker_quit(void) +{ + worker_stop_thread(); + worker_delete_eventfd(); +} + +void +worker_wakeup(void) +{ + ++worker_bailout; + log_debug("worker_bailout -> %d", worker_bailout); + + uint64_t cnt = 1; + if( write(worker_req_evfd, &cnt, sizeof cnt) == -1 ) { + log_err("failed to signal requested: %m"); + } +} + +static void +worker_notify(void) +{ + uint64_t cnt = 1; + if( write(worker_rsp_evfd, &cnt, sizeof cnt) == -1 ) { + log_err("failed to signal handled: %m"); + } +} diff --git a/src/usb_moded-worker.h b/src/usb_moded-worker.h new file mode 100644 index 0000000..4de2864 --- /dev/null +++ b/src/usb_moded-worker.h @@ -0,0 +1,52 @@ +/** + * @file usb_moded-worker.h + * + * Copyright (C) 2013-2018 Jolla. All rights reserved. + * + * @author: Philippe De Swert + * @author: Simo Piiroinen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Lesser GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef USB_MODED_WORKER_H_ +# define USB_MODED_WORKER_H_ + +# include + +/* ========================================================================= * + * Constants + * ========================================================================= */ + +/* ========================================================================= * + * Prototypes + * ========================================================================= */ + +/* -- worker -- */ + +bool worker_bailing_out (void); +const char *worker_get_usb_module (void); +bool worker_set_usb_module (const char *module); +void worker_clear_usb_module (void); +struct mode_list_elem *worker_get_usb_mode_data (void); +void worker_set_usb_mode_data (struct mode_list_elem *data); +void worker_request_hardware_mode(const char *mode); +void worker_clear_hardware_mode (void); +void worker_switch_to_mode (const char *mode); +bool worker_init (void); +void worker_quit (void); +void worker_wakeup (void); + +#endif /* USB_MODED_WORKER_H_ */ diff --git a/src/usb_moded.c b/src/usb_moded.c index 52fdc4e..190edf9 100644 --- a/src/usb_moded.c +++ b/src/usb_moded.c @@ -65,6 +65,10 @@ #include "usb_moded-android.h" #include "usb_moded-configfs.h" #include "usb_moded-systemd.h" +#include "usb_moded-worker.h" +#include "usb_moded-control.h" +#include "usb_moded-sigpipe.h" +#include "usb_moded-common.h" #ifdef MEEGOLOCK # include "usb_moded-dsme.h" @@ -100,810 +104,59 @@ #define CABLE_CONNECTION_DELAY_MAXIMUM 4000 -/* ========================================================================= * - * Types - * ========================================================================= */ - -/** A struct containing all the usb_moded info needed - */ -typedef struct usb_mode -{ - /** Connection status - * - * Access only via: - * - usbmoded_set_cable_state() - * - usbmoded_get_connection_state() - */ - cable_state_t cable_state; - - /** Mount status, true for mounted -UNUSED atm- */ - bool mounted; - - /** The logical mode name - * - * Full set of valid modes can occur here - */ - char *internal_mode; - - /* The hardware mode name - * - * How the usb hardware has been configured. - * - * For example internal_mode=MODE_ASK gets - * mapped to hardware_mode=MODE_CHARGING */ - char *hardware_mode; - - /* The external mode; - * - * What was the last mode signaled over D-Bus. - */ - char *external_mode; - - /**< The module name for the specific mode */ - char *module; - - /**< Contains the mode data */ - struct mode_list_elem *data; -} usb_mode; - -/** Mapping usb mode from internal to hardware/broadcast use */ -typedef struct modemapping_t -{ - /** Any valid usb mode */ - const char *internal_mode; - - /** Mode to use for usb configuration, or NULL = internal */ - const char *hardware_mode; - - /** Mode to use for D-Bus broadcast, or NULL = internal */ - const char *external_mode; -} modemapping_t; - /* ========================================================================= * * Prototypes * ========================================================================= */ -/* -- cable -- */ -const char *cable_state_repr(cable_state_t state); - /* -- usbmoded -- */ -static const char *usbmoded_map_mode_to_hardware(const char *internal_mode); -static const char *usbmoded_map_mode_to_external(const char *internal_mode); - -// ---------------------------------------------------------------- -void usbmoded_rethink_usb_charging_fallback(void); - -static bool usbmoded_switch_to_charging (void); -static void usbmoded_switch_to_mode (const char *mode); -static void usbmoded_mode_switched (const char *override); - -const char *usbmoded_get_hardware_mode (void); -static void usbmoded_update_hardware_mode (void); - -const char *usbmoded_get_external_mode (void); -static void usbmoded_set_external_mode (const char *mode); -static void usbmoded_update_external_mode (void); - -const char *usbmoded_get_usb_mode (void); -void usbmoded_set_usb_mode (const char *internal_mode); -void usbmoded_select_usb_mode (void); - -void usbmoded_set_cable_state (cable_state_t cable_state); -cable_state_t usbmoded_get_cable_state (void); -bool usbmoded_get_connection_state (void); - -// ---------------------------------------------------------------- -// internal movements - -static void usbmoded_set_cable_connection_delay (int delay_ms); - -static bool usbmoded_mode_in_list (const char *mode, char *const *modes); -int usbmoded_valid_mode (const char *mode); -gchar *usbmoded_get_mode_list (mode_list_type_t type); - -const char *usbmoded_get_usb_module (void); -bool usbmoded_set_usb_module (const char *module); - -struct mode_list_elem *usbmoded_get_usb_mode_data (void); -void usbmoded_set_usb_mode_data (struct mode_list_elem *data); - -void usbmoded_send_supported_modes_signal (void); -void usbmoded_send_available_modes_signal (void); -void usbmoded_send_hidden_modes_signal (void); -void usbmoded_send_whitelisted_modes_signal(void); - -static void usbmoded_write_to_sysfs_file (const char *path, const char *text); -void usbmoded_acquire_wakelock (const char *wakelock_name); -void usbmoded_release_wakelock (const char *wakelock_name); - -static gboolean usbmoded_allow_suspend_timer_cb (gpointer aptr); -void usbmoded_allow_suspend (void); -void usbmoded_delay_suspend (void); - -bool usbmoded_can_export (void); -bool usbmoded_init_done_p (void); -void usbmoded_set_init_done (bool reached); -void usbmoded_probe_init_done (void); - -void usbmoded_exit_mainloop (int exitcode); -static void usbmoded_handle_signal (int signum); - -static void usbmoded_init (void); -static void usbmoded_cleanup (void); - -int usbmoded_system_ (const char *file, int line, const char *func, const char *command); -FILE *usbmoded_popen_ (const char *file, int line, const char *func, const char *command, const char *type); -void usbmoded_usleep_ (const char *file, int line, const char *func, useconds_t usec); - -static void usbmoded_usage (void); - -/* -- sigpipe -- */ - -static gboolean sigpipe_read_signal_cb (GIOChannel *channel, GIOCondition condition, gpointer data); -static void sigpipe_trap_signal_cb (int sig); -static bool sigpipe_crate_pipe (void); -static void sigpipe_trap_signals (void); -static bool sigpipe_init (void); - -/* ========================================================================= * - * Data - * ========================================================================= */ - -static int usb_moded_exitcode = EXIT_FAILURE; -static GMainLoop *usb_moded_mainloop = NULL; - -bool usbmoded_rescue_mode = false; -static bool diag_mode = false; -static bool hw_fallback = false; -#ifdef SYSTEMD -static bool systemd_notify = false; -#endif - -/** Currently allowed cable detection delay - */ -int usbmoded_cable_connection_delay = CABLE_CONNECTION_DELAY_DEFAULT; - -static struct usb_mode current_mode = { - .cable_state = CABLE_STATE_UNKNOWN, - .mounted = false, - .internal_mode = NULL, - .hardware_mode = NULL, - .external_mode = NULL, - .module = NULL, - .data = NULL, -}; - -static GList *modelist = 0; - -/** Path to init-done flag file */ -static const char init_done_flagfile[] = "/run/systemd/boot-status/init-done"; - -/** cached init-done-reached state */ -static bool init_done_reached = false; - -/** Flag for: USB_MODED_WAKELOCK_STATE_CHANGE has been acquired */ -static bool blocking_suspend = false; - -/** Timer for releasing USB_MODED_WAKELOCK_STATE_CHANGE */ -static guint allow_suspend_timer_id = 0; - -/** Pipe fd for transferring signals to mainloop context */ -static int sigpipe_fd = -1; - -static const modemapping_t modemapping[] = -{ - { - .internal_mode = MODE_UNDEFINED, - .hardware_mode = MODE_CHARGING, - .external_mode = 0, - }, - { - .internal_mode = MODE_ASK, - .hardware_mode = MODE_CHARGING, - .external_mode = 0, - }, - { - .internal_mode = MODE_MASS_STORAGE, - .hardware_mode = 0, - .external_mode = 0, - }, - { - .internal_mode = MODE_DEVELOPER, - .hardware_mode = 0, - .external_mode = 0, - }, - { - .internal_mode = MODE_MTP, - .hardware_mode = 0, - .external_mode = 0, - }, - { - .internal_mode = MODE_HOST, - .hardware_mode = 0, - .external_mode = 0, - }, - { - .internal_mode = MODE_CONNECTION_SHARING, - .hardware_mode = 0, - .external_mode = 0, - }, - { - .internal_mode = MODE_DIAG, - .hardware_mode = 0, - .external_mode = 0, - }, - { - .internal_mode = MODE_ADB, - .hardware_mode = 0, - .external_mode = 0, - }, - { - .internal_mode = MODE_PC_SUITE, - .hardware_mode = 0, - .external_mode = 0, - }, - { - .internal_mode = MODE_CHARGING, - .hardware_mode = MODE_CHARGING, - .external_mode = 0, - }, - { - .internal_mode = MODE_CHARGING_FALLBACK, - .hardware_mode = MODE_CHARGING, - .external_mode = MODE_ASK, - }, - { - .internal_mode = MODE_CHARGER, - .hardware_mode = MODE_CHARGING, - .external_mode = 0, - }, - // sentinel - { - .internal_mode = 0, - .hardware_mode = 0, - .external_mode = 0, - } -}; +static void usbmoded_set_cable_connection_delay(int delay_ms); +static gboolean usbmoded_allow_suspend_timer_cb (gpointer aptr); +void usbmoded_allow_suspend (void); +void usbmoded_delay_suspend (void); +bool usbmoded_can_export (void); +bool usbmoded_init_done_p (void); +void usbmoded_set_init_done (bool reached); +void usbmoded_probe_init_done (void); +void usbmoded_exit_mainloop (int exitcode); +void usbmoded_handle_signal (int signum); +static void usbmoded_init (void); +static void usbmoded_cleanup (void); +static void usbmoded_usage (void); /* ========================================================================= * - * 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] = { - [CABLE_STATE_UNKNOWN] = "unknown", - [CABLE_STATE_DISCONNECTED] = "disconnected", - [CABLE_STATE_CHARGER_CONNECTED] = "charger_connected", - [CABLE_STATE_PC_CONNECTED] = "pc_connected", - }; - return lut[state]; -} - -static const char * -usbmoded_map_mode_to_hardware(const char *internal_mode) -{ - const char *hardware_mode = 0; - - for( size_t i = 0; modemapping[i].internal_mode; ++i ) { - if( strcmp(modemapping[i].internal_mode, internal_mode) ) - continue; - hardware_mode = modemapping[i].hardware_mode; - break; - } - return hardware_mode ?: internal_mode; -} - -static const char * -usbmoded_map_mode_to_external(const char *internal_mode) -{ - const char *external_mode = 0; - - for( size_t i = 0; modemapping[i].internal_mode; ++i ) { - if( strcmp(modemapping[i].internal_mode, internal_mode) ) - continue; - external_mode = modemapping[i].external_mode; - break; - } - return external_mode ?: internal_mode; -} - -/** Check if we can/should leave charging fallback mode - * - * Called when device lock status, or device status (dsme) - * changes. - */ -void -usbmoded_rethink_usb_charging_fallback(void) -{ - /* Cable must be connected to a pc */ - if( usbmoded_get_cable_state() != CABLE_STATE_PC_CONNECTED ) - goto EXIT; - - /* Switching can happen only from MODE_UNDEFINED - * or MODE_CHARGING_FALLBACK */ - const char *usb_mode = usbmoded_get_usb_mode(); - - if( strcmp(usb_mode, MODE_UNDEFINED) && - strcmp(usb_mode, MODE_CHARGING_FALLBACK) ) - goto EXIT; - - if( !usbmoded_can_export() ) { - log_notice("exporting data not allowed; stay in %s", usb_mode); - goto EXIT; - } - - log_debug("attempt to leave %s", usb_mode); - usbmoded_select_usb_mode(); - -EXIT: - return; -} - -static bool usbmoded_switch_to_charging(void) -{ - bool ack = true; - - if( android_set_charging_mode() ) - goto SUCCESS; - - if( configfs_set_charging_mode() ) - goto SUCCESS; - - if( modules_in_use() ) { - if( usbmoded_set_usb_module(MODULE_MASS_STORAGE) ) - goto SUCCESS; - usbmoded_set_usb_module(MODULE_NONE); - } - - log_err("switch to charging mode failed"); - - ack = false; -SUCCESS: - return ack; -} - -static void usbmoded_switch_to_mode(const char *mode) -{ - const char *override = 0; - - /* set return to 1 to be sure to error out if no matching mode is found either */ - - 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); - } - - log_debug("Setting %s\n", mode); - - /* Mode mapping should mean we only see MODE_CHARGING here, but just - * in case redirect fixed charging related things to charging ... */ - - if( !strcmp(mode, MODE_CHARGING) || - !strcmp(mode, MODE_CHARGING_FALLBACK) || - !strcmp(mode, MODE_CHARGER) || - !strcmp(mode, MODE_UNDEFINED) || - !strcmp(mode, MODE_ASK)) { - goto CHARGE; - } - - if( !usbmoded_can_export() ) { - log_warning("Policy does not allow mode: %s", mode); - goto FAILED; - } - - /* go through all the dynamic modes if the modelist exists*/ - for( GList *iter = modelist; iter; iter = g_list_next(iter) ) - { - struct mode_list_elem *data = iter->data; - if( strcmp(mode, data->mode_name) ) - continue; - - log_debug("Matching mode %s found.\n", mode); - - /* set data before calling any of the dynamic mode functions - * as they will use the usbmoded_get_usb_mode_data function */ - usbmoded_set_usb_mode_data(data); - - if( usbmoded_mode_is_mtp_mode(mode) ) { - if( !usbmoded_start_mtpd() ) - goto FAILED; - } - - if( !usbmoded_set_usb_module(data->mode_module) ) - goto FAILED; - - if( !modesetting_enter_dynamic_mode() ) - goto FAILED; - - goto SUCCESS; - } - - log_warning("Matching mode %s was not found.", mode); - -FAILED: - override = MODE_CHARGING; - log_warning("mode setting failed, try %s", override); - usbmoded_set_usb_mode_data(NULL); - -CHARGE: - if( usbmoded_switch_to_charging() ) - goto SUCCESS; - - log_crit("failed to activate charging, all bets are off"); - - /* FIXME: double check this error path */ - - /* If we get here then usb_module loading failed, - * no mode matched, and charging setup failed too. - */ - - override = MODE_UNDEFINED; - log_warning("mode setting failed, fallback to %s", override); - usbmoded_set_usb_module(MODULE_NONE); - -SUCCESS: - /* TODO: WORKER -> MAIN THREAD BARRIER */ - usbmoded_mode_switched(override); - - return; -} - -const char *usbmoded_get_hardware_mode(void) -{ - return current_mode.hardware_mode ?: MODE_UNDEFINED; -} - -static void usbmoded_update_hardware_mode(void) -{ - const char *internal_mode = usbmoded_get_usb_mode(); - const char *hardware_mode = usbmoded_map_mode_to_hardware(internal_mode); - - gchar *previous = current_mode.hardware_mode; - if( !g_strcmp0(previous, hardware_mode) ) { - usbmoded_mode_switched(0); - goto EXIT; - } - - log_debug("hardware_mode: %s -> %s", - previous, hardware_mode); - - current_mode.hardware_mode = g_strdup(hardware_mode); - g_free(previous); - - // DO THE MODESWITCH - - /* TODO: MAIN THREAD -> WORKERBARRIER */ - usbmoded_switch_to_mode(current_mode.hardware_mode); - -EXIT: - return; -} - -const char *usbmoded_get_external_mode(void) -{ - return current_mode.external_mode ?: MODE_UNDEFINED; -} - -static void usbmoded_set_external_mode(const char *mode) -{ - gchar *previous = current_mode.external_mode; - if( !g_strcmp0(previous, mode) ) - goto EXIT; - - log_debug("external_mode: %s -> %s", - previous, mode); - - current_mode.external_mode = g_strdup(mode); - g_free(previous); - - // DO THE DBUS BROADCAST - - if( !strcmp(current_mode.external_mode, MODE_ASK) ) { - /* send signal, mode will be set when the dialog service calls - * the set_mode method call. */ - umdbus_send_state_signal(USB_CONNECTED_DIALOG_SHOW); - } - - umdbus_send_state_signal(current_mode.external_mode); - -EXIT: - return; -} - -static void usbmoded_update_external_mode(void) -{ - const char *internal_mode = usbmoded_get_usb_mode(); - const char *external_mode = usbmoded_map_mode_to_external(internal_mode); - - usbmoded_set_external_mode(external_mode); -} - -/** get the usb mode - * - * @return the currently set mode - * - */ -const char * usbmoded_get_usb_mode(void) -{ - return current_mode.internal_mode; -} - -/** set the usb mode - * - * @param mode The requested USB mode - */ -void usbmoded_set_usb_mode(const char *mode) -{ - gchar *previous = current_mode.internal_mode; - if( !g_strcmp0(previous, mode) ) - goto EXIT; - - log_debug("internal_mode: %s -> %s", - previous, mode); - - current_mode.internal_mode = g_strdup(mode); - g_free(previous); - - /* Invalidate current mode for the duration of mode transition */ - usbmoded_set_external_mode(MODE_BUSY); - - // PROPAGATE DOWN TO GADGET CONFIG - usbmoded_update_hardware_mode(); - -EXIT: - return; -} - -static void usbmoded_mode_switched(const char *override) -{ - if( override ) { - /* Requested usb mode could not be activated at - * gadget control level. Update state info, but - * do not retrigger lower levels - */ - - log_debug("hardware_mode: %s -> %s (OVERRIDE)", - current_mode.hardware_mode, - override); - - g_free(current_mode.hardware_mode), - current_mode.hardware_mode = g_strdup(override); - - log_debug("internal_mode: %s -> %s (OVERRIDE)", - current_mode.internal_mode, - override); - - g_free(current_mode.internal_mode), - current_mode.internal_mode = g_strdup(override); - } - - // PROPAGATE UP DBUS - usbmoded_update_external_mode(); - - return; -} - -/** set the chosen usb state - * - * gauge what mode to enter and then call usbmoded_set_usb_mode() - * - */ -void usbmoded_select_usb_mode(void) -{ - char *mode_to_set = 0; - - if( usbmoded_rescue_mode ) { - log_debug("Entering rescue mode!\n"); - usbmoded_set_usb_mode(MODE_DEVELOPER); - goto EXIT; - } - - if( diag_mode ) { - log_debug("Entering diagnostic mode!\n"); - if( modelist ) { - /* XXX 1st entry is just assumed to be diag mode??? */ - GList *iter = modelist; - struct mode_list_elem *data = iter->data; - usbmoded_set_usb_mode(data->mode_name); - } - goto EXIT; - } - - mode_to_set = config_get_mode_setting(); - - /* If there is only one allowed mode, use it without - * going through ask-mode */ - if( !strcmp(MODE_ASK, mode_to_set) ) { - // FIXME free() vs g_free() conflict - gchar *available = usbmoded_get_mode_list(AVAILABLE_MODES_LIST); - if( *available && !strchr(available, ',') ) { - free(mode_to_set), mode_to_set = available, available = 0; - } - g_free(available); - } + * Data + * ========================================================================= */ - if( mode_to_set && usbmoded_can_export() ) { - usbmoded_set_usb_mode(mode_to_set); - } - else { - /* config is corrupted or we do not have a mode configured, fallback to charging - * We also fall back here in case the device is locked and we do not - * export the system contents. Or if we are in acting dead mode. - */ - usbmoded_set_usb_mode(MODE_CHARGING_FALLBACK); - } -EXIT: - free(mode_to_set); -} +GList *usbmoded_modelist = 0; +static int usbmoded_exitcode = EXIT_FAILURE; +static GMainLoop *usbmoded_mainloop = NULL; +bool usbmoded_rescue_mode = false; +bool usbmoded_diag_mode = false; +static bool usbmoded_hw_fallback = false; +#ifdef SYSTEMD +static bool usbmoded_systemd_notify = false; +#endif -/** set the usb connection status - * - * @param cable_state CABLE_STATE_DISCONNECTED, ... +/** Currently allowed cable detection delay */ -void usbmoded_set_cable_state(cable_state_t cable_state) -{ - cable_state_t prev = current_mode.cable_state; - current_mode.cable_state = cable_state; +int usbmoded_cable_connection_delay = CABLE_CONNECTION_DELAY_DEFAULT; - if( current_mode.cable_state == prev ) - goto EXIT; +/** Path to init-done flag file */ +static const char usbmoded_init_done_flagfile[] = "/run/systemd/boot-status/init-done"; - log_debug("current_mode.cable_state: %s -> %s", - cable_state_repr(prev), - cable_state_repr(current_mode.cable_state)); - - switch( current_mode.cable_state ) { - default: - case CABLE_STATE_DISCONNECTED: - usbmoded_set_usb_mode(MODE_UNDEFINED); - break; - case CABLE_STATE_CHARGER_CONNECTED: - usbmoded_set_usb_mode(MODE_CHARGER); - break; - case CABLE_STATE_PC_CONNECTED: - usbmoded_select_usb_mode(); - break; - } +/** cached init-done-reached state */ +static bool usbmoded_init_done_reached = false; -EXIT: - return; -} +/** Flag for: USB_MODED_WAKELOCK_STATE_CHANGE has been acquired */ +static bool usbmoded_blocking_suspend = false; -/** get the usb connection status - * - * @return CABLE_STATE_DISCONNECTED, ... - */ -cable_state_t usbmoded_get_cable_state(void) -{ - return current_mode.cable_state; -} +/** Timer for releasing USB_MODED_WAKELOCK_STATE_CHANGE */ +static guint usbmoded_allow_suspend_timer_id = 0; -/** Get if the cable (pc or charger) is connected or not - * - * @ return true if connected, false if disconnected - */ -bool usbmoded_get_connection_state(void) -{ - bool connected = false; - switch( usbmoded_get_cable_state() ) { - case CABLE_STATE_CHARGER_CONNECTED: - case CABLE_STATE_PC_CONNECTED: - connected = true; - break; - default: - break; - } - return connected; -} +/* ========================================================================= * + * Functions + * ========================================================================= */ /** Helper for setting allowed cable detection delay * @@ -920,316 +173,6 @@ static void usbmoded_set_cable_connection_delay(int delay_ms) } } -/* check if a mode is in a list */ -static bool usbmoded_mode_in_list(const char *mode, char * const *modes) -{ - int i; - - if (!modes) - return false; - - for(i = 0; modes[i] != NULL; i++) - { - if(!strcmp(modes[i], mode)) - return true; - } - return false; -} - -/** check if a given usb_mode exists - * - * @param mode The mode to look for - * @return 0 if mode exists, 1 if it does not exist - * - */ -int usbmoded_valid_mode(const char *mode) -{ - int valid = 1; - /* MODE_ASK, MODE_CHARGER and MODE_CHARGING_FALLBACK are not modes that are settable seen their special 'internal' status - * so we only check the modes that are announed outside. Only exception is the built in MODE_CHARGING */ - if(!strcmp(MODE_CHARGING, mode)) - valid = 0; - else - { - char *whitelist; - gchar **whitelist_split = NULL; - - whitelist = config_get_mode_whitelist(); - if (whitelist) - { - whitelist_split = g_strsplit(whitelist, ",", 0); - g_free(whitelist); - } - - /* check dynamic modes */ - if(modelist) - { - GList *iter; - - for( iter = modelist; iter; iter = g_list_next(iter) ) - { - struct mode_list_elem *data = iter->data; - if(!strcmp(mode, data->mode_name)) - { - if (!whitelist_split || usbmoded_mode_in_list(data->mode_name, whitelist_split)) - valid = 0; - break; - } - } - - g_strfreev(whitelist_split); - } - } - return valid; - -} - -/** make a list of all available usb modes - * - * @param type The type of list to return. Supported or available. - * @return a comma-separated list of modes (MODE_ASK not included as it is not a real mode) - * - */ -gchar *usbmoded_get_mode_list(mode_list_type_t type) -{ - GString *modelist_str; - - modelist_str = g_string_new(NULL); - - if(!diag_mode) - { - /* check dynamic modes */ - if(modelist) - { - GList *iter; - char *hidden_modes_list, *whitelist; - gchar **hidden_mode_split = NULL, **whitelist_split = NULL; - - hidden_modes_list = config_get_hidden_modes(); - if(hidden_modes_list) - { - hidden_mode_split = g_strsplit(hidden_modes_list, ",", 0); - g_free(hidden_modes_list); - } - - if (type == AVAILABLE_MODES_LIST) - { - whitelist = config_get_mode_whitelist(); - if (whitelist) - { - whitelist_split = g_strsplit(whitelist, ",", 0); - g_free(whitelist); - } - } - - for( iter = modelist; iter; iter = g_list_next(iter) ) - { - struct mode_list_elem *data = iter->data; - - /* skip items in the hidden list */ - if (usbmoded_mode_in_list(data->mode_name, hidden_mode_split)) - continue; - - /* if there is a whitelist skip items not in the list */ - if (whitelist_split && !usbmoded_mode_in_list(data->mode_name, whitelist_split)) - continue; - - modelist_str = g_string_append(modelist_str, data->mode_name); - modelist_str = g_string_append(modelist_str, ", "); - } - - g_strfreev(hidden_mode_split); - g_strfreev(whitelist_split); - } - - /* end with charging mode */ - g_string_append(modelist_str, MODE_CHARGING); - return g_string_free(modelist_str, false); - } - else - { - /* diag mode. there is only one active mode */ - g_string_append(modelist_str, MODE_DIAG); - return g_string_free(modelist_str, false); - } -} - -/** get the supposedly loaded module - * - * @return The name of the loaded module - * - */ -const char * usbmoded_get_usb_module(void) -{ - return current_mode.module ?: MODULE_NONE; -} - -/** set the loaded module - * - * @param module The module name for the requested mode - * - */ -bool usbmoded_set_usb_module(const char *module) -{ - bool ack = false; - - if( !module ) - module = MODULE_NONE; - - const char *current = usbmoded_get_usb_module(); - - log_debug("current module: %s -> %s", current, module); - - if( !g_strcmp0(current, module) ) - goto SUCCESS; - - if( modules_unload_module(current) != 0 ) - goto EXIT; - - free(current_mode.module), current_mode.module = 0; - - if( modules_load_module(module) != 0 ) - goto EXIT; - - if( g_strcmp0(module, MODULE_NONE) ) - current_mode.module = strdup(module); - -SUCCESS: - ack = true; -EXIT: - return ack; -} - -/** get the usb mode data - * - * @return a pointer to the usb mode data - * - */ -struct mode_list_elem * usbmoded_get_usb_mode_data(void) -{ - return current_mode.data; -} - -/** set the mode_list_elem data - * - * @param data mode_list_element pointer - * - */ -void usbmoded_set_usb_mode_data(struct mode_list_elem *data) -{ - current_mode.data = data; -} - -/** Send supported modes signal - */ -void usbmoded_send_supported_modes_signal(void) -{ - gchar *mode_list = usbmoded_get_mode_list(SUPPORTED_MODES_LIST); - umdbus_send_supported_modes_signal(mode_list); - g_free(mode_list); -} - -/** Send available modes signal - */ -void usbmoded_send_available_modes_signal(void) -{ - gchar *mode_list = usbmoded_get_mode_list(AVAILABLE_MODES_LIST); - umdbus_send_available_modes_signal(mode_list); - g_free(mode_list); -} - -/** Send hidden modes signal - */ -void usbmoded_send_hidden_modes_signal(void) -{ - gchar *mode_list = config_get_hidden_modes(); - if(mode_list) { - // TODO: cleared list not signaled? - umdbus_send_hidden_modes_signal(mode_list); - g_free(mode_list); - } -} - -/** Send whitelisted modes signal - */ -void usbmoded_send_whitelisted_modes_signal(void) -{ - gchar *mode_list = config_get_mode_whitelist(); - if(mode_list) { - // TODO: cleared list not signaled? - umdbus_send_whitelisted_modes_signal(mode_list); - g_free(mode_list); - } -} - -/** Write string to already existing sysfs file - * - * Note: Attempts to write to nonexisting files are silently ignored. - * - * @param path Where to write - * @param text What to write - */ -static void usbmoded_write_to_sysfs_file(const char *path, const char *text) -{ - int fd = -1; - - if (!path || !text) - goto EXIT; - - if ((fd = open(path, O_WRONLY)) == -1) { - if (errno != ENOENT) { - log_warning("%s: open for writing failed: %m", path); - } - goto EXIT; - } - - if (write(fd, text, strlen(text)) == -1) { - log_warning("%s: write failed : %m", path); - goto EXIT; - } -EXIT: - if (fd != -1) - close(fd); -} - -/** Acquire wakelock via sysfs - * - * Wakelock must be released via usbmoded_release_wakelock(). - * - * Automatically terminating wakelock is used, so that we - * do not block suspend indefinately in case usb_moded - * gets stuck or crashes. - * - * Note: The name should be unique within the system. - * - * @param wakelock_name Wake lock to be acquired - */ -void usbmoded_acquire_wakelock(const char *wakelock_name) -{ - char buff[256]; - snprintf(buff, sizeof buff, "%s %lld", - wakelock_name, - USB_MODED_SUSPEND_DELAY_MAXIMUM_MS * 1000000LL); - usbmoded_write_to_sysfs_file("/sys/power/wake_lock", buff); - -#if VERBOSE_WAKELOCKING - log_debug("usbmoded_acquire_wakelock %s", wakelock_name); -#endif -} - -/** Release wakelock via sysfs - * - * @param wakelock_name Wake lock to be released - */ -void usbmoded_release_wakelock(const char *wakelock_name) -{ -#if VERBOSE_WAKELOCKING - log_debug("usbmoded_release_wakelock %s", wakelock_name); -#endif - - usbmoded_write_to_sysfs_file("/sys/power/wake_unlock", wakelock_name); -} - /** Timer callback for releasing wakelock acquired via usbmoded_delay_suspend() * * @param aptr callback argument (not used) @@ -1238,7 +181,7 @@ static gboolean usbmoded_allow_suspend_timer_cb(gpointer aptr) { (void)aptr; - allow_suspend_timer_id = 0; + usbmoded_allow_suspend_timer_id = 0; usbmoded_allow_suspend(); @@ -1252,14 +195,14 @@ static gboolean usbmoded_allow_suspend_timer_cb(gpointer aptr) */ void usbmoded_allow_suspend(void) { - if( allow_suspend_timer_id ) { - g_source_remove(allow_suspend_timer_id), - allow_suspend_timer_id = 0; + if( usbmoded_allow_suspend_timer_id ) { + g_source_remove(usbmoded_allow_suspend_timer_id), + usbmoded_allow_suspend_timer_id = 0; } - if( blocking_suspend ) { - blocking_suspend = false; - usbmoded_release_wakelock(USB_MODED_WAKELOCK_STATE_CHANGE); + if( usbmoded_blocking_suspend ) { + usbmoded_blocking_suspend = false; + common_release_wakelock(USB_MODED_WAKELOCK_STATE_CHANGE); } } @@ -1277,14 +220,14 @@ void usbmoded_delay_suspend(void) { /* Use of automatically terminating wakelocks also means we need * to renew the wakelock when extending the suspend delay. */ - usbmoded_acquire_wakelock(USB_MODED_WAKELOCK_STATE_CHANGE); + common_acquire_wakelock(USB_MODED_WAKELOCK_STATE_CHANGE); - blocking_suspend = true; + usbmoded_blocking_suspend = true; - if( allow_suspend_timer_id ) - g_source_remove(allow_suspend_timer_id); + if( usbmoded_allow_suspend_timer_id ) + g_source_remove(usbmoded_allow_suspend_timer_id); - allow_suspend_timer_id = + usbmoded_allow_suspend_timer_id = g_timeout_add(USB_MODED_SUSPEND_DELAY_DEFAULT_MS, usbmoded_allow_suspend_timer_cb, 0); } @@ -1317,23 +260,23 @@ bool usbmoded_can_export(void) */ bool usbmoded_init_done_p(void) { - return init_done_reached; + return usbmoded_init_done_reached; } /** Update cached init-done-reached state */ void usbmoded_set_init_done(bool reached) { - if( init_done_reached != reached ) { - init_done_reached = reached; + if( usbmoded_init_done_reached != reached ) { + usbmoded_init_done_reached = reached; log_warning("init_done -> %s", - init_done_reached ? "reached" : "not reached"); + usbmoded_init_done_reached ? "reached" : "not reached"); } } /** Check whether init-done flag file exists */ void usbmoded_probe_init_done(void) { - usbmoded_set_init_done(access(init_done_flagfile, F_OK) == 0); + usbmoded_set_init_done(access(usbmoded_init_done_flagfile, F_OK) == 0); } /** Request orderly exit from mainloop @@ -1342,22 +285,22 @@ void usbmoded_exit_mainloop(int exitcode) { /* In case multiple exit request get done, retain the * highest exit code used. */ - if( usb_moded_exitcode < exitcode ) - usb_moded_exitcode = exitcode; + if( usbmoded_exitcode < exitcode ) + usbmoded_exitcode = exitcode; /* If there is no mainloop to exit, terminate immediately */ - if( !usb_moded_mainloop ) + if( !usbmoded_mainloop ) { log_warning("exit requested outside mainloop; exit(%d) now", - usb_moded_exitcode); - exit(usb_moded_exitcode); + usbmoded_exitcode); + exit(usbmoded_exitcode); } log_debug("stopping usb-moded mainloop"); - g_main_loop_quit(usb_moded_mainloop); + g_main_loop_quit(usbmoded_mainloop); } -static void usbmoded_handle_signal(int signum) +void usbmoded_handle_signal(int signum) { log_debug("handle signal: %s\n", strsignal(signum)); @@ -1369,12 +312,12 @@ static void usbmoded_handle_signal(int signum) else if( signum == SIGHUP ) { /* free and read in modelist again */ - dynconfig_free_mode_list(modelist); + dynconfig_free_mode_list(usbmoded_modelist); - modelist = dynconfig_read_mode_list(diag_mode); + usbmoded_modelist = dynconfig_read_mode_list(usbmoded_diag_mode); - usbmoded_send_supported_modes_signal(); - usbmoded_send_available_modes_signal(); + common_send_supported_modes_signal(); + common_send_available_modes_signal(); // FIXME invalidate current mode } @@ -1387,13 +330,6 @@ static void usbmoded_handle_signal(int signum) /* set default values for usb_moded */ static void usbmoded_init(void) { - current_mode.cable_state = CABLE_STATE_UNKNOWN; - current_mode.mounted = false; - current_mode.internal_mode = strdup(MODE_UNDEFINED); - current_mode.hardware_mode = NULL; - current_mode.external_mode = NULL; - current_mode.module = NULL; - /* check config, merge or create if outdated */ if(config_merge_conf_file() != 0) { @@ -1402,11 +338,11 @@ static void usbmoded_init(void) } #ifdef APP_SYNC - appsync_read_list(diag_mode); + appsync_read_list(usbmoded_diag_mode); #endif /* always read dyn modes even if appsync is not used */ - modelist = dynconfig_read_mode_list(diag_mode); + usbmoded_modelist = dynconfig_read_mode_list(usbmoded_diag_mode); if(config_check_trigger()) trigger_init(); @@ -1438,7 +374,7 @@ static void usbmoded_init(void) break; } - usbmoded_msleep(1000); + common_msleep(1000); } /* TODO: add more start-up clean-up and init here if needed */ @@ -1455,7 +391,7 @@ static void usbmoded_cleanup(void) trigger_stop(); /* Undo dynconfig_read_mode_list() */ - dynconfig_free_mode_list(modelist); + dynconfig_free_mode_list(usbmoded_modelist); #ifdef APP_SYNC /* Undo appsync_read_list() */ @@ -1463,233 +399,11 @@ static void usbmoded_cleanup(void) #endif /* Release dynamic memory */ - free(current_mode.module), - current_mode.module = 0; - - free(current_mode.internal_mode), - current_mode.internal_mode = 0; - - free(current_mode.hardware_mode), - current_mode.hardware_mode = 0; - - free(current_mode.external_mode), - current_mode.external_mode = 0; -} - -/** Wrapper to give visibility to blocking system() calls usb-moded is making - */ -int -usbmoded_system_(const char *file, int line, const char *func, - const char *command) -{ - log_debug("EXEC %s; from %s:%d: %s()", - command, file, line, func); - - int rc = system(command); - - if( rc != 0 ) - log_warning("EXEC %s; exit code is %d", command, rc); - - return rc; -} - -/** Wrapper to give visibility subprocesses usb-moded is invoking via popen() - */ -FILE * -usbmoded_popen_(const char *file, int line, const char *func, - const char *command, const char *type) -{ - log_debug("EXEC %s; from %s:%d: %s()", - command, file, line, func); - - return popen(command, type); -} - -/** Wrapper to give visibility to blocking sleeps usb-moded is making - */ -void -usbmoded_usleep_(const char *file, int line, const char *func, - useconds_t usec) -{ - struct timespec ts = { - .tv_sec = (usec / 1000000), - .tv_nsec = (usec % 1000000) * 1000 - }; - - long ms = (ts.tv_nsec + 1000000 - 1) / 1000000; - - if( !ms ) { - log_debug("SLEEP %ld seconds; from %s:%d: %s()", - (long)ts.tv_sec, file, line, func); - } - else if( ts.tv_sec ) { - log_debug("SLEEP %ld.%03ld seconds; from %s:%d: %s()", - (long)ts.tv_sec, ms, file, line, func); - } - else { - log_debug("SLEEP %ld milliseconds; from %s:%d: %s()", - ms, file, line, func); - } - - do { } while( nanosleep(&ts, &ts) == -1 && errno != EINTR ); -} - -/** Glib io watch callback for reading signals from signal pipe - * - * @param channel glib io channel - * @param condition wakeup reason - * @param data user data (unused) - * - * @return TRUE to keep the iowatch, or FALSE to disable it - */ -static gboolean -sigpipe_read_signal_cb(GIOChannel *channel, - GIOCondition condition, - gpointer data) -{ - gboolean keep_watch = FALSE; - - int fd, rc, sig; - - (void)data; - - /* Should never happen, but we must disable the io watch - * if the pipe fd still goes into unexpected state ... */ - if( condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL) ) - goto EXIT; - - if( (fd = g_io_channel_unix_get_fd(channel)) == -1 ) - goto EXIT; - - /* If the actual read fails, terminate with core dump */ - rc = TEMP_FAILURE_RETRY(read(fd, &sig, sizeof sig)); - if( rc != (int)sizeof sig ) - abort(); - - /* handle the signal */ - usbmoded_handle_signal(sig); - - keep_watch = TRUE; - -EXIT: - if( !keep_watch ) - log_crit("disabled signal handler io watch\n"); - - return keep_watch; -} - -/** Async signal handler for writing signals to signal pipe - * - * @param sig the signal number to pass to mainloop via pipe - */ -static void -sigpipe_trap_signal_cb(int sig) -{ - /* NOTE: This function *MUST* be kept async-signal-safe! */ - - static volatile int exit_tries = 0; - - int rc; - - /* Restore signal handler */ - signal(sig, sigpipe_trap_signal_cb); - - switch( sig ) - { - case SIGINT: - case SIGQUIT: - case SIGTERM: - /* If we receive multiple signals that should have - * caused the process to exit, assume that mainloop - * is stuck and terminate with core dump. */ - if( ++exit_tries >= 2 ) - abort(); - break; - - default: - break; - } - - /* Transfer the signal to mainloop via pipe ... */ - rc = TEMP_FAILURE_RETRY(write(sigpipe_fd, &sig, sizeof sig)); - - /* ... or terminate with core dump in case of failures */ - if( rc != (int)sizeof sig ) - abort(); -} - -/** Create a pipe and io watch for handling signal from glib mainloop - * - * @return true on success, or false in case of errors - */ -static bool -sigpipe_crate_pipe(void) -{ - bool res = false; - GIOChannel *chn = 0; - int pfd[2] = { -1, -1 }; - - if( pipe2(pfd, O_CLOEXEC) == -1 ) - goto EXIT; - - if( (chn = g_io_channel_unix_new(pfd[0])) == 0 ) - goto EXIT; - - if( !g_io_add_watch(chn, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - sigpipe_read_signal_cb, 0) ) - goto EXIT; - - g_io_channel_set_close_on_unref(chn, true), pfd[0] = -1; - sigpipe_fd = pfd[1], pfd[1] = -1; - - res = true; - -EXIT: - if( chn ) g_io_channel_unref(chn); - if( pfd[0] != -1 ) close(pfd[0]); - if( pfd[1] != -1 ) close(pfd[1]); - - return res; -} - -/** Install async signal handlers - */ -static void -sigpipe_trap_signals(void) -{ - static const int sig[] = - { - SIGINT, - SIGQUIT, - SIGTERM, - SIGHUP, - -1 - }; - - for( size_t i = 0; sig[i] != -1; ++i ) - { - signal(sig[i], sigpipe_trap_signal_cb); - } -} - -/** Initialize signal trapping - * - * @return true on success, or false in case of errors - */ -static bool -sigpipe_init(void) -{ - bool success = false; - - if( !sigpipe_crate_pipe() ) - goto EXIT; - - sigpipe_trap_signals(); - - success = true; - -EXIT: - return success; + worker_clear_usb_module(); + worker_clear_hardware_mode(); + control_clear_cable_state(); + control_clear_internal_mode(); + control_clear_external_mode(); } /* ========================================================================= * @@ -1779,7 +493,7 @@ int main(int argc, char* argv[]) log_warning("Deprecated option: --android_usb_broken_udev_events"); break; case 'f': - hw_fallback = true; + usbmoded_hw_fallback = true; break; case 's': log_set_type(LOG_TO_SYSLOG); @@ -1799,7 +513,7 @@ int main(int argc, char* argv[]) break; case 'd': - diag_mode = true; + usbmoded_diag_mode = true; break; case 'h': @@ -1811,7 +525,7 @@ int main(int argc, char* argv[]) break; #ifdef SYSTEMD case 'n': - systemd_notify = true; + usbmoded_systemd_notify = true; break; #endif case 'v': @@ -1839,7 +553,7 @@ int main(int argc, char* argv[]) * INITIALIZE * - - - - - - - - - - - - - - - - - - - */ - /* silence usbmoded_system() calls */ + /* silence common_system() calls */ if( log_get_type() != LOG_TO_STDERR && log_get_level() != LOG_DEBUG ) { if( !freopen("/dev/null", "a", stdout) ) { @@ -1857,6 +571,9 @@ int main(int argc, char* argv[]) g_thread_init(NULL); #endif + if( !worker_init() ) + goto EXIT; + /* Check if we are in mid-bootup */ usbmoded_probe_init_done(); @@ -1943,7 +660,7 @@ int main(int argc, char* argv[]) /* Initialize udev listener. Can cause mode changes. * * Failing here is allowed if '--fallback' commandline option is used. */ - if( !umudev_init() && !hw_fallback ) + if( !umudev_init() && !usbmoded_hw_fallback ) { log_crit("hwal init failed\n"); goto EXIT; @@ -1951,17 +668,17 @@ int main(int argc, char* argv[]) /* Broadcast supported / hidden modes */ // TODO: should this happen before umudev_init()? - usbmoded_send_supported_modes_signal(); - usbmoded_send_available_modes_signal(); - usbmoded_send_hidden_modes_signal(); - usbmoded_send_whitelisted_modes_signal(); + common_send_supported_modes_signal(); + common_send_available_modes_signal(); + common_send_hidden_modes_signal(); + common_send_whitelisted_modes_signal(); /* Act on '--fallback' commandline option */ - if(hw_fallback) + if(usbmoded_hw_fallback) { log_warning("Forcing USB state to connected always. ASK mode non functional!\n"); /* Since there will be no disconnect signals coming from hw the state should not change */ - usbmoded_set_cable_state(CABLE_STATE_PC_CONNECTED); + control_set_cable_state(CABLE_STATE_PC_CONNECTED); } /* - - - - - - - - - - - - - - - - - - - * @@ -1970,7 +687,7 @@ int main(int argc, char* argv[]) /* Tell systemd that we have started up */ #ifdef SYSTEMD - if( systemd_notify ) + if( usbmoded_systemd_notify ) { log_debug("notifying systemd\n"); sd_notify(0, "READY=1"); @@ -1978,20 +695,22 @@ int main(int argc, char* argv[]) #endif /* init succesful, run main loop */ - usb_moded_exitcode = EXIT_SUCCESS; - usb_moded_mainloop = g_main_loop_new(NULL, FALSE); + usbmoded_exitcode = EXIT_SUCCESS; + usbmoded_mainloop = g_main_loop_new(NULL, FALSE); log_debug("enter usb-moded mainloop"); - g_main_loop_run(usb_moded_mainloop); + g_main_loop_run(usbmoded_mainloop); log_debug("leave usb-moded mainloop"); - g_main_loop_unref(usb_moded_mainloop), - usb_moded_mainloop = 0; + g_main_loop_unref(usbmoded_mainloop), + usbmoded_mainloop = 0; /* - - - - - - - - - - - - - - - - - - - * * CLEANUP * - - - - - - - - - - - - - - - - - - - */ EXIT: + worker_quit(); + /* Detach from SystemBus. Components that hold reference to the * shared bus connection can still perform cleanup tasks, but new * references can't be obtained anymore and usb-moded method call @@ -2036,6 +755,6 @@ int main(int argc, char* argv[]) usbmoded_allow_suspend(); log_debug("usb-moded return from main, with exit code %d", - usb_moded_exitcode); - return usb_moded_exitcode; + usbmoded_exitcode); + return usbmoded_exitcode; } diff --git a/src/usb_moded.h b/src/usb_moded.h index dcb9e28..86dab4e 100644 --- a/src/usb_moded.h +++ b/src/usb_moded.h @@ -67,27 +67,6 @@ # define USB_MODED_SUSPEND_DELAY_MAXIMUM_MS \ (USB_MODED_SUSPEND_DELAY_DEFAULT_MS * 2) -/* ========================================================================= * - * Types - * ========================================================================= */ - -typedef enum { - CABLE_STATE_UNKNOWN, - CABLE_STATE_DISCONNECTED, - CABLE_STATE_CHARGER_CONNECTED, - CABLE_STATE_PC_CONNECTED, - CABLE_STATE_NUMOF -} cable_state_t; - -/** Mode list types - */ -typedef enum mode_list_type_t { - /** All configured modes */ - SUPPORTED_MODES_LIST, - /** Configured modes that can be activated */ - AVAILABLE_MODES_LIST -} mode_list_type_t; - /* ========================================================================= * * Data * ========================================================================= */ @@ -109,55 +88,23 @@ extern int usbmoded_cable_connection_delay; */ extern bool usbmoded_rescue_mode; +extern bool usbmoded_diag_mode; + +extern GList *usbmoded_modelist; + /* ========================================================================= * * Functions * ========================================================================= */ -/* -- cable -- */ - -const char *cable_state_repr(cable_state_t state); - /* -- usbmoded -- */ -void usbmoded_rethink_usb_charging_fallback(void); -const char *usbmoded_get_external_mode (void); -const char *usbmoded_get_usb_mode (void); -void usbmoded_set_usb_mode (const char *internal_mode); -void usbmoded_select_usb_mode (void); -void usbmoded_set_cable_state (cable_state_t cable_state); -cable_state_t usbmoded_get_cable_state (void); -bool usbmoded_get_connection_state (void); -int usbmoded_valid_mode (const char *mode); -gchar *usbmoded_get_mode_list (mode_list_type_t type); -const char *usbmoded_get_usb_module (void); -bool usbmoded_set_usb_module (const char *module); -struct mode_list_elem *usbmoded_get_usb_mode_data (void); -void usbmoded_set_usb_mode_data (struct mode_list_elem *data); -void usbmoded_send_supported_modes_signal (void); -void usbmoded_send_available_modes_signal (void); -void usbmoded_send_hidden_modes_signal (void); -void usbmoded_send_whitelisted_modes_signal(void); -void usbmoded_acquire_wakelock (const char *wakelock_name); -void usbmoded_release_wakelock (const char *wakelock_name); -void usbmoded_allow_suspend (void); -void usbmoded_delay_suspend (void); -bool usbmoded_can_export (void); -bool usbmoded_init_done_p (void); -void usbmoded_set_init_done (bool reached); -void usbmoded_probe_init_done (void); -void usbmoded_exit_mainloop (int exitcode); -int usbmoded_system_ (const char *file, int line, const char *func, const char *command); -FILE *usbmoded_popen_ (const char *file, int line, const char *func, const char *command, const char *type); -void usbmoded_usleep_ (const char *file, int line, const char *func, useconds_t usec); - -/* ========================================================================= * - * Macros - * ========================================================================= */ - -# define usbmoded_system(command) usbmoded_system_(__FILE__,__LINE__,__FUNCTION__,(command)) -# define usbmoded_popen(command, type) usbmoded_popen_(__FILE__,__LINE__,__FUNCTION__,(command),(type)) -# define usbmoded_usleep(usec) usbmoded_usleep_(__FILE__,__LINE__,__FUNCTION__,(usec)) -# define usbmoded_msleep(msec) usbmoded_usleep_(__FILE__,__LINE__,__FUNCTION__,(msec)*1000) -# define usbmoded_sleep(sec) usbmoded_usleep_(__FILE__,__LINE__,__FUNCTION__,(sec)*1000000) +void usbmoded_allow_suspend (void); +void usbmoded_delay_suspend (void); +bool usbmoded_can_export (void); +bool usbmoded_init_done_p (void); +void usbmoded_set_init_done (bool reached); +void usbmoded_probe_init_done(void); +void usbmoded_exit_mainloop (int exitcode); +void usbmoded_handle_signal (int signum); #endif /* USB_MODED_H_ */