Rewrite serial device communications.

Instead of vague "send something, wait something" the responses are now
analyzed by (overridable) parsers. Makes all the modem implementations much
easier since each caller knows without any code whether the call succeeded
or failed.

Another thing that makes modem code simpler (and the whole thing more robust),
is the queueing of sent commands. Each queued command has a command and a
callback which is quaranteed to get called, even if sending failed.

Define and implement error reporting.
This commit is contained in:
Tambet Ingo
2008-09-11 08:35:32 +03:00
parent bb874acea0
commit ac4409e7ce
23 changed files with 1463 additions and 1497 deletions

View File

@@ -9,7 +9,7 @@
#include <dbus/dbus-glib.h>
#include "mm-modem-hso.h"
#include "mm-serial.h"
#include "mm-modem-error.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
static void impl_hso_authenticate (MMModemHso *self,
@@ -58,19 +58,14 @@ mm_modem_hso_new (const char *serial_device,
static void
hso_enable_done (MMSerial *serial,
int reply_index,
GString *response,
GError *error,
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", "Enable/Disable failed.");
break;
}
if (error)
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
}
@@ -83,8 +78,6 @@ hso_enable (MMModemHso *self,
{
MMCallbackInfo *info;
char *command;
char *responses[] = { "_OWANCALL: ", "ERROR", "NO CARRIER", NULL };
guint id = 0;
info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
@@ -92,15 +85,8 @@ hso_enable (MMModemHso *self,
mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)),
enabled ? 1 : 0);
if (mm_serial_send_command_string (MM_SERIAL (self), command))
id = mm_serial_wait_for_reply (MM_SERIAL (self), 5, responses, responses, hso_enable_done, user_data);
mm_serial_queue_command (MM_SERIAL (self), command, 3, hso_enable_done, info);
g_free (command);
if (!id) {
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Enable/Disable failed.");
mm_callback_info_schedule (info);
}
}
static void
@@ -132,22 +118,18 @@ hso_disabled (MMModem *modem,
static void
auth_done (MMSerial *serial,
int reply_index,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
switch (reply_index) {
case 0:
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
} else
/* success, kill any existing connections first */
hso_enable (MM_MODEM_HSO (serial), FALSE, hso_disabled, info);
break;
default:
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Authentication failed");
break;
}
mm_callback_info_schedule (info);
}
void
@@ -159,8 +141,6 @@ mm_hso_modem_authenticate (MMModemHso *self,
{
MMCallbackInfo *info;
char *command;
char *responses[] = { "OK", "ERROR", NULL };
guint id = 0;
g_return_if_fail (MM_IS_MODEM_HSO (self));
g_return_if_fail (callback != NULL);
@@ -171,15 +151,8 @@ mm_hso_modem_authenticate (MMModemHso *self,
password ? password : "",
username ? username : "");
if (mm_serial_send_command_string (MM_SERIAL (self), command))
id = mm_serial_wait_for_reply (MM_SERIAL (self), 5, responses, responses, auth_done, user_data);
mm_serial_queue_command (MM_SERIAL (self), command, 3, auth_done, info);
g_free (command);
if (!id) {
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Authentication failed.");
mm_callback_info_schedule (info);
}
}
static void
@@ -205,7 +178,10 @@ ip4_callback_wrapper (MMModem *modem,
}
static void
get_ip4_config_done (MMSerial *serial, const char *response, gpointer user_data)
get_ip4_config_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
char **items, **iter;
@@ -214,15 +190,18 @@ get_ip4_config_done (MMSerial *serial, const char *response, gpointer user_data)
guint32 tmp;
guint cid;
if (!response || strncmp (response, OWANDATA_TAG, strlen (OWANDATA_TAG))) {
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s",
if (error) {
info->error = g_error_copy (error);
goto out;
} else if (g_str_has_prefix (response->str, OWANDATA_TAG)) {
info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Retrieving failed: invalid response.");
goto out;
}
cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (serial));
dns_array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 2);
items = g_strsplit (response + strlen (OWANDATA_TAG), ", ", 0);
items = g_strsplit (response->str + strlen (OWANDATA_TAG), ", ", 0);
for (iter = items, i = 0; *iter; iter++, i++) {
if (i == 0) { /* CID */
@@ -261,8 +240,6 @@ mm_hso_modem_get_ip4_config (MMModemHso *self,
{
MMCallbackInfo *info;
char *command;
const char terminators[] = { '\r', '\n', '\0' };
guint id = 0;
g_return_if_fail (MM_IS_MODEM_HSO (self));
g_return_if_fail (callback != NULL);
@@ -273,15 +250,8 @@ mm_hso_modem_get_ip4_config (MMModemHso *self,
mm_callback_info_set_data (info, "user-data", user_data, NULL);
command = g_strdup_printf ("AT_OWANDATA=%d", mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)));
if (mm_serial_send_command_string (MM_SERIAL (self), command))
id = mm_serial_get_reply (MM_SERIAL (self), 5, terminators, get_ip4_config_done, info);
mm_serial_queue_command (MM_SERIAL (self), command, 3, get_ip4_config_done, info);
g_free (command);
if (!id) {
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Retrieving failed.");
mm_callback_info_schedule (info);
}
}
/*****************************************************************************/

View File

@@ -5,8 +5,9 @@
#include <string.h>
#include "mm-modem-huawei.h"
#include "mm-modem-gsm-network.h"
#include "mm-modem-error.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-serial-parsers.h"
static gpointer mm_modem_huawei_parent_class = NULL;
@@ -14,7 +15,6 @@ static gpointer mm_modem_huawei_parent_class = NULL;
typedef struct {
MMSerial *monitor_device;
guint watch_id;
} MMModemHuaweiPrivate;
enum {
@@ -43,25 +43,37 @@ mm_modem_huawei_new (const char *data_device,
/*****************************************************************************/
typedef struct {
MMModemGsmNetwork *modem;
GRegex *r;
} MonitorData;
static void
parse_monitor_line (MMModemGsmNetwork *modem, char *buf)
monitor_info_free (gpointer data)
{
char **lines;
char **iter;
MonitorData *info = (MonitorData *) data;
lines = g_strsplit (buf, "\r\n", 0);
g_regex_unref (info->r);
g_slice_free (MonitorData, data);
}
for (iter = lines; iter && *iter; iter++) {
char *line = *iter;
static gboolean
monitor_parse (gpointer data,
GString *response,
GError **error)
{
MonitorData *info = (MonitorData *) data;
GMatchInfo *match_info;
gboolean found;
g_strstrip (line);
if (strlen (line) < 1 || line[0] != '^')
continue;
found = g_regex_match_full (info->r, response->str, response->len, 0, 0, &match_info, NULL);
if (found) {
char *str;
line += 1;
str = g_match_info_fetch (match_info, 1);
if (!strncmp (line, "RSSI:", 5)) {
int quality = atoi (line + 5);
if (g_str_has_prefix (str, "^RSSI:")) {
int quality = atoi (str + 6);
if (quality == 99)
/* 99 means unknown */
@@ -71,13 +83,13 @@ parse_monitor_line (MMModemGsmNetwork *modem, char *buf)
quality = quality * 100 / 31;
g_debug ("Signal quality: %d", quality);
mm_modem_gsm_network_signal_quality (modem, (guint32) quality);
} else if (!strncmp (line, "MODE:", 5)) {
mm_modem_gsm_network_signal_quality (info->modem, (guint32) quality);
} else if (g_str_has_prefix (str, "^MODE:")) {
MMModemGsmNetworkMode mode = 0;
int a;
int b;
if (sscanf (line + 5, "%d,%d", &a, &b)) {
if (sscanf (str + 6, "%d,%d", &a, &b)) {
if (a == 3 && b == 2)
mode = MM_MODEM_GSM_NETWORK_MODE_GPRS;
else if (a == 3 && b == 3)
@@ -89,40 +101,17 @@ parse_monitor_line (MMModemGsmNetwork *modem, char *buf)
if (mode) {
g_debug ("Mode: %d", mode);
mm_modem_gsm_network_mode (modem, mode);
}
mm_modem_gsm_network_mode (info->modem, mode);
}
}
}
g_strfreev (lines);
g_free (str);
g_match_info_free (match_info);
}
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_GSM_NETWORK (data), buf);
}
} while (bytes_read == 4096 || status == G_IO_STATUS_AGAIN);
}
if (condition & G_IO_HUP || condition & G_IO_ERR) {
return FALSE;
}
return TRUE;
return found;
}
static void
@@ -138,26 +127,17 @@ enable (MMModem *modem,
parent_modem_iface->enable (modem, enable, callback, user_data);
if (enable) {
GIOChannel *channel;
GError *error = NULL;
if (priv->watch_id == 0) {
mm_serial_open (priv->monitor_device);
channel = mm_serial_get_io_channel (priv->monitor_device);
priv->watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR | G_IO_HUP,
monitor_device_got_data, modem);
g_io_channel_unref (channel);
if (!mm_serial_open (priv->monitor_device, &error)) {
g_warning ("Could not open monitoring device %s: %s",
mm_serial_get_device (priv->monitor_device),
error->message);
g_error_free (error);
}
} else {
if (priv->watch_id) {
g_source_remove (priv->watch_id);
priv->watch_id = 0;
} else
mm_serial_close (priv->monitor_device);
}
}
}
static gboolean
parse_syscfg (const char *reply, int *mode_a, int *mode_b, guint32 *band, int *unknown1, int *unknown2)
@@ -173,49 +153,42 @@ parse_syscfg (const char *reply, int *mode_a, int *mode_b, guint32 *band, int *u
static void
set_network_mode_done (MMSerial *serial,
int reply_index,
GString *response,
GError *error,
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;
}
if (error)
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
}
static void
set_network_mode_get_done (MMSerial *serial, const char *reply, gpointer user_data)
set_network_mode_get_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
} else {
int a, b, u1, u2;
guint32 band;
guint id = 0;
if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) {
char *responses[] = { "OK", "ERROR", NULL };
if (parse_syscfg (response->str, &a, &b, &band, &u1, &u2)) {
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);
mm_serial_queue_command (serial, command, 3, 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);
}
}
@@ -226,8 +199,6 @@ set_network_mode (MMModemGsmNetwork *modem,
gpointer user_data)
{
MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
@@ -262,24 +233,25 @@ set_network_mode (MMModemGsmNetwork *modem,
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);
}
mm_serial_queue_command (MM_SERIAL (modem), "AT^SYSCFG?", 3, set_network_mode_get_done, info);
}
static void
get_network_mode_done (MMSerial *serial, const char *reply, gpointer user_data)
get_network_mode_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error)
info->error = g_error_copy (error);
else {
int a, b, u1, u2;
guint32 band;
guint32 result = 0;
if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) {
if (parse_syscfg (response->str, &a, &b, &band, &u1, &u2)) {
if (a == 2 && b == 1)
result = MM_MODEM_GSM_NETWORK_MODE_PREFER_2G;
else if (a == 2 && b == 2)
@@ -295,6 +267,7 @@ get_network_mode_done (MMSerial *serial, const char *reply, gpointer user_data)
"%s", "Could not parse network mode results");
else
mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
}
mm_callback_info_schedule (info);
}
@@ -305,64 +278,48 @@ get_network_mode (MMModemGsmNetwork *modem,
gpointer user_data)
{
MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_uint_new (MM_MODEM (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);
}
mm_serial_queue_command (MM_SERIAL (modem), "AT^SYSCFG?", 3, get_network_mode_done, info);
}
static void
set_band_done (MMSerial *serial,
int reply_index,
GString *response,
GError *error,
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;
}
if (error)
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
}
static void
set_band_get_done (MMSerial *serial, const char *reply, gpointer user_data)
set_band_get_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
} else {
int a, b, u1, u2;
guint32 band;
guint id = 0;
if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) {
char *responses[] = { "OK", "ERROR", NULL };
if (parse_syscfg (response->str, &a, &b, &band, &u1, &u2)) {
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);
mm_serial_queue_command (serial, command, 3, 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);
}
}
@@ -373,8 +330,6 @@ set_band (MMModemGsmNetwork *modem,
gpointer user_data)
{
MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
@@ -397,24 +352,25 @@ set_band (MMModemGsmNetwork *modem,
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);
}
mm_serial_queue_command (MM_SERIAL (modem), "AT^SYSCFG?", 3, set_band_get_done, info);
}
static void
get_band_done (MMSerial *serial, const char *reply, gpointer user_data)
get_band_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error)
info->error = g_error_copy (error);
else {
int a, b, u1, u2;
guint32 band;
guint32 result = 0xdeadbeaf;
if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) {
if (parse_syscfg (response->str, &a, &b, &band, &u1, &u2)) {
if (band == 0x3FFFFFFF)
result = MM_MODEM_GSM_NETWORK_BAND_ANY;
else if (band == 0x400380)
@@ -428,6 +384,7 @@ get_band_done (MMSerial *serial, const char *reply, gpointer user_data)
"%s", "Could not parse band results");
else
mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
}
mm_callback_info_schedule (info);
}
@@ -438,18 +395,9 @@ get_band (MMModemGsmNetwork *modem,
gpointer user_data)
{
MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_uint_new (MM_MODEM (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);
}
mm_serial_queue_command (MM_SERIAL (modem), "AT^SYSCFG?", 3, get_band_done, info);
}
/*****************************************************************************/
@@ -481,6 +429,7 @@ constructor (GType type,
{
GObject *object;
MMModemHuaweiPrivate *priv;
MonitorData *info;
object = G_OBJECT_CLASS (mm_modem_huawei_parent_class)->constructor (type,
n_construct_params,
@@ -496,6 +445,11 @@ constructor (GType type,
return NULL;
}
info = g_slice_new (MonitorData);
info->modem = MM_MODEM_GSM_NETWORK (object);
info->r = g_regex_new ("\\r\\n(.+)\r\n$", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_serial_set_response_parser (priv->monitor_device, monitor_parse, info, monitor_info_free);
return object;
}
@@ -538,14 +492,10 @@ finalize (GObject *object)
{
MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (object);
if (priv->watch_id) {
g_source_remove (priv->watch_id);
priv->watch_id = 0;
if (priv->monitor_device) {
mm_serial_close (priv->monitor_device);
}
if (priv->monitor_device)
g_object_unref (priv->monitor_device);
}
G_OBJECT_CLASS (mm_modem_huawei_parent_class)->finalize (object);
}

View File

@@ -15,22 +15,26 @@ modem_manager_SOURCES = \
mm-generic-cdma.h \
mm-generic-gsm.c \
mm-generic-gsm.h \
mm-errors.c \
mm-errors.h \
mm-manager.c \
mm-manager.h \
mm-modem.c \
mm-modem.h \
mm-modem-cdma.c \
mm-modem-cdma.h \
mm-modem-error.c \
mm-modem-error.h \
mm-modem-gsm-card.c \
mm-modem-gsm-card.h \
mm-modem-gsm-network.c \
mm-modem-gsm-network.h \
mm-options.c \
mm-options.h \
mm-plugin.c \
mm-plugin.h \
mm-serial.c \
mm-serial.h
mm-serial.h \
mm-serial-parsers.c \
mm-serial-parsers.h
mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml
dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $<

View File

@@ -3,6 +3,7 @@
#include <syslog.h>
#include <dbus/dbus-glib.h>
#include "mm-manager.h"
#include "mm-options.h"
static void
log_handler (const gchar *log_domain,
@@ -123,31 +124,13 @@ dbus_init (GMainLoop *loop)
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);
mm_options_parse (argc, argv);
g_type_init ();
if (!debug)
if (!mm_options_debug ())
logging_setup ();
loop = g_main_loop_new (NULL, FALSE);

View File

@@ -1,7 +1,7 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "mm-callback-info.h"
#include "mm-modem-error.h"
#include "mm-errors.h"
static void
callback_info_done (gpointer user_data)
@@ -23,9 +23,10 @@ callback_info_done (gpointer user_data)
if (info->error)
g_error_free (info->error);
if (info->modem)
g_object_unref (info->modem);
g_datalist_clear (&info->qdata);
g_datalist_clear (&info->qdata);
g_slice_free (MMCallbackInfo, info);
}
@@ -43,15 +44,6 @@ 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)
{

View File

@@ -31,8 +31,6 @@ MMCallbackInfo *mm_callback_info_string_new (MMModem *modem,
gpointer user_data);
void mm_callback_info_schedule (MMCallbackInfo *info);
void mm_callback_info_cancel (MMCallbackInfo *info);
void mm_callback_info_set_result (MMCallbackInfo *info,
gpointer data,
GDestroyNotify destroy);

175
src/mm-errors.c Normal file
View File

@@ -0,0 +1,175 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "mm-errors.h"
#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
GQuark
mm_serial_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0)
ret = g_quark_from_static_string ("mm_serial_error");
return ret;
}
GType
mm_serial_error_get_type (void)
{
static GType etype = 0;
if (etype == 0) {
static const GEnumValue values[] = {
ENUM_ENTRY (MM_SERIAL_OPEN_FAILED, "Could not open the serial device"),
ENUM_ENTRY (MM_SERIAL_SEND_FAILED, "Writing to serial device failed"),
ENUM_ENTRY (MM_SERIAL_RESPONSE_TIMEOUT, "Did not receive response"),
{ 0, 0, 0 }
};
etype = g_enum_register_static ("MMSerialError", values);
}
return etype;
}
GQuark
mm_modem_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0)
ret = g_quark_from_static_string ("mm_modem_error");
return ret;
}
GType
mm_modem_error_get_type (void)
{
static GType etype = 0;
if (etype == 0) {
static const GEnumValue values[] = {
ENUM_ENTRY (MM_MODEM_ERROR_GENERAL, "Unknown error"),
ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, "Operation not supported"),
{ 0, 0, 0 }
};
etype = g_enum_register_static ("MMModemError", values);
}
return etype;
}
GQuark
mm_modem_connect_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0)
ret = g_quark_from_static_string ("mm_modem_connect_error");
return ret;
}
GType
mm_modem_connect_error_get_type (void)
{
static GType etype = 0;
if (etype == 0) {
static const GEnumValue values[] = {
ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_NO_CARRIER, "No carrier"),
ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_NO_DIALTONE, "No dialtone"),
ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_BUSY, "Busy"),
ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_NO_ANSWER, "No answer"),
{ 0, 0, 0 }
};
etype = g_enum_register_static ("MMModemConnectError", values);
}
return etype;
}
GQuark
mm_mobile_error_quark (void)
{
static GQuark ret = 0;
if (ret == 0)
ret = g_quark_from_static_string ("mm_mobile_error");
return ret;
}
GType
mm_mobile_error_get_type (void)
{
static GType etype = 0;
if (etype == 0) {
static const GEnumValue values[] = {
ENUM_ENTRY (MM_MOBILE_ERROR_PHONE_FAILURE, "PhoneFailure"),
ENUM_ENTRY (MM_MOBILE_ERROR_NO_CONNECTION, "No connection to phone"),
ENUM_ENTRY (MM_MOBILE_ERROR_LINK_RESERVED, "Phone-adaptor link reserved"),
ENUM_ENTRY (MM_MOBILE_ERROR_NOT_ALLOWED, "Operation not allowed"),
ENUM_ENTRY (MM_MOBILE_ERROR_NOT_SUPPORTED, "Operation not supported"),
ENUM_ENTRY (MM_MOBILE_ERROR_PH_SIM_PIN, "PH-SIM PIN required"),
ENUM_ENTRY (MM_MOBILE_ERROR_PH_FSIM_PIN, "PH-FSIM PIN required"),
ENUM_ENTRY (MM_MOBILE_ERROR_PH_FSIM_PUK, "PH-FSIM PUK required"),
ENUM_ENTRY (MM_MOBILE_ERROR_SIM_NOT_INSERTED, "SIM not inserted"),
ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PIN, "SIM PIN required"),
ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PUK, "SIM PUK required"),
ENUM_ENTRY (MM_MOBILE_ERROR_SIM_FAILURE, "SIM failure"),
ENUM_ENTRY (MM_MOBILE_ERROR_SIM_BUSY, "SIM busy"),
ENUM_ENTRY (MM_MOBILE_ERROR_SIM_WRONG, "SIM wrong"),
ENUM_ENTRY (MM_MOBILE_ERROR_WRONG_PASSWORD, "Incorrect password"),
ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PIN2, "SIM PIN2 required"),
ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PUK2, "SIM PUK2 required"),
ENUM_ENTRY (MM_MOBILE_ERROR_MEMORY_FULL, "Memory full"),
ENUM_ENTRY (MM_MOBILE_ERROR_INVALID_INDEX, "Invalid index"),
ENUM_ENTRY (MM_MOBILE_ERROR_NOT_FOUND, "Not found"),
ENUM_ENTRY (MM_MOBILE_ERROR_MEMORY_FAILURE, "Memory failure"),
ENUM_ENTRY (MM_MOBILE_ERROR_TEXT_TOO_LONG, "Text string too long"),
ENUM_ENTRY (MM_MOBILE_ERROR_INVALID_CHARS, "Invalid charactes in text string"),
ENUM_ENTRY (MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG, "Dial string too long"),
ENUM_ENTRY (MM_MOBILE_ERROR_DIAL_STRING_INVALID, "Invalid charactes in dial string"),
ENUM_ENTRY (MM_MOBILE_ERROR_NO_NETWORK, "No network service"),
ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_TIMEOUT, "Network timeout"),
ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED, "Network not allowed - emergency calls only"),
ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_PIN, "Network personalization PIN required"),
ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_PUK, "Network personalization PUK required"),
ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_SUBSET_PIN, "Network subset personalization PIN required"),
ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_SUBSET_PUK, "Network subset personalization PUK required"),
ENUM_ENTRY (MM_MOBILE_ERROR_SERVICE_PIN, "Service provider personalization PIN required"),
ENUM_ENTRY (MM_MOBILE_ERROR_SERVICE_PUK, "Service provider personalization PUK required"),
ENUM_ENTRY (MM_MOBILE_ERROR_CORP_PIN, "Corporate personalization PIN required"),
ENUM_ENTRY (MM_MOBILE_ERROR_CORP_PUK, "Corporate personalization PUK required"),
ENUM_ENTRY (MM_MOBILE_ERROR_HIDDEN_KEY, "Hidden key required"),
ENUM_ENTRY (MM_MOBILE_ERROR_EAP_NOT_SUPPORTED, "EAP method not supported"),
ENUM_ENTRY (MM_MOBILE_ERROR_INCORRECT_PARAMS, "Incorrect parameters"),
ENUM_ENTRY (MM_MOBILE_ERROR_UNKNOWN, "Unknown"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_ILLEGAL_MS, "Illegal MS"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_ILLEGAL_ME, "Illegal ME"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED, "GPRS services not allowed"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED, "PLMN not allowed"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED, "Location area not allowed"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED, "Roaming not allowed in this location area"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED, "Service option not supported"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED, "Requested service option not subscribed"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER, "Service option temporarily out of order"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE, "PDP authentication failure"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_UNKNOWN, "Unspecified GPRS error"),
ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_INVALID_CLASS, "Invalid mobile class"),
{ 0, 0, 0 }
};
etype = g_enum_register_static ("MMMobileError", values);
g_print ("Is enum? %d %d\n", etype, G_TYPE_IS_ENUM (etype));
}
return etype;
}

