build: move plugins directory to src/plugins

We are going to allow including the plugin sources built within the
ModemManager daemon binary; moving the sources within the daemon
sources directory makes it easier.
This commit is contained in:
Aleksander Morgado
2023-01-03 13:56:25 +00:00
committed by Aleksander Morgado
parent 072d7ac906
commit e14b904cbd
318 changed files with 5 additions and 3 deletions
+174
View File
@@ -0,0 +1,174 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_mbm_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0bdb", GOTO="mm_mbm_ericsson_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0fce", GOTO="mm_mbm_sony_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="413c", GOTO="mm_mbm_dell_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="03f0", GOTO="mm_mbm_hp_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0930", GOTO="mm_mbm_toshiba_vendorcheck"
GOTO="mm_mbm_end"
LABEL="mm_mbm_ericsson_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Ericsson F3507g
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1900", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1900", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1902", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1902", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson F3607gw
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1904", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1904", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1905", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1905", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1906", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1906", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson F3307
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190a", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1909", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson F3307 R2
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1914", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson C3607w
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1049", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson C3607w v2
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190b", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson F5521gw
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1911", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1911", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson H5321gw
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1919", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson H5321w
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="191d", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson F5321gw
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1917", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson F5321w
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="191b", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson C5621gw
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="191f", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson C5621w
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1921", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson H5321gw
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1926", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1926", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1927", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1927", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson C3304w
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1928", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson C5621 TFF
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1936", ENV{ID_MM_ERICSSON_MBM}="1"
# Lenovo N5321gw
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="193e", ENV{ID_MM_ERICSSON_MBM}="1"
GOTO="mm_mbm_end"
LABEL="mm_mbm_sony_vendorcheck"
# Sony-Ericsson MD300
ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{ID_MM_ERICSSON_MBM}="1"
# Sony-Ericsson MD400
ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0e1", ENV{ID_MM_ERICSSON_MBM}="1"
# Sony-Ericsson MD400G
ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d103", ENV{ID_MM_ERICSSON_MBM}="1"
GOTO="mm_mbm_end"
LABEL="mm_mbm_dell_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Dell 5560
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818e", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818e", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
# Dell 5550
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818d", ENV{ID_MM_ERICSSON_MBM}="1"
# Dell 5530 HSDPA
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1"
# Dell F3607gw
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{ID_MM_ERICSSON_MBM}="1"
# Dell F3307
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{ID_MM_ERICSSON_MBM}="1"
GOTO="mm_mbm_end"
LABEL="mm_mbm_hp_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# HP hs2330 Mobile Broadband Module
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="271d", ENV{ID_MM_ERICSSON_MBM}="1"
# HP hs2320 Mobile Broadband Module
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="261d", ENV{ID_MM_ERICSSON_MBM}="1"
# HP hs2340 Mobile Broadband Module
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="3a1d", ENV{ID_MM_ERICSSON_MBM}="1"
# HP hs2350 Mobile Broadband Module
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="3d1d", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="3d1d", ENV{ID_MM_ERICSSON_MBM}="1"
# HP lc2000 Mobile Broadband Module
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="301d", ENV{ID_MM_ERICSSON_MBM}="1"
# HP lc2010 Mobile Broadband Module
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="2f1d", ENV{ID_MM_ERICSSON_MBM}="1"
GOTO="mm_mbm_end"
LABEL="mm_mbm_toshiba_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Toshiba
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{ID_MM_ERICSSON_MBM}="1"
# Toshiba F3607gw
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130c", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130c", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1311", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1311", ENV{ID_MM_ERICSSON_MBM}="1"
# Toshiba F3307
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1315", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1316", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1317", ENV{ID_MM_ERICSSON_MBM}="1"
# Toshiba F5521gw
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1313", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1314", ENV{ID_MM_ERICSSON_MBM}="1"
# Toshiba H5321gw
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1319", ENV{ID_MM_ERICSSON_MBM}="1"
GOTO="mm_mbm_end"
LABEL="mm_mbm_end"
+911
View File
@@ -0,0 +1,911 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2008 - 2010 Ericsson AB
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
* Copyright (C) 2017 Aleksander Morgado <aleksander@aleksander.es>
*
* Author: Per Hallsmark <per.hallsmark@ericsson.com>
* Bjorn Runaker <bjorn.runaker@ericsson.com>
* Torgny Johansson <torgny.johansson@ericsson.com>
* Jonas Sjöquist <jonas.sjoquist@ericsson.com>
* Dan Williams <dcbw@redhat.com>
* Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-base-modem-at.h"
#include "mm-broadband-bearer-mbm.h"
#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-mbm.h"
#include "mm-daemon-enums-types.h"
G_DEFINE_TYPE (MMBroadbandBearerMbm, mm_broadband_bearer_mbm, MM_TYPE_BROADBAND_BEARER)
struct _MMBroadbandBearerMbmPrivate {
GTask *connect_pending;
GTask *disconnect_pending;
};
/*****************************************************************************/
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
guint cid;
MMPort *data;
guint poll_count;
guint poll_id;
GError *saved_error;
} Dial3gppContext;
static void
dial_3gpp_context_free (Dial3gppContext *ctx)
{
g_assert (!ctx->poll_id);
g_assert (!ctx->saved_error);
g_clear_object (&ctx->data);
g_clear_object (&ctx->primary);
g_clear_object (&ctx->modem);
g_slice_free (Dial3gppContext, ctx);
}
static MMPort *
dial_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return MM_PORT (g_task_propagate_pointer (G_TASK (res), error));
}
static void
connect_reset_ready (MMBroadbandBearer *self,
GAsyncResult *res,
GTask *task)
{
Dial3gppContext *ctx;
ctx = g_task_get_task_data (task);
MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self, res, NULL);
/* When reset is requested, it was either cancelled or an error was stored */
if (!g_task_return_error_if_cancelled (task)) {
g_assert (ctx->saved_error);
g_task_return_error (task, ctx->saved_error);
ctx->saved_error = NULL;
}
g_object_unref (task);
}
static void
connect_reset (GTask *task)
{
MMBroadbandBearerMbm *self;
Dial3gppContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp (
MM_BROADBAND_BEARER (self),
MM_BROADBAND_MODEM (ctx->modem),
ctx->primary,
NULL,
ctx->data,
ctx->cid,
(GAsyncReadyCallback) connect_reset_ready,
task);
}
static void
process_pending_connect_attempt (MMBroadbandBearerMbm *self,
MMBearerConnectionStatus status)
{
GTask *task;
Dial3gppContext *ctx;
/* Recover connection task */
task = self->priv->connect_pending;
self->priv->connect_pending = NULL;
g_assert (task != NULL);
ctx = g_task_get_task_data (task);
if (ctx->poll_id) {
g_source_remove (ctx->poll_id);
ctx->poll_id = 0;
}
/* Received 'CONNECTED' during a connection attempt? */
if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) {
/* If we wanted to get cancelled before, do it now. */
if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
connect_reset (task);
return;
}
g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
g_object_unref (task);
return;
}
/* If we wanted to get cancelled before and now we couldn't connect,
* use the cancelled error and return */
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
/* Otherwise, received 'DISCONNECTED' during a connection attempt? */
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Call setup failed");
g_object_unref (task);
}
static gboolean connect_poll_cb (MMBroadbandBearerMbm *self);
static void
connect_poll_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerMbm *self)
{
GTask *task;
Dial3gppContext *ctx;
GError *error = NULL;
const gchar *response;
guint state;
task = g_steal_pointer (&self->priv->connect_pending);
if (!task) {
mm_obj_dbg (self, "connection context was finished already by an unsolicited message");
/* Run _finish() to finalize the async call, even if we don't care
* the result */
mm_base_modem_at_command_full_finish (modem, res, NULL);
return;
}
ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!response) {
ctx->saved_error = error;
connect_reset (task);
return;
}
if (sscanf (response, "*ENAP: %d", &state) == 1 && state == 1) {
/* Success! Connected... */
g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
g_object_unref (task);
return;
}
/* Restore pending task and check again in one second */
self->priv->connect_pending = task;
g_assert (ctx->poll_id == 0);
ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc) connect_poll_cb, self);
}
static gboolean
connect_poll_cb (MMBroadbandBearerMbm *self)
{
GTask *task;
Dial3gppContext *ctx;
task = g_steal_pointer (&self->priv->connect_pending);
g_assert (task);
ctx = g_task_get_task_data (task);
ctx->poll_id = 0;
/* Complete if we were cancelled */
if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
connect_reset (task);
return G_SOURCE_REMOVE;
}
/* Too many retries... */
if (ctx->poll_count > MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT) {
g_assert (!ctx->saved_error);
ctx->saved_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
"Connection attempt timed out");
connect_reset (task);
return G_SOURCE_REMOVE;
}
/* Restore pending task and poll */
self->priv->connect_pending = task;
ctx->poll_count++;
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"AT*ENAP?",
3,
FALSE,
FALSE, /* raw */
g_task_get_cancellable (task),
(GAsyncReadyCallback)connect_poll_ready,
self);
return G_SOURCE_REMOVE;
}
static void
activate_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerMbm *self)
{
GTask *task;
Dial3gppContext *ctx;
GError *error = NULL;
/* Try to recover the connection context. If none found, it means the
* context was already completed and we have nothing else to do. */
task = g_steal_pointer (&self->priv->connect_pending);
if (!task) {
mm_obj_dbg (self, "connection context was finished already by an unsolicited message");
/* Run _finish() to finalize the async call, even if we don't care
* the result */
mm_base_modem_at_command_full_finish (modem, res, NULL);
goto out;
}
/* From now on, if we get cancelled, we'll need to run the connection
* reset ourselves just in case */
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
goto out;
}
ctx = g_task_get_task_data (task);
/* No unsolicited E2NAP status yet; wait for it and periodically poll
* to handle very old F3507g/MD300 firmware that may not send E2NAP. */
self->priv->connect_pending = task;
ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc)connect_poll_cb, self);
out:
/* Balance refcount with the extra ref we passed to command_full() */
g_object_unref (self);
}
static void
activate (GTask *task)
{
MMBroadbandBearerMbm *self;
Dial3gppContext *ctx;
gchar *command;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
/* The unsolicited response to ENAP may come before the OK does.
* We will keep the connection context in the bearer private data so
* that it is accessible from the unsolicited message handler. */
g_assert (self->priv->connect_pending == NULL);
self->priv->connect_pending = task;
/* Activate the PDP context and start the data session */
command = g_strdup_printf ("AT*ENAP=1,%d", ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
10,
FALSE,
FALSE, /* raw */
g_task_get_cancellable (task),
(GAsyncReadyCallback)activate_ready,
g_object_ref (self)); /* we pass the bearer object! */
g_free (command);
}
static void
authenticate_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
activate (task);
}
static void
authenticate (GTask *task)
{
MMBroadbandBearerMbm *self;
Dial3gppContext *ctx;
const gchar *user;
const gchar *password;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
/* Both user and password are required; otherwise firmware returns an error */
if (user || password) {
g_autofree gchar *command = NULL;
g_autofree gchar *user_enc = NULL;
g_autofree gchar *password_enc = NULL;
GError *error = NULL;
user_enc = mm_modem_charset_str_from_utf8 (user,
mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (ctx->modem)),
FALSE,
&error);
if (!user_enc) {
g_prefix_error (&error, "Couldn't convert user to current charset: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
password_enc = mm_modem_charset_str_from_utf8 (password,
mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (ctx->modem)),
FALSE,
&error);
if (!password_enc) {
g_prefix_error (&error, "Couldn't convert password to current charset: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
command = g_strdup_printf ("AT*EIAAUW=%d,1,\"%s\",\"%s\"",
ctx->cid, user_enc, password_enc);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
3,
FALSE,
FALSE, /* raw */
g_task_get_cancellable (task),
(GAsyncReadyCallback) authenticate_ready,
task);
return;
}
mm_obj_dbg (self, "authentication not needed");
activate (task);
}
static void
dial_3gpp (MMBroadbandBearer *_self,
MMBaseModem *modem,
MMPortSerialAt *primary,
guint cid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self);
GTask *task;
Dial3gppContext *ctx;
g_assert (primary != NULL);
task = g_task_new (self, cancellable, callback, user_data);
ctx = g_slice_new0 (Dial3gppContext);
ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
ctx->cid = cid;
g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free);
/* We need a net data port */
ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET);
if (!ctx->data) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_NOT_FOUND,
"No valid data port found to launch connection");
g_object_unref (task);
return;
}
authenticate (task);
}
/*****************************************************************************/
/* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
MMBearerIpFamily family;
} GetIpConfig3gppContext;
static void
get_ip_config_context_free (GetIpConfig3gppContext *ctx)
{
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
g_free (ctx);
}
static gboolean
get_ip_config_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
MMBearerIpConfig **ipv4_config,
MMBearerIpConfig **ipv6_config,
GError **error)
{
MMBearerConnectResult *configs;
MMBearerIpConfig *ipv4, *ipv6;
configs = g_task_propagate_pointer (G_TASK (res), error);
if (!configs)
return FALSE;
ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs);
ipv6 = mm_bearer_connect_result_peek_ipv6_config (configs);
g_assert (ipv4 || ipv6);
if (ipv4_config && ipv4)
*ipv4_config = g_object_ref (ipv4);
if (ipv6_config && ipv6)
*ipv6_config = g_object_ref (ipv6);
mm_bearer_connect_result_unref (configs);
return TRUE;
}
static void
ip_config_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
GetIpConfig3gppContext *ctx;
MMBearerIpConfig *ipv4_config = NULL;
MMBearerIpConfig *ipv6_config = NULL;
const gchar *response;
GError *error = NULL;
MMBearerConnectResult *connect_result;
ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
g_error_free (error);
/* Fall back to DHCP configuration; early devices don't support *E2IPCFG */
if (ctx->family == MM_BEARER_IP_FAMILY_IPV4 || ctx->family == MM_BEARER_IP_FAMILY_IPV4V6) {
ipv4_config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP);
}
if (ctx->family == MM_BEARER_IP_FAMILY_IPV6 || ctx->family == MM_BEARER_IP_FAMILY_IPV4V6) {
ipv6_config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
}
} else {
if (!mm_mbm_parse_e2ipcfg_response (response,
&ipv4_config,
&ipv6_config,
&error)) {
g_task_return_error (task, error);
goto out;
}
if (!ipv4_config && !ipv6_config) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't get IP config: couldn't parse response '%s'",
response);
goto out;
}
}
connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary),
ipv4_config,
ipv6_config);
g_task_return_pointer (task,
connect_result,
(GDestroyNotify)mm_bearer_connect_result_unref);
out:
g_object_unref (task);
g_clear_object (&ipv4_config);
g_clear_object (&ipv6_config);
}
static void
get_ip_config_3gpp (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
MMBearerIpFamily ip_family,
GAsyncReadyCallback callback,
gpointer user_data)
{
GetIpConfig3gppContext *ctx;
GTask *task;
ctx = g_new0 (GetIpConfig3gppContext, 1);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->family = ip_family;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)get_ip_config_context_free);
mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
primary,
"*E2IPCFG?",
3,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)ip_config_ready,
task);
}
/*****************************************************************************/
/* 3GPP disconnect */
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
guint poll_count;
guint poll_id;
} DisconnectContext;
static void
disconnect_context_free (DisconnectContext *ctx)
{
g_assert (!ctx->poll_id);
g_clear_object (&ctx->primary);
g_clear_object (&ctx->modem);
g_free (ctx);
}
static gboolean
disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
process_pending_disconnect_attempt (MMBroadbandBearerMbm *self,
MMBearerConnectionStatus status)
{
GTask *task;
DisconnectContext *ctx;
/* Recover disconnection task */
task = g_steal_pointer (&self->priv->disconnect_pending);
ctx = g_task_get_task_data (task);
if (ctx->poll_id) {
g_source_remove (ctx->poll_id);
ctx->poll_id = 0;
}
/* Received 'DISCONNECTED' during a disconnection attempt? */
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
mm_obj_dbg (self, "connection disconnect indicated by an unsolicited message");
g_task_return_boolean (task, TRUE);
} else {
/* Otherwise, report error */
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Disconnection failed");
}
g_object_unref (task);
}
static gboolean disconnect_poll_cb (MMBroadbandBearerMbm *self);
static void
disconnect_poll_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerMbm *self)
{
GTask *task;
DisconnectContext *ctx;
GError *error = NULL;
const gchar *response;
guint state;
task = g_steal_pointer (&self->priv->disconnect_pending);
if (!task) {
mm_obj_dbg (self, "disconnection context was finished already by an unsolicited message");
/* Run _finish() to finalize the async call, even if we don't care
* the result */
mm_base_modem_at_command_full_finish (modem, res, NULL);
goto out;
}
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!response) {
g_task_return_error (task, error);
g_object_unref (task);
goto out;
}
if (sscanf (response, "*ENAP: %d", &state) == 1 && state == 0) {
/* Disconnected */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
goto out;
}
/* Restore pending task and check in 1s */
self->priv->disconnect_pending = task;
ctx = g_task_get_task_data (task);
g_assert (ctx->poll_id == 0);
ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc) disconnect_poll_cb, self);
out:
/* Balance refcount with the extra ref we passed to command_full() */
g_object_unref (self);
}
static gboolean
disconnect_poll_cb (MMBroadbandBearerMbm *self)
{
GTask *task;
DisconnectContext *ctx;
task = self->priv->disconnect_pending;
self->priv->disconnect_pending = NULL;
g_assert (task);
ctx = g_task_get_task_data (task);
ctx->poll_id = 0;
/* Too many retries... */
if (ctx->poll_count > MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT) {
g_task_return_new_error (task,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
"Disconnection attempt timed out");
g_object_unref (task);
return G_SOURCE_REMOVE;
}
/* Restore pending task and poll */
self->priv->disconnect_pending = task;
ctx->poll_count++;
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"AT*ENAP?",
3,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback) disconnect_poll_ready,
g_object_ref (self)); /* we pass the bearer object! */
return G_SOURCE_REMOVE;
}
static void
disconnect_enap_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerMbm *self)
{
DisconnectContext *ctx;
GTask *task;
GError *error = NULL;
task = g_steal_pointer (&self->priv->disconnect_pending);
/* Try to recover the disconnection context. If none found, it means the
* context was already completed and we have nothing else to do. */
if (!task) {
mm_base_modem_at_command_full_finish (modem, res, NULL);
goto out;
}
ctx = g_task_get_task_data (task);
/* Ignore errors for now */
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
mm_obj_dbg (self, "disconnection failed (not fatal): %s", error->message);
g_error_free (error);
}
/* No unsolicited E2NAP status yet; wait for it and periodically poll
* to handle very old F3507g/MD300 firmware that may not send E2NAP. */
self->priv->disconnect_pending = task;
ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc)disconnect_poll_cb, self);
out:
/* Balance refcount with the extra ref we passed to command_full() */
g_object_unref (self);
}
static void
disconnect_3gpp (MMBroadbandBearer *_self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self);
GTask *task;
DisconnectContext *ctx;
g_assert (primary != NULL);
task = g_task_new (self, NULL, callback, user_data);
ctx = g_new0 (DisconnectContext, 1);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
g_task_set_task_data (task, ctx, (GDestroyNotify) disconnect_context_free);
/* The unsolicited response to ENAP may come before the OK does.
* We will keep the disconnection context in the bearer private data so
* that it is accessible from the unsolicited message handler. */
g_assert (self->priv->disconnect_pending == NULL);
self->priv->disconnect_pending = task;
mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
primary,
"*ENAP=0",
3,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_enap_ready,
g_object_ref (self)); /* we pass the bearer object! */
}
/*****************************************************************************/
static void
report_connection_status (MMBaseBearer *_self,
MMBearerConnectionStatus status,
const GError *connection_error)
{
MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self);
g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED ||
status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
/* Process pending connection attempt */
if (self->priv->connect_pending) {
process_pending_connect_attempt (self, status);
return;
}
/* Process pending disconnection attempt */
if (self->priv->disconnect_pending) {
process_pending_disconnect_attempt (self, status);
return;
}
mm_obj_dbg (self, "received spontaneous E2NAP (%s)",
mm_bearer_connection_status_get_string (status));
/* Received a random 'DISCONNECTED'...*/
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED ||
status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) {
/* If no connection/disconnection attempt on-going, make sure we mark ourselves as
* disconnected. Make sure we only pass 'DISCONNECTED' to the parent */
MM_BASE_BEARER_CLASS (mm_broadband_bearer_mbm_parent_class)->report_connection_status (
_self,
MM_BEARER_CONNECTION_STATUS_DISCONNECTED,
NULL);
}
}
/*****************************************************************************/
MMBaseBearer *
mm_broadband_bearer_mbm_new_finish (GAsyncResult *res,
GError **error)
{
GObject *bearer;
GObject *source;
source = g_async_result_get_source_object (res);
bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
g_object_unref (source);
if (!bearer)
return NULL;
/* Only export valid bearers */
mm_base_bearer_export (MM_BASE_BEARER (bearer));
return MM_BASE_BEARER (bearer);
}
void
mm_broadband_bearer_mbm_new (MMBroadbandModemMbm *modem,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (
MM_TYPE_BROADBAND_BEARER_MBM,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_BEARER_MODEM, modem,
MM_BASE_BEARER_CONFIG, config,
NULL);
}
static void
mm_broadband_bearer_mbm_init (MMBroadbandBearerMbm *self)
{
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_BROADBAND_BEARER_MBM,
MMBroadbandBearerMbmPrivate);
}
static void
mm_broadband_bearer_mbm_class_init (MMBroadbandBearerMbmClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMBroadbandBearerMbmPrivate));
base_bearer_class->report_connection_status = report_connection_status;
base_bearer_class->load_connection_status = NULL;
base_bearer_class->load_connection_status_finish = NULL;
#if defined WITH_SUSPEND_RESUME
base_bearer_class->reload_connection_status = NULL;
base_bearer_class->reload_connection_status_finish = NULL;
#endif
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp;
broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
}
+68
View File
@@ -0,0 +1,68 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2008 - 2010 Ericsson AB
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
*
* Author: Per Hallsmark <per.hallsmark@ericsson.com>
* Bjorn Runaker <bjorn.runaker@ericsson.com>
* Torgny Johansson <torgny.johansson@ericsson.com>
* Jonas Sjöquist <jonas.sjoquist@ericsson.com>
* Dan Williams <dcbw@redhat.com>
* Aleksander Morgado <aleksander@lanedo.com>
*/
#ifndef MM_BROADBAND_BEARER_MBM_H
#define MM_BROADBAND_BEARER_MBM_H
#include <glib.h>
#include <glib-object.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-broadband-bearer.h"
#include "mm-broadband-modem-mbm.h"
#define MM_TYPE_BROADBAND_BEARER_MBM (mm_broadband_bearer_mbm_get_type ())
#define MM_BROADBAND_BEARER_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_MBM, MMBroadbandBearerMbm))
#define MM_BROADBAND_BEARER_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_MBM, MMBroadbandBearerMbmClass))
#define MM_IS_BROADBAND_BEARER_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_MBM))
#define MM_IS_BROADBAND_BEARER_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_MBM))
#define MM_BROADBAND_BEARER_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_MBM, MMBroadbandBearerMbmClass))
typedef struct _MMBroadbandBearerMbm MMBroadbandBearerMbm;
typedef struct _MMBroadbandBearerMbmClass MMBroadbandBearerMbmClass;
typedef struct _MMBroadbandBearerMbmPrivate MMBroadbandBearerMbmPrivate;
struct _MMBroadbandBearerMbm {
MMBroadbandBearer parent;
MMBroadbandBearerMbmPrivate *priv;
};
struct _MMBroadbandBearerMbmClass {
MMBroadbandBearerClass parent;
};
GType mm_broadband_bearer_mbm_get_type (void);
/* Default 3GPP bearer creation implementation */
void mm_broadband_bearer_mbm_new (MMBroadbandModemMbm *modem,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MMBaseBearer *mm_broadband_bearer_mbm_new_finish (GAsyncResult *res,
GError **error);
#endif /* MM_BROADBAND_BEARER_MBM_H */
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2008 - 2010 Ericsson AB
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
*
* Author: Per Hallsmark <per.hallsmark@ericsson.com>
* Bjorn Runaker <bjorn.runaker@ericsson.com>
* Torgny Johansson <torgny.johansson@ericsson.com>
* Jonas Sjöquist <jonas.sjoquist@ericsson.com>
* Dan Williams <dcbw@redhat.com>
* Aleksander Morgado <aleksander@lanedo.com>
*/
#ifndef MM_BROADBAND_MODEM_MBM_H
#define MM_BROADBAND_MODEM_MBM_H
#include "mm-broadband-modem.h"
#define MM_TYPE_BROADBAND_MODEM_MBM (mm_broadband_modem_mbm_get_type ())
#define MM_BROADBAND_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBM, MMBroadbandModemMbm))
#define MM_BROADBAND_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBM, MMBroadbandModemMbmClass))
#define MM_IS_BROADBAND_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBM))
#define MM_IS_BROADBAND_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBM))
#define MM_BROADBAND_MODEM_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBM, MMBroadbandModemMbmClass))
typedef struct _MMBroadbandModemMbm MMBroadbandModemMbm;
typedef struct _MMBroadbandModemMbmClass MMBroadbandModemMbmClass;
typedef struct _MMBroadbandModemMbmPrivate MMBroadbandModemMbmPrivate;
struct _MMBroadbandModemMbm {
MMBroadbandModem parent;
MMBroadbandModemMbmPrivate *priv;
};
struct _MMBroadbandModemMbmClass{
MMBroadbandModemClass parent;
};
GType mm_broadband_modem_mbm_get_type (void);
MMBroadbandModemMbm *mm_broadband_modem_mbm_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_MBM_H */
+337
View File
@@ -0,0 +1,337 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2012 Google, Inc.
* Copyright (C) 2012 - 2013 Aleksander Morgado <aleksander@gnu.org>
* Copyright (C) 2014 Dan Williams <dcbw@redhat.com>
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-mbm.h"
/*****************************************************************************/
/* *E2IPCFG response parser */
static gboolean
validate_address (int family, const char *addr)
{
struct in6_addr tmp6 = IN6ADDR_ANY_INIT;
if (inet_pton (family, addr, (void *) &tmp6) != 1)
{
g_message ("%s: famil '%s'", __func__, addr);
return FALSE;
}
if ((family == AF_INET6) && IN6_IS_ADDR_UNSPECIFIED (&tmp6))
return FALSE;
return TRUE;
}
#define E2IPCFG_TAG "*E2IPCFG"
gboolean
mm_mbm_parse_e2ipcfg_response (const gchar *response,
MMBearerIpConfig **out_ip4_config,
MMBearerIpConfig **out_ip6_config,
GError **error)
{
MMBearerIpConfig **ip_config = NULL;
gboolean got_address = FALSE;
gboolean got_gw = FALSE;
gboolean got_dns = FALSE;
g_autoptr(GRegex) r = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
GError *match_error = NULL;
gchar *dns[3] = { 0 };
guint dns_idx = 0;
int family = AF_INET;
MMBearerIpMethod method = MM_BEARER_IP_METHOD_STATIC;
g_return_val_if_fail (out_ip4_config, FALSE);
g_return_val_if_fail (out_ip6_config, FALSE);
if (!response || !g_str_has_prefix (response, E2IPCFG_TAG)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing " E2IPCFG_TAG " prefix");
return FALSE;
}
response = mm_strip_tag (response, "*E2IPCFG: ");
if (strchr (response, ':')) {
family = AF_INET6;
ip_config = out_ip6_config;
method = MM_BEARER_IP_METHOD_DHCP;
} else if (strchr (response, '.')) {
family = AF_INET;
ip_config = out_ip4_config;
method = MM_BEARER_IP_METHOD_STATIC;
} else {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Failed to detect " E2IPCFG_TAG " address family");
return FALSE;
}
/* *E2IPCFG: (1,<IP>)(2,<gateway>)(3,<DNS>)(3,<DNS>)
*
* *E2IPCFG: (1,"46.157.32.246")(2,"46.157.32.243")(3,"193.213.112.4")(3,"130.67.15.198")
* *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0000:e537:1801")(3,"2001:4600:0004:0fff:0000:0000:0000:0054")(3,"2001:4600:0004:1fff:0000:0000:0000:0054")
* *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0027:b7fe:9401")(3,"fd00:976a:0000:0000:0000:0000:0000:0009")
*/
r = g_regex_new ("\\((\\d),\"([0-9a-fA-F.:]+)\"\\)", 0, 0, NULL);
g_assert (r != NULL);
if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) {
if (match_error) {
g_propagate_error (error, match_error);
g_prefix_error (error, "Could not parse " E2IPCFG_TAG " results: ");
} else {
g_set_error_literal (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't match " E2IPCFG_TAG " reply");
}
return FALSE;
}
*ip_config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (*ip_config, method);
while (g_match_info_matches (match_info)) {
g_autofree gchar *id = NULL;
g_autofree gchar *str = NULL;
id = g_match_info_fetch (match_info, 1);
str = g_match_info_fetch (match_info, 2);
switch (atoi (id)) {
case 1:
if (validate_address (family, str)) {
mm_bearer_ip_config_set_address (*ip_config, str);
mm_bearer_ip_config_set_prefix (*ip_config, (family == AF_INET6) ? 64 : 28);
got_address = TRUE;
}
break;
case 2:
if ((family == AF_INET) && validate_address (family, str)) {
mm_bearer_ip_config_set_gateway (*ip_config, str);
got_gw = TRUE;
}
break;
case 3:
if (validate_address (family, str)) {
dns[dns_idx++] = g_strdup (str);
got_dns = TRUE;
}
break;
default:
break;
}
g_match_info_next (match_info, NULL);
}
if (got_dns) {
mm_bearer_ip_config_set_dns (*ip_config, (const gchar **) dns);
g_free (dns[0]);
g_free (dns[1]);
}
if (!got_address || (family == AF_INET && !got_gw)) {
g_object_unref (*ip_config);
*ip_config = NULL;
g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Got incomplete IP configuration from " E2IPCFG_TAG);
return FALSE;
}
return TRUE;
}
/*****************************************************************************/
#define CFUN_TAG "+CFUN:"
static void
add_supported_mode (guint mode,
gpointer log_object,
guint32 *mask)
{
g_assert (mask);
if (mode >= 32)
mm_obj_warn (log_object, "ignored unexpected mode in +CFUN match: %d", mode);
else
*mask |= (1 << mode);
}
gboolean
mm_mbm_parse_cfun_test (const gchar *response,
gpointer log_object,
guint32 *supported_mask,
GError **error)
{
gchar **groups;
guint32 mask = 0;
g_assert (supported_mask);
if (!response || !g_str_has_prefix (response, CFUN_TAG)) {
g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Missing " CFUN_TAG " prefix");
return FALSE;
}
/*
* AT+CFUN=?
* +CFUN: (0,1,4-6),(0,1)
* OK
*/
/* Strip tag from response */
response = mm_strip_tag (response, CFUN_TAG);
/* Split response in (groups) */
groups = mm_split_string_groups (response);
/* First group is the one listing supported modes */
if (groups && groups[0]) {
gchar **supported_modes;
supported_modes = g_strsplit_set (groups[0], ", ", -1);
if (supported_modes) {
guint i;
for (i = 0; supported_modes[i]; i++) {
gchar *separator;
guint mode;
if (!supported_modes[i][0])
continue;
/* Check if this is a range that's being given to us */
separator = strchr (supported_modes[i], '-');
if (separator) {
gchar *first_str;
gchar *last_str;
guint first;
guint last;
*separator = '\0';
first_str = supported_modes[i];
last_str = separator + 1;
if (!mm_get_uint_from_str (first_str, &first))
mm_obj_warn (log_object, "couldn't match range start: '%s'", first_str);
else if (!mm_get_uint_from_str (last_str, &last))
mm_obj_warn (log_object, "couldn't match range stop: '%s'", last_str);
else if (first >= last)
mm_obj_warn (log_object, "couldn't match range: wrong first '%s' and last '%s' items", first_str, last_str);
else {
for (mode = first; mode <= last; mode++)
add_supported_mode (mode, log_object, &mask);
}
} else {
if (!mm_get_uint_from_str (supported_modes[i], &mode))
mm_obj_warn (log_object, "couldn't match mode: '%s'", supported_modes[i]);
else
add_supported_mode (mode, log_object, &mask);
}
}
g_strfreev (supported_modes);
}
}
g_strfreev (groups);
if (mask)
*supported_mask = mask;
return !!mask;
}
/*****************************************************************************/
/* AT+CFUN? response parsers */
gboolean
mm_mbm_parse_cfun_query_power_state (const gchar *response,
MMModemPowerState *out_state,
GError **error)
{
guint state;
if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
return FALSE;
switch (state) {
case MBM_NETWORK_MODE_OFFLINE:
*out_state = MM_MODEM_POWER_STATE_OFF;
return TRUE;
case MBM_NETWORK_MODE_LOW_POWER:
*out_state = MM_MODEM_POWER_STATE_LOW;
return TRUE;
case MBM_NETWORK_MODE_ANY:
case MBM_NETWORK_MODE_2G:
case MBM_NETWORK_MODE_3G:
*out_state = MM_MODEM_POWER_STATE_ON;
return TRUE;
default:
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Unknown +CFUN pòwer state: '%u'", state);
return FALSE;
}
}
gboolean
mm_mbm_parse_cfun_query_current_modes (const gchar *response,
MMModemMode *allowed,
gint *mbm_mode,
GError **error)
{
guint state;
g_assert (mbm_mode);
g_assert (allowed);
if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
return FALSE;
switch (state) {
case MBM_NETWORK_MODE_OFFLINE:
case MBM_NETWORK_MODE_LOW_POWER:
/* Do not update mbm_mode */
*allowed = MM_MODEM_MODE_NONE;
return TRUE;
case MBM_NETWORK_MODE_2G:
*mbm_mode = MBM_NETWORK_MODE_2G;
*allowed = MM_MODEM_MODE_2G;
return TRUE;
case MBM_NETWORK_MODE_3G:
*mbm_mode = MBM_NETWORK_MODE_3G;
*allowed = MM_MODEM_MODE_3G;
return TRUE;
case MBM_NETWORK_MODE_ANY:
/* Do not update mbm_mode */
*allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
return TRUE;
default:
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Unknown +CFUN current mode: '%u'", state);
return FALSE;
}
}
+51
View File
@@ -0,0 +1,51 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2014 Dan Williams <dcbw@redhat.com>
*/
#ifndef MM_MODEM_HELPERS_MBM_H
#define MM_MODEM_HELPERS_MBM_H
#include "glib.h"
/* *E2IPCFG response parser */
gboolean mm_mbm_parse_e2ipcfg_response (const gchar *response,
MMBearerIpConfig **out_ip4_config,
MMBearerIpConfig **out_ip6_config,
GError **error);
typedef enum {
MBM_NETWORK_MODE_OFFLINE = 0,
MBM_NETWORK_MODE_ANY = 1,
MBM_NETWORK_MODE_LOW_POWER = 4,
MBM_NETWORK_MODE_2G = 5,
MBM_NETWORK_MODE_3G = 6,
} MbmNetworkMode;
/* AT+CFUN=? test parser
* Returns a bitmask, bit index set for the supported modes reported */
gboolean mm_mbm_parse_cfun_test (const gchar *response,
gpointer log_object,
guint32 *supported_mask,
GError **error);
/* AT+CFUN? response parsers */
gboolean mm_mbm_parse_cfun_query_power_state (const gchar *response,
MMModemPowerState *out_state,
GError **error);
gboolean mm_mbm_parse_cfun_query_current_modes (const gchar *response,
MMModemMode *allowed,
gint *mbm_mode,
GError **error);
#endif /* MM_MODEM_HELPERS_MBM_H */
+101
View File
@@ -0,0 +1,101 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2008 Ericsson AB
* Copyright (C) 2012 Lanedo GmbH
*
* Author: Per Hallsmark <per.hallsmark@ericsson.com>
* Author: Aleksander Morgado <aleksander@lanedo.com>
*/
#include <string.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log-object.h"
#include "mm-plugin-mbm.h"
#include "mm-broadband-modem-mbm.h"
#if defined WITH_MBIM
#include "mm-broadband-modem-mbim.h"
#endif
G_DEFINE_TYPE (MMPluginMbm, mm_plugin_mbm, MM_TYPE_PLUGIN)
MM_PLUGIN_DEFINE_MAJOR_VERSION
MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
guint16 subsystem_vendor,
GList *probes,
GError **error)
{
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
mm_obj_dbg (self, "MBIM-powered Ericsson modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
return MM_BASE_MODEM (mm_broadband_modem_mbm_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const gchar *udev_tags[] = {
"ID_MM_ERICSSON_MBM",
NULL
};
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_MBM,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
NULL));
}
static void
mm_plugin_mbm_init (MMPluginMbm *self)
{
}
static void
mm_plugin_mbm_class_init (MMPluginMbmClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+43
View File
@@ -0,0 +1,43 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2008 Ericsson AB
* Copyright (C) 2012 Lanedo GmbH
*
* Author: Per Hallsmark <per.hallsmark@ericsson.com>
*/
#ifndef MM_PLUGIN_MBM_H
#define MM_PLUGIN_MBM_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_MBM (mm_plugin_mbm_get_type ())
#define MM_PLUGIN_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_MBM, MMPluginMbm))
#define MM_PLUGIN_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_MBM, MMPluginMbmClass))
#define MM_IS_PLUGIN_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_MBM))
#define MM_IS_PLUGIN_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_MBM))
#define MM_PLUGIN_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_MBM, MMPluginMbmClass))
typedef struct {
MMPlugin parent;
} MMPluginMbm;
typedef struct {
MMPluginClass parent;
} MMPluginMbmClass;
GType mm_plugin_mbm_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_MBM_H */
+242
View File
@@ -0,0 +1,242 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log-object.h"
#include "mm-base-modem-at.h"
#include "mm-sim-mbm.h"
G_DEFINE_TYPE (MMSimMbm, mm_sim_mbm, MM_TYPE_BASE_SIM)
/*****************************************************************************/
/* SEND PIN/PUK (Generic implementation) */
typedef struct {
MMBaseModem *modem;
guint retries;
} SendPinPukContext;
static void
send_pin_puk_context_free (SendPinPukContext *ctx)
{
g_object_unref (ctx->modem);
g_slice_free (SendPinPukContext, ctx);
}
static gboolean
common_send_pin_puk_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void wait_for_unlocked_status (GTask *task);
static void
cpin_query_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
const gchar *result;
result = mm_base_modem_at_command_finish (modem, res, NULL);
if (result && strstr (result, "READY")) {
/* All done! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* Need to recheck */
wait_for_unlocked_status (task);
}
static gboolean
cpin_query_cb (GTask *task)
{
SendPinPukContext *ctx;
ctx = g_task_get_task_data (task);
mm_base_modem_at_command (ctx->modem,
"+CPIN?",
20,
FALSE,
(GAsyncReadyCallback)cpin_query_ready,
task);
return G_SOURCE_REMOVE;
}
static void
wait_for_unlocked_status (GTask *task)
{
MMSimMbm *self;
SendPinPukContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
/* Oops... :/ */
if (ctx->retries == 0) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"PIN was sent but modem didn't report unlocked");
g_object_unref (task);
return;
}
/* Check status */
ctx->retries--;
mm_obj_dbg (self, "scheduling lock state check...");
g_timeout_add_seconds (1, (GSourceFunc)cpin_query_cb, task);
}
static void
send_pin_puk_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
SendPinPukContext *ctx;
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* No explicit error sending the PIN/PUK, now check status until we have the
* expected lock status */
ctx = g_task_get_task_data (task);
ctx->retries = 3;
wait_for_unlocked_status (task);
}
static void
common_send_pin_puk (MMBaseSim *self,
const gchar *pin,
const gchar *puk,
GAsyncReadyCallback callback,
gpointer user_data)
{
SendPinPukContext *ctx;
GTask *task;
gchar *command;
ctx = g_slice_new (SendPinPukContext);
g_object_get (self,
MM_BASE_SIM_MODEM, &ctx->modem,
NULL);
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)send_pin_puk_context_free);
command = (puk ?
g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin) :
g_strdup_printf ("+CPIN=\"%s\"", pin));
mm_base_modem_at_command (ctx->modem,
command,
3,
FALSE,
(GAsyncReadyCallback)send_pin_puk_ready,
task);
g_free (command);
}
static void
send_puk (MMBaseSim *self,
const gchar *puk,
const gchar *new_pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
common_send_pin_puk (self, new_pin, puk, callback, user_data);
}
static void
send_pin (MMBaseSim *self,
const gchar *pin,
GAsyncReadyCallback callback,
gpointer user_data)
{
common_send_pin_puk (self, pin, NULL, callback, user_data);
}
/*****************************************************************************/
MMBaseSim *
mm_sim_mbm_new_finish (GAsyncResult *res,
GError **error)
{
GObject *source;
GObject *sim;
source = g_async_result_get_source_object (res);
sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
g_object_unref (source);
if (!sim)
return NULL;
/* Only export valid SIMs */
mm_base_sim_export (MM_BASE_SIM (sim));
return MM_BASE_SIM (sim);
}
void
mm_sim_mbm_new (MMBaseModem *modem,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (MM_TYPE_SIM_MBM,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
"active", TRUE, /* by default always active */
NULL);
}
static void
mm_sim_mbm_init (MMSimMbm *self)
{
}
static void
mm_sim_mbm_class_init (MMSimMbmClass *klass)
{
MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass);
base_sim_class->send_pin = send_pin;
base_sim_class->send_pin_finish = common_send_pin_puk_finish;
base_sim_class->send_puk = send_puk;
base_sim_class->send_puk_finish = common_send_pin_puk_finish;
}
+51
View File
@@ -0,0 +1,51 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
*/
#ifndef MM_SIM_MBM_H
#define MM_SIM_MBM_H
#include <glib.h>
#include <glib-object.h>
#include "mm-base-sim.h"
#define MM_TYPE_SIM_MBM (mm_sim_mbm_get_type ())
#define MM_SIM_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_MBM, MMSimMbm))
#define MM_SIM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_MBM, MMSimMbmClass))
#define MM_IS_SIM_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_MBM))
#define MM_IS_SIM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_MBM))
#define MM_SIM_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_MBM, MMSimMbmClass))
typedef struct _MMSimMbm MMSimMbm;
typedef struct _MMSimMbmClass MMSimMbmClass;
struct _MMSimMbm {
MMBaseSim parent;
};
struct _MMSimMbmClass {
MMBaseSimClass parent;
};
GType mm_sim_mbm_get_type (void);
void mm_sim_mbm_new (MMBaseModem *modem,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MMBaseSim *mm_sim_mbm_new_finish (GAsyncResult *res,
GError **error);
#endif /* MM_SIM_MBM_H */
@@ -0,0 +1,268 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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:
*
* Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
* Copyright (C) 2014 Dan Williams <dcbw@redhat.com>
*/
#include <glib.h>
#include <glib-object.h>
#include <locale.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log-test.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-mbm.h"
/*****************************************************************************/
/* Test *E2IPCFG responses */
typedef struct {
const gchar *str;
/* IPv4 */
const gchar *ipv4_addr;
const gchar *ipv4_gw;
const gchar *ipv4_dns1;
const gchar *ipv4_dns2;
/* IPv6 */
const gchar *ipv6_addr;
const gchar *ipv6_dns1;
const gchar *ipv6_dns2;
} E2ipcfgTest;
static const E2ipcfgTest tests[] = {
{ "*E2IPCFG: (1,\"46.157.32.246\")(2,\"46.157.32.243\")(3,\"193.213.112.4\")(3,\"130.67.15.198\")\r\n",
"46.157.32.246", "46.157.32.243", "193.213.112.4", "130.67.15.198",
NULL, NULL },
{ "*E2IPCFG: (1,\"fe80:0000:0000:0000:0000:0000:e537:1801\")(3,\"2001:4600:0004:0fff:0000:0000:0000:0054\")(3,\"2001:4600:0004:1fff:0000:0000:0000:0054\")\r\n",
NULL, NULL, NULL, NULL,
"fe80:0000:0000:0000:0000:0000:e537:1801", "2001:4600:0004:0fff:0000:0000:0000:0054", "2001:4600:0004:1fff:0000:0000:0000:0054" },
{ "*E2IPCFG: (1,\"fe80:0000:0000:0000:0000:0027:b7fe:9401\")(3,\"fd00:976a:0000:0000:0000:0000:0000:0009\")\r\n",
NULL, NULL, NULL, NULL,
"fe80:0000:0000:0000:0000:0027:b7fe:9401", "fd00:976a:0000:0000:0000:0000:0000:0009", NULL },
{ NULL }
};
static void
test_e2ipcfg (void)
{
guint i;
for (i = 0; tests[i].str; i++) {
gboolean success;
GError *error = NULL;
MMBearerIpConfig *ipv4 = NULL;
MMBearerIpConfig *ipv6 = NULL;
const gchar **dns;
guint dnslen;
success = mm_mbm_parse_e2ipcfg_response (tests[i].str, &ipv4, &ipv6, &error);
g_assert_no_error (error);
g_assert (success);
/* IPv4 */
if (tests[i].ipv4_addr) {
g_assert (ipv4);
g_assert_cmpint (mm_bearer_ip_config_get_method (ipv4), ==, MM_BEARER_IP_METHOD_STATIC);
g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv4), ==, tests[i].ipv4_addr);
g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv4), ==, 28);
g_assert_cmpstr (mm_bearer_ip_config_get_gateway (ipv4), ==, tests[i].ipv4_gw);
dns = mm_bearer_ip_config_get_dns (ipv4);
g_assert (dns);
dnslen = g_strv_length ((gchar **) dns);
if (tests[i].ipv4_dns2 != NULL)
g_assert_cmpint (dnslen, ==, 2);
else
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, tests[i].ipv4_dns1);
g_assert_cmpstr (dns[1], ==, tests[i].ipv4_dns2);
g_object_unref (ipv4);
} else
g_assert (ipv4 == NULL);
/* IPv6 */
if (tests[i].ipv6_addr) {
struct in6_addr a6;
g_assert (ipv6);
g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv6), ==, tests[i].ipv6_addr);
g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv6), ==, 64);
g_assert (inet_pton (AF_INET6, mm_bearer_ip_config_get_address (ipv6), &a6));
if (IN6_IS_ADDR_LINKLOCAL (&a6))
g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP);
else
g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_STATIC);
dns = mm_bearer_ip_config_get_dns (ipv6);
g_assert (dns);
dnslen = g_strv_length ((gchar **) dns);
if (tests[i].ipv6_dns2 != NULL)
g_assert_cmpint (dnslen, ==, 2);
else
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, tests[i].ipv6_dns1);
g_assert_cmpstr (dns[1], ==, tests[i].ipv6_dns2);
g_object_unref (ipv6);
} else
g_assert (ipv6 == NULL);
}
}
/*****************************************************************************/
/* Test +CFUN test responses */
#define MAX_MODES 32
typedef struct {
const gchar *str;
guint32 expected_mask;
} CfunTest;
static const CfunTest cfun_tests[] = {
{
"+CFUN: (0,1,4-6),(1-0)\r\n",
((1 << MBM_NETWORK_MODE_OFFLINE) |
(1 << MBM_NETWORK_MODE_ANY) |
(1 << MBM_NETWORK_MODE_LOW_POWER) |
(1 << MBM_NETWORK_MODE_2G) |
(1 << MBM_NETWORK_MODE_3G))
},
{
"+CFUN: (0,1,4-6)\r\n",
((1 << MBM_NETWORK_MODE_OFFLINE) |
(1 << MBM_NETWORK_MODE_ANY) |
(1 << MBM_NETWORK_MODE_LOW_POWER) |
(1 << MBM_NETWORK_MODE_2G) |
(1 << MBM_NETWORK_MODE_3G))
},
{
"+CFUN: (0,1,4)\r\n",
((1 << MBM_NETWORK_MODE_OFFLINE) |
(1 << MBM_NETWORK_MODE_ANY) |
(1 << MBM_NETWORK_MODE_LOW_POWER))
},
{
"+CFUN: (0,1)\r\n",
((1 << MBM_NETWORK_MODE_OFFLINE) |
(1 << MBM_NETWORK_MODE_ANY))
},
};
static void
test_cfun_test (void)
{
guint i;
for (i = 0; i < G_N_ELEMENTS (cfun_tests); i++) {
guint32 mask;
gboolean success;
GError *error = NULL;
success = mm_mbm_parse_cfun_test (cfun_tests[i].str, NULL, &mask, &error);
g_assert_no_error (error);
g_assert (success);
g_assert_cmpuint (mask, ==, cfun_tests[i].expected_mask);
}
}
/*****************************************************************************/
typedef struct {
const gchar *str;
MMModemPowerState state;
} CfunQueryPowerStateTest;
static const CfunQueryPowerStateTest cfun_query_power_state_tests[] = {
{ "+CFUN: 0", MM_MODEM_POWER_STATE_OFF },
{ "+CFUN: 1", MM_MODEM_POWER_STATE_ON },
{ "+CFUN: 4", MM_MODEM_POWER_STATE_LOW },
{ "+CFUN: 5", MM_MODEM_POWER_STATE_ON },
{ "+CFUN: 6", MM_MODEM_POWER_STATE_ON },
};
static void
test_cfun_query_power_state (void)
{
guint i;
for (i = 0; i < G_N_ELEMENTS (cfun_query_power_state_tests); i++) {
GError *error = NULL;
gboolean success;
MMModemPowerState state;
success = mm_mbm_parse_cfun_query_power_state (cfun_query_power_state_tests[i].str, &state, &error);
g_assert_no_error (error);
g_assert (success);
g_assert_cmpuint (cfun_query_power_state_tests[i].state, ==, state);
}
}
typedef struct {
const gchar *str;
MMModemMode allowed;
gint mbm_mode;
} CfunQueryCurrentModeTest;
static const CfunQueryCurrentModeTest cfun_query_current_mode_tests[] = {
{ "+CFUN: 0", MM_MODEM_MODE_NONE, -1 },
{ "+CFUN: 1", MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, -1 },
{ "+CFUN: 4", MM_MODEM_MODE_NONE, -1 },
{ "+CFUN: 5", MM_MODEM_MODE_2G, MBM_NETWORK_MODE_2G },
{ "+CFUN: 6", MM_MODEM_MODE_3G, MBM_NETWORK_MODE_3G },
};
static void
test_cfun_query_current_modes (void)
{
guint i;
for (i = 0; i < G_N_ELEMENTS (cfun_query_current_mode_tests); i++) {
GError *error = NULL;
gboolean success;
MMModemMode allowed = MM_MODEM_MODE_NONE;
gint mbm_mode = -1;
success = mm_mbm_parse_cfun_query_current_modes (cfun_query_current_mode_tests[i].str, &allowed, &mbm_mode, &error);
g_assert_no_error (error);
g_assert (success);
g_assert_cmpuint (cfun_query_current_mode_tests[i].allowed, ==, allowed);
g_assert_cmpint (cfun_query_current_mode_tests[i].mbm_mode, ==, mbm_mode);
}
}
/*****************************************************************************/
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/mbm/e2ipcfg", test_e2ipcfg);
g_test_add_func ("/MM/mbm/cfun/test", test_cfun_test);
g_test_add_func ("/MM/mbm/cfun/query/power-state", test_cfun_query_power_state);
g_test_add_func ("/MM/mbm/cfun/query/current-modes", test_cfun_query_current_modes);
return g_test_run ();
}