Usage

  1. Wader DBus overview
  2. Operations you might want to do on a device
  3. Troubleshooting

Wader DBus overview

Wader is started automatically the first time you invoke the Wader service:

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader / es.warp.Wader.AddDevice \
    string:/org/freedesktop/Hal/devices/usb_device_12d1_1001_HUAWEI_DEVICE
method return sender=:1.47 -> dest=:1.48 reply_serial=2
   object path "/org/freedesktop/Hal/devices/usb_device_12d1_1001_HUAWEI_DEVICE"

The argument that AddDevice recibes is the root udi of the device that we are adding, in this case a Huawei E620. The AddDevice method should always be followed by a es.warp.Wader.Card.Enable operation on that device:

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1001_HUAWEI_DEVICE \
    es.warp.Wader.Card.Enable
Error es.warp.Wader.Error.PIN: CMEErrorSIMPINRequired: es.warp.Wader.Error.PIN: es.warp.Wader.Error.PIN:

The Enable method performs some initial setup on the device and will return inmediatly if the device has no auth enabled, or has been previously auth'ed. If the device needs some kind of auth, it will raise an exception with what's needed. Once auth is complete, there's no need to execute the Enable method again. After a successful auth, the device is given about fifteen seconds to settle and perform its internal setup.

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1001_HUAWEI_DEVICE \
    es.warp.Wader.PIN.Send string:0000
method return sender=:1.47 -> dest=:1.50 reply_serial=2
   string "OK"

Had the device been already auth'ed, the previous shell commands could be comprised to just:

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader / es.warp.Wader.AddDevice \
    string:/org/freedesktop/Hal/devices/usb_device_12d1_1001_HUAWEI_DEVICE
method return sender=:1.52 -> dest=:1.51 reply_serial=2
   object path "/org/freedesktop/Hal/devices/usb_device_12d1_1001_HUAWEI_DEVICE"
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1001_HUAWEI_DEVICE \
    es.warp.Wader.Card.Enable
method return sender=:1.52 -> dest=:1.53 reply_serial=2

After this point you can interact with the device as you please.

And how do you obtain the UDIs of the devices then?

Wader internally interacts with hal and requests the UDIs of all the devices that have modem capabilities. As this is the UDI of the device's child that has modem capabilities, we need to go up the haltree until we find the "root" udi of that device, i.e. the last node which still has the product and vendor tuple properties that the node hal reported as modem-capable. Why all this hassle?, you might ask, instead of directly using FindDeviceByCapability("modem") results? The reasoning behind this is because most devices register a data port and a control port. We are not only interested on dialing1, but also on monitoring the device, and for that we need the control port of the device.

Wader will emit a DeviceAdded signal when a new 3G device has been added, and a DeviceRemoved signal when a 3G device has been removed.

The method es.warp.Wader.GetDevices returns an array of object paths, one for each device found in the system.

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader / es.warp.Wader.GetDevices
method return sender=:1.156 -> dest=:1.155 reply_serial=2
   array [
      object path "/org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial"
      object path "/org/freedesktop/Hal/devices/pci_1931_c"
      object path "/org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial_0"
   ]
This is the response of GetDevices with a Huawei E172, a E870 and a Option GlobeTrotter 3G+ (Nozomi). Wader is able to detect, configure and use the three at the same time with no interference between them whatsoever:
# this small for loop will Add and Enable the three devices
$ ARRAY=(/org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial \
         /org/freedesktop/Hal/devices/pci_1931_c \
         /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial_0)