110
src/mm-errors.h Normal file
View File

@@ -0,0 +1,110 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef MM_MODEM_ERROR_H
#define MM_MODEM_ERROR_H
#include <glib-object.h>
enum {
MM_SERIAL_OPEN_FAILED = 0,
MM_SERIAL_SEND_FAILED = 1,
MM_SERIAL_RESPONSE_TIMEOUT = 2
};
#define MM_SERIAL_ERROR (mm_serial_error_quark ())
#define MM_TYPE_SERIAL_ERROR (mm_serial_error_get_type ())
GQuark mm_serial_error_quark (void);
GType mm_serial_error_get_type (void);
enum {
MM_MODEM_ERROR_GENERAL = 0,
MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED = 1
};
#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);
enum {
MM_MODEM_CONNECT_ERROR_NO_CARRIER = 3,
MM_MODEM_CONNECT_ERROR_NO_DIALTONE = 6,
MM_MODEM_CONNECT_ERROR_BUSY = 7,
MM_MODEM_CONNECT_ERROR_NO_ANSWER = 8,
};
#define MM_MODEM_CONNECT_ERROR (mm_modem_connect_error_quark ())
#define MM_TYPE_MODEM_CONNECT_ERROR (mm_modem_connect_error_get_type ())
GQuark mm_modem_connect_error_quark (void);
GType mm_modem_connect_error_get_type (void);
enum {
MM_MOBILE_ERROR_PHONE_FAILURE = 0,
MM_MOBILE_ERROR_NO_CONNECTION = 1,
MM_MOBILE_ERROR_LINK_RESERVED = 2,
MM_MOBILE_ERROR_NOT_ALLOWED = 3,
MM_MOBILE_ERROR_NOT_SUPPORTED = 4,
MM_MOBILE_ERROR_PH_SIM_PIN = 5,
MM_MOBILE_ERROR_PH_FSIM_PIN = 6,
MM_MOBILE_ERROR_PH_FSIM_PUK = 7,
MM_MOBILE_ERROR_SIM_NOT_INSERTED = 10,
MM_MOBILE_ERROR_SIM_PIN = 11,
MM_MOBILE_ERROR_SIM_PUK = 12,
MM_MOBILE_ERROR_SIM_FAILURE = 13,
MM_MOBILE_ERROR_SIM_BUSY = 14,
MM_MOBILE_ERROR_SIM_WRONG = 15,
MM_MOBILE_ERROR_WRONG_PASSWORD = 16,
MM_MOBILE_ERROR_SIM_PIN2 = 17,
MM_MOBILE_ERROR_SIM_PUK2 = 18,
MM_MOBILE_ERROR_MEMORY_FULL = 20,
MM_MOBILE_ERROR_INVALID_INDEX = 21,
MM_MOBILE_ERROR_NOT_FOUND = 22,
MM_MOBILE_ERROR_MEMORY_FAILURE = 23,
MM_MOBILE_ERROR_TEXT_TOO_LONG = 24,
MM_MOBILE_ERROR_INVALID_CHARS = 25,
MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG = 26,
MM_MOBILE_ERROR_DIAL_STRING_INVALID = 27,
MM_MOBILE_ERROR_NO_NETWORK = 30,
MM_MOBILE_ERROR_NETWORK_TIMEOUT = 31,
MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED = 32,
MM_MOBILE_ERROR_NETWORK_PIN = 40,
MM_MOBILE_ERROR_NETWORK_PUK = 41,
MM_MOBILE_ERROR_NETWORK_SUBSET_PIN = 42,
MM_MOBILE_ERROR_NETWORK_SUBSET_PUK = 43,
MM_MOBILE_ERROR_SERVICE_PIN = 44,
MM_MOBILE_ERROR_SERVICE_PUK = 45,
MM_MOBILE_ERROR_CORP_PIN = 46,
MM_MOBILE_ERROR_CORP_PUK = 47,
MM_MOBILE_ERROR_HIDDEN_KEY = 48,
MM_MOBILE_ERROR_EAP_NOT_SUPPORTED = 49,
MM_MOBILE_ERROR_INCORRECT_PARAMS = 50,
MM_MOBILE_ERROR_UNKNOWN = 100,
MM_MOBILE_ERROR_GPRS_ILLEGAL_MS = 103,
MM_MOBILE_ERROR_GPRS_ILLEGAL_ME = 106,
MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED = 107,
MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED = 111,
MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED = 112,
MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED = 113,
MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED = 132,
MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED = 133,
MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER = 134,
MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE = 149,
MM_MOBILE_ERROR_GPRS_UNKNOWN = 148,
MM_MOBILE_ERROR_GPRS_INVALID_CLASS = 150
};
#define MM_MOBILE_ERROR (mm_mobile_error_quark ())
#define MM_TYPE_MOBILE_ERROR (mm_mobile_error_get_type ())
GQuark mm_mobile_error_quark (void);
GType mm_mobile_error_get_type (void);
#endif /* MM_MODEM_ERROR_H */

