From cc31458b18e8c274641cf124f5bf7ec6fe506dfb Mon Sep 17 00:00:00 2001 From: Tambet Ingo Date: Thu, 31 Jul 2008 09:43:00 +0300 Subject: [PATCH] Initial commit. --- AUTHORS | 2 + COPYING | 1 + ChangeLog | 0 Makefile.am | 30 + ModemManager.pc.in | 12 + NEWS | 0 README | 0 autogen.sh | 20 + configure.in | 24 + introspection/Makefile.am | 4 + introspection/mm-manager.xml | 19 + introspection/mm-modem.xml | 85 ++ org.freedesktop.ModemManager.conf | 19 + org.freedesktop.ModemManager.service.in | 4 + plugins/Makefile.am | 14 + plugins/mm-modem-huawei.c | 556 +++++++++++ plugins/mm-modem-huawei.h | 31 + plugins/mm-plugin-huawei.c | 155 ++++ plugins/mm-plugin-huawei.h | 26 + src/Makefile.am | 39 + src/main.c | 166 ++++ src/mm-callback-info.c | 98 ++ src/mm-callback-info.h | 38 + src/mm-generic-cdma.c | 259 ++++++ src/mm-generic-cdma.h | 29 + src/mm-generic-gsm.c | 705 ++++++++++++++ src/mm-generic-gsm.h | 29 + src/mm-manager.c | 457 ++++++++++ src/mm-manager.h | 36 + src/mm-modem-error.c | 37 + src/mm-modem-error.h | 22 + src/mm-modem.c | 460 ++++++++++ src/mm-modem.h | 196 ++++ src/mm-plugin.c | 81 ++ src/mm-plugin.h | 55 ++ src/mm-serial.c | 1117 +++++++++++++++++++++++ src/mm-serial.h | 85 ++ 37 files changed, 4911 insertions(+) create mode 100644 AUTHORS create mode 120000 COPYING create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 ModemManager.pc.in create mode 100644 NEWS create mode 100644 README create mode 100755 autogen.sh create mode 100644 configure.in create mode 100644 introspection/Makefile.am create mode 100644 introspection/mm-manager.xml create mode 100644 introspection/mm-modem.xml create mode 100644 org.freedesktop.ModemManager.conf create mode 100644 org.freedesktop.ModemManager.service.in create mode 100644 plugins/Makefile.am create mode 100644 plugins/mm-modem-huawei.c create mode 100644 plugins/mm-modem-huawei.h create mode 100644 plugins/mm-plugin-huawei.c create mode 100644 plugins/mm-plugin-huawei.h create mode 100644 src/Makefile.am create mode 100644 src/main.c create mode 100644 src/mm-callback-info.c create mode 100644 src/mm-callback-info.h create mode 100644 src/mm-generic-cdma.c create mode 100644 src/mm-generic-cdma.h create mode 100644 src/mm-generic-gsm.c create mode 100644 src/mm-generic-gsm.h create mode 100644 src/mm-manager.c create mode 100644 src/mm-manager.h create mode 100644 src/mm-modem-error.c create mode 100644 src/mm-modem-error.h create mode 100644 src/mm-modem.c create mode 100644 src/mm-modem.h create mode 100644 src/mm-plugin.c create mode 100644 src/mm-plugin.h create mode 100644 src/mm-serial.c create mode 100644 src/mm-serial.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..12af19c8 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Tambet Ingo + 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 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 diff --git a/README b/README new file mode 100644 index 00000000..e69de29b 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 @@ + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + 512 + 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 +#include +#include +#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 +#include +#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 +#include +#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 +#include +#include +#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 +#include +#include +#include +#include +#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 +#include +#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 + +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 +#include +#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 + +#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 +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +#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 */