aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
l---------COPYING1
-rw-r--r--ChangeLog0
-rw-r--r--Makefile.am30
-rw-r--r--ModemManager.pc.in12
-rw-r--r--NEWS0
-rw-r--r--README0
-rwxr-xr-xautogen.sh20
-rw-r--r--configure.in24
-rw-r--r--introspection/Makefile.am4
-rw-r--r--introspection/mm-manager.xml19
-rw-r--r--introspection/mm-modem.xml85
-rw-r--r--org.freedesktop.ModemManager.conf19
-rw-r--r--org.freedesktop.ModemManager.service.in4
-rw-r--r--plugins/Makefile.am14
-rw-r--r--plugins/mm-modem-huawei.c556
-rw-r--r--plugins/mm-modem-huawei.h31
-rw-r--r--plugins/mm-plugin-huawei.c155
-rw-r--r--plugins/mm-plugin-huawei.h26
-rw-r--r--src/Makefile.am39
-rw-r--r--src/main.c166
-rw-r--r--src/mm-callback-info.c98
-rw-r--r--src/mm-callback-info.h38
-rw-r--r--src/mm-generic-cdma.c259
-rw-r--r--src/mm-generic-cdma.h29
-rw-r--r--src/mm-generic-gsm.c705
-rw-r--r--src/mm-generic-gsm.h29
-rw-r--r--src/mm-manager.c457
-rw-r--r--src/mm-manager.h36
-rw-r--r--src/mm-modem-error.c37
-rw-r--r--src/mm-modem-error.h22
-rw-r--r--src/mm-modem.c460
-rw-r--r--src/mm-modem.h196
-rw-r--r--src/mm-plugin.c81
-rw-r--r--src/mm-plugin.h55
-rw-r--r--src/mm-serial.c1117
-rw-r--r--src/mm-serial.h85
37 files changed, 4911 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 00000000..12af19c8
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,2 @@
+Tambet Ingo <tambet@gmail.com>
+
diff --git a/COPYING b/COPYING
new file mode 120000
index 00000000..0b6cbf81
--- /dev/null
+++ b/COPYING
@@ -0,0 +1 @@
+/usr/share/automake-1.10/COPYING \ No newline at end of file
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ChangeLog
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 00000000..e0de740d
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,30 @@
+SUBDIRS = src plugins introspection
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = ModemManager.pc
+
+dbusservicedir = $(DBUS_SYS_DIR)
+dbusservice_DATA = org.freedesktop.ModemManager.conf
+
+dbusactivationdir = $(datadir)/dbus-1/system-services
+dbusactivation_in_files = org.freedesktop.ModemManager.service.in
+dbusactivation_DATA = $(dbusactivation_in_files:.service.in=.service)
+
+%service: %service.in
+ $(edit) $< >$@
+
+edit = @sed \
+ -e 's|@sbindir[@]|$(sbindir)|g' \
+ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \
+ -e 's|@localstatedir[@]|$(localstatedir)|g' \
+ -e 's|@libexecdir[@]|$(libexecdir)|g'
+
+DISTCLEANFILES = \
+ ModemManager.pc \
+ $(dbusactivation_DATA)
+
+
+EXTRA_DIST = \
+ ModemManager.pc.in \
+ $(dbusservice_DATA) \
+ $(dbusactivation_in_files)
diff --git a/ModemManager.pc.in b/ModemManager.pc.in
new file mode 100644
index 00000000..9214209a
--- /dev/null
+++ b/ModemManager.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+libgnome_serverdir=@libexecdir@
+
+Name: ModemManager
+Description: DBus service to interact with modems.
+Requires: dbus-glib-1 glib-2.0
+Version: @VERSION@
+Cflags: -I${includedir}/ModemManager
+Libs: -L${libdir} -lmm
diff --git a/NEWS b/NEWS
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/README
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 00000000..7d7a26dc
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+REQUIRED_AUTOMAKE_VERSION=1.7
+PKG_NAME=ModemManager
+
+(test -f $srcdir/configure.in \
+ && test -f $srcdir/src/mm-modem.c) || {
+ echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+ echo " top-level $PKG_NAME directory"
+ exit 1
+}
+
+(cd $srcdir;
+ autoreconf --install --symlink &&
+ autoreconf &&
+ ./configure --enable-maintainer-mode $@
+)
diff --git a/configure.in b/configure.in
new file mode 100644
index 00000000..98f2e1c8
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,24 @@
+AC_PREREQ(2.52)
+
+AC_INIT(ModemManager, 0.1, tambet@gmail.com, ModemManager)
+AM_INIT_AUTOMAKE([])
+AM_MAINTAINER_MODE
+
+AC_CONFIG_HEADERS(config.h)
+
+dnl Required programs
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+PKG_CHECK_MODULES(MM, dbus-glib-1 >= 0.75 glib-2.0 >= 2.14 gmodule-2.0 gobject-2.0 hal)
+
+AC_CONFIG_FILES([
+Makefile
+src/Makefile
+plugins/Makefile
+introspection/Makefile
+ModemManager.pc
+])
+AC_OUTPUT
diff --git a/introspection/Makefile.am b/introspection/Makefile.am
new file mode 100644
index 00000000..a3c5b122
--- /dev/null
+++ b/introspection/Makefile.am
@@ -0,0 +1,4 @@
+EXTRA_DIST = \
+ mm-manager.xml \
+ mm-modem.xml
+
diff --git a/introspection/mm-manager.xml b/introspection/mm-manager.xml
new file mode 100644
index 00000000..55912905
--- /dev/null
+++ b/introspection/mm-manager.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/">
+ <interface name="org.freedesktop.ModemManager">
+ <method name="EnumerateDevices">
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_manager_enumerate_devices"/>
+ <arg name="devices" type="ao" direction="out"/>
+ </method>
+
+ <signal name="DeviceAdded">
+ <arg name="device" type="o"/>
+ </signal>
+
+ <signal name="DeviceRemoved">
+ <arg name="device" type="o"/>
+ </signal>
+
+ </interface>
+</node>
diff --git a/introspection/mm-modem.xml b/introspection/mm-modem.xml
new file mode 100644
index 00000000..44bb72df
--- /dev/null
+++ b/introspection/mm-modem.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/">
+ <interface name="org.freedesktop.ModemManager.Modem">
+ <method name="Enable">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_enable"/>
+ <arg name="enable" type="b" direction="in"/>
+ </method>
+
+ <method name="SetPin">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_set_pin"/>
+ <arg name="secret" type="s" direction="in"/>
+ </method>
+
+ <method name="Register">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_register"/>
+ <arg name="network_id" type="s" direction="in"/>
+ </method>
+
+ <method name="Connect">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_connect"/>
+ <arg name="number" type="s" direction="in"/>
+ <arg name="apn" type="s" direction="in"/>
+ </method>
+
+ <method name="Disconnect">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_disconnect"/>
+ </method>
+
+ <method name="Scan">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_scan"/>
+ <arg name="results" type="aa{ss}" direction="out"/>
+ </method>
+
+ <method name="GetSignalQuality">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_get_signal_quality"/>
+ <arg name="quality" type="u" direction="out"/>
+ </method>
+
+ <method name="SetBand">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_set_band"/>
+ <arg name="band" type="u" direction="in"/>
+ </method>
+
+ <method name="GetBand">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_get_band"/>
+ <arg name="band" type="u" direction="out"/>
+ </method>
+
+ <method name="SetNetworkMode">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_set_network_mode"/>
+ <arg name="mode" type="u" direction="in"/>
+ </method>
+
+ <method name="GetNetworkMode">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_get_network_mode"/>
+ <arg name="mode" type="u" direction="out"/>
+ </method>
+
+
+ <property name="DataDevice" type="s" access="read"/>
+ <property name="Driver" type="s" access="read"/>
+ <property name="Type" type="u" access="read"/>
+
+ <signal name="SignalQuality">
+ <arg name="quality" type="u"/>
+ </signal>
+
+ <signal name="NetworkMode">
+ <arg name="mode" type="u"/>
+ </signal>
+
+ </interface>
+</node>
diff --git a/org.freedesktop.ModemManager.conf b/org.freedesktop.ModemManager.conf
new file mode 100644
index 00000000..a4af7a93
--- /dev/null
+++ b/org.freedesktop.ModemManager.conf
@@ -0,0 +1,19 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="root">
+ <allow own="org.freedesktop.ModemManager"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"/>
+ <allow send_interface="org.freedesktop.ModemManager"/>
+ </policy>
+ <policy context="default">
+ <deny own="org.freedesktop.ModemManager"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"/>
+ <allow send_interface="org.freedesktop.ModemManager"/>
+ </policy>
+
+ <limit name="max_replies_per_connection">512</limit>
+</busconfig>
diff --git a/org.freedesktop.ModemManager.service.in b/org.freedesktop.ModemManager.service.in
new file mode 100644
index 00000000..2b73ccea
--- /dev/null
+++ b/org.freedesktop.ModemManager.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.freedesktop.ModemManager
+Exec=@sbindir@/modem-manager
+User=root
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
new file mode 100644
index 00000000..1ca505ac
--- /dev/null
+++ b/plugins/Makefile.am
@@ -0,0 +1,14 @@
+pkglib_LTLIBRARIES = \
+ libmm-plugin-huawei.la
+
+libmm_plugin_huawei_la_SOURCES = \
+ mm-modem-huawei.c \
+ mm-modem-huawei.h \
+ mm-plugin-huawei.c \
+ mm-plugin-huawei.h
+
+libmm_plugin_huawei_la_CPPFLAGS = \
+ $(MM_CFLAGS) \
+ -I$(top_srcdir)/src
+
+libmm_plugin_huawei_la_LDFLAGS = -modeule -avoid-version
diff --git a/plugins/mm-modem-huawei.c b/plugins/mm-modem-huawei.c
new file mode 100644
index 00000000..990d1d03
--- /dev/null
+++ b/plugins/mm-modem-huawei.c
@@ -0,0 +1,556 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "mm-modem-huawei.h"
+#include "mm-modem-error.h"
+#include "mm-callback-info.h"
+
+static MMModem *parent_class_iface = NULL;
+
+static void modem_init (MMModem *modem_class);
+
+G_DEFINE_TYPE_EXTENDED (MMModemHuawei, mm_modem_huawei, MM_TYPE_GENERIC_GSM,
+ 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init))
+
+#define MM_MODEM_HUAWEI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HUAWEI, MMModemHuaweiPrivate))
+
+typedef struct {
+ MMSerial *monitor_device;
+} MMModemHuaweiPrivate;
+
+enum {
+ PROP_0,
+ PROP_MONITOR_DEVICE,
+
+ LAST_PROP
+};
+
+MMModem *
+mm_modem_huawei_new (const char *data_device,
+ const char *monitor_device,
+ const char *driver)
+{
+ g_return_val_if_fail (data_device != NULL, NULL);
+ g_return_val_if_fail (monitor_device != NULL, NULL);
+ g_return_val_if_fail (driver != NULL, NULL);
+
+ return MM_MODEM (g_object_new (MM_TYPE_MODEM_HUAWEI,
+ MM_SERIAL_DEVICE, data_device,
+ MM_MODEM_DRIVER, driver,
+ MM_MODEM_HUAWEI_MONITOR_DEVICE, monitor_device,
+ NULL));
+}
+
+
+/*****************************************************************************/
+
+static void
+parse_monitor_line (MMModem *modem, char *buf)
+{
+ char **lines;
+ char **iter;
+
+ lines = g_strsplit (buf, "\r\n", 0);
+
+ for (iter = lines; iter && *iter; iter++) {
+ char *line = *iter;
+
+ g_strstrip (line);
+ if (strlen (line) < 1 || line[0] != '^')
+ continue;
+
+ line += 1;
+
+ if (!strncmp (line, "RSSI:", 5)) {
+ int quality = atoi (line + 5);
+
+ if (quality == 99)
+ /* 99 means unknown */
+ quality = 0;
+ else
+ /* Normalize the quality */
+ quality = quality * 100 / 31;
+
+ g_debug ("Signal quality: %d", quality);
+ mm_modem_signal_quality (modem, (guint32) quality);
+ } else if (!strncmp (line, "MODE:", 5)) {
+ MMModemNetworkMode mode = 0;
+ int a;
+ int b;
+
+ if (sscanf (line + 5, "%d,%d", &a, &b)) {
+ if (a == 3 && b == 2)
+ mode = MM_MODEM_NETWORK_MODE_GPRS;
+ else if (a == 3 && b == 3)
+ mode = MM_MODEM_NETWORK_MODE_EDGE;
+ else if (a == 5 && b == 4)
+ mode = MM_MODEM_NETWORK_MODE_3G;
+ else if (a ==5 && b == 5)
+ mode = MM_MODEM_NETWORK_MODE_HSDPA;
+
+ if (mode) {
+ g_debug ("Mode: %d", mode);
+ mm_modem_network_mode (modem, mode);
+ }
+ }
+ }
+ }
+
+ g_strfreev (lines);
+}
+
+static gboolean
+monitor_device_got_data (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ gsize bytes_read;
+ char buf[4096];
+ GIOStatus status;
+
+ if (condition & G_IO_IN) {
+ do {
+ status = g_io_channel_read_chars (source, buf, 4096, &bytes_read, NULL);
+
+ if (bytes_read) {
+ buf[bytes_read] = '\0';
+ parse_monitor_line (MM_MODEM (data), buf);
+ }
+ } while (bytes_read == 4096 || status == G_IO_STATUS_AGAIN);
+ }
+
+ if (condition & G_IO_HUP || condition & G_IO_ERR) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+enable (MMModem *modem,
+ gboolean enable,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (modem);
+
+ if (enable) {
+ GIOChannel *channel;
+
+ mm_serial_open (priv->monitor_device);
+ channel = mm_serial_get_io_channel (priv->monitor_device);
+ g_io_add_watch (channel, G_IO_IN | G_IO_ERR | G_IO_HUP,
+ monitor_device_got_data, modem);
+
+ g_io_channel_unref (channel);
+ } else
+ mm_serial_close (priv->monitor_device);
+
+ parent_class_iface->enable (modem, enable, callback, user_data);
+}
+
+static gboolean
+parse_syscfg (const char *reply, int *mode_a, int *mode_b, guint32 *band, int *unknown1, int *unknown2)
+{
+ if (strncmp (reply, "^SYSCFG:", 8))
+ return FALSE;
+
+ if (sscanf (reply + 8, "%d,%d,%x,%d,%d", mode_a, mode_b, band, unknown1, unknown2))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+get_network_mode_done (MMSerial *serial, const char *reply, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ int a, b, u1, u2;
+ guint32 band;
+
+ if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) {
+ if (a == 2 && b == 1)
+ info->uint_result = MM_MODEM_NETWORK_MODE_PREFER_2G;
+ else if (a == 2 && b == 2)
+ info->uint_result = MM_MODEM_NETWORK_MODE_PREFER_3G;
+ else if (a == 13 && b == 1)
+ info->uint_result = MM_MODEM_NETWORK_MODE_GPRS;
+ else if (a == 14 && b == 2)
+ info->uint_result = MM_MODEM_NETWORK_MODE_3G;
+ }
+
+ if (info->uint_result == 0)
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Could not parse network mode results");
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_network_mode (MMModem *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *terminators = "\r\n";
+ guint id = 0;
+
+ info = mm_callback_info_uint_new (modem, callback, user_data);
+
+ if (mm_serial_send_command_string (MM_SERIAL (modem), "AT^SYSCFG?"))
+ id = mm_serial_get_reply (MM_SERIAL (modem), 10, terminators, get_network_mode_done, info);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Getting network mode failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+set_network_mode_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* Success */
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Setting network mode failed");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_network_mode_get_done (MMSerial *serial, const char *reply, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ int a, b, u1, u2;
+ guint32 band;
+ guint id = 0;
+
+ if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) {
+ char *responses[] = { "OK", "ERROR", NULL };
+ char *command;
+
+ a = GPOINTER_TO_INT (mm_callback_info_get_data (info, "mode-a"));
+ b = GPOINTER_TO_INT (mm_callback_info_get_data (info, "mode-b"));
+ command = g_strdup_printf ("AT^SYSCFG=%d,%d,%X,%d,%d", a, b, band, u1, u2);
+
+ if (mm_serial_send_command_string (serial, command))
+ id = mm_serial_wait_for_reply (serial, 3, responses, responses, set_network_mode_done, info);
+
+ g_free (command);
+ }
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Could not set network mode");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+set_network_mode (MMModem *modem,
+ MMModemNetworkMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *terminators = "\r\n";
+ guint id = 0;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ switch (mode) {
+ case MM_MODEM_NETWORK_MODE_ANY:
+ /* Do nothing */
+ mm_callback_info_schedule (info);
+ return;
+ break;
+ case MM_MODEM_NETWORK_MODE_GPRS:
+ case MM_MODEM_NETWORK_MODE_EDGE:
+ mm_callback_info_set_data (info, "mode-a", GINT_TO_POINTER (13), NULL);
+ mm_callback_info_set_data (info, "mode-b", GINT_TO_POINTER (1), NULL);
+ break;
+ case MM_MODEM_NETWORK_MODE_3G:
+ case MM_MODEM_NETWORK_MODE_HSDPA:
+ mm_callback_info_set_data (info, "mode-a", GINT_TO_POINTER (14), NULL);
+ mm_callback_info_set_data (info, "mode-b", GINT_TO_POINTER (2), NULL);
+ break;
+ case MM_MODEM_NETWORK_MODE_PREFER_2G:
+ mm_callback_info_set_data (info, "mode-a", GINT_TO_POINTER (2), NULL);
+ mm_callback_info_set_data (info, "mode-b", GINT_TO_POINTER (1), NULL);
+ break;
+ case MM_MODEM_NETWORK_MODE_PREFER_3G:
+ mm_callback_info_set_data (info, "mode-a", GINT_TO_POINTER (2), NULL);
+ mm_callback_info_set_data (info, "mode-b", GINT_TO_POINTER (2), NULL);
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Invalid mode.");
+ mm_callback_info_schedule (info);
+ return;
+ break;
+ }
+
+ if (mm_serial_send_command_string (MM_SERIAL (modem), "AT^SYSCFG?"))
+ id = mm_serial_get_reply (MM_SERIAL (modem), 10, terminators, set_network_mode_get_done, info);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Setting network mode failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+get_band_done (MMSerial *serial, const char *reply, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ int a, b, u1, u2;
+ guint32 band;
+
+ info->uint_result = 0xdeadbeaf;
+
+ if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) {
+ if (band == 0x3FFFFFFF)
+ info->uint_result = MM_MODEM_BAND_ANY;
+ else if (band == 0x400380)
+ info->uint_result = MM_MODEM_BAND_DCS;
+ else if (band == 0x200000)
+ info->uint_result = MM_MODEM_BAND_PCS;
+ }
+
+ if (info->uint_result == 0xdeadbeaf) {
+ info->uint_result = 0;
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Could not parse band results");
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_band (MMModem *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *terminators = "\r\n";
+ guint id = 0;
+
+ info = mm_callback_info_uint_new (modem, callback, user_data);
+
+ if (mm_serial_send_command_string (MM_SERIAL (modem), "AT^SYSCFG?"))
+ id = mm_serial_get_reply (MM_SERIAL (modem), 10, terminators, get_band_done, info);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Getting band failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+set_band_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* Success */
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Setting band failed");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_band_get_done (MMSerial *serial, const char *reply, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ int a, b, u1, u2;
+ guint32 band;
+ guint id = 0;
+
+ if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) {
+ char *responses[] = { "OK", "ERROR", NULL };
+ char *command;
+
+ band = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "band"));
+ command = g_strdup_printf ("AT^SYSCFG=%d,%d,%X,%d,%d", a, b, band, u1, u2);
+
+ if (mm_serial_send_command_string (serial, command))
+ id = mm_serial_wait_for_reply (serial, 3, responses, responses, set_band_done, info);
+
+ g_free (command);
+ }
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Could not set band");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+set_band (MMModem *modem,
+ MMModemBand band,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *terminators = "\r\n";
+ guint id = 0;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ switch (band) {
+ case MM_MODEM_BAND_ANY:
+ mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (0x3FFFFFFF), NULL);
+ break;
+ case MM_MODEM_BAND_EGSM:
+ case MM_MODEM_BAND_DCS:
+ case MM_MODEM_BAND_U2100:
+ mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (0x400380), NULL);
+ break;
+ case MM_MODEM_BAND_PCS:
+ mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (0x200000), NULL);
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Invalid band.");
+ mm_callback_info_schedule (info);
+ return;
+ break;
+ }
+
+ if (mm_serial_send_command_string (MM_SERIAL (modem), "AT^SYSCFG?"))
+ id = mm_serial_get_reply (MM_SERIAL (modem), 10, terminators, set_band_get_done, info);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Setting band failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+modem_init (MMModem *modem_class)
+{
+ parent_class_iface = g_type_interface_peek_parent (modem_class);
+
+ /* interface implementation */
+ modem_class->enable = enable;
+ modem_class->set_network_mode = set_network_mode;
+ modem_class->get_network_mode = get_network_mode;
+ modem_class->set_band = set_band;
+ modem_class->get_band = get_band;
+}
+
+static void
+mm_modem_huawei_init (MMModemHuawei *self)
+{
+}
+
+static GObject*
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ MMModemHuaweiPrivate *priv;
+
+ object = G_OBJECT_CLASS (mm_modem_huawei_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+ if (!object)
+ return NULL;
+
+ priv = MM_MODEM_HUAWEI_GET_PRIVATE (object);
+
+ if (!priv->monitor_device) {
+ g_warning ("No monitor device provided");
+ g_object_unref (object);
+ return NULL;
+ }
+
+ return object;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MONITOR_DEVICE:
+ /* Construct only */
+ priv->monitor_device = MM_SERIAL (g_object_new (MM_TYPE_SERIAL,
+ MM_SERIAL_DEVICE, g_value_get_string (value),
+ NULL));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MONITOR_DEVICE:
+ g_value_set_string (value, mm_serial_get_device (priv->monitor_device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+static void
+finalize (GObject *object)
+{
+ MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (object);
+
+ if (priv->monitor_device)
+ g_object_unref (priv->monitor_device);
+
+ G_OBJECT_CLASS (mm_modem_huawei_parent_class)->finalize (object);
+}
+
+static void
+mm_modem_huawei_class_init (MMModemHuaweiClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMModemHuaweiPrivate));
+
+ /* Virtual methods */
+ object_class->constructor = constructor;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* Properties */
+ g_object_class_install_property
+ (object_class, PROP_MONITOR_DEVICE,
+ g_param_spec_string (MM_MODEM_HUAWEI_MONITOR_DEVICE,
+ "MonitorDevice",
+ "Monitor device",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
diff --git a/plugins/mm-modem-huawei.h b/plugins/mm-modem-huawei.h
new file mode 100644
index 00000000..dd377581
--- /dev/null
+++ b/plugins/mm-modem-huawei.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_MODEM_HUAWEI_H
+#define MM_MODEM_HUAWEI_H
+
+#include "mm-generic-gsm.h"
+
+#define MM_TYPE_MODEM_HUAWEI (mm_modem_huawei_get_type ())
+#define MM_MODEM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_HUAWEI, MMModemHuawei))
+#define MM_MODEM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_HUAWEI, MMModemHuaweiClass))
+#define MM_IS_MODEM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_HUAWEI))
+#define MM_IS_MODEM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_HUAWEI))
+#define MM_MODEM_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_HUAWEI, MMModemHuaweiClass))
+
+#define MM_MODEM_HUAWEI_MONITOR_DEVICE "monitor-device"
+
+typedef struct {
+ MMGenericGsm parent;
+} MMModemHuawei;
+
+typedef struct {
+ MMGenericGsmClass parent;
+} MMModemHuaweiClass;
+
+GType mm_modem_huawei_get_type (void);
+
+MMModem *mm_modem_huawei_new (const char *data_device,
+ const char *monitor_device,
+ const char *driver);
+
+#endif /* MM_MODEM_HUAWEI_H */
diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c
new file mode 100644
index 00000000..6c621fdb
--- /dev/null
+++ b/plugins/mm-plugin-huawei.c
@@ -0,0 +1,155 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <string.h>
+#include <gmodule.h>
+#include "mm-plugin-huawei.h"
+#include "mm-modem-huawei.h"
+
+static void plugin_init (MMPlugin *plugin_class);
+
+G_DEFINE_TYPE_EXTENDED (MMPluginHuawei, mm_plugin_huawei, G_TYPE_OBJECT,
+ 0, G_IMPLEMENT_INTERFACE (MM_TYPE_PLUGIN, plugin_init))
+
+int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
+int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_HUAWEI, NULL));
+}
+
+/*****************************************************************************/
+
+static const char *
+get_name (MMPlugin *plugin)
+{
+ return "Huawei";
+}
+
+static char **
+list_supported_udis (MMPlugin *plugin, LibHalContext *hal_ctx)
+{
+ char **supported = NULL;
+ char **devices;
+ int num_devices;
+ int i;
+
+ devices = libhal_find_device_by_capability (hal_ctx, "modem", &num_devices, NULL);
+ if (devices) {
+ GPtrArray *array;
+
+ array = g_ptr_array_new ();
+
+ for (i = 0; i < num_devices; i++) {
+ char *udi = devices[i];
+
+ if (mm_plugin_supports_udi (plugin, hal_ctx, udi))
+ g_ptr_array_add (array, g_strdup (udi));
+ }
+
+ if (array->len > 0) {
+ g_ptr_array_add (array, NULL);
+ supported = (char **) g_ptr_array_free (array, FALSE);
+ } else
+ g_ptr_array_free (array, TRUE);
+ }
+
+ g_strfreev (devices);
+
+ return supported;
+}
+
+static gboolean
+supports_udi (MMPlugin *plugin, LibHalContext *hal_ctx, const char *udi)
+{
+ char **capabilities;
+ char **iter;
+ gboolean supported = FALSE;
+
+ capabilities = libhal_device_get_property_strlist (hal_ctx, udi, "modem.command_sets", NULL);
+ for (iter = capabilities; iter && *iter && !supported; iter++) {
+ if (!strcmp (*iter, "GSM-07.07")) {
+ char *parent_udi;
+
+ parent_udi = libhal_device_get_property_string (hal_ctx, udi, "info.parent", NULL);
+ if (parent_udi) {
+ int vendor;
+ int product;
+
+ vendor = libhal_device_get_property_int (hal_ctx, parent_udi, "usb.vendor_id", NULL);
+ product = libhal_device_get_property_int (hal_ctx, parent_udi, "usb.product_id", NULL);
+
+ if (vendor == 0x12d1 && (product == 0x1001 || product == 0x1003 || product == 0x1004))
+ supported = TRUE;
+
+ libhal_free_string (parent_udi);
+ }
+ }
+ }
+ g_strfreev (capabilities);
+
+ return supported;
+}
+
+static char *
+get_driver_name (LibHalContext *ctx, const char *udi)
+{
+ char *parent_udi;
+ char *driver = NULL;
+
+ parent_udi = libhal_device_get_property_string (ctx, udi, "info.parent", NULL);
+ if (parent_udi) {
+ driver = libhal_device_get_property_string (ctx, parent_udi, "info.linux.driver", NULL);
+ libhal_free_string (parent_udi);
+ }
+
+ return driver;
+}
+
+static MMModem *
+create_modem (MMPlugin *plugin, LibHalContext *hal_ctx, const char *udi)
+{
+ char *data_device;
+ char *monitor_device;
+ char *driver;
+ MMModem *modem;
+
+ data_device = libhal_device_get_property_string (hal_ctx, udi, "serial.device", NULL);
+ g_return_val_if_fail (data_device != NULL, NULL);
+
+ driver = get_driver_name (hal_ctx, udi);
+ g_return_val_if_fail (driver != NULL, NULL);
+
+ /* FIXME: Get monitoring device from HAL */
+ monitor_device = "/dev/ttyUSB1";
+
+ modem = MM_MODEM (mm_modem_huawei_new (data_device, monitor_device, driver));
+
+ g_free (data_device);
+ g_free (driver);
+
+ return modem;
+}
+
+/*****************************************************************************/
+
+static void
+plugin_init (MMPlugin *plugin_class)
+{
+ /* interface implementation */
+ plugin_class->get_name = get_name;
+ plugin_class->list_supported_udis = list_supported_udis;
+ plugin_class->supports_udi = supports_udi;
+ plugin_class->create_modem = create_modem;
+}
+
+static void
+mm_plugin_huawei_init (MMPluginHuawei *self)
+{
+}
+
+static void
+mm_plugin_huawei_class_init (MMPluginHuaweiClass *klass)
+{
+}
diff --git a/plugins/mm-plugin-huawei.h b/plugins/mm-plugin-huawei.h
new file mode 100644
index 00000000..291c18d9
--- /dev/null
+++ b/plugins/mm-plugin-huawei.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_PLUGIN_HUAWEI_H
+#define MM_PLUGIN_HUAWEI_H
+
+#include "mm-plugin.h"
+#include "mm-generic-gsm.h"
+
+#define MM_TYPE_PLUGIN_HUAWEI (mm_plugin_huawei_get_type ())
+#define MM_PLUGIN_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuawei))
+#define MM_PLUGIN_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuaweiClass))
+#define MM_IS_PLUGIN_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_HUAWEI))
+#define MM_IS_PLUGIN_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_HUAWEI))
+#define MM_PLUGIN_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuaweiClass))
+
+typedef struct {
+ GObject parent;
+} MMPluginHuawei;
+
+typedef struct {
+ GObjectClass parent;
+} MMPluginHuaweiClass;
+
+GType mm_plugin_huawei_get_type (void);
+
+#endif /* MM_PLUGIN_HUAWEI_H */
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 00000000..5a0a317e
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,39 @@
+sbin_PROGRAMS = modem-manager
+
+modem_manager_CPPFLAGS = \
+ $(MM_CFLAGS) \
+ -DPLUGINDIR=\"$(pkglibdir)\"
+
+modem_manager_LDADD = \
+ $(MM_LIBS)
+
+modem_manager_SOURCES = \
+ main.c \
+ mm-callback-info.c \
+ mm-callback-info.h \
+ mm-generic-cdma.c \
+ mm-generic-cdma.h \
+ mm-generic-gsm.c \
+ mm-generic-gsm.h \
+ mm-manager.c \
+ mm-manager.h \
+ mm-modem.c \
+ mm-modem.h \
+ mm-modem-error.c \
+ mm-modem-error.h \
+ mm-plugin.c \
+ mm-plugin.h \
+ mm-serial.c \
+ mm-serial.h
+
+mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml
+ dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $<
+
+mm-modem-glue.h: $(top_srcdir)/introspection/mm-modem.xml
+ dbus-binding-tool --prefix=mm_modem --mode=glib-server --output=$@ $<
+
+BUILT_SOURCES = \
+ mm-manager-glue.h \
+ mm-modem-glue.h
+
+CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 00000000..a18ad3b5
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,166 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <syslog.h>
+#include <dbus/dbus-glib.h>
+#include "mm-manager.h"
+
+static void
+log_handler (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer ignored)
+{
+ int syslog_priority;
+
+ switch (log_level) {
+ case G_LOG_LEVEL_ERROR:
+ syslog_priority = LOG_CRIT;
+ break;
+
+ case G_LOG_LEVEL_CRITICAL:
+ syslog_priority = LOG_ERR;
+ break;
+
+ case G_LOG_LEVEL_WARNING:
+ syslog_priority = LOG_WARNING;
+ break;
+
+ case G_LOG_LEVEL_MESSAGE:
+ syslog_priority = LOG_NOTICE;
+ break;
+
+ case G_LOG_LEVEL_DEBUG:
+ syslog_priority = LOG_DEBUG;
+ break;
+
+ case G_LOG_LEVEL_INFO:
+ default:
+ syslog_priority = LOG_INFO;
+ break;
+ }
+
+ syslog (syslog_priority, "%s", message);
+}
+
+
+static void
+logging_setup (void)
+{
+ openlog (G_LOG_DOMAIN, LOG_CONS, LOG_DAEMON);
+ g_log_set_handler (G_LOG_DOMAIN,
+ G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
+ log_handler,
+ NULL);
+}
+
+static void
+logging_shutdown (void)
+{
+ closelog ();
+}
+
+static void
+destroy_cb (DBusGProxy *proxy, gpointer user_data)
+{
+ GMainLoop *loop = (GMainLoop *) user_data;
+
+ g_message ("disconnected from the system bus, exiting.");
+ g_main_loop_quit (loop);
+}
+
+static gboolean
+dbus_init (GMainLoop *loop)
+{
+ DBusGConnection *connection;
+ DBusGProxy *proxy;
+ GError *err = NULL;
+ int request_name_result;
+
+ connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
+ if (!connection) {
+ g_warning ("Could not get the system bus. Make sure "
+ "the message bus daemon is running! Message: %s",
+ err->message);
+ g_error_free (err);
+ return FALSE;
+ }
+
+ proxy = dbus_g_proxy_new_for_name (connection,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus");
+
+ if (!dbus_g_proxy_call (proxy, "RequestName", &err,
+ G_TYPE_STRING, MM_DBUS_SERVICE,
+ G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &request_name_result,
+ G_TYPE_INVALID)) {
+ g_warning ("Could not acquire the %s service.\n"
+ " Message: '%s'", MM_DBUS_SERVICE, err->message);
+ g_error_free (err);
+ goto err;
+ }
+
+ if (request_name_result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ g_warning ("Could not acquire the NetworkManagerSystemSettings service "
+ "as it is already taken. Return: %d",
+ request_name_result);
+ goto err;
+ }
+
+ g_signal_connect (proxy, "destroy", G_CALLBACK (destroy_cb), loop);
+
+ return TRUE;
+
+ err:
+ dbus_g_connection_unref (connection);
+ g_object_unref (proxy);
+
+ return FALSE;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GOptionContext *opt_ctx;
+ GError *error = NULL;
+ GMainLoop *loop;
+ MMManager *manager;
+ gboolean debug = FALSE;
+ GOptionEntry entries[] = {
+ { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL },
+ { NULL }
+ };
+
+ opt_ctx = g_option_context_new (NULL);
+ g_option_context_set_summary (opt_ctx, "DBus system service to communicate with modems.");
+ g_option_context_add_main_entries (opt_ctx, entries, NULL);
+
+ if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) {
+ g_warning ("%s\n", error->message);
+ g_error_free (error);
+ return 1;
+ }
+
+ g_option_context_free (opt_ctx);
+
+ g_type_init ();
+
+ if (!debug)
+ logging_setup ();
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ if (!dbus_init (loop))
+ return -1;
+
+ manager = mm_manager_new ();
+
+ g_main_loop_run (loop);
+
+ g_object_unref (manager);
+ logging_shutdown ();
+
+ return 0;
+}
diff --git a/src/mm-callback-info.c b/src/mm-callback-info.c
new file mode 100644
index 00000000..d4fa55c5
--- /dev/null
+++ b/src/mm-callback-info.c
@@ -0,0 +1,98 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include "mm-callback-info.h"
+#include "mm-modem-error.h"
+
+static void
+callback_info_done (gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ info->pending_id = 0;
+
+ if (info->callback)
+ info->callback (info->modem, info->error, info->user_data);
+ else if (info->uint_callback)
+ info->uint_callback (info->modem, info->uint_result, info->error, info->user_data);
+
+ if (info->error)
+ g_error_free (info->error);
+
+ g_object_unref (info->modem);
+ g_datalist_clear (&info->qdata);
+
+ g_slice_free (MMCallbackInfo, info);
+}
+
+static gboolean
+callback_info_do (gpointer user_data)
+{
+ /* Nothing here, everything is done in callback_info_done to make sure the info->callback
+ always gets called, even if the pending call gets cancelled. */
+ return FALSE;
+}
+
+void
+mm_callback_info_schedule (MMCallbackInfo *info)
+{
+ info->pending_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, callback_info_do, info, callback_info_done);
+}
+
+void
+mm_callback_info_cancel (MMCallbackInfo *info)
+{
+ if (info->pending_id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Call cancelled");
+ mm_callback_info_schedule (info);
+ }
+}
+
+MMCallbackInfo *
+mm_callback_info_new (MMModem *modem, MMModemFn callback, gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = g_slice_new0 (MMCallbackInfo);
+ g_datalist_init (&info->qdata);
+ info->modem = g_object_ref (modem);
+ info->callback = callback;
+ info->user_data = user_data;
+
+ return info;
+}
+
+MMCallbackInfo *
+mm_callback_info_uint_new (MMModem *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = g_slice_new0 (MMCallbackInfo);
+ g_datalist_init (&info->qdata);
+ info->modem = g_object_ref (modem);
+ info->uint_callback = callback;
+ info->user_data = user_data;
+
+ return info;
+}
+
+void
+mm_callback_info_set_data (MMCallbackInfo *info,
+ const char *key,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ g_datalist_id_set_data_full (&info->qdata, g_quark_from_string (key), data,
+ data ? destroy : (GDestroyNotify) NULL);
+}
+
+gpointer
+mm_callback_info_get_data (MMCallbackInfo *info, const char *key)
+{
+ GQuark quark;
+
+ quark = g_quark_try_string (key);
+
+ return quark ? g_datalist_id_get_data (&info->qdata, quark) : NULL;
+}
diff --git a/src/mm-callback-info.h b/src/mm-callback-info.h
new file mode 100644
index 00000000..c8321bb1
--- /dev/null
+++ b/src/mm-callback-info.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_CALLBACK_INFO_H
+#define MM_CALLBACK_INFO_H
+
+#include "mm-modem.h"
+
+typedef struct {
+ GData *qdata;
+ MMModem *modem;
+ MMModemFn callback;
+ MMModemUIntFn uint_callback;
+ guint32 uint_result;
+ gpointer user_data;
+ GError *error;
+ guint pending_id;
+} MMCallbackInfo;
+
+MMCallbackInfo *mm_callback_info_new (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data);
+
+MMCallbackInfo *mm_callback_info_uint_new (MMModem *modem,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_callback_info_schedule (MMCallbackInfo *info);
+void mm_callback_info_cancel (MMCallbackInfo *info);
+
+void mm_callback_info_set_data (MMCallbackInfo *info,
+ const char *key,
+ gpointer data,
+ GDestroyNotify destroy);
+
+gpointer mm_callback_info_get_data (MMCallbackInfo *info,
+ const char *key);
+
+#endif /* MM_CALLBACK_INFO_H */
diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c
new file mode 100644
index 00000000..1bef4fe2
--- /dev/null
+++ b/src/mm-generic-cdma.c
@@ -0,0 +1,259 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include "mm-generic-cdma.h"
+#include "mm-modem-error.h"
+#include "mm-callback-info.h"
+
+static void modem_init (MMModem *modem_class);
+
+G_DEFINE_TYPE_EXTENDED (MMGenericCdma, mm_generic_cdma, MM_TYPE_SERIAL,
+ 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init))
+
+#define MM_GENERIC_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_GENERIC_CDMA, MMGenericCdmaPrivate))
+
+typedef struct {
+ char *driver;
+} MMGenericCdmaPrivate;
+
+MMModem *
+mm_generic_cdma_new (const char *serial_device, const char *driver)
+{
+ g_return_val_if_fail (serial_device != NULL, NULL);
+ g_return_val_if_fail (driver != NULL, NULL);
+
+ return MM_MODEM (g_object_new (MM_TYPE_GENERIC_CDMA,
+ MM_SERIAL_DEVICE, serial_device,
+ MM_MODEM_DRIVER, driver,
+ NULL));
+}
+
+/*****************************************************************************/
+
+static void
+init_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* success */
+ break;
+ case -1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Modem initialization timed out.");
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Modem initialization failed");
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+flash_done (MMSerial *serial, gpointer user_data)
+{
+ char *responses[] = { "OK", "ERROR", "ERR", NULL };
+ guint id = 0;
+
+ if (mm_serial_send_command_string (serial, "AT E0"))
+ id = mm_serial_wait_for_reply (serial, 10, responses, responses, init_done, user_data);
+
+ if (!id) {
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Turning modem echo off failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+enable (MMModem *modem,
+ gboolean enable,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ if (!enable) {
+ mm_serial_close (MM_SERIAL (modem));
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ if (mm_serial_open (MM_SERIAL (modem))) {
+ guint id;
+
+ id = mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info);
+ if (!id)
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Could not communicate with serial device.");
+ } else
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not open serial device.");
+
+ if (info->error)
+ mm_callback_info_schedule (info);
+}
+
+static void
+dial_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* success */
+ break;
+ case 1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: Busy");
+ break;
+ case 2:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: No dial tone");
+ break;
+ case 3:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: No carrier");
+ break;
+ case -1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing timed out");
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing failed");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+connect (MMModem *modem,
+ const char *number,
+ const char *apn,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *command;
+ char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL };
+ guint id = 0;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ command = g_strconcat ("ATDT", number, NULL);
+ if (mm_serial_send_command_string (MM_SERIAL (modem), command))
+ id = mm_serial_wait_for_reply (MM_SERIAL (modem), 60, responses, responses, dial_done, info);
+
+ g_free (command);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+disconnect (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+ mm_serial_close (MM_SERIAL (modem));
+ mm_callback_info_schedule (info);
+}
+
+/*****************************************************************************/
+
+static void
+modem_init (MMModem *modem_class)
+{
+ /* interface implementation */
+ modem_class->enable = enable;
+ modem_class->connect = connect;
+ modem_class->disconnect = disconnect;
+}
+
+static void
+mm_generic_cdma_init (MMGenericCdma *self)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case MM_MODEM_PROP_DRIVER:
+ /* Construct only */
+ MM_GENERIC_CDMA_GET_PRIVATE (object)->driver = g_value_dup_string (value);
+ break;
+ case MM_MODEM_PROP_DATA_DEVICE:
+ case MM_MODEM_PROP_TYPE:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case MM_MODEM_PROP_DATA_DEVICE:
+ g_value_set_string (value, mm_serial_get_device (MM_SERIAL (object)));
+ break;
+ case MM_MODEM_PROP_DRIVER:
+ g_value_set_string (value, MM_GENERIC_CDMA_GET_PRIVATE (object)->driver);
+ break;
+ case MM_MODEM_PROP_TYPE:
+ g_value_set_uint (value, MM_MODEM_TYPE_CDMA);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (object);
+
+ g_free (priv->driver);
+
+ G_OBJECT_CLASS (mm_generic_cdma_parent_class)->finalize (object);
+}
+
+static void
+mm_generic_cdma_class_init (MMGenericCdmaClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMGenericCdmaPrivate));
+
+ /* Virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* Properties */
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_DATA_DEVICE,
+ MM_MODEM_DATA_DEVICE);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_DRIVER,
+ MM_MODEM_DRIVER);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_TYPE,
+ MM_MODEM_TYPE);
+
+ mm_modem_install_dbus_info (G_TYPE_FROM_CLASS (klass));
+}
diff --git a/src/mm-generic-cdma.h b/src/mm-generic-cdma.h
new file mode 100644
index 00000000..125b540a
--- /dev/null
+++ b/src/mm-generic-cdma.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_GENERIC_CDMA_H
+#define MM_GENERIC_CDMA_H
+
+#include "mm-modem.h"
+#include "mm-serial.h"
+
+#define MM_TYPE_GENERIC_CDMA (mm_generic_cdma_get_type ())
+#define MM_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdma))
+#define MM_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass))
+#define MM_IS_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_CDMA))
+#define MM_IS_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_CDMA))
+#define MM_GENERIC_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass))
+
+typedef struct {
+ MMSerial parent;
+} MMGenericCdma;
+
+typedef struct {
+ MMSerialClass parent;
+} MMGenericCdmaClass;
+
+GType mm_generic_cdma_get_type (void);
+
+MMModem *mm_generic_cdma_new (const char *serial_device,
+ const char *driver);
+
+#endif /* MM_GENERIC_CDMA_H */
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
new file mode 100644
index 00000000..2713cc00
--- /dev/null
+++ b/src/mm-generic-gsm.c
@@ -0,0 +1,705 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "mm-generic-gsm.h"
+#include "mm-modem-error.h"
+#include "mm-callback-info.h"
+
+static void modem_init (MMModem *modem_class);
+
+G_DEFINE_TYPE_EXTENDED (MMGenericGsm, mm_generic_gsm, MM_TYPE_SERIAL,
+ 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init))
+
+#define MM_GENERIC_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_GENERIC_GSM, MMGenericGsmPrivate))
+
+typedef struct {
+ char *driver;
+ guint32 pending_id;
+} MMGenericGsmPrivate;
+
+static void register_auto (MMModem *modem, MMCallbackInfo *info);
+
+MMModem *
+mm_generic_gsm_new (const char *serial_device, const char *driver)
+{
+ g_return_val_if_fail (serial_device != NULL, NULL);
+ g_return_val_if_fail (driver != NULL, NULL);
+
+ return MM_MODEM (g_object_new (MM_TYPE_GENERIC_GSM,
+ MM_SERIAL_DEVICE, serial_device,
+ MM_MODEM_DRIVER, driver,
+ NULL));
+}
+
+/*****************************************************************************/
+
+static void
+check_pin_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* success */
+ break;
+ case 1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_PIN_NEEDED, "%s", "PIN needed");
+ break;
+ case 2:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_PUK_NEEDED, "%s", "PUK needed");
+ break;
+ case -1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "PIN checking timed out.");
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "PIN checking failed.");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+check_pin (MMSerial *serial, gpointer user_data)
+{
+ char *responses[] = { "READY", "SIM PIN", "SIM PUK", "ERROR", "ERR", NULL };
+ char *terminators[] = { "OK", "ERROR", "ERR", NULL };
+ guint id = 0;
+
+ if (mm_serial_send_command_string (serial, "AT+CPIN?"))
+ id = mm_serial_wait_for_reply (serial, 3, responses, terminators, check_pin_done, user_data);
+
+ if (!id) {
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "PIN checking failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+init_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* success */
+ check_pin (serial, user_data);
+ break;
+ case -1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Modem initialization timed out.");
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Modem initialization failed");
+ }
+
+ if (info->error)
+ mm_callback_info_schedule (info);
+}
+
+static void
+flash_done (MMSerial *serial, gpointer user_data)
+{
+ char *responses[] = { "OK", "ERROR", "ERR", NULL };
+ guint id = 0;
+
+ if (mm_serial_send_command_string (serial, "AT E0"))
+ id = mm_serial_wait_for_reply (serial, 10, responses, responses, init_done, user_data);
+
+ if (!id) {
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Turning modem echo off failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+enable (MMModem *modem,
+ gboolean enable,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ if (!enable) {
+ mm_serial_close (MM_SERIAL (modem));
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ if (mm_serial_open (MM_SERIAL (modem))) {
+ guint id;
+
+ id = mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info);
+ if (!id)
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Could not communicate with serial device.");
+ } else
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not open serial device.");
+
+ if (info->error)
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_pin_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* success */
+ break;
+ case -1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Did not receive response for secret");
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_INVALID_SECRET, "%s", "Invalid secret");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_pin (MMModem *modem,
+ const char *pin,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *command;
+ char *responses[] = { "OK", "ERROR", "ERR", NULL };
+ guint id = 0;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ command = g_strdup_printf ("AT+CPIN=\"%s\"", pin);
+ if (mm_serial_send_command_string (MM_SERIAL (modem), command))
+ id = mm_serial_wait_for_reply (MM_SERIAL (modem), 3, responses, responses, set_pin_done, info);
+
+ g_free (command);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "PIN checking failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+register_manual_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* success */
+ break;
+ case -1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Manual registration timed out");
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Manual registration failed");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+register_manual (MMModem *modem, const char *network_id, MMCallbackInfo *info)
+{
+ char *command;
+ char *responses[] = { "OK", "ERROR", "ERR", NULL };
+ guint id = 0;
+
+ command = g_strdup_printf ("AT+COPS=1,2,\"%s\"", network_id);
+ if (mm_serial_send_command_string (MM_SERIAL (modem), command))
+ id = mm_serial_wait_for_reply (MM_SERIAL (modem), 30, responses, responses,
+ register_manual_done, info);
+
+ g_free (command);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Manual registration failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static gboolean
+automatic_registration_again (gpointer data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) data;
+
+ register_auto (MM_MODEM (mm_callback_info_get_data (info, "modem")), info);
+
+ mm_callback_info_set_data (info, "modem", NULL, NULL);
+
+ return FALSE;
+}
+
+static void
+register_auto_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ g_message ("Registered on Home network");
+ break;
+ case 1:
+ g_message ("Registered on Roaming network");
+ break;
+ case 2:
+ mm_callback_info_set_data (info, "modem", g_object_ref (serial), g_object_unref);
+ MM_GENERIC_GSM_GET_PRIVATE (serial)->pending_id = g_timeout_add (1000, automatic_registration_again, info);
+ return;
+ break;
+ case 3:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Automatic registration failed: not registered and not searching.");
+ break;
+ case -1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Automatic registration timed out");
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Automatic registration failed");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+register_auto (MMModem *modem, MMCallbackInfo *info)
+{
+ char *responses[] = { "+CREG: 0,1", "+CREG: 0,5", "+CREG: 0,2", "+CREG: 0,0", NULL };
+ char *terminators[] = { "OK", "ERROR", "ERR", NULL };
+ guint id = 0;
+
+ if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CREG?"))
+ id = mm_serial_wait_for_reply (MM_SERIAL (modem), 60, responses, terminators,
+ register_auto_done, info);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Automatic registration failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+do_register (MMModem *modem,
+ const char *network_id,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ if (network_id)
+ register_manual (modem, network_id, info);
+ else
+ register_auto (modem, info);
+}
+
+static void
+dial_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* success */
+ break;
+ case 1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: Busy");
+ break;
+ case 2:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: No dial tone");
+ break;
+ case 3:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: No carrier");
+ break;
+ case -1:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing timed out");
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing failed");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+
+static void
+dial (MMModem *modem, guint cid, const char *number, MMCallbackInfo *info)
+{
+ char *command;
+ char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL };
+ guint id = 0;
+
+ if (cid) {
+ GString *str;
+
+ str = g_string_new ("ATD");
+ if (g_str_has_suffix (number, "#"))
+ str = g_string_append_len (str, number, strlen (number) - 1);
+ else
+ str = g_string_append (str, number);
+
+ g_string_append_printf (str, "***%d#", cid);
+ command = g_string_free (str, FALSE);
+ } else
+ command = g_strconcat ("ATDT", number, NULL);
+
+ if (mm_serial_send_command_string (MM_SERIAL (modem), command))
+ id = mm_serial_wait_for_reply (MM_SERIAL (modem), 60, responses, responses, dial_done, info);
+
+ g_free (command);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+set_apn_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ const char *number = (char *) mm_callback_info_get_data (info, "number");
+
+ switch (reply_index) {
+ case 0:
+ dial (MM_MODEM (serial), 1, number, info);
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Setting APN failed");
+ break;
+ }
+
+ if (info->error)
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_apn (MMModem *modem, const char *apn, MMCallbackInfo *info)
+{
+ char *command;
+ char *responses[] = { "OK", "ERROR", NULL };
+ guint cid = 1;
+ guint id = 0;
+
+ command = g_strdup_printf ("AT+CGDCONT=%d, \"IP\", \"%s\"", cid, apn);
+ if (mm_serial_send_command_string (MM_SERIAL (modem), command))
+ id = mm_serial_wait_for_reply (MM_SERIAL (modem), 3, responses, responses, set_apn_done, info);
+
+ g_free (command);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Setting APN failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+connect (MMModem *modem,
+ const char *number,
+ const char *apn,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ if (apn) {
+ mm_callback_info_set_data (info, "number", g_strdup (number), g_free);
+ set_apn (modem, apn, info);
+ } else
+ dial (modem, 0, number, info);
+}
+
+static void
+disconnect (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+ mm_serial_close (MM_SERIAL (modem));
+ mm_callback_info_schedule (info);
+}
+
+static void
+scan_callback_wrapper (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemScanFn scan_fn;
+ GPtrArray *results;
+ gpointer data;
+
+ scan_fn = (MMModemScanFn) mm_callback_info_get_data (info, "scan-callback");
+ results = (GPtrArray *) mm_callback_info_get_data (info, "scan-results");
+ data = mm_callback_info_get_data (info, "scan-data");
+
+ scan_fn (modem, results, error, data);
+}
+
+static void
+destroy_scan_data (gpointer data)
+{
+ GPtrArray *results = (GPtrArray *) data;
+
+ g_ptr_array_foreach (results, (GFunc) g_hash_table_destroy, NULL);
+ g_ptr_array_free (results, TRUE);
+}
+
+static void
+scan_done (MMSerial *serial, const char *reply, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GPtrArray *results;
+
+ results = g_ptr_array_new ();
+
+ if (!strncmp (reply, "+COPS: ", 7)) {
+ /* Got valid reply */
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *err = NULL;
+
+ reply += 7;
+
+ /* Pattern without crazy escaping using | for matching: (|\d|,"|.+|","|.+|","|.+|",|\d|) */
+ r = g_regex_new ("\\((\\d),\"(.+)\",\"(.+)\",\"(.+)\",(\\d)\\)", G_REGEX_UNGREEDY, 0, &err);
+ if (err) {
+ g_error ("Invalid regular expression: %s", err->message);
+ g_error_free (err);
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not parse scan results.");
+ goto out;
+ }
+
+ g_regex_match (r, reply, 0, &match_info);
+ while (g_match_info_matches (match_info)) {
+ GHashTable *hash;
+
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ g_hash_table_insert (hash, g_strdup ("status"), g_match_info_fetch (match_info, 1));
+ g_hash_table_insert (hash, g_strdup ("operator-long"), g_match_info_fetch (match_info, 2));
+ g_hash_table_insert (hash, g_strdup ("operator-short"), g_match_info_fetch (match_info, 3));
+ g_hash_table_insert (hash, g_strdup ("operator-num"), g_match_info_fetch (match_info, 4));
+
+ g_ptr_array_add (results, hash);
+ g_match_info_next (match_info, NULL);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ } else
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not parse scan results");
+
+ mm_callback_info_set_data (info, "scan-results", results, destroy_scan_data);
+
+ out:
+ mm_callback_info_schedule (info);
+}
+
+static void
+scan (MMModem *modem,
+ MMModemScanFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *terminators = "\r\n";
+ guint id = 0;
+
+ info = mm_callback_info_new (modem, scan_callback_wrapper, NULL);
+ info->user_data = info;
+ mm_callback_info_set_data (info, "scan-callback", callback, NULL);
+ mm_callback_info_set_data (info, "scan-data", user_data, NULL);
+
+ if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+COPS=?"))
+ id = mm_serial_get_reply (MM_SERIAL (modem), 60, terminators, scan_done, info);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Scanning failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+get_signal_quality_done (MMSerial *serial, const char *reply, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ guint32 result = 0;
+
+ if (!strncmp (reply, "+CSQ: ", 6)) {
+ /* Got valid reply */
+ int quality;
+ int ber;
+
+ reply += 6;
+
+ if (sscanf (reply, "%d,%d", &quality, &ber)) {
+ /* 99 means unknown */
+ if (quality != 99)
+ /* Normalize the quality */
+ result = quality * 100 / 31;
+ } else
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Could not parse signal quality results");
+ } else
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Could not parse signal quality results");
+
+ info->uint_result = result;
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_signal_quality (MMModem *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *terminators = "\r\n";
+ guint id = 0;
+
+ info = mm_callback_info_uint_new (modem, callback, user_data);
+
+ if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CSQ"))
+ id = mm_serial_get_reply (MM_SERIAL (modem), 10, terminators, get_signal_quality_done, info);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Getting signal quality failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+modem_init (MMModem *modem_class)
+{
+ /* interface implementation */
+ modem_class->enable = enable;
+ modem_class->set_pin = set_pin;
+ modem_class->do_register = do_register;
+ modem_class->connect = connect;
+ modem_class->disconnect = disconnect;
+ modem_class->scan = scan;
+ modem_class->get_signal_quality = get_signal_quality;
+}
+
+static void
+mm_generic_gsm_init (MMGenericGsm *self)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case MM_MODEM_PROP_DRIVER:
+ /* Construct only */
+ MM_GENERIC_GSM_GET_PRIVATE (object)->driver = g_value_dup_string (value);
+ break;
+ case MM_MODEM_PROP_DATA_DEVICE:
+ case MM_MODEM_PROP_TYPE:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case MM_MODEM_PROP_DATA_DEVICE:
+ g_value_set_string (value, mm_serial_get_device (MM_SERIAL (object)));
+ break;
+ case MM_MODEM_PROP_DRIVER:
+ g_value_set_string (value, MM_GENERIC_GSM_GET_PRIVATE (object)->driver);
+ break;
+ case MM_MODEM_PROP_TYPE:
+ g_value_set_uint (value, MM_MODEM_TYPE_GSM);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object);
+
+ if (priv->pending_id) {
+ g_source_remove (priv->pending_id);
+ priv->pending_id = 0;
+ }
+
+ g_free (priv->driver);
+
+ G_OBJECT_CLASS (mm_generic_gsm_parent_class)->finalize (object);
+}
+
+static void
+mm_generic_gsm_class_init (MMGenericGsmClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMGenericGsmPrivate));
+
+ /* Virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* Properties */
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_DATA_DEVICE,
+ MM_MODEM_DATA_DEVICE);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_DRIVER,
+ MM_MODEM_DRIVER);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_TYPE,
+ MM_MODEM_TYPE);
+
+ mm_modem_install_dbus_info (G_TYPE_FROM_CLASS (klass));
+}
diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
new file mode 100644
index 00000000..25e9d7c2
--- /dev/null
+++ b/src/mm-generic-gsm.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_GENERIC_GSM_H
+#define MM_GENERIC_GSM_H
+
+#include "mm-modem.h"
+#include "mm-serial.h"
+
+#define MM_TYPE_GENERIC_GSM (mm_generic_gsm_get_type ())
+#define MM_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsm))
+#define MM_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_GSM, MMGenericGsmClass))
+#define MM_IS_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_GSM))
+#define MM_IS_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_GSM))
+#define MM_GENERIC_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsmClass))
+
+typedef struct {
+ MMSerial parent;
+} MMGenericGsm;
+
+typedef struct {
+ MMSerialClass parent;
+} MMGenericGsmClass;
+
+GType mm_generic_gsm_get_type (void);
+
+MMModem *mm_generic_gsm_new (const char *serial_device,
+ const char *driver);
+
+#endif /* MM_GENERIC_GSM_H */
diff --git a/src/mm-manager.c b/src/mm-manager.c
new file mode 100644
index 00000000..6ea936f6
--- /dev/null
+++ b/src/mm-manager.c
@@ -0,0 +1,457 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <string.h>
+#include <gmodule.h>
+#include <libhal.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include "mm-manager.h"
+#include "mm-modem-error.h"
+#include "mm-generic-gsm.h"
+#include "mm-generic-cdma.h"
+#include "mm-plugin.h"
+
+static gboolean impl_manager_enumerate_devices (MMManager *manager,
+ GPtrArray **devices,
+ GError **err);
+
+#include "mm-manager-glue.h"
+
+G_DEFINE_TYPE (MMManager, mm_manager, G_TYPE_OBJECT)
+
+enum {
+ DEVICE_ADDED,
+ DEVICE_REMOVED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+#define MM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MANAGER, MMManagerPrivate))
+
+typedef struct {
+ DBusGConnection *connection;
+ LibHalContext *hal_ctx;
+ GSList *plugins;
+ GHashTable *modems;
+} MMManagerPrivate;
+
+static MMPlugin *
+load_plugin (const char *path)
+{
+ MMPlugin *plugin = NULL;
+ GModule *module;
+ MMPluginCreateFunc plugin_create_func;
+ int *major_plugin_version, *minor_plugin_version;
+
+ module = g_module_open (path, G_MODULE_BIND_LAZY);
+ if (!module) {
+ g_warning ("Could not load plugin %s: %s", path, g_module_error ());
+ return NULL;
+ }
+
+ if (!g_module_symbol (module, "mm_plugin_major_version", (gpointer *) &major_plugin_version)) {
+ g_warning ("Could not load plugin %s: Missing major version info", path);
+ goto out;
+ }
+
+ if (*major_plugin_version != MM_PLUGIN_MAJOR_VERSION) {
+ g_warning ("Could not load plugin %s: Plugin major version %d, %d is required",
+ path, *major_plugin_version, MM_PLUGIN_MAJOR_VERSION);
+ goto out;
+ }
+
+ if (!g_module_symbol (module, "mm_plugin_minor_version", (gpointer *) &minor_plugin_version)) {
+ g_warning ("Could not load plugin %s: Missing minor version info", path);
+ goto out;
+ }
+
+ if (*minor_plugin_version != MM_PLUGIN_MINOR_VERSION) {
+ g_warning ("Could not load plugin %s: Plugin minor version %d, %d is required",
+ path, *minor_plugin_version, MM_PLUGIN_MINOR_VERSION);
+ goto out;
+ }
+
+ if (!g_module_symbol (module, "mm_plugin_create", (gpointer *) &plugin_create_func)) {
+ g_warning ("Could not load plugin %s: %s", path, g_module_error ());
+ goto out;
+ }
+
+ plugin = (*plugin_create_func) ();
+ if (plugin) {
+ g_object_weak_ref (G_OBJECT (plugin), (GWeakNotify) g_module_close, module);
+ g_message ("Loaded plugin %s", mm_plugin_get_name (plugin));
+ } else
+ g_warning ("Could not load plugin %s: initialization failed", path);
+
+ out:
+ if (!plugin)
+ g_module_close (module);
+
+ return plugin;
+}
+
+static void
+load_plugins (MMManager *manager)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ GDir *dir;
+ const char *fname;
+
+ if (!g_module_supported ()) {
+ g_warning ("GModules are not supported on your platform!");
+ return;
+ }
+
+ dir = g_dir_open (PLUGINDIR, 0, NULL);
+ if (!dir) {
+ g_warning ("No plugins found");
+ return;
+ }
+
+ while ((fname = g_dir_read_name (dir)) != NULL) {
+ char *path;
+ MMPlugin *plugin;
+
+ if (!strstr (fname, G_MODULE_SUFFIX))
+ continue;
+
+ path = g_module_build_path (PLUGINDIR, fname);
+ plugin = load_plugin (path);
+ g_free (path);
+
+ if (plugin)
+ priv->plugins = g_slist_append (priv->plugins, plugin);
+ }
+
+ g_dir_close (dir);
+}
+
+MMManager *
+mm_manager_new (void)
+{
+ return g_object_new (MM_TYPE_MANAGER, NULL);
+}
+
+static char *
+get_driver_name (LibHalContext *ctx, const char *udi)
+{
+ char *parent_udi;
+ char *driver = NULL;
+
+ parent_udi = libhal_device_get_property_string (ctx, udi, "info.parent", NULL);
+ if (parent_udi) {
+ driver = libhal_device_get_property_string (ctx, parent_udi, "info.linux.driver", NULL);
+ libhal_free_string (parent_udi);
+ }
+
+ return driver;
+}
+
+static MMModem *
+create_generic_modem (MMManager *manager, const char *udi)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ MMModem *modem;
+ char **capabilities;
+ char **iter;
+ char *serial_device;
+ char *driver;
+ gboolean type_gsm = FALSE;
+ gboolean type_cdma = FALSE;
+
+ capabilities = libhal_device_get_property_strlist (priv->hal_ctx, udi, "modem.command_sets", NULL);
+ for (iter = capabilities; iter && *iter; iter++) {
+ if (!strcmp (*iter, "GSM-07.07")) {
+ type_gsm = TRUE;
+ break;
+ }
+ if (!strcmp (*iter, "IS-707-A")) {
+ type_cdma = TRUE;
+ break;
+ }
+ }
+ g_strfreev (capabilities);
+
+ if (!type_gsm && !type_cdma)
+ return NULL;
+
+ serial_device = libhal_device_get_property_string (priv->hal_ctx, udi, "serial.device", NULL);
+ g_return_val_if_fail (serial_device != NULL, NULL);
+
+ driver = get_driver_name (priv->hal_ctx, udi);
+ g_return_val_if_fail (driver != NULL, NULL);
+
+ if (type_gsm)
+ modem = mm_generic_gsm_new (serial_device, driver);
+ else
+ modem = mm_generic_cdma_new (serial_device, driver);
+
+ g_free (serial_device);
+ g_free (driver);
+
+ return modem;
+}
+
+static void
+add_modem (MMManager *manager, const char *udi, MMModem *modem)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+
+ g_debug ("Added modem %s", udi);
+ g_hash_table_insert (priv->modems, g_strdup (udi), modem);
+ dbus_g_connection_register_g_object (priv->connection, udi, G_OBJECT (modem));
+
+ g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem);
+}
+
+static MMModem *
+modem_exists (MMManager *manager, const char *udi)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+
+ return (MMModem *) g_hash_table_lookup (priv->modems, udi);
+}
+
+static void
+create_initial_modems_from_plugins (MMManager *manager)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ GSList *iter;
+
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ MMPlugin *plugin = MM_PLUGIN (iter->data);
+ GSList *udis;
+ GSList *udi_iter;
+
+ udis = mm_plugin_list_supported_udis (plugin, priv->hal_ctx);
+ for (udi_iter = udis; udi_iter; udi_iter = udi_iter->next) {
+ char *udi = (char *) udi_iter->data;
+ MMModem *modem;
+
+ if (modem_exists (manager, udi)) {
+ g_warning ("Modem for UDI %s already exists, ignoring", udi);
+ continue;
+ }
+
+ modem = mm_plugin_create_modem (plugin, priv->hal_ctx, udi);
+ if (modem)
+ add_modem (manager, udi, modem);
+ else
+ g_warning ("Plugin failed to create modem for UDI %s", udi);
+ }
+
+ g_slist_foreach (udis, (GFunc) g_free, NULL);
+ g_slist_free (udis);
+ }
+}
+
+static void
+create_initial_modems_generic (MMManager *manager)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ char **devices;
+ int num_devices;
+ int i;
+ DBusError err;
+
+ dbus_error_init (&err);
+ devices = libhal_find_device_by_capability (priv->hal_ctx, "modem", &num_devices, &err);
+ if (dbus_error_is_set (&err)) {
+ g_warning ("Could not list HAL devices: %s", err.message);
+ dbus_error_free (&err);
+ }
+
+ if (devices) {
+ for (i = 0; i < num_devices; i++) {
+ char *udi = devices[i];
+ MMModem *modem;
+
+ if (modem_exists (manager, udi))
+ /* Already exists, most likely handled by a plugin */
+ continue;
+
+ modem = create_generic_modem (manager, udi);
+ if (modem)
+ add_modem (manager, g_strdup (udi), modem);
+ }
+ }
+
+ g_strfreev (devices);
+}
+
+static void
+create_initial_modems (MMManager *manager)
+{
+ create_initial_modems_from_plugins (manager);
+ create_initial_modems_generic (manager);
+}
+
+static void
+enumerate_devices_cb (gpointer key, gpointer val, gpointer user_data)
+{
+ GPtrArray **devices = (GPtrArray **) user_data;
+
+ g_ptr_array_add (*devices, g_strdup ((char *) key));
+}
+
+static gboolean
+impl_manager_enumerate_devices (MMManager *manager,
+ GPtrArray **devices,
+ GError **err)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+
+ *devices = g_ptr_array_sized_new (g_hash_table_size (priv->modems));
+ g_hash_table_foreach (priv->modems, enumerate_devices_cb, devices);
+
+ return TRUE;
+}
+
+static void
+device_added (LibHalContext *ctx, const char *udi)
+{
+ MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx));
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ GSList *iter;
+ MMModem *modem = NULL;
+
+ if (modem_exists (manager, udi))
+ /* Shouldn't happen */
+ return;
+
+ for (iter = priv->plugins; iter && modem == NULL; iter = iter->next) {
+ MMPlugin *plugin = MM_PLUGIN (iter->data);
+
+ if (mm_plugin_supports_udi (plugin, ctx, udi)) {
+ modem = mm_plugin_create_modem (plugin, ctx, udi);
+ if (modem)
+ break;
+ }
+ }
+
+ if (!modem)
+ /* None of the plugins supported the udi, try generic devices */
+ modem = create_generic_modem (manager, udi);
+
+ if (modem)
+ add_modem (manager, udi, modem);
+}
+
+static void
+device_removed (LibHalContext *ctx, const char *udi)
+{
+ MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx));
+ MMModem *modem;
+
+ modem = modem_exists (manager, udi);
+ if (modem) {
+ g_debug ("Removed modem %s", udi);
+ g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem);
+ g_hash_table_remove (MM_MANAGER_GET_PRIVATE (manager)->modems, udi);
+ }
+}
+
+static void
+device_new_capability (LibHalContext *ctx, const char *udi, const char *capability)
+{
+ device_added (ctx, udi);
+}
+
+static void
+mm_manager_init (MMManager *manager)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ GError *err = NULL;
+ DBusError dbus_error;
+
+ priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+ priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
+ if (!priv->connection)
+ g_error ("Could not connect to system bus.");
+
+ dbus_g_connection_register_g_object (priv->connection,
+ MM_DBUS_PATH,
+ G_OBJECT (manager));
+
+ priv->hal_ctx = libhal_ctx_new ();
+ if (!priv->hal_ctx)
+ g_error ("Could not get connection to the HAL service.");
+
+ libhal_ctx_set_dbus_connection (priv->hal_ctx, dbus_g_connection_get_connection (priv->connection));
+
+ dbus_error_init (&dbus_error);
+ if (!libhal_ctx_init (priv->hal_ctx, &dbus_error))
+ g_error ("libhal_ctx_init() failed: %s\n"
+ "Make sure the hal daemon is running?",
+ dbus_error.message);
+
+ load_plugins (manager);
+
+ libhal_ctx_set_user_data (priv->hal_ctx, manager);
+ libhal_ctx_set_device_added (priv->hal_ctx, device_added);
+ libhal_ctx_set_device_removed (priv->hal_ctx, device_removed);
+ libhal_ctx_set_device_new_capability (priv->hal_ctx, device_new_capability);
+
+ create_initial_modems (manager);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->modems);
+
+ g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL);
+ g_slist_free (priv->plugins);
+
+ if (priv->hal_ctx) {
+ libhal_ctx_shutdown (priv->hal_ctx, NULL);
+ libhal_ctx_free (priv->hal_ctx);
+ }
+
+ if (priv->connection)
+ dbus_g_connection_unref (priv->connection);
+
+ G_OBJECT_CLASS (mm_manager_parent_class)->finalize (object);
+}
+
+static void
+mm_manager_class_init (MMManagerClass *manager_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+ g_type_class_add_private (object_class, sizeof (MMManagerPrivate));
+
+ /* Virtual methods */
+ object_class->finalize = finalize;
+
+ /* Signals */
+ signals[DEVICE_ADDED] =
+ g_signal_new ("device-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMManagerClass, device_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ signals[DEVICE_REMOVED] =
+ g_signal_new ("device-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMManagerClass, device_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class),
+ &dbus_glib_mm_manager_object_info);
+
+ dbus_g_error_domain_register (MM_MODEM_ERROR, NULL, MM_TYPE_MODEM_ERROR);
+}
diff --git a/src/mm-manager.h b/src/mm-manager.h
new file mode 100644
index 00000000..ecb7f408
--- /dev/null
+++ b/src/mm-manager.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_MANAGER_H
+#define MM_MANAGER_H
+
+#include <glib/gtypes.h>
+#include <glib-object.h>
+#include "mm-modem.h"
+
+#define MM_TYPE_MANAGER (mm_manager_get_type ())
+#define MM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MANAGER, MMManager))
+#define MM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MANAGER, MMManagerClass))
+#define MM_IS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MANAGER))
+#define MM_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MANAGER))
+#define MM_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MANAGER, MMManagerClass))
+
+#define MM_DBUS_SERVICE "org.freedesktop.ModemManager"
+#define MM_DBUS_PATH "/org/freedesktop/ModemManager"
+
+typedef struct {
+ GObject parent;
+} MMManager;
+
+typedef struct {
+ GObjectClass parent;
+
+ /* Signals */
+ void (*device_added) (MMManager *manager, MMModem *device);
+ void (*device_removed) (MMManager *manager, MMModem *device);
+} MMManagerClass;
+
+GType mm_manager_get_type (void);
+
+MMManager *mm_manager_new (void);
+
+#endif /* MM_MANAGER_H */
diff --git a/src/mm-modem-error.c b/src/mm-modem-error.c
new file mode 100644
index 00000000..c415fb1f
--- /dev/null
+++ b/src/mm-modem-error.c
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include "mm-modem-error.h"
+
+GQuark
+mm_modem_error_quark (void)
+{
+ static GQuark ret = 0;
+
+ if (ret == 0)
+ ret = g_quark_from_static_string ("mm_modem_error");
+
+ return ret;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+mm_modem_error_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ ENUM_ENTRY (MM_MODEM_ERROR_GENERAL, "GeneralError"),
+ ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, "OperationNotSupported"),
+ ENUM_ENTRY (MM_MODEM_ERROR_PIN_NEEDED, "PINNeeded"),
+ ENUM_ENTRY (MM_MODEM_ERROR_PUK_NEEDED, "PUKNeeded"),
+ ENUM_ENTRY (MM_MODEM_ERROR_INVALID_SECRET, "InvalidSecret"),
+ { 0, 0, 0 }
+ };
+
+ etype = g_enum_register_static ("MMModemError", values);
+ }
+
+ return etype;
+}
diff --git a/src/mm-modem-error.h b/src/mm-modem-error.h
new file mode 100644
index 00000000..f5c3c57e
--- /dev/null
+++ b/src/mm-modem-error.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_MODEM_ERROR_H
+#define MM_MODEM_ERROR_H
+
+#include <glib-object.h>
+
+enum {
+ MM_MODEM_ERROR_GENERAL = 0,
+ MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ MM_MODEM_ERROR_PIN_NEEDED,
+ MM_MODEM_ERROR_PUK_NEEDED,
+ MM_MODEM_ERROR_INVALID_SECRET
+};
+
+#define MM_MODEM_ERROR (mm_modem_error_quark ())
+#define MM_TYPE_MODEM_ERROR (mm_modem_error_get_type ())
+
+GQuark mm_modem_error_quark (void);
+GType mm_modem_error_get_type (void);
+
+#endif /* MM_MODEM_ERROR_H */
diff --git a/src/mm-modem.c b/src/mm-modem.c
new file mode 100644
index 00000000..27052585
--- /dev/null
+++ b/src/mm-modem.c
@@ -0,0 +1,460 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+#include "mm-modem.h"
+#include "mm-modem-error.h"
+#include "mm-callback-info.h"
+
+static void impl_modem_enable (MMModem *modem, gboolean enable, DBusGMethodInvocation *context);
+static void impl_modem_set_pin (MMModem *modem, const char *pin, DBusGMethodInvocation *context);
+static void impl_modem_register (MMModem *modem, const char *network_id, DBusGMethodInvocation *context);
+static void impl_modem_connect (MMModem *modem, const char *number, const char *apn, DBusGMethodInvocation *context);
+static void impl_modem_disconnect (MMModem *modem, DBusGMethodInvocation *context);
+static void impl_modem_scan (MMModem *modem, DBusGMethodInvocation *context);
+static void impl_modem_get_signal_quality (MMModem *modem, DBusGMethodInvocation *context);
+static void impl_modem_set_band (MMModem *modem, guint32 band, DBusGMethodInvocation *context);
+static void impl_modem_get_band (MMModem *modem, DBusGMethodInvocation *context);
+static void impl_modem_set_network_mode (MMModem *modem, guint32 mode, DBusGMethodInvocation *context);
+static void impl_modem_get_network_mode (MMModem *modem, DBusGMethodInvocation *context);
+
+#include "mm-modem-glue.h"
+
+enum {
+ SIGNAL_QUALITY,
+ NETWORK_MODE,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+async_op_not_supported (MMModem *self,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (self, callback, user_data);
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "%s", "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+static void
+async_call_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context);
+}
+
+static void
+uint_op_not_supported (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_uint_new (self, callback, user_data);
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "%s", "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+static void
+uint_call_done (MMModem *modem, guint32 result, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context, result);
+}
+
+void
+mm_modem_enable (MMModem *self,
+ gboolean enable,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->enable)
+ MM_MODEM_GET_INTERFACE (self)->enable (self, enable, callback, user_data);
+ else
+ async_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_enable (MMModem *modem,
+ gboolean enable,
+ DBusGMethodInvocation *context)
+{
+ mm_modem_enable (modem, enable, async_call_done, context);
+}
+
+void
+mm_modem_set_pin (MMModem *self,
+ const char *pin,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+ g_return_if_fail (pin != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->set_pin)
+ MM_MODEM_GET_INTERFACE (self)->set_pin (self, pin, callback, user_data);
+ else
+ async_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_set_pin (MMModem *modem,
+ const char *pin,
+ DBusGMethodInvocation *context)
+{
+ mm_modem_set_pin (modem, pin, async_call_done, context);
+}
+
+void
+mm_modem_register (MMModem *self,
+ const char *network_id,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->do_register)
+ MM_MODEM_GET_INTERFACE (self)->do_register (self, network_id, callback, user_data);
+ else
+ async_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_register (MMModem *modem,
+ const char *network_id,
+ DBusGMethodInvocation *context)
+{
+ const char *id;
+
+ /* DBus does not support NULL strings, so the caller should pass an empty string
+ for manual registration. */
+ if (strlen (network_id) < 1)
+ id = NULL;
+ else
+ id = network_id;
+
+ mm_modem_register (modem, id, async_call_done, context);
+}
+
+void
+mm_modem_connect (MMModem *self,
+ const char *number,
+ const char *apn,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+ g_return_if_fail (number != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->connect)
+ MM_MODEM_GET_INTERFACE (self)->connect (self, number, apn, callback, user_data);
+ else
+ async_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_connect (MMModem *modem,
+ const char *number,
+ const char *apn,
+ DBusGMethodInvocation *context)
+{
+ const char *real_apn;
+
+ /* DBus does not support NULL strings, so the caller should pass an empty string
+ for no APN. */
+ if (strlen (apn) < 1)
+ real_apn = NULL;
+ else
+ real_apn = apn;
+
+ mm_modem_connect (modem, number, real_apn, async_call_done, context);
+}
+
+void
+mm_modem_disconnect (MMModem *self,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->disconnect)
+ MM_MODEM_GET_INTERFACE (self)->disconnect (self, callback, user_data);
+ else
+ async_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_disconnect (MMModem *modem,
+ DBusGMethodInvocation *context)
+{
+ mm_modem_disconnect (modem, async_call_done, context);
+}
+
+void
+mm_modem_scan (MMModem *self,
+ MMModemScanFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->scan)
+ MM_MODEM_GET_INTERFACE (self)->scan (self, callback, user_data);
+ else
+ /* FIXME */ ;
+}
+
+static void
+impl_scan_done (MMModem *modem, GPtrArray *results, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context, results);
+}
+
+static void
+impl_modem_scan (MMModem *modem,
+ DBusGMethodInvocation *context)
+{
+ mm_modem_scan (modem, impl_scan_done, context);
+}
+
+void
+mm_modem_get_signal_quality (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->get_signal_quality)
+ MM_MODEM_GET_INTERFACE (self)->get_signal_quality (self, callback, user_data);
+ else
+ uint_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_get_signal_quality (MMModem *modem, DBusGMethodInvocation *context)
+{
+ mm_modem_get_signal_quality (modem, uint_call_done, context);
+}
+
+void
+mm_modem_set_band (MMModem *self,
+ MMModemBand band,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->set_band)
+ MM_MODEM_GET_INTERFACE (self)->set_band (self, band, callback, user_data);
+ else
+ async_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_set_band (MMModem *modem, guint32 band, DBusGMethodInvocation *context)
+{
+ mm_modem_set_band (modem, band, async_call_done, context);
+}
+
+void
+mm_modem_get_band (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->get_band)
+ MM_MODEM_GET_INTERFACE (self)->get_band (self, callback, user_data);
+ else
+ uint_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_get_band (MMModem *modem, DBusGMethodInvocation *context)
+{
+ mm_modem_get_band (modem, uint_call_done, context);
+}
+
+void
+mm_modem_set_network_mode (MMModem *self,
+ MMModemNetworkMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->set_network_mode)
+ MM_MODEM_GET_INTERFACE (self)->set_network_mode (self, mode, callback, user_data);
+ else
+ async_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_set_network_mode (MMModem *modem, guint32 mode, DBusGMethodInvocation *context)
+{
+ mm_modem_set_network_mode (modem, mode, async_call_done, context);
+}
+
+void
+mm_modem_get_network_mode (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->get_network_mode)
+ MM_MODEM_GET_INTERFACE (self)->get_network_mode (self, callback, user_data);
+ else
+ uint_op_not_supported (self, callback, user_data);
+}
+
+static void
+impl_modem_get_network_mode (MMModem *modem, DBusGMethodInvocation *context)
+{
+ mm_modem_get_network_mode (modem, uint_call_done, context);
+}
+
+
+void
+mm_modem_install_dbus_info (GType type)
+{
+ dbus_g_object_type_install_info (type, &dbus_glib_mm_modem_object_info);
+}
+
+void
+mm_modem_signal_quality (MMModem *self,
+ guint32 quality)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+
+ g_signal_emit (self, signals[SIGNAL_QUALITY], 0, quality);
+}
+
+void
+mm_modem_network_mode (MMModem *self,
+ MMModemNetworkMode mode)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+
+ g_signal_emit (self, signals[NETWORK_MODE], 0, mode);
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_init (gpointer g_iface)
+{
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_DATA_DEVICE,
+ "DataDevice",
+ "DataDevice",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_DRIVER,
+ "Driver",
+ "Driver",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_TYPE,
+ "Type",
+ "Type",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /* Signals */
+ signals[SIGNAL_QUALITY] =
+ g_signal_new ("signal-quality",
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMModem, signal_quality),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
+ signals[NETWORK_MODE] =
+ g_signal_new ("network-mode",
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMModem, network_mode),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
+ initialized = TRUE;
+}
+
+GType
+mm_modem_get_type (void)
+{
+ static GType modem_type = 0;
+
+ if (!G_UNLIKELY (modem_type)) {
+ const GTypeInfo modem_info = {
+ sizeof (MMModem), /* class_size */
+ mm_modem_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ modem_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMModem",
+ &modem_info, 0);
+
+ g_type_interface_add_prerequisite (modem_type, G_TYPE_OBJECT);
+ }
+
+ return modem_type;
+}
diff --git a/src/mm-modem.h b/src/mm-modem.h
new file mode 100644
index 00000000..c664a323
--- /dev/null
+++ b/src/mm-modem.h
@@ -0,0 +1,196 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_MODEM_H
+#define MM_MODEM_H
+
+#include <glib-object.h>
+
+#define MM_TYPE_MODEM (mm_modem_get_type ())
+#define MM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM, MMModem))
+#define MM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM))
+#define MM_MODEM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM, MMModem))
+
+#define MM_MODEM_DATA_DEVICE "data-device"
+#define MM_MODEM_DRIVER "driver"
+#define MM_MODEM_TYPE "type"
+
+#define MM_MODEM_TYPE_GSM 1
+#define MM_MODEM_TYPE_CDMA 2
+
+typedef enum {
+ MM_MODEM_PROP_FIRST = 0x1000,
+
+ MM_MODEM_PROP_DATA_DEVICE = MM_MODEM_PROP_FIRST,
+ MM_MODEM_PROP_DRIVER,
+ MM_MODEM_PROP_TYPE
+} MMModemProp;
+
+typedef enum {
+ MM_MODEM_NETWORK_MODE_ANY = 0,
+ MM_MODEM_NETWORK_MODE_GPRS = 1,
+ MM_MODEM_NETWORK_MODE_EDGE = 2,
+ MM_MODEM_NETWORK_MODE_3G = 3,
+ MM_MODEM_NETWORK_MODE_HSDPA = 4,
+ MM_MODEM_NETWORK_MODE_PREFER_2G = 5,
+ MM_MODEM_NETWORK_MODE_PREFER_3G = 6
+} MMModemNetworkMode;
+
+typedef enum {
+ MM_MODEM_BAND_ANY = 0,
+ MM_MODEM_BAND_EGSM = 1, /* 900 MHz */
+ MM_MODEM_BAND_DCS = 2, /* 1800 MHz */
+ MM_MODEM_BAND_PCS = 3, /* 1900 MHz */
+ MM_MODEM_BAND_G850 = 4, /* 850 MHz */
+ MM_MODEM_BAND_U2100 = 5, /* WCDMA 2100 MHz (Class I) */
+ MM_MODEM_BAND_U1700 = 6, /* WCDMA 3GPP UMTS1800 MHz (Class III) */
+ MM_MODEM_BAND_17IV = 7, /* WCDMA 3GPP AWS 1700/2100 MHz (Class IV) */
+ MM_MODEM_BAND_U800 = 8, /* WCDMA 3GPP UMTS800 MHz (Class VI) */
+ MM_MODEM_BAND_U850 = 9, /* WCDMA 3GPP UMTS850 MHz (Class V) */
+ MM_MODEM_BAND_U900 = 10, /* WCDMA 3GPP UMTS900 MHz (Class VIII) */
+ MM_MODEM_BAND_U17IX = 11 /* WCDMA 3GPP UMTS MHz (Class IX) */
+} MMModemBand;
+
+typedef struct _MMModem MMModem;
+
+typedef void (*MMModemFn) (MMModem *modem,
+ GError *error,
+ gpointer user_data);
+
+typedef void (*MMModemUIntFn) (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data);
+
+typedef void (*MMModemScanFn) (MMModem *modem,
+ GPtrArray *results,
+ GError *error,
+ gpointer user_data);
+
+struct _MMModem {
+ GTypeInterface g_iface;
+
+ /* Methods */
+ void (*enable) (MMModem *self,
+ gboolean enable,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*set_pin) (MMModem *self,
+ const char *pin,
+ MMModemFn callback,
+ gpointer user_data);
+
+ /* 'register' is a reserved word */
+ void (*do_register) (MMModem *self,
+ const char *network_id,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*connect) (MMModem *self,
+ const char *number,
+ const char *apn,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*disconnect) (MMModem *self,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*scan) (MMModem *self,
+ MMModemScanFn callback,
+ gpointer user_data);
+
+ void (*get_signal_quality) (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ void (*set_band) (MMModem *self,
+ MMModemBand band,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*get_band) (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ void (*set_network_mode) (MMModem *self,
+ MMModemNetworkMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*get_network_mode) (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ /* Signals */
+ void (*signal_quality) (MMModem *self,
+ guint32 quality);
+
+ void (*network_mode) (MMModem *self,
+ MMModemNetworkMode mode);
+};
+
+GType mm_modem_get_type (void);
+
+void mm_modem_enable (MMModem *self,
+ gboolean enable,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_set_pin (MMModem *self,
+ const char *pin,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_register (MMModem *self,
+ const char *network_id,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_connect (MMModem *self,
+ const char *number,
+ const char *apn,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_disconnect (MMModem *self,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_scan (MMModem *self,
+ MMModemScanFn callback,
+ gpointer user_data);
+
+void mm_modem_get_signal_quality (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_modem_set_band (MMModem *self,
+ MMModemBand band,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_get_band (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_modem_set_network_mode (MMModem *self,
+ MMModemNetworkMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_get_network_mode (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+/* Protected */
+
+void mm_modem_install_dbus_info (GType type);
+
+void mm_modem_signal_quality (MMModem *self,
+ guint32 quality);
+
+void mm_modem_network_mode (MMModem *self,
+ MMModemNetworkMode mode);
+
+#endif /* MM_MODEM_H */
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
new file mode 100644
index 00000000..deca9291
--- /dev/null
+++ b/src/mm-plugin.c
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include "mm-plugin.h"
+
+const char *
+mm_plugin_get_name (MMPlugin *plugin)
+{
+ g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL);
+
+ return MM_PLUGIN_GET_INTERFACE (plugin)->get_name (plugin);
+}
+
+char **
+mm_plugin_list_supported_udis (MMPlugin *plugin,
+ LibHalContext *hal_ctx)
+{
+ g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL);
+ g_return_val_if_fail (hal_ctx != NULL, NULL);
+
+ return MM_PLUGIN_GET_INTERFACE (plugin)->list_supported_udis (plugin, hal_ctx);
+}
+
+gboolean
+mm_plugin_supports_udi (MMPlugin *plugin,
+ LibHalContext *hal_ctx,
+ const char *udi)
+{
+ g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE);
+ g_return_val_if_fail (hal_ctx != NULL, FALSE);
+ g_return_val_if_fail (udi != NULL, FALSE);
+
+ return MM_PLUGIN_GET_INTERFACE (plugin)->supports_udi (plugin, hal_ctx, udi);
+}
+
+MMModem *
+mm_plugin_create_modem (MMPlugin *plugin,
+ LibHalContext *hal_ctx,
+ const char *udi)
+{
+ g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL);
+ g_return_val_if_fail (hal_ctx != NULL, NULL);
+ g_return_val_if_fail (udi != NULL, NULL);
+
+ return MM_PLUGIN_GET_INTERFACE (plugin)->create_modem (plugin, hal_ctx, udi);
+}
+
+
+/*****************************************************************************/
+
+static void
+mm_plugin_init (gpointer g_iface)
+{
+}
+
+GType
+mm_plugin_get_type (void)
+{
+ static GType plugin_type = 0;
+
+ if (!G_UNLIKELY (plugin_type)) {
+ const GTypeInfo plugin_info = {
+ sizeof (MMPlugin), /* class_size */
+ mm_plugin_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ plugin_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMPlugin",
+ &plugin_info, 0);
+
+ g_type_interface_add_prerequisite (plugin_type, G_TYPE_OBJECT);
+ }
+
+ return plugin_type;
+}
diff --git a/src/mm-plugin.h b/src/mm-plugin.h
new file mode 100644
index 00000000..0f9a600b
--- /dev/null
+++ b/src/mm-plugin.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_PLUGIN_H
+#define MM_PLUGIN_H
+
+#include <glib-object.h>
+#include <libhal.h>
+#include <mm-modem.h>
+
+#define MM_PLUGIN_MAJOR_VERSION 1
+#define MM_PLUGIN_MINOR_VERSION 0
+
+#define MM_TYPE_PLUGIN (mm_plugin_get_type ())
+#define MM_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN, MMPlugin))
+#define MM_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN))
+#define MM_PLUGIN_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_PLUGIN, MMPlugin))
+
+typedef struct _MMPlugin MMPlugin;
+
+typedef MMPlugin *(*MMPluginCreateFunc) (void);
+
+struct _MMPlugin {
+ GTypeInterface g_iface;
+
+ /* Methods */
+ const char *(*get_name) (MMPlugin *self);
+
+ char **(*list_supported_udis) (MMPlugin *self,
+ LibHalContext *hal_ctx);
+
+ gboolean (*supports_udi) (MMPlugin *self,
+ LibHalContext *hal_ctx,
+ const char *udi);
+
+ MMModem *(*create_modem) (MMPlugin *self,
+ LibHalContext *hal_ctx,
+ const char *udi);
+};
+
+GType mm_plugin_get_type (void);
+
+const char *mm_plugin_get_name (MMPlugin *plugin);
+
+char **mm_plugin_list_supported_udis (MMPlugin *plugin,
+ LibHalContext *hal_ctx);
+
+gboolean mm_plugin_supports_udi (MMPlugin *plugin,
+ LibHalContext *hal_ctx,
+ const char *udi);
+
+MMModem *mm_plugin_create_modem (MMPlugin *plugin,
+ LibHalContext *hal_ctx,
+ const char *udi);
+
+#endif /* MM_PLUGIN_H */
diff --git a/src/mm-serial.c b/src/mm-serial.c
new file mode 100644
index 00000000..a9f3f349
--- /dev/null
+++ b/src/mm-serial.c
@@ -0,0 +1,1117 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#define _GNU_SOURCE /* for strcasestr() */
+
+#include <termio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+#include "mm-serial.h"
+
+G_DEFINE_TYPE (MMSerial, mm_serial, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_DEVICE,
+ PROP_BAUD,
+ PROP_BITS,
+ PROP_PARITY,
+ PROP_STOPBITS,
+ PROP_SEND_DELAY,
+
+ LAST_PROP
+};
+
+#define MM_DEBUG_SERIAL 1
+#define SERIAL_BUF_SIZE 2048
+
+#define MM_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL, MMSerialPrivate))
+
+typedef struct {
+ int fd;
+ GIOChannel *channel;
+ struct termios old_t;
+
+ char *device;
+ guint baud;
+ guint bits;
+ char parity;
+ guint stopbits;
+ guint64 send_delay;
+
+ guint pending_id;
+ guint timeout_id;
+} MMSerialPrivate;
+
+const char *
+mm_serial_get_device (MMSerial *serial)
+{
+ g_return_val_if_fail (MM_IS_SERIAL (serial), NULL);
+
+ return MM_SERIAL_GET_PRIVATE (serial)->device;
+}
+
+static int
+parse_baudrate (guint i)
+{
+ int speed;
+
+ switch (i) {
+ case 0:
+ speed = B0;
+ break;
+ case 50:
+ speed = B50;
+ break;
+ case 75:
+ speed = B75;
+ break;
+ case 110:
+ speed = B110;
+ break;
+ case 150:
+ speed = B150;
+ break;
+ case 300:
+ speed = B300;
+ break;
+ case 600:
+ speed = B600;
+ break;
+ case 1200:
+ speed = B1200;
+ break;
+ case 2400:
+ speed = B2400;
+ break;
+ case 4800:
+ speed = B4800;
+ break;
+ case 9600:
+ speed = B9600;
+ break;
+ case 19200:
+ speed = B19200;
+ break;
+ case 38400:
+ speed = B38400;
+ break;
+ case 57600:
+ speed = B57600;
+ break;
+ case 115200:
+ speed = B115200;
+ break;
+ case 460800:
+ speed = B460800;
+ break;
+ default:
+ g_warning ("Invalid baudrate '%d'", i);
+ speed = B9600;
+ }
+
+ return speed;
+}
+
+static int
+parse_bits (guint i)
+{
+ int bits;
+
+ switch (i) {
+ case 5:
+ bits = CS5;
+ break;
+ case 6:
+ bits = CS6;
+ break;
+ case 7:
+ bits = CS7;
+ break;
+ case 8:
+ bits = CS8;
+ break;
+ default:
+ g_warning ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i);
+ bits = CS8;
+ }
+
+ return bits;
+}
+
+static int
+parse_parity (char c)
+{
+ int parity;
+
+ switch (c) {
+ case 'n':
+ case 'N':
+ parity = 0;
+ break;
+ case 'e':
+ case 'E':
+ parity = PARENB;
+ break;
+ case 'o':
+ case 'O':
+ parity = PARENB | PARODD;
+ break;
+ default:
+ g_warning ("Invalid parity (%c). Valid values are n, e, o", c);
+ parity = 0;
+ }
+
+ return parity;
+}
+
+static int
+parse_stopbits (guint i)
+{
+ int stopbits;
+
+ switch (i) {
+ case 1:
+ stopbits = 0;
+ break;
+ case 2:
+ stopbits = CSTOPB;
+ break;
+ default:
+ g_warning ("Invalid stop bits (%d). Valid values are 1 and 2)", i);
+ stopbits = 0;
+ }
+
+ return stopbits;
+}
+
+#ifdef MM_DEBUG_SERIAL
+static inline void
+serial_debug (const char *prefix, const char *data, int len)
+{
+ GString *str;
+ int i;
+
+ str = g_string_sized_new (len);
+ for (i = 0; i < len; i++) {
+ if (data[i] == '\0')
+ g_string_append_c (str, ' ');
+ else if (data[i] == '\r')
+ g_string_append_c (str, '\n');
+ else
+ g_string_append_c (str, data[i]);
+ }
+
+ g_debug ("%s '%s'", prefix, str->str);
+ g_string_free (str, TRUE);
+}
+#else
+static inline void
+serial_debug (const char *prefix, const char *data, int len)
+{
+}
+#endif /* MM_DEBUG_SERIAL */
+
+/* Timeout handling */
+
+static void
+mm_serial_timeout_removed (gpointer data)
+{
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (data);
+
+ priv->timeout_id = 0;
+}
+
+static gboolean
+mm_serial_timed_out (gpointer data)
+{
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (data);
+
+ /* Cancel data reading */
+ if (priv->pending_id)
+ g_source_remove (priv->pending_id);
+ else
+ g_warning ("Timeout reached, but there's nothing to time out");
+
+ return FALSE;
+}
+
+static void
+mm_serial_add_timeout (MMSerial *self, guint timeout)
+{
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
+
+ if (priv->pending_id == 0)
+ g_warning ("Adding a time out while not waiting for any data");
+
+ if (priv->timeout_id) {
+ g_warning ("Trying to add a new time out while the old one still exists");
+ g_source_remove (priv->timeout_id);
+ }
+
+ priv->timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT,
+ timeout * 1000,
+ mm_serial_timed_out,
+ self,
+ mm_serial_timeout_removed);
+ if (G_UNLIKELY (priv->timeout_id == 0))
+ g_warning ("Registering serial device time out failed.");
+}
+
+static void
+mm_serial_remove_timeout (MMSerial *self)
+{
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
+
+ if (priv->timeout_id)
+ g_source_remove (priv->timeout_id);
+}
+
+/* Pending data reading */
+
+static guint
+mm_serial_set_pending (MMSerial *self,
+ guint timeout,
+ GIOFunc callback,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
+
+ if (G_UNLIKELY (priv->pending_id)) {
+ /* FIXME: Probably should queue up pending calls instead? */
+ /* Multiple pending calls on the same GIOChannel doesn't work, so let's cancel the previous one. */
+ g_warning ("Adding new pending call while previous one isn't finished.");
+ g_warning ("Cancelling the previous pending call.");
+ g_source_remove (priv->pending_id);
+ }
+
+ priv->pending_id = g_io_add_watch_full (priv->channel,
+ G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ callback, user_data, notify);
+
+ mm_serial_add_timeout (self, timeout);
+
+ return priv->pending_id;
+}
+
+static void
+mm_serial_pending_done (MMSerial *self)
+{
+ MM_SERIAL_GET_PRIVATE (self)->pending_id = 0;
+ mm_serial_remove_timeout (self);
+}
+
+/****/
+
+static gboolean
+config_fd (MMSerial *self)
+{
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
+ struct termio stbuf;
+ int speed;
+ int bits;
+ int parity;
+ int stopbits;
+
+ speed = parse_baudrate (priv->baud);
+ bits = parse_bits (priv->bits);
+ parity = parse_parity (priv->parity);
+ stopbits = parse_stopbits (priv->stopbits);
+
+ ioctl (priv->fd, TCGETA, &stbuf);
+
+ stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR );
+ stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
+ stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL);
+ stbuf.c_lflag &= ~(ECHO | ECHOE);
+ stbuf.c_cc[VMIN] = 1;
+ stbuf.c_cc[VTIME] = 0;
+ stbuf.c_cc[VEOF] = 1;
+
+ stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
+ stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits);
+
+ if (ioctl (priv->fd, TCSETA, &stbuf) < 0) {
+ g_warning ("(%s) cannot control device (errno %d)", priv->device, errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+mm_serial_open (MMSerial *self)
+{
+ MMSerialPrivate *priv;
+
+ g_return_val_if_fail (MM_IS_SERIAL (self), FALSE);
+
+ priv = MM_SERIAL_GET_PRIVATE (self);
+
+ g_debug ("(%s) opening serial device...", priv->device);
+ priv->fd = open (priv->device, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
+
+ if (priv->fd < 0) {
+ g_warning ("(%s) cannot open device: %s", priv->device, strerror (errno));
+ return FALSE;
+ }
+
+ if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) {
+ g_warning ("(%s) cannot control device (errno %d)", priv->device, errno);
+ close (priv->fd);
+ return FALSE;
+ }
+
+ if (!config_fd (self)) {
+ close (priv->fd);
+ return FALSE;
+ }
+
+ priv->channel = g_io_channel_unix_new (priv->fd);
+
+ return TRUE;
+}
+
+void
+mm_serial_close (MMSerial *self)
+{
+ MMSerialPrivate *priv;
+
+ g_return_if_fail (MM_IS_SERIAL (self));
+
+ priv = MM_SERIAL_GET_PRIVATE (self);
+
+ if (priv->pending_id)
+ g_source_remove (priv->pending_id);
+
+ if (priv->fd) {
+ g_message ("Closing device '%s'", priv->device);
+
+ if (priv->channel) {
+ g_io_channel_unref (priv->channel);
+ priv->channel = NULL;
+ }
+
+ ioctl (priv->fd, TCSETA, &priv->old_t);
+ close (priv->fd);
+ priv->fd = 0;
+ }
+}
+
+gboolean
+mm_serial_send_command (MMSerial *self, GByteArray *command)
+{
+ MMSerialPrivate *priv;
+ int fd;
+ int i;
+ ssize_t status;
+
+ g_return_val_if_fail (MM_IS_SERIAL (self), FALSE);
+ g_return_val_if_fail (command != NULL, FALSE);
+
+ priv = MM_SERIAL_GET_PRIVATE (self);
+
+ fd = priv->fd;
+
+ serial_debug ("Sending:", (char *) command->data, command->len);
+
+ for (i = 0; i < command->len; i++) {
+ again:
+ status = write (fd, command->data + i, 1);
+
+ if (status < 0) {
+ if (errno == EAGAIN)
+ goto again;
+
+ g_warning ("Error in writing (errno %d)", errno);
+ return FALSE;
+ }
+
+ if (priv->send_delay)
+ usleep (priv->send_delay);
+ }
+
+ return TRUE;
+}
+
+gboolean
+mm_serial_send_command_string (MMSerial *self, const char *str)
+{
+ GByteArray *command;
+ gboolean ret;
+
+ g_return_val_if_fail (MM_IS_SERIAL (self), FALSE);
+ g_return_val_if_fail (str != NULL, FALSE);
+
+ command = g_byte_array_new ();
+ g_byte_array_append (command, (guint8 *) str, strlen (str));
+ g_byte_array_append (command, (guint8 *) "\r", 1);
+
+ ret = mm_serial_send_command (self, command);
+ g_byte_array_free (command, TRUE);
+
+ return ret;
+}
+
+typedef struct {
+ MMSerial *serial;
+ char *terminators;
+ GString *result;
+ MMSerialGetReplyFn callback;
+ gpointer user_data;
+} GetReplyInfo;
+
+static void
+get_reply_done (gpointer data)
+{
+ GetReplyInfo *info = (GetReplyInfo *) data;
+
+ mm_serial_pending_done (info->serial);
+
+ /* Call the callback */
+ info->callback (info->serial, info->result->str, info->user_data);
+
+ /* Free info */
+ g_free (info->terminators);
+ g_string_free (info->result, TRUE);
+
+ g_slice_free (GetReplyInfo, info);
+}
+
+static gboolean
+get_reply_got_data (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ GetReplyInfo *info = (GetReplyInfo *) data;
+ gsize bytes_read;
+ char buf[SERIAL_BUF_SIZE + 1];
+ GIOStatus status;
+ gboolean done = FALSE;
+ int i;
+
+ if (condition & G_IO_HUP || condition & G_IO_ERR) {
+ g_string_truncate (info->result, 0);
+ return FALSE;
+ }
+
+ do {
+ GError *err = NULL;
+
+ status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err);
+ if (status == G_IO_STATUS_ERROR) {
+ g_warning ("%s", err->message);
+ g_error_free (err);
+ err = NULL;
+ }
+
+ if (bytes_read > 0) {
+ char *p;
+
+ serial_debug ("Got:", buf, bytes_read);
+
+ p = &buf[0];
+ for (i = 0; i < bytes_read && !done; i++, p++) {
+ int j;
+ gboolean is_terminator = FALSE;
+
+ for (j = 0; j < strlen (info->terminators); j++) {
+ if (*p == info->terminators[j]) {
+ is_terminator = TRUE;
+ break;
+ }
+ }
+
+ if (is_terminator) {
+ /* Ignore terminators in the beginning of the output */
+ if (info->result->len > 0)
+ done = TRUE;
+ } else
+ g_string_append_c (info->result, *p);
+ }
+ }
+
+ /* Limit the size of the buffer */
+ if (info->result->len > SERIAL_BUF_SIZE) {
+ g_warning ("%s (%s): response buffer filled before repsonse received",
+ __func__, MM_SERIAL_GET_PRIVATE (info->serial)->device);
+ g_string_truncate (info->result, 0);
+ done = TRUE;
+ }
+ } while (!done || bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
+
+ return !done;
+}
+
+guint
+mm_serial_get_reply (MMSerial *self,
+ guint timeout,
+ const char *terminators,
+ MMSerialGetReplyFn callback,
+ gpointer user_data)
+{
+ GetReplyInfo *info;
+
+ g_return_val_if_fail (MM_IS_SERIAL (self), 0);
+ g_return_val_if_fail (terminators != NULL, 0);
+ g_return_val_if_fail (callback != NULL, 0);
+
+ info = g_slice_new0 (GetReplyInfo);
+ info->serial = self;
+ info->terminators = g_strdup (terminators);
+ info->result = g_string_new (NULL);
+ info->callback = callback;
+ info->user_data = user_data;
+
+ return mm_serial_set_pending (self, timeout, get_reply_got_data, info, get_reply_done);
+}
+
+typedef struct {
+ MMSerial *serial;
+ char **str_needles;
+ char **terminators;
+ GString *result;
+ MMSerialWaitForReplyFn callback;
+ gpointer user_data;
+ int reply_index;
+ guint timeout;
+ time_t start;
+} WaitForReplyInfo;
+
+static void
+wait_for_reply_done (gpointer data)
+{
+ WaitForReplyInfo *info = (WaitForReplyInfo *) data;
+
+ mm_serial_pending_done (info->serial);
+
+ /* Call the callback */
+ info->callback (info->serial, info->reply_index, info->user_data);
+
+ /* Free info */
+ if (info->result)
+ g_string_free (info->result, TRUE);
+
+ g_strfreev (info->str_needles);
+ g_strfreev (info->terminators);
+ g_slice_free (WaitForReplyInfo, info);
+}
+
+static gboolean
+find_terminator (const char *line, char **terminators)
+{
+ int i;
+
+ for (i = 0; terminators[i]; i++) {
+ if (!strncasecmp (line, terminators[i], strlen (terminators[i])))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+find_response (const char *line, char **responses, gint *idx)
+{
+ int i;
+
+ /* Don't look for a result again if we got one previously */
+ for (i = 0; responses[i]; i++) {
+ if (strcasestr (line, responses[i])) {
+ *idx = i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+#define RESPONSE_LINE_MAX 128
+
+static gboolean
+wait_for_reply_got_data (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ WaitForReplyInfo *info = (WaitForReplyInfo *) data;
+ gchar buf[SERIAL_BUF_SIZE + 1];
+ gsize bytes_read;
+ GIOStatus status;
+ gboolean got_response = FALSE;
+ gboolean done = FALSE;
+
+ if (condition & G_IO_HUP || condition & G_IO_ERR)
+ return FALSE;
+
+ do {
+ GError *err = NULL;
+
+ status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err);
+ if (status == G_IO_STATUS_ERROR) {
+ g_warning ("%s", err->message);
+ g_error_free (err);
+ err = NULL;
+ }
+
+ if (bytes_read > 0) {
+ buf[bytes_read] = 0;
+ g_string_append (info->result, buf);
+
+ serial_debug ("Got:", info->result->str, info->result->len);
+ }
+
+ /* Look for needles and terminators */
+ if ((bytes_read > 0) && info->result->str) {
+ char *p = info->result->str;
+
+ /* Break the response up into lines and process each one */
+ while ( (p < info->result->str + strlen (info->result->str))
+ && !(done && got_response)) {
+ char line[RESPONSE_LINE_MAX] = { '\0', };
+ char *tmp;
+ int i;
+ gboolean got_something = FALSE;
+
+ for (i = 0; *p && (i < RESPONSE_LINE_MAX - 1); p++) {
+ /* Ignore front CR/LF */
+ if ((*p == '\n') || (*p == '\r')) {
+ if (got_something)
+ break;
+ } else {
+ line[i++] = *p;
+ got_something = TRUE;
+ }
+ }
+ line[i] = '\0';
+
+ tmp = g_strstrip (line);
+ if (tmp && strlen (tmp)) {
+ done = find_terminator (tmp, info->terminators);
+ if (info->reply_index == -1)
+ got_response = find_response (tmp, info->str_needles, &(info->reply_index));
+ }
+ }
+
+ if (done && got_response)
+ break;
+ }
+
+ /* Limit the size of the buffer */
+ if (info->result->len > SERIAL_BUF_SIZE) {
+ g_warning ("%s (%s): response buffer filled before repsonse received",
+ __func__, MM_SERIAL_GET_PRIVATE (info->serial)->device);
+ done = TRUE;
+ break;
+ }
+
+ /* Make sure we don't go over the timeout, in addition to the timeout
+ * handler that's been scheduled. If for some reason this loop doesn't
+ * terminate (terminator not found, whatever) then this should make
+ * sure that we don't spin the CPU forever.
+ */
+ if (time (NULL) - info->start > info->timeout + 1) {
+ done = TRUE;
+ break;
+ } else
+ g_usleep (50);
+ } while (!done || bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
+
+ return !done;
+}
+
+guint
+mm_serial_wait_for_reply (MMSerial *self,
+ guint timeout,
+ char **responses,
+ char **terminators,
+ MMSerialWaitForReplyFn callback,
+ gpointer user_data)
+{
+ WaitForReplyInfo *info;
+
+ g_return_val_if_fail (MM_IS_SERIAL (self), 0);
+ g_return_val_if_fail (responses != NULL, 0);
+ g_return_val_if_fail (callback != NULL, 0);
+
+ info = g_slice_new0 (WaitForReplyInfo);
+ info->serial = self;
+ info->str_needles = g_strdupv (responses);
+ info->terminators = g_strdupv (terminators);
+ info->result = g_string_new (NULL);
+ info->callback = callback;
+ info->user_data = user_data;
+ info->reply_index = -1;
+ info->timeout = timeout * 1000;
+ info->start = time (NULL);
+
+ return mm_serial_set_pending (self, timeout, wait_for_reply_got_data, info, wait_for_reply_done);
+}
+
+#if 0
+typedef struct {
+ MMSerial *serial;
+ gboolean timed_out;
+ MMSerialWaitQuietFn callback;
+ gpointer user_data;
+} WaitQuietInfo;
+
+static void
+wait_quiet_done (gpointer data)
+{
+ WaitQuietInfo *info = (WaitQuietInfo *) data;
+
+ mm_serial_pending_done (info->serial);
+
+ /* Call the callback */
+ info->callback (info->serial, info->timed_out, info->user_data);
+
+ /* Free info */
+ g_slice_free (WaitQuietInfo, info);
+}
+
+static gboolean
+wait_quiet_quiettime (gpointer data)
+{
+ WaitQuietInfo *info = (WaitQuietInfo *) data;
+
+ info->timed_out = FALSE;
+ g_source_remove (MM_SERIAL_GET_PRIVATE (info->serial)->pending);
+
+ return FALSE;
+}
+
+static gboolean
+wait_quiet_got_data (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ WaitQuietInfo *info = (WaitQuietInfo *) data;
+ gsize bytes_read;
+ char buf[4096];
+ GIOStatus status;
+
+ if (condition & G_IO_HUP || condition & G_IO_ERR)
+ return FALSE;
+
+ if (condition & G_IO_IN) {
+ do {
+ status = g_io_channel_read_chars (source, buf, 4096, &bytes_read, NULL);
+
+ if (bytes_read) {
+ /* Reset the quiet time timeout */
+ g_source_remove (info->quiet_id);
+ info->quiet_id = g_timeout_add (info->quiet_time, wait_quiet_quiettime, info);
+ }
+ } while (bytes_read == 4096 || status == G_IO_STATUS_AGAIN);
+ }
+
+ return TRUE;
+}
+
+void
+mm_serial_wait_quiet (MMSerial *self,
+ guint timeout,
+ guint quiet_time,
+ MMSerialWaitQuietFn callback,
+ gpointer user_data)
+{
+ WaitQuietInfo *info;
+
+ g_return_if_fail (MM_IS_SERIAL (self));
+ g_return_if_fail (callback != NULL);
+
+ info = g_slice_new0 (WaitQuietInfo);
+ info->serial = self;
+ info->timed_out = TRUE;
+ info->callback = callback;
+ info->user_data = user_data;
+ info->quiet_id = g_timeout_add (quiet_time,
+ wait_quiet_timeout,
+ info);
+
+ return mm_serial_set_pending (self, timeout, wait_quiet_got_data, info, wait_quiet_done);
+}
+
+#endif
+
+typedef struct {
+ MMSerial *serial;
+ speed_t current_speed;
+ MMSerialFlashFn callback;
+ gpointer user_data;
+} FlashInfo;
+
+static speed_t
+get_speed (MMSerial *self)
+{
+ struct termios options;
+
+ tcgetattr (MM_SERIAL_GET_PRIVATE (self)->fd, &options);
+
+ return cfgetospeed (&options);
+}
+
+static void
+set_speed (MMSerial *self, speed_t speed)
+{
+ struct termios options;
+ int fd;
+
+ fd = MM_SERIAL_GET_PRIVATE (self)->fd;
+ tcgetattr (fd, &options);
+
+ cfsetispeed (&options, speed);
+ cfsetospeed (&options, speed);
+
+ options.c_cflag |= (CLOCAL | CREAD);
+ tcsetattr (fd, TCSANOW, &options);
+}
+
+static void
+flash_done (gpointer data)
+{
+ FlashInfo *info = (FlashInfo *) data;
+
+ MM_SERIAL_GET_PRIVATE (info->serial)->pending_id = 0;
+
+ info->callback (info->serial, info->user_data);
+
+ g_slice_free (FlashInfo, info);
+}
+
+static gboolean
+flash_do (gpointer data)
+{
+ FlashInfo *info = (FlashInfo *) data;
+
+ set_speed (info->serial, info->current_speed);
+
+ return FALSE;
+}
+
+guint
+mm_serial_flash (MMSerial *self,
+ guint32 flash_time,
+ MMSerialFlashFn callback,
+ gpointer user_data)
+{
+ FlashInfo *info;
+ guint id;
+
+ g_return_val_if_fail (MM_IS_SERIAL (self), 0);
+ g_return_val_if_fail (callback != NULL, 0);
+
+ info = g_slice_new0 (FlashInfo);
+ info->serial = self;
+ info->current_speed = get_speed (self);
+ info->callback = callback;
+ info->user_data = user_data;
+
+ set_speed (self, B0);
+
+ id = g_timeout_add_full (G_PRIORITY_DEFAULT,
+ flash_time,
+ flash_do,
+ info,
+ flash_done);
+
+ MM_SERIAL_GET_PRIVATE (self)->pending_id = id;
+
+ return id;
+}
+
+GIOChannel *
+mm_serial_get_io_channel (MMSerial *self)
+{
+ MMSerialPrivate *priv;
+
+ g_return_val_if_fail (MM_IS_SERIAL (self), NULL);
+
+ priv = MM_SERIAL_GET_PRIVATE (self);
+ if (priv->channel)
+ return g_io_channel_ref (priv->channel);
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+static void
+mm_serial_init (MMSerial *self)
+{
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
+
+ priv->baud = 57600;
+ priv->bits = 8;
+ priv->parity = 'n';
+ priv->stopbits = 1;
+ priv->send_delay = 0;
+}
+
+static GObject*
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ MMSerialPrivate *priv;
+
+ object = G_OBJECT_CLASS (mm_serial_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+ if (!object)
+ return NULL;
+
+ priv = MM_SERIAL_GET_PRIVATE (object);
+
+ if (!priv->device) {
+ g_warning ("No device provided");
+ g_object_unref (object);
+ return NULL;
+ }
+
+ return object;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ /* Construct only */
+ priv->device = g_value_dup_string (value);
+ break;
+ case PROP_BAUD:
+ priv->baud = g_value_get_uint (value);
+ break;
+ case PROP_BITS:
+ priv->bits = g_value_get_uint (value);
+ break;
+ case PROP_PARITY:
+ priv->parity = g_value_get_char (value);
+ break;
+ case PROP_STOPBITS:
+ priv->stopbits = g_value_get_uint (value);
+ break;
+ case PROP_SEND_DELAY:
+ priv->send_delay = g_value_get_uint64 (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ g_value_set_string (value, priv->device);
+ break;
+ case PROP_BAUD:
+ g_value_set_uint (value, priv->baud);
+ break;
+ case PROP_BITS:
+ g_value_set_uint (value, priv->bits);
+ break;
+ case PROP_PARITY:
+ g_value_set_char (value, priv->parity);
+ break;
+ case PROP_STOPBITS:
+ g_value_set_uint (value, priv->stopbits);
+ break;
+ case PROP_SEND_DELAY:
+ g_value_set_uint64 (value, priv->send_delay);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ MMSerial *self = MM_SERIAL (object);
+ MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
+
+ mm_serial_close (self);
+ g_free (priv->device);
+
+ G_OBJECT_CLASS (mm_serial_parent_class)->finalize (object);
+}
+
+static void
+mm_serial_class_init (MMSerialClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMSerialPrivate));
+
+ /* Virtual methods */
+ object_class->constructor = constructor;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* Properties */
+ g_object_class_install_property
+ (object_class, PROP_DEVICE,
+ g_param_spec_string (MM_SERIAL_DEVICE,
+ "Device",
+ "Serial device",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_BAUD,
+ g_param_spec_uint (MM_SERIAL_BAUD,
+ "Baud",
+ "Baud rate",
+ 0, G_MAXUINT, 57600,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_BITS,
+ g_param_spec_uint (MM_SERIAL_BITS,
+ "Bits",
+ "Bits",
+ 5, 8, 8,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_PARITY,
+ g_param_spec_char (MM_SERIAL_PARITY,
+ "Parity",
+ "Parity",
+ 'E', 'o', 'n',
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_STOPBITS,
+ g_param_spec_uint (MM_SERIAL_STOPBITS,
+ "Stopbits",
+ "Stopbits",
+ 1, 2, 1,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_SEND_DELAY,
+ g_param_spec_uint64 (MM_SERIAL_SEND_DELAY,
+ "SendDelay",
+ "Send delay",
+ 0, G_MAXUINT64, 0,
+ G_PARAM_READWRITE));
+}
diff --git a/src/mm-serial.h b/src/mm-serial.h
new file mode 100644
index 00000000..bd138516
--- /dev/null
+++ b/src/mm-serial.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_SERIAL_H
+#define MM_SERIAL_H
+
+#include <glib/gtypes.h>
+#include <glib-object.h>
+
+#define MM_TYPE_SERIAL (mm_serial_get_type ())
+#define MM_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SERIAL, MMSerial))
+#define MM_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SERIAL, MMSerialClass))
+#define MM_IS_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SERIAL))
+#define MM_IS_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL))
+#define MM_SERIAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL, MMSerialClass))
+
+#define MM_SERIAL_DEVICE "device"
+#define MM_SERIAL_BAUD "baud"
+#define MM_SERIAL_BITS "bits"
+#define MM_SERIAL_PARITY "parity"
+#define MM_SERIAL_STOPBITS "stopbits"
+#define MM_SERIAL_SEND_DELAY "send-delay"
+
+typedef struct {
+ GObject parent;
+} MMSerial;
+
+typedef struct {
+ GObjectClass parent;
+} MMSerialClass;
+
+GType mm_serial_get_type (void);
+
+typedef void (*MMSerialGetReplyFn) (MMSerial *serial,
+ const char *reply,
+ gpointer user_data);
+
+typedef void (*MMSerialWaitForReplyFn) (MMSerial *serial,
+ int reply_index,
+ gpointer user_data);
+
+typedef void (*MMSerialWaitQuietFn) (MMSerial *serial,
+ gboolean timed_out,
+ gpointer user_data);
+
+typedef void (*MMSerialFlashFn) (MMSerial *serial,
+ gpointer user_data);
+
+const char *mm_serial_get_device (MMSerial *serial);
+
+gboolean mm_serial_open (MMSerial *self);
+
+void mm_serial_close (MMSerial *self);
+gboolean mm_serial_send_command (MMSerial *self,
+ GByteArray *command);
+
+gboolean mm_serial_send_command_string (MMSerial *self,
+ const char *str);
+
+guint mm_serial_get_reply (MMSerial *self,
+ guint timeout,
+ const char *terminators,
+ MMSerialGetReplyFn callback,
+ gpointer user_data);
+
+guint mm_serial_wait_for_reply (MMSerial *self,
+ guint timeout,
+ char **responses,
+ char **terminators,
+ MMSerialWaitForReplyFn callback,
+ gpointer user_data);
+
+void mm_serial_wait_quiet (MMSerial *self,
+ guint timeout,
+ guint quiet_time,
+ MMSerialWaitQuietFn callback,
+ gpointer user_data);
+
+guint mm_serial_flash (MMSerial *self,
+ guint32 flash_time,
+ MMSerialFlashFn callback,
+ gpointer user_data);
+
+GIOChannel *mm_serial_get_io_channel (MMSerial *self);
+
+#endif /* MM_SERIAL_H */