Commit 6e1d2043 authored by phdeswer's avatar phdeswer

improved dynamic modes, trigger and added docs

Signed-off-by: phdeswer's avatarPhilippe De Swert <philippedeswert@gmail.com>
parent ac296c87
* verify appsync/usb_moded functionality with multiple sessions
* Fix daemon mode
......@@ -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
......
......@@ -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 <philippe.de-swert@nokia.com> Tue, 03 May 2011 14:57:38 +0300
-- Philippe De Swert <philippe.de-swert@nokia.com> Mon May 16 13:06:17 EEST 2011
usb-moded (0.37) unstable; urgency=low
......
......@@ -33,6 +33,7 @@
<credential name="CAP::net_admin" />
<credential name="CAP::sys_ptrace" />
<credential name="CAP::kill" />
<credential name="CAP::dac_override" />
<for path="/usr/sbin/usb_moded" />
</request>
</aegis>
......
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:'<mode_name>'
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:'<mode_name>'
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 <mode_name>
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
......@@ -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
......
......@@ -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 @@ cleanup:
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 */
......
......@@ -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)
{
......
......@@ -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 */
......@@ -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);
}
......
......@@ -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);
}
......
/**
@file usb_moded-trigger.c
Copyright (C) 2011 Nokia Corporation. All rights reserved.
@author: Philippe De Swert <philippe.de-swert@nokia.com>
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 <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <unistd.h>
#include <poll.h>
#include <libudev.h>
#include <glib.h>
#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;
}
}
/**
@file usb_moded-trigger.h
Copyright (C) 2011 Nokia Corporation. All rights reserved.
@author: Philippe De Swert <philippe.de-swert@nokia.com>
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);
/**
@file usb_moded-udev.c
Copyright (C) 2011 Nokia Corporation. All rights reserved.
@author: Philippe De Swert <philippe.de-swert@nokia.com>
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 <stdio.h>
#include <stdlib.h>
#include <locale.h>
......
......@@ -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 */
}
......
......@@ -12,3 +12,4 @@ mount = /home/user/MyDocs
[cdrom]
path =
timeout =
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment