diff --git a/TODO b/TODO index e69de29..03c3ed5 100644 --- a/TODO +++ b/TODO @@ -0,0 +1,2 @@ +* verify appsync/usb_moded functionality with multiple sessions +* Fix daemon mode diff --git a/configure.ac b/configure.ac index c536e73..019245b 100644 --- a/configure.ac +++ b/configure.ac @@ -95,7 +95,7 @@ AM_CONDITIONAL([UDEV], [test x$udev = xtrue]) PKG_CHECK_MODULES([USB_MODED], [ - glib-2.0 >= 2.28.0 + glib-2.0 >= 2.24.0 dbus-1 >= 1.2.1 dbus-glib-1 >= 0.78 gobject-2.0 >= 2.16.6 diff --git a/debian/changelog b/debian/changelog index 6a9c7e2..7f6cc7b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,8 +4,11 @@ usb-moded (0.38) unstable; urgency=low * Fix doxygen warnings and improve documentation * Add dynamic mode support * Add debug printing for finding who keeps the mass-storage device busy + * Add more aegis capabilities. + * Remove dependency on glib 2.28.0, glib 2.24.2 is enough now. + * Fix fallback mount options. Fixes: NB#255798 - -- Philippe De Swert Tue, 03 May 2011 14:57:38 +0300 + -- Philippe De Swert Mon May 16 13:06:17 EEST 2011 usb-moded (0.37) unstable; urgency=low diff --git a/debian/usb-moded.aegis b/debian/usb-moded.aegis index 002926c..1bd2f9e 100644 --- a/debian/usb-moded.aegis +++ b/debian/usb-moded.aegis @@ -33,6 +33,7 @@ + diff --git a/docs/usb_moded-doc.txt b/docs/usb_moded-doc.txt new file mode 100644 index 0000000..90918bb --- /dev/null +++ b/docs/usb_moded-doc.txt @@ -0,0 +1,190 @@ +USB_MODED use and API description/overview. +========================================== + +Usb_moded is a relatively simple daemon which tracks the +usb cable connection status and activates a certain USB +profile based on that. +To do this it has a number of built in common profiles, +an optional application launcher (app_sync) and configurable +dynamic modes. + +All the system wide communication goes over the DBus system bus, +while application launching is handled on the session bus. +Thus if you need this functionality, usb_moded needs to be started +with the session. +(functionality not verified yet on multi-user setups. See TODO) + +Usb cable detection is supported by several different methods. +Old deprecated hal, through Nokia's own battery monitoring bme, +or the preferred udev option. (This has to be chosen at compile time) + +Starting usb_moded +------------------- + +Simply start with usb_moded. +usb_moded --help will give you more details + +*NOTE: daemon mode does not work correctly. See TODO* + + +Status and configuration query/setting +--------------------------------------- + +The current status can be queried any time over DBus. From a program +using a method call or using dbus-send. + +dbus-send --system --type=method_call --print-reply --dest=com.meego.usb_moded /com/meego/usb_moded com.meego.usb_moded.mode_request + +Similary a mode can be set. + +dbus-send --system --type=method_call --print-reply --dest=com.meego.usb_moded /com/meego/usb_moded com.meego.usb_moded.set_mode string:'' + +Even the configuration can be set over DBus. + +dbus-send --system --type=method_call --print-reply --dest=com.meego.usb_moded /com/meego/usb_moded com.meego.usb_moded.set_config string:'' + +However this can also be handled by setting the following GConf key. + +/Meego/System/UsbMode (querying and setting can be done as follows: + +gconftool-2 -g /Meego/System/UsbMode +gconftool-2 -s /Meego/System/UsbMode --type=string + +Usb_moded will also broadcast changes and errors over the system bus. +This will happen on the com.meego.usb_moded interface + +For regular signals: sig_usb_state_ind +And errors: sig_usb_state_error_ind + +More info and details in usb_moded-dbus.h + +There is also a configuration file and some configuration directories under /etc/usb-moded +It should have some pre-filled fields for the default mass-storage profile + +The first one would be mountpoints, this defines which device/filesystem entry should be +exported over mass-storage (this ideally also has an entry in /etc/fstab). You can add more +filesystems to the mount option, by making it a comma-seperated list in case there are +several exports (like internal mmc and sd card for example) + +[mountpoints] +mount = /dev/mmcblk0p1 + +The following option plays with certain sync options that exist and have to be set per +fs and thus cannot be handled by setting them in /etc/modprobe.d/....conf + +[sync] +nofua = 1 + +This mount is the alternative mountpoint for in case something goes wrong. Usb_moded +will mount a 512 RO tmpfs on that location to mitigate potential disasters on the system, +and make clear to programs on the device that something is wrong with the fs they want to use. + +[altmount] +mount = /home/user/MyDocs + +The other settings and config dirs will be handled later in the appsync and dynamic modes part. +(This is optional and can be compiled out) + +Functional overview +-------------------- + +When a usb cable is insertion is detected usb_moded will start to act. +First it will warn on the system bus that a cable is connected. +Then it will check what the configuration setting is. Thus it knows how to act. +If it is a known configured mode it will load the correct modules (after checking +what is loaded and clean up if needed), perform other needed operations and +make sure the chosen mode works. At this point it will broadcast which mode +is set to warn other programs. +If things fail it will go to undefined state (and also broadcast this). + +There is the special case where the config option is called "ask". In this +case usb_moded will enable a fake mass_storage profile to enable enumeration +so charging can be done, and wait until it is instructed which mode needs +to be selected. Thus this state can be used for an UI that will set the right +mode on user interaction. For this purpose usb_moded broadcasts it goes in ask +mode, and also stays in ask mode until a chosen mode is requested or the cable +is disconnected. This also avoids race conditions in case the UI starts +after a cable is inserted and usb_moded has also been started. The UI can then +query the state to know if it needs to show a selection dialog or not. + +On disconnect usb_moded will broadcast a disconnect signal, so that all programs +that use the usb interface/mode know that a cable is disconnected and then can act +(if needed) on the changed situation. It will then do all the necessary to reset +the system to a clean state. + +Appsync feature +--------------- + +In case you need some program to be started along some mode the appsync option +provides this option. +Only condition is that it can be activated by dbus and that (preferably) it will +notify usb_moded that it is ready by ready_method call on the session bus. +This ready method call is just calling the regular usb_moded interface, but now +on the session bus, with as argument the program name as defined in the config file. + +To achieve this you need to have a config file in /etc/usb-moded/run. +Best practice would be giving it a descriptive name followed by .ini + +For example: +/etc/usb-moded/run/foo.ini + +Where foo.ini would be: +[info] +name = foo +mode = foo_mode +launch = com.meego.foo + +Those files will be read on start and usb_moded will keep a list of apps to launch +for a certain mode. This also means that if you change the files or add/remove some +you need to restart usb_moded. Later when the mode is activated, usb_moded will start +each of them after the module has been loaded and keep track if they have been started. +It will warn you if that failed. This works together with an optional softconnect option +that will need kernel support. (Nokia only atm) + +Dynamic modes +------------- + +Dynamic modes will only work if the appsync feature is enabled, since it is expected +that this will need some special userspace programs. These will be started +through the appsync part and thus need their own config files in the normal appsync dir. + +Dynamic modes need to be defined with an ini file also but this time in +/etc/usb-moded/dyn-modes. + +The format would be for example for : /etc/usb-moded/dyn-modes/dyn-mode-1.ini + +[mode] +name = dyn-mode-1 +module = h_dyn-mode +appsync = 1 +network = 1 +network_interface = usb0 + +Only the mode name and module are mandatory. +However if you specify that there has to be network bringup, the +network interface is mandatory. Atm the network interface +bring-up will only work on debian based systems, since it uses +ifup/ifdown as the network configuration has to be handled +in the regular places to avoid conflicts and keep it easy to +configure. + +If appsync is not defined or explicitly set to 0 it will not be used. + +Trigger support +--------------- + +This will only work if udev is configured as it is a udev trigger. +Atm only one trigger is supported. +This is to support special equipment that will send a trigger event. +Usually this will be in combination with a dynamic mode. + +You need to add the following to usb-moded.ini to get a trigger activated + +For example + +[trigger] +path = /sys/devices/platform/musb_hdrc +udev_subsystem = platform +mode = mass_storage +property = TRIGGER_CMD + diff --git a/src/Makefile.am b/src/Makefile.am index e167161..c54b121 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -40,7 +40,8 @@ endif if UDEV usb_moded_SOURCES += \ - usb_moded-udev.c + usb_moded-udev.c \ + usb_moded-trigger.c endif if NOKIA diff --git a/src/usb_moded-appsync.c b/src/usb_moded-appsync.c index 4232354..3f3906f 100644 --- a/src/usb_moded-appsync.c +++ b/src/usb_moded-appsync.c @@ -60,7 +60,9 @@ static void free_list(void) { if( sync_list != 0 ) { - g_list_free_full(sync_list, free_elem); + /*g_list_free_full(sync_list, free_elem); */ + g_list_foreach (sync_list, (GFunc) free_elem, NULL); + g_list_free (sync_list); sync_list = 0; } } @@ -140,6 +142,7 @@ static struct list_elem *read_file(const gchar *filename) int activate_sync(const char *mode) { GList *iter; + int count = 0, count2 = 0; log_debug("activate sync"); @@ -156,12 +159,26 @@ int activate_sync(const char *mode) for( iter = sync_list; iter; iter = g_list_next(iter) ) { struct list_elem *data = iter->data; + + count++; if(!strcmp(data->mode, mode)) data->active = 0; else + { + count2++; data->active = 1; + } } + /* if the number of active modes is equal to the number of existing modes + we enumerate immediately */ + if(count == count2) + { + log_debug("Nothing to launch.\n"); + enumerate_usb(NULL); + return(1); + } + /* add dbus filter. Use session bus for ready method call? */ if(!usb_moded_app_sync_init()) { @@ -171,7 +188,7 @@ int activate_sync(const char *mode) } /* start timer */ - log_debug("Starting timer\n"); + log_debug("Starting appsync timer\n"); g_timeout_add_seconds(2, enumerate_usb, NULL); /* go through list and launch apps */ diff --git a/src/usb_moded-config.c b/src/usb_moded-config.c index eb3f421..469510d 100644 --- a/src/usb_moded-config.c +++ b/src/usb_moded-config.c @@ -78,17 +78,32 @@ int find_cdrom_timeout(void) } #endif /* NOKIA */ -#ifdef APP_SYNC +#ifdef UDEV const char * check_trigger(void) { return(get_conf_string(TRIGGER_ENTRY, TRIGGER_PATH_KEY)); } -const char * check_trigger_mode(void) +const char * get_trigger_subsystem(void) +{ + return(get_conf_string(TRIGGER_ENTRY, TRIGGER_UDEV_SUBSYSTEM)); +} + +const char * get_trigger_mode(void) { return(get_conf_string(TRIGGER_ENTRY, TRIGGER_MODE_KEY)); } -#endif /* APP_SYNC */ + +const char * get_trigger_property(void) +{ + return(get_conf_string(TRIGGER_ENTRY, TRIGGER_PROPERTY_KEY)); +} + +const char * get_trigger_value(void) +{ + return(get_conf_string(TRIGGER_ENTRY, TRIGGER_PROPERTY_VALUE_KEY)); +} +#endif /* UDEV */ static int get_conf_int(const gchar *entry, const gchar *key) { diff --git a/src/usb_moded-config.h b/src/usb_moded-config.h index 988ed3e..a07df6a 100644 --- a/src/usb_moded-config.h +++ b/src/usb_moded-config.h @@ -36,7 +36,10 @@ #define CDROM_TIMEOUT_KEY "timeout" #define TRIGGER_ENTRY "trigger" #define TRIGGER_PATH_KEY "path" +#define TRIGGER_UDEV_SUBSYSTEM "udev_subsystem" #define TRIGGER_MODE_KEY "mode" +#define TRIGGER_PROPERTY_KEY "property" +#define TRIGGER_PROPERTY_VALUE_KEY "value" const char * find_mounts(void); int find_sync(void); @@ -51,7 +54,10 @@ const char * find_cdrom_path(void); int find_cdrom_timeout(void); #endif -#ifdef APP_SYNC +#ifdef UDEV const char * check_trigger(void); -const char * check_trigger_mode(void); -#endif /* APP_SYNC */ +const char * get_trigger_subsystem(void); +const char * get_trigger_mode(void); +const char * get_trigger_property(void); +const char * get_trigger_value(void); +#endif /* UDEV */ diff --git a/src/usb_moded-dyn-config.c b/src/usb_moded-dyn-config.c index 4d459d6..86b652b 100644 --- a/src/usb_moded-dyn-config.c +++ b/src/usb_moded-dyn-config.c @@ -53,7 +53,7 @@ GList *read_mode_list(void) g_dir_close(confdir); } else - log_debug("confdir open failed.\n"); + log_debug("Dynamic mode confdir open failed.\n"); return(modelist); } diff --git a/src/usb_moded-modesetting.c b/src/usb_moded-modesetting.c index b5ab9e4..935c3da 100644 --- a/src/usb_moded-modesetting.c +++ b/src/usb_moded-modesetting.c @@ -331,12 +331,14 @@ int usb_moded_mode_cleanup(const char *module) mount = find_alt_mount(); if(mount) { + /* check if it is already mounted, if not mount failure fallback */ command = g_strconcat("mount | grep ", mount, NULL); ret = system(command); g_free(command); if(ret) { - command = g_strconcat("mount -t tmpfs tmpfs -o ro -size=512K ", mount, NULL); + command = g_strconcat("mount -t tmpfs tmpfs -o ro --size=512K ", mount, NULL); + log_debug("Total failure, mount ro tmpfs as fallback\n"); ret = system(command); g_free(command); } diff --git a/src/usb_moded-trigger.c b/src/usb_moded-trigger.c new file mode 100644 index 0000000..efdd77b --- /dev/null +++ b/src/usb_moded-trigger.c @@ -0,0 +1,183 @@ +/** + @file usb_moded-trigger.c + + Copyright (C) 2011 Nokia Corporation. All rights reserved. + + @author: Philippe De Swert + + 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 +#include +#include +#include + +#include + +#include + +#include + +#include "usb_moded.h" +#include "usb_moded-log.h" +#include "usb_moded-config.h" +#include "usb_moded-hw-ab.h" +#include "usb_moded-trigger.h" + +/* global variables */ +static struct udev *udev; +static struct udev_monitor *mon; +static GIOChannel *iochannel; +static guint watch_id; +static const char *dev_name; + +/* static function definitions */ +static gboolean monitor_udev(GIOChannel *iochannel G_GNUC_UNUSED, GIOCondition cond, + gpointer data G_GNUC_UNUSED); +static void udev_parse(struct udev_device *dev); + +gboolean trigger_init(void) +{ + const gchar *udev_path = NULL; + struct udev_device *dev; + int ret = 0; + + /* Create the udev object */ + udev = udev_new(); + if (!udev) + { + log_err("Can't create udev\n"); + return 1; + } + + udev_path = check_trigger(); + if(udev_path) + dev = udev_device_new_from_syspath(udev, udev_path); + else + { + log_err("No trigger path. Not starting trigger.\n"); + return 1; + } + if (!dev) + { + log_err("Unable to find the trigger device."); + return 1; + } + else + { + dev_name = udev_device_get_sysname(dev); + log_debug("device name = %s\n", dev_name); + } + mon = udev_monitor_new_from_netlink (udev, "udev"); + if (!mon) + { + log_err("Unable to monitor the netlink\n"); + /* communicate failure, mainloop will exit and call appropriate clean-up */ + return 1; + } + ret = udev_monitor_filter_add_match_subsystem_devtype(mon, get_trigger_subsystem(), NULL); + if(ret != 0) + { + log_err("Udev match failed.\n"); + return 1; + } + ret = udev_monitor_enable_receiving (mon); + if(ret != 0) + { + log_err("Failed to enable monitor recieving.\n"); + return 1; + } + + /* check if we are already connected */ + udev_parse(dev); + + iochannel = g_io_channel_unix_new(udev_monitor_get_fd(mon)); + watch_id = g_io_add_watch(iochannel, G_IO_IN, monitor_udev, NULL); + + /* everything went well */ + log_debug("Trigger enabled!\n"); + return 0; +} + +static gboolean monitor_udev(GIOChannel *iochannel G_GNUC_UNUSED, GIOCondition cond, + gpointer data G_GNUC_UNUSED) +{ + struct udev_device *dev; + + if(cond & G_IO_IN) + { + /* This normally blocks but G_IO_IN indicates that we can read */ + dev = udev_monitor_receive_device (mon); + if (dev) + { + /* check if it is the actual device we want to check */ + if(strcmp(dev_name, udev_device_get_sysname(dev))) + return 0; + + if(!strcmp(udev_device_get_action(dev), "change")) + { + log_debug("Trigger event recieved.\n"); + udev_parse(dev); + } + udev_device_unref(dev); + } + /* if we get something else something bad happened stop watching to avoid busylooping */ + else + { + log_debug("Bad trigger data. Stopping\n"); + trigger_stop(); + } + } + + /* keep watching */ + return TRUE; +} + +void trigger_stop(void) +{ + g_source_remove(watch_id); + watch_id = 0; + g_io_channel_unref(iochannel); + iochannel = NULL; + udev_monitor_unref(mon); + udev_unref(udev); +} + +static void udev_parse(struct udev_device *dev) +{ + const char *tmp; + + tmp = udev_device_get_property_value(dev, get_trigger_property()); + if(!tmp) + { + /* do nothing and return */ + return; + } + else + { + if(get_trigger_value()) + { + if(!strcmp(tmp, get_trigger_value())) + set_usb_mode(get_trigger_mode()); + else + return; + } + else + set_usb_mode(get_trigger_mode()); + return; + } +} diff --git a/src/usb_moded-trigger.h b/src/usb_moded-trigger.h new file mode 100644 index 0000000..5bdae60 --- /dev/null +++ b/src/usb_moded-trigger.h @@ -0,0 +1,25 @@ +/** + @file usb_moded-trigger.h + + Copyright (C) 2011 Nokia Corporation. All rights reserved. + + @author: Philippe De Swert + + 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 +*/ + + +gboolean trigger_init(void); +void trigger_stop(void); diff --git a/src/usb_moded-udev.c b/src/usb_moded-udev.c index 2c6a2f9..08b5638 100644 --- a/src/usb_moded-udev.c +++ b/src/usb_moded-udev.c @@ -1,3 +1,26 @@ +/** + @file usb_moded-udev.c + + Copyright (C) 2011 Nokia Corporation. All rights reserved. + + @author: Philippe De Swert + + 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 #include #include diff --git a/src/usb_moded.c b/src/usb_moded.c index d3b042b..742dd79 100644 --- a/src/usb_moded.c +++ b/src/usb_moded.c @@ -42,6 +42,8 @@ #include "usb_moded-modesetting.h" #include "usb_moded-modules.h" #include "usb_moded-appsync.h" +#include "usb_moded-trigger.h" +#include "usb_moded-config.h" /* global definitions */ @@ -236,6 +238,8 @@ void set_usb_mode(const char *mode) struct mode_list_elem *data = iter->data; if(!strcmp(mode, data->mode_name)) { + /* set state to connected as we might get a disconnect in between */ + current_mode.connected = TRUE; check_module_state(data->mode_module); set_usb_module(data->mode_module); ret = usb_moded_load_module(data->mode_module); @@ -362,6 +366,10 @@ static void usb_moded_init(void) readlist(); modelist = read_mode_list(); #endif /* APP_SYNC */ +#ifdef UDEV + if(check_trigger()) + trigger_init(); +#endif /* UDEV */ /* TODO: add more start-up clean-up and init here if needed */ } diff --git a/usb-moded.ini b/usb-moded.ini index 7067440..277121e 100644 --- a/usb-moded.ini +++ b/usb-moded.ini @@ -12,3 +12,4 @@ mount = /home/user/MyDocs [cdrom] path = timeout = +