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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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=$@ $<
|
||||
|
23
src/main.c
23
src/main.c
@@ -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);
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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
175
src/mm-errors.c
Normal 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
110
src/mm-errors.h
Normal 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 */
|
@@ -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
@@ -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
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
@@ -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 */
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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
36
src/mm-options.c
Normal 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
9
src/mm-options.h
Normal 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
266
src/mm-serial-parsers.c
Normal 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
26
src/mm-serial-parsers.h
Normal 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 */
|
804
src/mm-serial.c
804
src/mm-serial.c
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
Reference in New Issue
Block a user