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 <dbus/dbus-glib.h>
#include "mm-modem-hso.h" #include "mm-modem-hso.h"
#include "mm-serial.h" #include "mm-serial.h"
#include "mm-modem-error.h" #include "mm-errors.h"
#include "mm-callback-info.h" #include "mm-callback-info.h"
static void impl_hso_authenticate (MMModemHso *self, static void impl_hso_authenticate (MMModemHso *self,
@@ -58,19 +58,14 @@ mm_modem_hso_new (const char *serial_device,
static void static void
hso_enable_done (MMSerial *serial, hso_enable_done (MMSerial *serial,
int reply_index, GString *response,
GError *error,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
switch (reply_index) { if (error)
case 0: info->error = g_error_copy (error);
/* Success */
break;
default:
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Enable/Disable failed.");
break;
}
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
} }
@@ -83,8 +78,6 @@ hso_enable (MMModemHso *self,
{ {
MMCallbackInfo *info; MMCallbackInfo *info;
char *command; char *command;
char *responses[] = { "_OWANCALL: ", "ERROR", "NO CARRIER", NULL };
guint id = 0;
info = mm_callback_info_new (MM_MODEM (self), callback, user_data); 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)), mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)),
enabled ? 1 : 0); enabled ? 1 : 0);
if (mm_serial_send_command_string (MM_SERIAL (self), command)) mm_serial_queue_command (MM_SERIAL (self), command, 3, hso_enable_done, info);
id = mm_serial_wait_for_reply (MM_SERIAL (self), 5, responses, responses, hso_enable_done, user_data);
g_free (command); 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 static void
@@ -132,22 +118,18 @@ hso_disabled (MMModem *modem,
static void static void
auth_done (MMSerial *serial, auth_done (MMSerial *serial,
int reply_index, GString *response,
GError *error,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
switch (reply_index) { if (error) {
case 0: info->error = g_error_copy (error);
mm_callback_info_schedule (info);
} else
/* success, kill any existing connections first */ /* success, kill any existing connections first */
hso_enable (MM_MODEM_HSO (serial), FALSE, hso_disabled, info); 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 void
@@ -159,8 +141,6 @@ mm_hso_modem_authenticate (MMModemHso *self,
{ {
MMCallbackInfo *info; MMCallbackInfo *info;
char *command; char *command;
char *responses[] = { "OK", "ERROR", NULL };
guint id = 0;
g_return_if_fail (MM_IS_MODEM_HSO (self)); g_return_if_fail (MM_IS_MODEM_HSO (self));
g_return_if_fail (callback != NULL); g_return_if_fail (callback != NULL);
@@ -171,15 +151,8 @@ mm_hso_modem_authenticate (MMModemHso *self,
password ? password : "", password ? password : "",
username ? username : ""); username ? username : "");
if (mm_serial_send_command_string (MM_SERIAL (self), command)) mm_serial_queue_command (MM_SERIAL (self), command, 3, auth_done, info);
id = mm_serial_wait_for_reply (MM_SERIAL (self), 5, responses, responses, auth_done, user_data);
g_free (command); 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 static void
@@ -205,7 +178,10 @@ ip4_callback_wrapper (MMModem *modem,
} }
static void 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; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
char **items, **iter; char **items, **iter;
@@ -214,15 +190,18 @@ get_ip4_config_done (MMSerial *serial, const char *response, gpointer user_data)
guint32 tmp; guint32 tmp;
guint cid; guint cid;
if (!response || strncmp (response, OWANDATA_TAG, strlen (OWANDATA_TAG))) { if (error) {
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", info->error = g_error_copy (error);
"Retrieving failed: invalid response."); 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; goto out;
} }
cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (serial)); cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (serial));
dns_array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 2); 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++) { for (iter = items, i = 0; *iter; iter++, i++) {
if (i == 0) { /* CID */ if (i == 0) { /* CID */
@@ -261,8 +240,6 @@ mm_hso_modem_get_ip4_config (MMModemHso *self,
{ {
MMCallbackInfo *info; MMCallbackInfo *info;
char *command; char *command;
const char terminators[] = { '\r', '\n', '\0' };
guint id = 0;
g_return_if_fail (MM_IS_MODEM_HSO (self)); g_return_if_fail (MM_IS_MODEM_HSO (self));
g_return_if_fail (callback != NULL); 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); 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))); 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)) mm_serial_queue_command (MM_SERIAL (self), command, 3, get_ip4_config_done, info);
id = mm_serial_get_reply (MM_SERIAL (self), 5, terminators, get_ip4_config_done, info);
g_free (command); 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 <string.h>
#include "mm-modem-huawei.h" #include "mm-modem-huawei.h"
#include "mm-modem-gsm-network.h" #include "mm-modem-gsm-network.h"
#include "mm-modem-error.h" #include "mm-errors.h"
#include "mm-callback-info.h" #include "mm-callback-info.h"
#include "mm-serial-parsers.h"
static gpointer mm_modem_huawei_parent_class = NULL; static gpointer mm_modem_huawei_parent_class = NULL;
@@ -14,7 +15,6 @@ static gpointer mm_modem_huawei_parent_class = NULL;
typedef struct { typedef struct {
MMSerial *monitor_device; MMSerial *monitor_device;
guint watch_id;
} MMModemHuaweiPrivate; } MMModemHuaweiPrivate;
enum { enum {
@@ -43,25 +43,37 @@ mm_modem_huawei_new (const char *data_device,
/*****************************************************************************/ /*****************************************************************************/
typedef struct {
MMModemGsmNetwork *modem;
GRegex *r;
} MonitorData;
static void static void
parse_monitor_line (MMModemGsmNetwork *modem, char *buf) monitor_info_free (gpointer data)
{ {
char **lines; MonitorData *info = (MonitorData *) data;
char **iter;
lines = g_strsplit (buf, "\r\n", 0); g_regex_unref (info->r);
g_slice_free (MonitorData, data);
}
for (iter = lines; iter && *iter; iter++) { static gboolean
char *line = *iter; monitor_parse (gpointer data,
GString *response,
GError **error)
{
MonitorData *info = (MonitorData *) data;
GMatchInfo *match_info;
gboolean found;
g_strstrip (line); found = g_regex_match_full (info->r, response->str, response->len, 0, 0, &match_info, NULL);
if (strlen (line) < 1 || line[0] != '^') if (found) {
continue; char *str;
line += 1; str = g_match_info_fetch (match_info, 1);
if (!strncmp (line, "RSSI:", 5)) { if (g_str_has_prefix (str, "^RSSI:")) {
int quality = atoi (line + 5); int quality = atoi (str + 6);
if (quality == 99) if (quality == 99)
/* 99 means unknown */ /* 99 means unknown */
@@ -71,13 +83,13 @@ parse_monitor_line (MMModemGsmNetwork *modem, char *buf)
quality = quality * 100 / 31; quality = quality * 100 / 31;
g_debug ("Signal quality: %d", quality); g_debug ("Signal quality: %d", quality);
mm_modem_gsm_network_signal_quality (modem, (guint32) quality); mm_modem_gsm_network_signal_quality (info->modem, (guint32) quality);
} else if (!strncmp (line, "MODE:", 5)) { } else if (g_str_has_prefix (str, "^MODE:")) {
MMModemGsmNetworkMode mode = 0; MMModemGsmNetworkMode mode = 0;
int a; int a;
int b; int b;
if (sscanf (line + 5, "%d,%d", &a, &b)) { if (sscanf (str + 6, "%d,%d", &a, &b)) {
if (a == 3 && b == 2) if (a == 3 && b == 2)
mode = MM_MODEM_GSM_NETWORK_MODE_GPRS; mode = MM_MODEM_GSM_NETWORK_MODE_GPRS;
else if (a == 3 && b == 3) else if (a == 3 && b == 3)
@@ -89,40 +101,17 @@ parse_monitor_line (MMModemGsmNetwork *modem, char *buf)
if (mode) { if (mode) {
g_debug ("Mode: %d", mode); g_debug ("Mode: %d", mode);
mm_modem_gsm_network_mode (modem, mode); mm_modem_gsm_network_mode (info->modem, mode);
} }
} }
} }
g_free (str);
g_match_info_free (match_info);
} }
g_strfreev (lines); return found;
}
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;
} }
static void static void
@@ -138,25 +127,16 @@ enable (MMModem *modem,
parent_modem_iface->enable (modem, enable, callback, user_data); parent_modem_iface->enable (modem, enable, callback, user_data);
if (enable) { if (enable) {
GIOChannel *channel; GError *error = NULL;
if (priv->watch_id == 0) { if (!mm_serial_open (priv->monitor_device, &error)) {
mm_serial_open (priv->monitor_device); g_warning ("Could not open monitoring device %s: %s",
mm_serial_get_device (priv->monitor_device),
channel = mm_serial_get_io_channel (priv->monitor_device); error->message);
priv->watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR | G_IO_HUP, g_error_free (error);
monitor_device_got_data, modem);
g_io_channel_unref (channel);
} }
} else { } else
if (priv->watch_id) { mm_serial_close (priv->monitor_device);
g_source_remove (priv->watch_id);
priv->watch_id = 0;
mm_serial_close (priv->monitor_device);
}
}
} }
static gboolean static gboolean
@@ -173,49 +153,42 @@ parse_syscfg (const char *reply, int *mode_a, int *mode_b, guint32 *band, int *u
static void static void
set_network_mode_done (MMSerial *serial, set_network_mode_done (MMSerial *serial,
int reply_index, GString *response,
GError *error,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
switch (reply_index) { if (error)
case 0: info->error = g_error_copy (error);
/* 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); mm_callback_info_schedule (info);
} }
static void 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; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
int a, b, u1, u2;
guint32 band;
guint id = 0;
if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) { if (error) {
char *responses[] = { "OK", "ERROR", NULL }; info->error = g_error_copy (error);
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); mm_callback_info_schedule (info);
} else {
int a, b, u1, u2;
guint32 band;
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);
mm_serial_queue_command (serial, command, 3, set_network_mode_done, info);
g_free (command);
}
} }
} }
@@ -226,8 +199,6 @@ set_network_mode (MMModemGsmNetwork *modem,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info; MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
@@ -262,40 +233,42 @@ set_network_mode (MMModemGsmNetwork *modem,
break; break;
} }
if (mm_serial_send_command_string (MM_SERIAL (modem), "AT^SYSCFG?")) mm_serial_queue_command (MM_SERIAL (modem), "AT^SYSCFG?", 3, set_network_mode_get_done, info);
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 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; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
int a, b, u1, u2;
guint32 band;
guint32 result = 0;
if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) { if (error)
if (a == 2 && b == 1) info->error = g_error_copy (error);
result = MM_MODEM_GSM_NETWORK_MODE_PREFER_2G; else {
else if (a == 2 && b == 2) int a, b, u1, u2;
result = MM_MODEM_GSM_NETWORK_MODE_PREFER_3G; guint32 band;
else if (a == 13 && b == 1) guint32 result = 0;
result = MM_MODEM_GSM_NETWORK_MODE_GPRS;
else if (a == 14 && b == 2) if (parse_syscfg (response->str, &a, &b, &band, &u1, &u2)) {
result = MM_MODEM_GSM_NETWORK_MODE_3G; if (a == 2 && b == 1)
result = MM_MODEM_GSM_NETWORK_MODE_PREFER_2G;
else if (a == 2 && b == 2)
result = MM_MODEM_GSM_NETWORK_MODE_PREFER_3G;
else if (a == 13 && b == 1)
result = MM_MODEM_GSM_NETWORK_MODE_GPRS;
else if (a == 14 && b == 2)
result = MM_MODEM_GSM_NETWORK_MODE_3G;
}
if (result == 0)
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"%s", "Could not parse network mode results");
else
mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
} }
if (result == 0)
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"%s", "Could not parse network mode results");
else
mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
} }
@@ -305,64 +278,48 @@ get_network_mode (MMModemGsmNetwork *modem,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info; MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
mm_serial_queue_command (MM_SERIAL (modem), "AT^SYSCFG?", 3, get_network_mode_done, info);
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 static void
set_band_done (MMSerial *serial, set_band_done (MMSerial *serial,
int reply_index, GString *response,
GError *error,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
switch (reply_index) { if (error)
case 0: info->error = g_error_copy (error);
/* 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); mm_callback_info_schedule (info);
} }
static void 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; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
int a, b, u1, u2;
guint32 band;
guint id = 0;
if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) { if (error) {
char *responses[] = { "OK", "ERROR", NULL }; info->error = g_error_copy (error);
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); mm_callback_info_schedule (info);
} else {
int a, b, u1, u2;
guint32 band;
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);
mm_serial_queue_command (serial, command, 3, set_band_done, info);
g_free (command);
}
} }
} }
@@ -373,8 +330,6 @@ set_band (MMModemGsmNetwork *modem,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info; MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
@@ -397,38 +352,40 @@ set_band (MMModemGsmNetwork *modem,
break; break;
} }
if (mm_serial_send_command_string (MM_SERIAL (modem), "AT^SYSCFG?")) mm_serial_queue_command (MM_SERIAL (modem), "AT^SYSCFG?", 3, set_band_get_done, info);
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 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; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
int a, b, u1, u2;
guint32 band;
guint32 result = 0xdeadbeaf;
if (parse_syscfg (reply, &a, &b, &band, &u1, &u2)) { if (error)
if (band == 0x3FFFFFFF) info->error = g_error_copy (error);
result = MM_MODEM_GSM_NETWORK_BAND_ANY; else {
else if (band == 0x400380) int a, b, u1, u2;
result = MM_MODEM_GSM_NETWORK_BAND_DCS; guint32 band;
else if (band == 0x200000) guint32 result = 0xdeadbeaf;
result = MM_MODEM_GSM_NETWORK_BAND_PCS;
if (parse_syscfg (response->str, &a, &b, &band, &u1, &u2)) {
if (band == 0x3FFFFFFF)
result = MM_MODEM_GSM_NETWORK_BAND_ANY;
else if (band == 0x400380)
result = MM_MODEM_GSM_NETWORK_BAND_DCS;
else if (band == 0x200000)
result = MM_MODEM_GSM_NETWORK_BAND_PCS;
}
if (result == 0xdeadbeaf)
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"%s", "Could not parse band results");
else
mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
} }
if (result == 0xdeadbeaf)
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"%s", "Could not parse band results");
else
mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
} }
@@ -438,18 +395,9 @@ get_band (MMModemGsmNetwork *modem,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info; MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
mm_serial_queue_command (MM_SERIAL (modem), "AT^SYSCFG?", 3, get_band_done, info);
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);
}
} }
/*****************************************************************************/ /*****************************************************************************/
@@ -481,6 +429,7 @@ constructor (GType type,
{ {
GObject *object; GObject *object;
MMModemHuaweiPrivate *priv; MMModemHuaweiPrivate *priv;
MonitorData *info;
object = G_OBJECT_CLASS (mm_modem_huawei_parent_class)->constructor (type, object = G_OBJECT_CLASS (mm_modem_huawei_parent_class)->constructor (type,
n_construct_params, n_construct_params,
@@ -496,6 +445,11 @@ constructor (GType type,
return NULL; 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; return object;
} }
@@ -538,14 +492,10 @@ finalize (GObject *object)
{ {
MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (object); MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (object);
if (priv->watch_id) { if (priv->monitor_device) {
g_source_remove (priv->watch_id);
priv->watch_id = 0;
mm_serial_close (priv->monitor_device); mm_serial_close (priv->monitor_device);
}
if (priv->monitor_device)
g_object_unref (priv->monitor_device); g_object_unref (priv->monitor_device);
}
G_OBJECT_CLASS (mm_modem_huawei_parent_class)->finalize (object); 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-cdma.h \
mm-generic-gsm.c \ mm-generic-gsm.c \
mm-generic-gsm.h \ mm-generic-gsm.h \
mm-errors.c \
mm-errors.h \
mm-manager.c \ mm-manager.c \
mm-manager.h \ mm-manager.h \
mm-modem.c \ mm-modem.c \
mm-modem.h \ mm-modem.h \
mm-modem-cdma.c \ mm-modem-cdma.c \
mm-modem-cdma.h \ mm-modem-cdma.h \
mm-modem-error.c \
mm-modem-error.h \
mm-modem-gsm-card.c \ mm-modem-gsm-card.c \
mm-modem-gsm-card.h \ mm-modem-gsm-card.h \
mm-modem-gsm-network.c \ mm-modem-gsm-network.c \
mm-modem-gsm-network.h \ mm-modem-gsm-network.h \
mm-options.c \
mm-options.h \
mm-plugin.c \ mm-plugin.c \
mm-plugin.h \ mm-plugin.h \
mm-serial.c \ 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 mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml
dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $< dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $<

View File

@@ -3,6 +3,7 @@
#include <syslog.h> #include <syslog.h>
#include <dbus/dbus-glib.h> #include <dbus/dbus-glib.h>
#include "mm-manager.h" #include "mm-manager.h"
#include "mm-options.h"
static void static void
log_handler (const gchar *log_domain, log_handler (const gchar *log_domain,
@@ -123,31 +124,13 @@ dbus_init (GMainLoop *loop)
int int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
GOptionContext *opt_ctx;
GError *error = NULL;
GMainLoop *loop; GMainLoop *loop;
MMManager *manager; 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 (); g_type_init ();
if (!debug) if (!mm_options_debug ())
logging_setup (); logging_setup ();
loop = g_main_loop_new (NULL, FALSE); 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 -*- */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "mm-callback-info.h" #include "mm-callback-info.h"
#include "mm-modem-error.h" #include "mm-errors.h"
static void static void
callback_info_done (gpointer user_data) callback_info_done (gpointer user_data)
@@ -23,9 +23,10 @@ callback_info_done (gpointer user_data)
if (info->error) if (info->error)
g_error_free (info->error); g_error_free (info->error);
g_object_unref (info->modem); if (info->modem)
g_datalist_clear (&info->qdata); g_object_unref (info->modem);
g_datalist_clear (&info->qdata);
g_slice_free (MMCallbackInfo, info); 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); 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 * MMCallbackInfo *
mm_callback_info_new (MMModem *modem, MMModemFn callback, gpointer user_data) 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); gpointer user_data);
void mm_callback_info_schedule (MMCallbackInfo *info); void mm_callback_info_schedule (MMCallbackInfo *info);
void mm_callback_info_cancel (MMCallbackInfo *info);
void mm_callback_info_set_result (MMCallbackInfo *info, void mm_callback_info_set_result (MMCallbackInfo *info,
gpointer data, gpointer data,
GDestroyNotify destroy); 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-generic-cdma.h"
#include "mm-modem-cdma.h" #include "mm-modem-cdma.h"
#include "mm-modem-error.h" #include "mm-errors.h"
#include "mm-callback-info.h" #include "mm-callback-info.h"
static gpointer mm_generic_cdma_parent_class = NULL; 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 static void
init_done (MMSerial *serial, init_done (MMSerial *serial,
int reply_index, GString *response,
GError *error,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
switch (reply_index) { if (error)
case 0: info->error = g_error_copy (error);
/* 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); mm_callback_info_schedule (info);
} }
@@ -54,18 +47,7 @@ init_done (MMSerial *serial,
static void static void
flash_done (MMSerial *serial, gpointer user_data) flash_done (MMSerial *serial, gpointer user_data)
{ {
char *responses[] = { "OK", "ERROR", "ERR", NULL }; mm_serial_queue_command (serial, "Z E0 V1 X4 &C1 +CMEE=1", 3, init_done, user_data);
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 static void
@@ -84,15 +66,8 @@ enable (MMModem *modem,
return; return;
} }
if (mm_serial_open (MM_SERIAL (modem))) { if (mm_serial_open (MM_SERIAL (modem), &info->error))
guint id; mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info);
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) if (info->error)
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
@@ -100,31 +75,14 @@ enable (MMModem *modem,
static void static void
dial_done (MMSerial *serial, dial_done (MMSerial *serial,
int reply_index, GString *response,
GError *error,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
switch (reply_index) { if (error)
case 0: info->error = g_error_copy (error);
/* 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); mm_callback_info_schedule (info);
} }
@@ -137,21 +95,17 @@ connect (MMModem *modem,
{ {
MMCallbackInfo *info; MMCallbackInfo *info;
char *command; char *command;
char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL };
guint id = 0;
info = mm_callback_info_new (modem, callback, user_data); info = mm_callback_info_new (modem, callback, user_data);
command = g_strconcat ("DT", number, NULL);
command = g_strconcat ("ATDT", number, NULL); mm_serial_queue_command (MM_SERIAL (modem), command, 60, dial_done, info);
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); g_free (command);
}
if (!id) { static void
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing failed."); disconnect_flash_done (MMSerial *serial, gpointer user_data)
mm_callback_info_schedule (info); {
} mm_callback_info_schedule ((MMCallbackInfo *) user_data);
} }
static void static void
@@ -162,17 +116,21 @@ disconnect (MMModem *modem,
MMCallbackInfo *info; MMCallbackInfo *info;
info = mm_callback_info_new (modem, callback, user_data); info = mm_callback_info_new (modem, callback, user_data);
mm_serial_close (MM_SERIAL (modem)); mm_serial_flash (MM_SERIAL (modem), 1000, disconnect_flash_done, info);
mm_callback_info_schedule (info);
} }
static void 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; 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 */ /* Got valid reply */
int quality; int quality;
int ber; int ber;
@@ -183,15 +141,14 @@ get_signal_quality_done (MMSerial *serial, const char *reply, gpointer user_data
/* 99 means unknown */ /* 99 means unknown */
if (quality != 99) if (quality != 99)
/* Normalize the quality */ /* Normalize the quality */
result = quality * 100 / 31; quality = quality * 100 / 31;
mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
} else } else
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"%s", "Could not parse signal quality results"); "%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");
mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
} }
@@ -201,18 +158,9 @@ get_signal_quality (MMModemCdma *modem,
gpointer user_data) gpointer user_data)
{ {
MMCallbackInfo *info; MMCallbackInfo *info;
char *terminators = "\r\n";
guint id = 0;
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
mm_serial_queue_command (MM_SERIAL (modem), "+CSQ", 3, get_signal_quality_done, info);
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);
}
} }
/*****************************************************************************/ /*****************************************************************************/

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.h>
#include <dbus/dbus-glib-lowlevel.h> #include <dbus/dbus-glib-lowlevel.h>
#include "mm-manager.h" #include "mm-manager.h"
#include "mm-modem-error.h" #include "mm-errors.h"
#include "mm-generic-gsm.h" #include "mm-generic-gsm.h"
#include "mm-generic-cdma.h" #include "mm-generic-cdma.h"
#include "mm-plugin.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_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class),
&dbus_glib_mm_manager_object_info); &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_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 <string.h>
#include <dbus/dbus-glib.h> #include <dbus/dbus-glib.h>
#include "mm-modem-cdma.h" #include "mm-modem-cdma.h"
#include "mm-modem-error.h" #include "mm-errors.h"
#include "mm-callback-info.h" #include "mm-callback-info.h"
static void impl_modem_cdma_get_signal_quality (MMModemCdma *modem, DBusGMethodInvocation *context); 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 <dbus/dbus-glib.h>
#include "mm-modem-gsm-card.h" #include "mm-modem-gsm-card.h"
#include "mm-modem-error.h" #include "mm-errors.h"
#include "mm-callback-info.h" #include "mm-callback-info.h"
static void impl_gsm_modem_get_imei (MMModemGsmCard *modem, static void impl_gsm_modem_get_imei (MMModemGsmCard *modem,

View File

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

View File

@@ -3,7 +3,7 @@
#include <string.h> #include <string.h>
#include <dbus/dbus-glib.h> #include <dbus/dbus-glib.h>
#include "mm-modem.h" #include "mm-modem.h"
#include "mm-modem-error.h" #include "mm-errors.h"
#include "mm-callback-info.h" #include "mm-callback-info.h"
static void impl_modem_enable (MMModem *modem, gboolean enable, DBusGMethodInvocation *context); 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() */ #define _GNU_SOURCE /* for strcasestr() */
#include <stdio.h>
#include <stdlib.h>
#include <termio.h> #include <termio.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
@@ -12,6 +14,10 @@
#include <string.h> #include <string.h>
#include "mm-serial.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) G_DEFINE_TYPE (MMSerial, mm_serial, G_TYPE_OBJECT)
@@ -27,7 +33,6 @@ enum {
LAST_PROP LAST_PROP
}; };
#define MM_DEBUG_SERIAL 1
#define SERIAL_BUF_SIZE 2048 #define SERIAL_BUF_SIZE 2048
#define MM_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL, MMSerialPrivate)) #define MM_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL, MMSerialPrivate))
@@ -35,6 +40,15 @@ enum {
typedef struct { typedef struct {
int fd; int fd;
GIOChannel *channel; 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; struct termios old_t;
char *device; char *device;
@@ -44,7 +58,8 @@ typedef struct {
guint stopbits; guint stopbits;
guint64 send_delay; guint64 send_delay;
guint pending_id; guint watch_id;
guint timeout_id;
} MMSerialPrivate; } MMSerialPrivate;
const char * const char *
@@ -189,86 +204,6 @@ parse_stopbits (guint i)
return stopbits; 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 static gboolean
config_fd (MMSerial *self) config_fd (MMSerial *self)
{ {
@@ -305,8 +240,229 @@ config_fd (MMSerial *self)
return TRUE; 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 gboolean
mm_serial_open (MMSerial *self) mm_serial_open (MMSerial *self, GError **error)
{ {
MMSerialPrivate *priv; MMSerialPrivate *priv;
@@ -322,23 +478,30 @@ mm_serial_open (MMSerial *self)
priv->fd = open (priv->device, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); priv->fd = open (priv->device, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
if (priv->fd < 0) { 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; return FALSE;
} }
if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) { 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); close (priv->fd);
return FALSE; return FALSE;
} }
if (!config_fd (self)) { 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); close (priv->fd);
priv->fd = 0; priv->fd = 0;
return FALSE; return FALSE;
} }
priv->channel = g_io_channel_unix_new (priv->fd); 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; return TRUE;
} }
@@ -352,13 +515,12 @@ mm_serial_close (MMSerial *self)
priv = MM_SERIAL_GET_PRIVATE (self); priv = MM_SERIAL_GET_PRIVATE (self);
if (priv->pending_id)
g_source_remove (priv->pending_id);
if (priv->fd) { if (priv->fd) {
g_message ("Closing device '%s'", priv->device); g_message ("Closing device '%s'", priv->device);
if (priv->channel) { if (priv->channel) {
g_source_remove (priv->watch_id);
g_io_channel_shutdown (priv->channel, TRUE, NULL);
g_io_channel_unref (priv->channel); g_io_channel_unref (priv->channel);
priv->channel = NULL; 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 void
mm_serial_wait_quiet (MMSerial *self, mm_serial_queue_command (MMSerial *self,
guint timeout, const char *command,
guint quiet_time, guint32 timeout_seconds,
MMSerialWaitQuietFn callback, MMSerialResponseFn callback,
gpointer user_data) 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 (MM_IS_SERIAL (self));
g_return_if_fail (callback != NULL); g_return_if_fail (command != NULL);
info = g_slice_new0 (WaitQuietInfo); info = g_slice_new0 (MMQueueData);
info->serial = self; info->command = g_strdup (command);
info->timed_out = TRUE; info->timeout = timeout_seconds * 1000;
info->callback = callback; info->callback = callback;
info->user_data = user_data; 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 { typedef struct {
MMSerial *serial; MMSerial *serial;
speed_t current_speed; speed_t current_speed;
@@ -841,8 +594,6 @@ flash_done (gpointer data)
{ {
FlashInfo *info = (FlashInfo *) data; FlashInfo *info = (FlashInfo *) data;
MM_SERIAL_GET_PRIVATE (info->serial)->pending_id = 0;
info->callback (info->serial, info->user_data); info->callback (info->serial, info->user_data);
g_slice_free (FlashInfo, info); g_slice_free (FlashInfo, info);
@@ -884,25 +635,9 @@ mm_serial_flash (MMSerial *self,
info, info,
flash_done); flash_done);
MM_SERIAL_GET_PRIVATE (self)->pending_id = id;
return 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 static void
@@ -915,6 +650,10 @@ mm_serial_init (MMSerial *self)
priv->parity = 'n'; priv->parity = 'n';
priv->stopbits = 1; priv->stopbits = 1;
priv->send_delay = 0; 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* static GObject*
@@ -1012,8 +751,15 @@ finalize (GObject *object)
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
mm_serial_close (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); 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); G_OBJECT_CLASS (mm_serial_parent_class)->finalize (object);
} }

View File

@@ -6,12 +6,12 @@
#include <glib/gtypes.h> #include <glib/gtypes.h>
#include <glib-object.h> #include <glib-object.h>
#define MM_TYPE_SERIAL (mm_serial_get_type ()) #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(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_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(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_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_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL, MMSerialClass))
#define MM_SERIAL_DEVICE "device" #define MM_SERIAL_DEVICE "device"
#define MM_SERIAL_BAUD "baud" #define MM_SERIAL_BAUD "baud"
@@ -20,66 +20,51 @@
#define MM_SERIAL_STOPBITS "stopbits" #define MM_SERIAL_STOPBITS "stopbits"
#define MM_SERIAL_SEND_DELAY "send-delay" #define MM_SERIAL_SEND_DELAY "send-delay"
typedef struct { typedef struct _MMSerial MMSerial;
GObject parent; typedef struct _MMSerialClass MMSerialClass;
} MMSerial;
typedef struct { typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data,
GObjectClass parent; GString *response,
} MMSerialClass; GError **error);
GType mm_serial_get_type (void); typedef void (*MMSerialResponseFn) (MMSerial *serial,
GString *response,
typedef void (*MMSerialGetReplyFn) (MMSerial *serial, GError *error,
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); gpointer user_data);
typedef void (*MMSerialFlashFn) (MMSerial *serial, typedef void (*MMSerialFlashFn) (MMSerial *serial,
gpointer user_data); 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;
};
void mm_serial_close (MMSerial *self); GType mm_serial_get_type (void);
gboolean mm_serial_send_command (MMSerial *self,
GByteArray *command);
gboolean mm_serial_send_command_string (MMSerial *self, void mm_serial_set_response_parser (MMSerial *self,
const char *str); MMSerialResponseParserFn fn,
gpointer user_data,
GDestroyNotify notify);
guint mm_serial_get_reply (MMSerial *self, gboolean mm_serial_open (MMSerial *self,
guint timeout, GError **error);
const char *terminators,
MMSerialGetReplyFn callback,
gpointer user_data);
guint mm_serial_wait_for_reply (MMSerial *self, void mm_serial_close (MMSerial *self);
guint timeout, void mm_serial_queue_command (MMSerial *self,
char **responses, const char *command,
char **terminators, guint32 timeout_seconds,
MMSerialWaitForReplyFn callback, MMSerialResponseFn callback,
gpointer user_data); gpointer user_data);
void mm_serial_wait_quiet (MMSerial *self, guint mm_serial_flash (MMSerial *self,
guint timeout, guint32 flash_time,
guint quiet_time, MMSerialFlashFn callback,
MMSerialWaitQuietFn callback, gpointer user_data);
gpointer user_data);
guint mm_serial_flash (MMSerial *self, const char *mm_serial_get_device (MMSerial *self);
guint32 flash_time,
MMSerialFlashFn callback,
gpointer user_data);
GIOChannel *mm_serial_get_io_channel (MMSerial *self);
#endif /* MM_SERIAL_H */ #endif /* MM_SERIAL_H */