Files
ModemManager/plugins/mm-modem-mbm.c
T
Tambet Ingo 1215bd6a9d Rework unsolicited message handling.
Implement registration using unsolicited messages for generic GSM class (which
is disabled by default, HSO and Huawei plugins enable it).

Modify all GSM modem subclasses that used unsolicited messages to use the new
method.
2009-02-18 14:48:08 +02:00

521 lines
15 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
Additions to NetworkManager, network-manager-applet and modemmanager
for supporting Ericsson modules like F3507g.
Copyright (C) 2008 Ericsson MBM
Author: Per Hallsmark <per@hallsmark.se>
Bjorn Runaker <bjorn.runaker@ericsson.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <dbus/dbus-glib.h>
#include "mm-modem-mbm.h"
#include "mm-serial.h"
#include "mm-serial-parsers.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
static gpointer mm_modem_mbm_parent_class = NULL;
#define MM_MODEM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_MBM, MMModemMbmPrivate))
typedef struct {
guint32 signal_quality;
} MMModemMbmPrivate;
MMModem *
mm_modem_mbm_new (const char *serial_device,
const char *network_device,
const char *driver)
{
g_return_val_if_fail (serial_device != NULL, NULL);
g_return_val_if_fail (network_device != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
return MM_MODEM (g_object_new (MM_TYPE_MODEM_MBM,
MM_SERIAL_DEVICE, serial_device,
MM_SERIAL_SEND_DELAY, (guint64) 10000,
MM_MODEM_DRIVER, driver,
MM_MODEM_DEVICE, network_device,
MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_DHCP,
MM_MODEM_TYPE, MM_MODEM_TYPE_GSM,
NULL));
}
static void
eiapsw_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);
}
static void
eiac_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
char *command;
if (error)
info->error = g_error_copy (error);
command = g_strdup_printf ("AT*EIAPSW=1,1,\"%s\"",
(char *) mm_callback_info_get_data (info, "apn"));
mm_serial_queue_command (serial, command, 10, eiapsw_done, info);
g_free (command);
}
static void
eiad_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error)
info->error = g_error_copy (error);
mm_serial_queue_command (serial, "AT*EIAC=1", 10, eiac_done, info);
}
static void
set_apn (MMModemGsmNetwork *modem,
const char *apn,
MMModemFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
mm_callback_info_set_data (info, "apn", g_strdup (apn), g_free);
mm_serial_queue_command (MM_SERIAL (modem), "AT*EIAD=0", 10, eiad_done, info);
}
static void
do_cmer_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);
}
static void
do_register_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error)
info->error = g_error_copy (error);
mm_serial_queue_command (serial, "AT+CMER=3,0,0,1", 10, do_cmer_done, info);
}
static void
do_register (MMModemGsmNetwork *modem,
const char *network_id,
MMModemFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
sleep (4);
mm_serial_queue_command (MM_SERIAL (modem), "AT*ENAP=1,1", 10, do_register_done, info);
}
/*****************************************************************************/
/* Modem class override functions */
/*****************************************************************************/
static void
enable_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);
}
static void
init_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
mm_serial_queue_command (serial, "+CFUN=1", 5, enable_done, user_data);
}
static void
enable_flash_done (MMSerial *serial, gpointer user_data)
{
mm_serial_queue_command (serial, "&F E0 +CMEE=1", 3, init_done, user_data);
}
static void
disable_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
mm_serial_close (serial);
mm_callback_info_schedule ((MMCallbackInfo *) user_data);
}
static void
disable_flash_done (MMSerial *serial, gpointer user_data)
{
mm_serial_queue_command (serial, "+CFUN=4", 5, disable_done, user_data);
}
static void
enable (MMModem *modem,
gboolean do_enable,
MMModemFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
/* First, reset the previously used CID */
mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
info = mm_callback_info_new (modem, callback, user_data);
if (!do_enable) {
if (mm_serial_is_connected (MM_SERIAL (modem)))
mm_serial_flash (MM_SERIAL (modem), 1000, disable_flash_done, info);
else
disable_flash_done (MM_SERIAL (modem), info);
} else {
if (mm_serial_open (MM_SERIAL (modem), &info->error))
mm_serial_flash (MM_SERIAL (modem), 100, enable_flash_done, info);
if (info->error)
mm_callback_info_schedule (info);
}
}
static gboolean
parse_erinfo (const char *reply, int *mode, int *gsm_rinfo, int *umts_rinfo)
{
if (reply == NULL || strncmp (reply, "*ERINFO:", 8))
return FALSE;
if (sscanf (reply + 8, "%d,%d,%d", mode, gsm_rinfo, umts_rinfo))
return TRUE;
return FALSE;
}
static void
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 mode;
int gsm_rinfo;
int umts_rinfo;
guint32 result = 0;
if (parse_erinfo (response->str, &mode, &gsm_rinfo, &umts_rinfo)) {
if (umts_rinfo == 2)
result = MM_MODEM_GSM_NETWORK_MODE_HSDPA;
else if (umts_rinfo && !gsm_rinfo)
result = MM_MODEM_GSM_NETWORK_MODE_3G;
else if (umts_rinfo && gsm_rinfo)
result = MM_MODEM_GSM_NETWORK_MODE_PREFER_3G;
else if (gsm_rinfo)
result = MM_MODEM_GSM_NETWORK_MODE_GPRS;
}
if (result == 0)
info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Could not parse network mode results");
else
mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
}
mm_callback_info_schedule (info);
}
static void
get_network_mode (MMModemGsmNetwork *modem,
MMModemUIntFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
mm_serial_queue_command (MM_SERIAL (modem), "AT*ERINFO?", 3, get_network_mode_done, info);
}
/* GetSignalQuality */
static void
get_signal_quality_done (MMSerial *serial,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
char *reply = response->str;
if (error)
info->error = g_error_copy (error);
else if (!strncmp (reply, "+CIND: ", 7)) {
/* Got valid reply */
int battch;
int quality;
reply += 7;
if (sscanf (reply, "%d,%d", &battch, &quality)) {
/* Normalize the quality */
quality = quality * 100 / 5;
MM_MODEM_MBM_GET_PRIVATE (serial)->signal_quality = quality;
mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
} else
info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Could not parse signal quality results");
}
mm_callback_info_schedule (info);
}
static void
get_signal_quality (MMModemGsmNetwork *modem,
MMModemUIntFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
if (mm_serial_is_connected (MM_SERIAL (modem))) {
g_message ("Returning saved signal quality %d", MM_MODEM_MBM_GET_PRIVATE (modem)->signal_quality);
callback (MM_MODEM (modem), MM_MODEM_MBM_GET_PRIVATE (modem)->signal_quality, NULL, user_data);
return;
}
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
mm_serial_queue_command (MM_SERIAL (modem), "+CIND?", 3, get_signal_quality_done, info);
}
/*****************************************************************************/
static void
boot_trig (MMSerial *serial,
GMatchInfo *match_info,
gpointer user_data)
{
mm_serial_queue_command (serial, "AT*ENAP=1,1", 10, NULL, NULL);
}
static void
ciev_trig (MMSerial *serial,
GMatchInfo *match_info,
gpointer user_data)
{
char *str;
int event, value;
guint32 quality;
str = g_match_info_fetch (match_info, 1);
event = str[0] - '0';
g_free (str);
str = g_match_info_fetch (match_info, 2);
value = str[0] - '0';
g_free (str);
switch (event) {
case 2: /* signal quality, value 0-5 */
quality = value * 100 / 5;
mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (serial), quality);
break;
case 9: /* roaming, value 0 or 1 */
g_debug ("%s: roaming %s\n", __FUNCTION__, value ? "active" : "inactive");
break;
default:
g_debug ("%s: got unknown event %d with value %d\n", __FUNCTION__, event, value);
}
}
static void
mm_modem_mbm_init (MMModemMbm *self)
{
GRegex *regex;
regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, boot_trig, NULL, NULL);
g_regex_unref (regex);
regex = g_regex_new ("\\r\\n[\\*]EMWI\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, NULL, NULL, NULL);
g_regex_unref (regex);
regex = g_regex_new ("\\r\\n\\+CIEV: (\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, ciev_trig, NULL, NULL);
g_regex_unref (regex);
}
static void
modem_init (MMModem *modem_class)
{
modem_class->enable = enable;
}
static void
modem_gsm_network_init (MMModemGsmNetwork *class)
{
class->do_register = do_register;
class->set_apn = set_apn;
class->get_network_mode = get_network_mode;
class->get_signal_quality = get_signal_quality;
}
static GObject*
constructor (GType type,
guint n_construct_params,
GObjectConstructParam *construct_params)
{
GObject *object;
char *modem_device;
char *serial_device;
object = G_OBJECT_CLASS (mm_modem_mbm_parent_class)->constructor (type,
n_construct_params,
construct_params);
if (!object)
return NULL;
/* Make sure both serial device and data device are provided */
g_object_get (object,
MM_MODEM_DEVICE, &modem_device,
MM_SERIAL_DEVICE, &serial_device,
NULL);
if (!modem_device || !serial_device || !strcmp (modem_device, serial_device)) {
g_warning ("No network device provided");
g_object_unref (object);
object = NULL;
}
g_free (modem_device);
g_free (serial_device);
return object;
}
static void
mm_modem_mbm_class_init (MMModemMbmClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
mm_modem_mbm_parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (object_class, sizeof (MMModemMbmPrivate));
/* Virtual methods */
object_class->constructor = constructor;
}
GType
mm_modem_mbm_get_type (void)
{
static GType modem_mbm_type = 0;
if (G_UNLIKELY (modem_mbm_type == 0)) {
static const GTypeInfo modem_mbm_type_info = {
sizeof (MMModemMbmClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) mm_modem_mbm_class_init,
(GClassFinalizeFunc) NULL,
NULL, /* class_data */
sizeof (MMModemMbm),
0, /* n_preallocs */
(GInstanceInitFunc) mm_modem_mbm_init,
};
static const GInterfaceInfo modem_iface_info = {
(GInterfaceInitFunc) modem_init
};
static const GInterfaceInfo modem_gsm_network_info = {
(GInterfaceInitFunc) modem_gsm_network_init
};
modem_mbm_type = g_type_register_static (MM_TYPE_GENERIC_GSM, "MMModemMbm", &modem_mbm_type_info, 0);
g_type_add_interface_static (modem_mbm_type, MM_TYPE_MODEM, &modem_iface_info);
g_type_add_interface_static (modem_mbm_type, MM_TYPE_MODEM_GSM_NETWORK, &modem_gsm_network_info);
}
return modem_mbm_type;
}