$ ELEMENTS=${#ARRAY[@]}
$ for ((i=0;i<$ELEMENTS;i++)); do
    dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
        / es.warp.Wader.AddDevice string:${ARRAY[${i}]};
    dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
        ${ARRAY[${i}]} es.warp.Wader.Card.Enable;
  done

# devices are added and enabled right now
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial_0 es.warp.Wader.Card.GetModel
method return sender=:1.166 -> dest=:1.173 reply_serial=2
   string "E272"
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial es.warp.Wader.Card.GetModel
method return sender=:1.166 -> dest=:1.174 reply_serial=2
   string "E17X"
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/pci_1931_c es.warp.Wader.Card.GetModel
method return sender=:1.166 -> dest=:1.175 reply_serial=2
   string "GlobeTrotter 3G+"
# after getting the models, lets get the signal level
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial es.warp.Wader.Network.GetSignal
method return sender=:1.166 -> dest=:1.179 reply_serial=2
   int32 26
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial_0 es.warp.Wader.Network.GetSignal
method return sender=:1.166 -> dest=:1.178 reply_serial=2
   int32 24
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/pci_1931_c es.warp.Wader.Network.GetSignal
method return sender=:1.166 -> dest=:1.177 reply_serial=2
   int32 25

Operations you might want to do on a device

Once your device is set up, you will probably want to register with a given operator, or perhaps letting Wader to choose itself? Perhaps you want to do a high-level operation such as configuring the band and connection mode? We are going to provide examples for every one of them:

Registering to a network

Registering to a network will not be necessary most of the time as the devices themselves will register to its home network. Manually specifying a MNC to connect to an arbitrary network is, of course, possible:

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1001_HUAWEI_DEVICE \
    es.warp.Wader.Network.RegisterWithNetID int32:21401
method return sender=:1.47 -> dest=:1.51 reply_serial=2

The MNC 21401 is Vodafone Spain my current network provider. If I try to connect to Telefonica's MNC 21407, the operation will probably fail horribly:

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1001_HUAWEI_DEVICE \
    es.warp.Wader.Network.RegisterWithNetID int32:21407
method return sender=:1.47 -> dest=:1.52 reply_serial=2

Oops, it didn't fail :). Although if I try to connect to Internet it will fail for sure as the APN is completely different.

Configuring connection settings

You might be interested on changing the connection mode from 2G to 3G. Or perhaps you are interested on changing from GSM1900 to GSM850 if you are roaming. Whatever your needs are, you are looking for the ConfigureConnection method. ConfigureConnection accepts a dictionary with one or two keys:

As dbus-send does not support a syntax for specifying dictionaries, we will use a python script to illustrate its usage:
# -*- coding: utf-8 -*-
# Copyright (C) 2008 Warp Networks S.L.
# Author:  Pablo Martí
"""
Example DBus client

I will configure a connection with some preferences. I obtain the device
udi as the only argument to the script i.e.

$ ./client2.py /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial
"""

import sys
import traceback

import gobject

import dbus
import dbus.mainloop.glib

from wader.common.consts import (WADER_SERVICE, WADER_OBJPATH, WADER_INTFACE,
                               CRD_INTFACE)

from wader.common.hardware import CONN_OPTS_REV, BAND_OPTS_REV

def handle_response(resp=None):
    """
    Callback for methods that emit output or not
    """
    if resp:
        print "Response", resp
    else:
        print "No response"

def handle_error(resp):
    """
    Handles any error occurred
    """
    print "ERROR", resp

def remove_device():
    """
    Removes the device specified by C{sys.argv[1]}
    """
    print "Removing device after configuring it"
    remote_object.RemoveDevice(sys.argv[1],
                               dbus_interface=WADER_INTFACE,
                               reply_handler=handle_response,
                               error_handler=handle_error)
    return True


def obtain_device():
    """
    Obtains the device specified by C{sys.argv[1]}
    """
    remote_object.AddDevice(sys.argv[1],
                            dbus_interface=WADER_INTFACE,
                            reply_handler=handle_get_device,
                            error_handler=handle_error)

    return False


def handle_get_device(opath):
    """
    Obtains a reference to C{opath} and starts device operations
    """
    # now we've obtained a proxy to the object
    device_object = bus.get_object(WADER_SERVICE, opath)
    # Enable it
    device_object.Enable(dbus_interface=CRD_INTFACE,
                         reply_handler=handle_response,
                         error_handler=handle_error)
    # in 1.5seconds configure the connection
    gobject.timeout_add(1500, configure_conn, device_object)

def configure_conn(device_object):
    """
    Configures the connection in C{device_object}
    """
    # we want 3GONLY and a WCDMA2100 band
    conf = dict(conn=CONN_OPTS_REV['3GONLY'],
                band=BAND_OPTS_REV['U2100'])
    #conf = dict(conn=1, band=4)
    # issue the ConfigureConnection command
    device_object.ConfigureConnection(conf,
                           dbus_interface=CRD_INTFACE,
                           reply_handler=handle_configure_conn,
                           error_handler=handle_error)

def handle_configure_conn(response=None):
    handle_response(response)
    gobject.timeout_add(3000, remove_device)
    return False

if __name__ == '__main__':
    if not len(sys.argv):
        raise SystemExit("You must specify a device root udi")

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SessionBus()
    try:
        # get a reference to the main WADER object
        remote_object = bus.get_object(WADER_SERVICE, WADER_OBJPATH)
    except dbus.DBusException:
        traceback.print_exc()
        sys.exit(1)

    # Start the chained calls in 1s
    gobject.timeout_add(1000, obtain_device)

    loop = gobject.MainLoop()
    loop.run()
Source listing - ../examples/client2.py
The script is a bit tricky to follow because of the chained calls, but it basically boils down to: After this operation, the device should be configured with the given parameters.

Sending a SMS

Sending a SMS can not be any easier:

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial \
    es.warp.Wader.SMS.Send string:+34606575119 string:"hey dude"
method return sender sender=:1.65 -> dest=:1.67 reply_serial=2
   uint32 21
And sending an UCS2 encoded SMS can't get any easier either:
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial \
    es.warp.Wader.SMS.Send string:+34606575119 string:中兴通讯
method return sender=:1.65 -> dest=:1.68 reply_serial=2
   uint32 22

Adding/Reading a Contact

Adding a contact to the SIM and getting the index where it was stored:

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial \
    es.warp.Wader.Contacts.Add string:Pablo string:+34545665655
method return sender=:1.54 -> dest=:1.57 reply_serial=2
   uint32 1
And reading it again:
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial \
    es.warp.Wader.Contacts.GetByIndex uint32:1
method return sender=:1.54 -> dest=:1.58 reply_serial=2
   struct {
      uint32 1
      string "Pablo"
      string "+34545665655"
   }
Now lets add another contact and read all the contacts in the SIM card:
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial \
    es.warp.Wader.Contacts.Add string:John string:+33546657784
method return sender=:1.54 -> dest=:1.60 reply_serial=2
   uint32 2
$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader \
    /org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial \
    es.warp.Wader.Contacts.List
method return sender=:1.54 -> dest=:1.61 reply_serial=2
   array [
      struct {
         uint32 1
         string "Pablo"
         string "+34545665655"
      }
      struct {
         uint32 2
         string "John"
         string "+33546657784"
      }
   ]

Troubleshooting

"Launch helper exited with unknown return code 0"

$ dbus-send --type=method_call --print-reply --dest=es.warp.Wader / es.warp.Wader.AddDevice \
    string:/org/freedesktop/Hal/devices/usb_device_12d1_1003_noserial
Error org.freedesktop.DBus.Error.Spawn.ChildExited: Launch helper exited with unknown return code 0

If the operation es.warp.Wader.AddDevice returns with the above error message, you will have to repeat the operation one more time and it will succeed this one. We do not have a workaround for this yet.

Operation X failed on my device

Every device its a world on its own, sometimes they are shipped with a buggy firmware, sometimes a device will reply to a command on a slightly different way that will break the parsing of the reply.

Wader ships with a test suite that might yield some clues about what went wrong. Instructions to execute it:

cp /usr/share/wader/resources/test/settings.conf /tmp
trial -r gtk2 wader.test
We need to copy that file to /tmp first because as we are going to fiddle with the PIN, SMSC and other potential dangerous settings, we need a place where the "good" ones are stored. Do not forget to edit the file according to your settings! Also do not forget the -r gtk2 switch, it will pick the gtk2 reactor to run the tests, otherwise all the glib-dependent tests, like the DBus ones would fail.

Footnotes

  1. As FindDeviceByCapability("modem") returns the UDIs of all the data ports