View File

@@ -5,7 +5,7 @@
#include "mm-generic-cdma.h"
#include "mm-modem-cdma.h"
#include "mm-modem-error.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
static gpointer mm_generic_cdma_parent_class = NULL;
@@ -32,21 +32,14 @@ mm_generic_cdma_new (const char *serial_device, const char *driver)
static void
init_done (MMSerial *serial,
int reply_index,
GString *response,
GError *error,
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");
}
if (error)
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
}
@@ -54,18 +47,7 @@ init_done (MMSerial *serial,
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);
}
mm_serial_queue_command (serial, "Z E0 V1 X4 &C1 +CMEE=1", 3, init_done, user_data);
}
static void
@@ -84,15 +66,8 @@ enable (MMModem *modem,
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 (mm_serial_open (MM_SERIAL (modem), &info->error))
mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info);
if (info->error)
mm_callback_info_schedule (info);
@@ -100,31 +75,14 @@ enable (MMModem *modem,
static void
dial_done (MMSerial *serial,
int reply_index,
GString *response,
GError *error,
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;
}
if (error)
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
}
@@ -137,21 +95,17 @@ connect (MMModem *modem,
{
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);
command = g_strconcat ("DT", number, NULL);
mm_serial_queue_command (MM_SERIAL (modem), command, 60, 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_flash_done (MMSerial *serial, gpointer user_data)
{
mm_callback_info_schedule ((MMCallbackInfo *) user_data);
}
static void
@@ -162,17 +116,21 @@ disconnect (MMModem *modem,
MMCallbackInfo *info;
info = mm_callback_info_new (modem, callback, user_data);
mm_serial_close (MM_SERIAL (modem));
mm_callback_info_schedule (info);
mm_serial_flash (MM_SERIAL (modem), 1000, disconnect_flash_done, info);
}
static void
get_signal_quality_done (MMSerial *serial, const char *reply, gpointer user_data)
get_signal_quality_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
guint32 result = 0;
char *reply = response->str;
if (!strncmp (reply, "+CSQ: ", 6)) {
if (error)
info->error = g_error_copy (error);
else if (!strncmp (reply, "+CSQ: ", 6)) {
/* Got valid reply */
int quality;
int ber;
@@ -183,15 +141,14 @@ get_signal_quality_done (MMSerial *serial, const char *reply, gpointer user_data
/* 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");
quality = quality * 100 / 31;
mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
} else
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"%s", "Could not parse signal quality results");
}
mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
mm_callback_info_schedule (info);
}
@@ -201,18 +158,9 @@ get_signal_quality (MMModemCdma *modem,
gpointer user_data)
{
MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_uint_new (MM_MODEM (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);
}
mm_serial_queue_command (MM_SERIAL (modem), "+CSQ", 3, get_signal_quality_done, info);
}
/*****************************************************************************/

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "mm-manager.h"
#include "mm-modem-error.h"
#include "mm-errors.h"
#include "mm-generic-gsm.h"
#include "mm-generic-cdma.h"
#include "mm-plugin.h"
@@ -454,5 +454,11 @@ mm_manager_class_init (MMManagerClass *manager_class)
dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class),
&dbus_glib_mm_manager_object_info);
/* FIXME: Sigh, these don't work either */
#if 0
dbus_g_error_domain_register (MM_SERIAL_ERROR, NULL, MM_TYPE_SERIAL_ERROR);
dbus_g_error_domain_register (MM_MODEM_ERROR, NULL, MM_TYPE_MODEM_ERROR);
dbus_g_error_domain_register (MM_MODEM_CONNECT_ERROR, NULL, MM_TYPE_MODEM_CONNECT_ERROR);
dbus_g_error_domain_register (MM_MOBILE_ERROR, NULL, MM_TYPE_MOBILE_ERROR);
#endif
}

View File

@@ -3,7 +3,7 @@
#include <string.h>
#include <dbus/dbus-glib.h>
#include "mm-modem-cdma.h"
#include "mm-modem-error.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
static void impl_modem_cdma_get_signal_quality (MMModemCdma *modem, DBusGMethodInvocation *context);

View File

@@ -1,37 +0,0 @@
/* -*- 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;
}

View File

@@ -1,22 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef MM_MODEM_ERROR_H
#define MM_MODEM_ERROR_H
#include <glib-object.h>
enum {
MM_MODEM_ERROR_GENERAL = 0,
MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
MM_MODEM_ERROR_PIN_NEEDED,
MM_MODEM_ERROR_PUK_NEEDED,
MM_MODEM_ERROR_INVALID_SECRET
};
#define MM_MODEM_ERROR (mm_modem_error_quark ())
#define MM_TYPE_MODEM_ERROR (mm_modem_error_get_type ())
GQuark mm_modem_error_quark (void);
GType mm_modem_error_get_type (void);
#endif /* MM_MODEM_ERROR_H */

View File

@@ -3,7 +3,7 @@
#include <dbus/dbus-glib.h>
#include "mm-modem-gsm-card.h"
#include "mm-modem-error.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
static void impl_gsm_modem_get_imei (MMModemGsmCard *modem,

View File

@@ -4,7 +4,7 @@
#include <dbus/dbus-glib.h>
#include "mm-modem-gsm-network.h"
#include "mm-modem-error.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
static void impl_gsm_modem_register (MMModemGsmNetwork *modem,

View File

@@ -3,7 +3,7 @@
#include <string.h>
#include <dbus/dbus-glib.h>
#include "mm-modem.h"
#include "mm-modem-error.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
static void impl_modem_enable (MMModem *modem, gboolean enable, DBusGMethodInvocation *context);

36
src/mm-options.c Normal file
View File

@@ -0,0 +1,36 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <stdlib.h>
#include <glib.h>
#include "mm-options.h"
static gboolean debug = FALSE;
void
mm_options_parse (int argc, char *argv[])
{
GOptionContext *opt_ctx;
GError *error = NULL;
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);
exit (1);
}
g_option_context_free (opt_ctx);
}
gboolean
mm_options_debug (void)
{
return debug;
}

9
src/mm-options.h Normal file
View File

@@ -0,0 +1,9 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef MM_OPTIONS_H
#define MM_OPTIONS_H
void mm_options_parse (int argc, char *argv[]);
gboolean mm_options_debug (void);
#endif /* MM_OPTIONS_H */

266
src/mm-serial-parsers.c Normal file
View File

@@ -0,0 +1,266 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <string.h>
#include <stdlib.h>
#include "mm-serial-parsers.h"
#include "mm-errors.h"
/* Clean up the response by removing control characters like <CR><LF> etc */
static void
response_clean (GString *response)
{
char *s;
/* Ends with '<CR><LF>' */
s = response->str + response->len - 1;
if (*s == '\n' && *(--s) == '\r')
g_string_truncate (response, response->len - 2);
/* Starts with '<CR><LF>' */
s = response->str;
if (*s == '\r' && *(++s) == '\n')
g_string_erase (response, 0, 2);
}
static gboolean
remove_eval_cb (const GMatchInfo *match_info,
GString *result,
gpointer user_data)
{
int *result_len = (int *) user_data;
int start;
int end;
if (g_match_info_fetch_pos (match_info, 0, &start, &end))
*result_len -= (end - start);
return TRUE;
}
static void
remove_matches (GRegex *r, GString *string)
{
char *str;
int result_len = string->len;
str = g_regex_replace_eval (r, string->str, string->len, 0, 0,
remove_eval_cb, &result_len, NULL);
g_string_truncate (string, 0);
g_string_append_len (string, str, result_len);
g_free (str);
}
/* FIXME: V0 parser is not finished */
#if 0
typedef struct {
GRegex *generic_response;
GRegex *detailed_error;
} MMSerialParserV0;
gpointer
mm_serial_parser_v0_new (void)
{
MMSerialParserV0 *parser;
GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE;
parser = g_slice_new (MMSerialParserV0);
parser->generic_response = g_regex_new ("(\\d)\\r%", flags, 0, NULL);
parser->detailed_error = g_regex_new ("+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL);
return parser;
}
gboolean
mm_serial_parser_v0_parse (gpointer parser,
GString *response,
GError **error)
{
MMSerialParserV0 *parser = (MMSerialParserV0 *) data;
GMatchInfo *match_info;
char *str;
int code;
gboolean found;
found = g_regex_match_full (parser->generic_response, response->str, response->len, 0, 0, &match_info, NULL);
if (found) {
str = g_match_info_fetch (match_info, 1);
if (str) {
code = atoi (str);
g_free (str);
}
g_match_info_free (match_info);
return TRUE;
}
found = g_regex_match_full (parser->detailed_error, response->str, response->len, 0, 0, &match_info, NULL);
if (found) {
str = g_match_info_fetch (match_info, 1);
if (str) {
code = atoi (str);
g_free (str);
} else
code = MM_MOBILE_ERROR_UNKNOWN;
g_match_info_free (match_info);
g_debug ("Got error code %d: %s", code, msg);
g_set_error (error, MM_MOBILE_ERROR, code, "%s", msg);
return TRUE;
}
return FALSE;
}
void
mm_serial_parser_v0_destroy (gpointer parser)
{
MMSerialParserV0 *parser = (MMSerialParserV0 *) data;
g_regex_unref (parser->generic_response);
g_regex_unref (parser->detailed_error);
g_slice_free (MMSerialParserV0, data);
}
#endif
typedef struct {
GRegex *regex_ok;
GRegex *regex_connect;
GRegex *regex_detailed_error;
GRegex *regex_unknown_error;
GRegex *regex_connect_failed;
} MMSerialParserV1;
gpointer
mm_serial_parser_v1_new (void)
{
MMSerialParserV1 *parser;
GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE;
parser = g_slice_new (MMSerialParserV1);
parser->regex_ok = g_regex_new ("\\r\\nOK\\r\\n$", flags, 0, NULL);
parser->regex_connect = g_regex_new ("\\r\\nCONNECT\\s*\\d*\\r\\n$", flags, 0, NULL);
parser->regex_detailed_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL);
parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL);
parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$", flags, 0, NULL);
return parser;
}
gboolean
mm_serial_parser_v1_parse (gpointer data,
GString *response,
GError **error)
{
MMSerialParserV1 *parser = (MMSerialParserV1 *) data;
GMatchInfo *match_info;
const char *msg;
int code;
gboolean found = FALSE;
/* First, check for successfule responses */
found = g_regex_match_full (parser->regex_ok, response->str, response->len, 0, 0, NULL, NULL);
if (found)
remove_matches (parser->regex_ok, response);
else
found = g_regex_match_full (parser->regex_connect, response->str, response->len, 0, 0, NULL, NULL);
if (found) {
response_clean (response);
return TRUE;
}
/* Now failures */
code = MM_MOBILE_ERROR_UNKNOWN;
found = g_regex_match_full (parser->regex_detailed_error,
response->str, response->len,
0, 0, &match_info, NULL);
if (found) {
char *str;
str = g_match_info_fetch (match_info, 1);
if (str) {
code = atoi (str);
g_free (str);
}
g_match_info_free (match_info);
} else
found = g_regex_match_full (parser->regex_unknown_error, response->str, response->len, 0, 0, NULL, NULL);
if (found) {
#if 0
/* FIXME: This does not work for some reason. */
GEnumValue *enum_value;
enum_value = g_enum_get_value ((GEnumClass *) g_type_class_peek_static (), code);
msg = enum_value->value_nick;
#endif
msg = "FIXME";
g_debug ("Got error code %d: %s", code, msg);
g_set_error (error, MM_MOBILE_ERROR, code, "%s", msg);
} else {
found = g_regex_match_full (parser->regex_connect_failed,
response->str, response->len,
0, 0, &match_info, NULL);
if (found) {
char *str;
str = g_match_info_fetch (match_info, 1);
if (str) {
if (!strcmp (str, "NO CARRIER")) {
code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
msg = "No carrier";
} else if (!strcmp (str, "BUSY")) {
code = MM_MODEM_CONNECT_ERROR_BUSY;
msg = "Busy";
} else if (!strcmp (str, "NO ANSWER")) {
code = MM_MODEM_CONNECT_ERROR_NO_ANSWER;
msg = "No answer";
} else if (!strcmp (str, "NO DIALTONE")) {
code = MM_MODEM_CONNECT_ERROR_NO_DIALTONE;
msg = "No dialtone";
} else {
g_warning ("Got matching connect failure, but can't parse it");
/* uhm... make something up (yes, ok, lie!). */
code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
msg = "No carrier";
}
g_free (str);
}
g_match_info_free (match_info);
g_debug ("Got connect failure code %d: %s", code, msg);
g_set_error (error, MM_MODEM_CONNECT_ERROR, code, "%s", msg);
}
}
return found;
}
void
mm_serial_parser_v1_destroy (gpointer data)
{
MMSerialParserV1 *parser = (MMSerialParserV1 *) data;
g_regex_unref (parser->regex_ok);
g_regex_unref (parser->regex_connect);
g_regex_unref (parser->regex_detailed_error);
g_regex_unref (parser->regex_unknown_error);
g_regex_unref (parser->regex_connect_failed);
g_slice_free (MMSerialParserV1, data);
}

26
src/mm-serial-parsers.h Normal file
View File

@@ -0,0 +1,26 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef MM_SERIAL_PARSERS_H
#define MM_SERIAL_PARSERS_H
#include <glib.h>
/* FIXME: V0 parser is not finished */
#if 0
gpointer mm_serial_parser_v0_new (void);
gboolean mm_serial_parser_v0_parse (gpointer parser,
GString *response,
GError **error);
void mm_serial_parser_v0_destroy (gpointer parser);
#endif
gpointer mm_serial_parser_v1_new (void);
gboolean mm_serial_parser_v1_parse (gpointer parser,
GString *response,
GError **error);
void mm_serial_parser_v1_destroy (gpointer parser);
#endif /* MM_SERIAL_PARSERS_H */

View File

@@ -2,6 +2,8 @@
#define _GNU_SOURCE /* for strcasestr() */
#include <stdio.h>
#include <stdlib.h>
#include <termio.h>
#include <unistd.h>
#include <sys/types.h>
@@ -12,6 +14,10 @@
#include <string.h>
#include "mm-serial.h"
#include "mm-errors.h"
#include "mm-options.h"
static gboolean mm_serial_queue_process (gpointer data);
G_DEFINE_TYPE (MMSerial, mm_serial, G_TYPE_OBJECT)
@@ -27,7 +33,6 @@ enum {
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))
@@ -35,6 +40,15 @@ enum {
typedef struct {
int fd;
GIOChannel *channel;
GQueue *queue;
GString *command;
GString *response;
/* Response parser data */
MMSerialResponseParserFn response_parser_fn;
gpointer response_parser_user_data;
GDestroyNotify response_parser_notify;
struct termios old_t;
char *device;
@@ -44,7 +58,8 @@ typedef struct {
guint stopbits;
guint64 send_delay;
guint pending_id;
guint watch_id;
guint timeout_id;
} MMSerialPrivate;
const char *
@@ -189,86 +204,6 @@ parse_stopbits (guint i)
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 */
/* Pending data reading */
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);
return FALSE;
}
static guint
mm_serial_set_pending (MMSerial *self,
guint timeout,
GIOFunc callback,
gpointer user_data,
GDestroyNotify notify)
{
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
GSource *source;
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);
source = g_timeout_source_new (timeout * 100);
g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_timed_out), G_OBJECT (self)));
g_source_attach (source, NULL);
g_source_unref (source);
return priv->pending_id;
}
static void
mm_serial_pending_done (MMSerial *self)
{
MM_SERIAL_GET_PRIVATE (self)->pending_id = 0;
}
/****/
static gboolean
config_fd (MMSerial *self)
{
@@ -305,8 +240,229 @@ config_fd (MMSerial *self)
return TRUE;
}
static void
serial_debug (const char *prefix, const char *buf, int len)
{
const char *s;
if (!mm_options_debug ())
return;
if (len < 0)
len = strlen (buf);
g_print ("%s '", prefix);
s = buf;
while (len--) {
if (g_ascii_isprint (*s))
g_print ("%c", *s);
else if (*s == '\r')
g_print ("<CR>");
else if (*s == '\n')
g_print ("<LF>");
else
g_print ("\\%d", *s);
s++;
}
g_print ("'\n");
}
static gboolean
mm_serial_send_command (MMSerial *self, const char *command)
{
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
const char *s;
int status;
g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2);
g_string_append (priv->command, command);
if (command[strlen (command)] != '\r')
g_string_append_c (priv->command, '\r');
serial_debug ("-->", priv->command->str, -1);
s = priv->command->str;
while (*s) {
again:
status = write (priv->fd, s, 1);
if (status < 0) {
if (errno == EAGAIN)
goto again;
else {
g_warning ("Error writing to serial device: %s", strerror (errno));
break;
}
}
if (priv->send_delay)
usleep (priv->send_delay);
s++;
}
return *s == '\0';
}
typedef struct {
char *command;
MMSerialResponseFn callback;
gpointer user_data;
guint32 timeout;
} MMQueueData;
static void
mm_serial_got_response (MMSerial *self, GError *error)
{
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
MMQueueData *info;
if (priv->timeout_id)
g_source_remove (priv->timeout_id);
info = (MMQueueData *) g_queue_pop_head (priv->queue);
if (info) {
if (info->callback)
info->callback (self, priv->response, error, info->user_data);
g_free (info->command);
g_slice_free (MMQueueData, info);
}
if (error)
g_error_free (error);
g_string_truncate (priv->response, 0);
if (!g_queue_is_empty (priv->queue))
g_idle_add (mm_serial_queue_process, self);
}
static gboolean
mm_serial_timed_out (gpointer data)
{
MMSerial *self = MM_SERIAL (data);
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
GError *error;
priv->timeout_id = 0;
error = g_error_new (MM_SERIAL_ERROR,
MM_SERIAL_RESPONSE_TIMEOUT,
"%s", "Serial command timed out");
/* FIXME: This is not completely correct - if the response finally arrives and there's
some other command waiting for response right now, the other command will
get the output of the timed out command. Maybe flashing would help here? */
mm_serial_got_response (self, error);
return FALSE;
}
static gboolean
mm_serial_queue_process (gpointer data)
{
MMSerial *self = MM_SERIAL (data);
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
MMQueueData *info;
info = (MMQueueData *) g_queue_peek_head (priv->queue);
if (!info)
return FALSE;
if (mm_serial_send_command (self, info->command)) {
GSource *source;
source = g_timeout_source_new (info->timeout);
g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_timed_out), G_OBJECT (self)));
g_source_attach (source, NULL);
priv->timeout_id = g_source_get_id (source);
g_source_unref (source);
} else {
GError *error;
error = g_error_new (MM_SERIAL_ERROR,
MM_SERIAL_SEND_FAILED,
"%s", "Sending command failed");
mm_serial_got_response (self, error);
}
return FALSE;
}
void
mm_serial_set_response_parser (MMSerial *self,
MMSerialResponseParserFn fn,
gpointer user_data,
GDestroyNotify notify)
{
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
g_return_if_fail (MM_IS_SERIAL (self));
if (priv->response_parser_notify)
priv->response_parser_notify (priv->response_parser_user_data);
priv->response_parser_fn = fn;
priv->response_parser_user_data = user_data;
priv->response_parser_notify = notify;
}
static gboolean
parse_response (MMSerial *self,
GString *response,
GError **error)
{
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE);
return priv->response_parser_fn (priv->response_parser_user_data, response, error);
}
static gboolean
data_available (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
MMSerial *self = MM_SERIAL (data);
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
char buf[SERIAL_BUF_SIZE + 1];
gsize bytes_read;
GIOStatus status;
if (condition & G_IO_HUP || condition & G_IO_ERR) {
g_string_truncate (priv->response, 0);
return TRUE;
}
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) {
serial_debug ("<--", buf, bytes_read);
g_string_append_len (priv->response, buf, bytes_read);
}
if (parse_response (self, priv->response, &err))
mm_serial_got_response (self, err);
} while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
return TRUE;
}
gboolean
mm_serial_open (MMSerial *self)
mm_serial_open (MMSerial *self, GError **error)
{
MMSerialPrivate *priv;
@@ -322,23 +478,30 @@ mm_serial_open (MMSerial *self)
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));
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
"Could not open serial device %s: %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);
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
"Could not open serial device %s: %s", priv->device, strerror (errno));
close (priv->fd);
return FALSE;
}
if (!config_fd (self)) {
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
"Could not open serial device %s: %s", priv->device, strerror (errno));
close (priv->fd);
priv->fd = 0;
return FALSE;
}
priv->channel = g_io_channel_unix_new (priv->fd);
priv->watch_id = g_io_add_watch (priv->channel,
G_IO_IN | G_IO_ERR | G_IO_HUP,
data_available, self);
return TRUE;
}
@@ -352,13 +515,12 @@ mm_serial_close (MMSerial *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_source_remove (priv->watch_id);
g_io_channel_shutdown (priv->channel, TRUE, NULL);
g_io_channel_unref (priv->channel);
priv->channel = NULL;
}
@@ -369,440 +531,31 @@ mm_serial_close (MMSerial *self)
}
}
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;
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,
mm_serial_queue_command (MMSerial *self,
const char *command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data)
{
WaitQuietInfo *info;
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
MMQueueData *info;
g_return_if_fail (MM_IS_SERIAL (self));
g_return_if_fail (callback != NULL);
g_return_if_fail (command != NULL);
info = g_slice_new0 (WaitQuietInfo);
info->serial = self;
info->timed_out = TRUE;
info = g_slice_new0 (MMQueueData);
info->command = g_strdup (command);
info->timeout = timeout_seconds * 1000;
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);
g_queue_push_tail (priv->queue, info);
if (g_queue_get_length (priv->queue) == 1)
g_idle_add (mm_serial_queue_process, self);
}
#endif
typedef struct {
MMSerial *serial;
speed_t current_speed;
@@ -841,8 +594,6 @@ 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);
@@ -884,25 +635,9 @@ mm_serial_flash (MMSerial *self,
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
@@ -915,6 +650,10 @@ mm_serial_init (MMSerial *self)
priv->parity = 'n';
priv->stopbits = 1;
priv->send_delay = 0;
priv->queue = g_queue_new ();
priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE);
priv->response = g_string_sized_new (SERIAL_BUF_SIZE);
}
static GObject*
@@ -1012,8 +751,15 @@ finalize (GObject *object)
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
mm_serial_close (self);
g_queue_free (priv->queue);
g_string_free (priv->command, TRUE);
g_string_free (priv->response, TRUE);
g_free (priv->device);
if (priv->response_parser_notify)
priv->response_parser_notify (priv->response_parser_user_data);
G_OBJECT_CLASS (mm_serial_parent_class)->finalize (object);
}

View File

@@ -20,59 +20,44 @@
#define MM_SERIAL_STOPBITS "stopbits"
#define MM_SERIAL_SEND_DELAY "send-delay"
typedef struct {
GObject parent;
} MMSerial;
typedef struct _MMSerial MMSerial;
typedef struct _MMSerialClass MMSerialClass;
typedef struct {
GObjectClass parent;
} MMSerialClass;
typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data,
GString *response,
GError **error);
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,
typedef void (*MMSerialResponseFn) (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data);
typedef void (*MMSerialFlashFn) (MMSerial *serial,
gpointer user_data);
const char *mm_serial_get_device (MMSerial *serial);
struct _MMSerial {
GObject parent;
};
gboolean mm_serial_open (MMSerial *self);
struct _MMSerialClass {
GObjectClass parent;
};
GType mm_serial_get_type (void);
void mm_serial_set_response_parser (MMSerial *self,
MMSerialResponseParserFn fn,
gpointer user_data,
GDestroyNotify notify);
gboolean mm_serial_open (MMSerial *self,
GError **error);
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,
void mm_serial_queue_command (MMSerial *self,
const char *command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data);
guint mm_serial_flash (MMSerial *self,
@@ -80,6 +65,6 @@ guint mm_serial_flash (MMSerial *self,
MMSerialFlashFn callback,
gpointer user_data);
GIOChannel *mm_serial_get_io_channel (MMSerial *self);
const char *mm_serial_get_device (MMSerial *self);
#endif /* MM_SERIAL_H */