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
+160
View File
@@ -0,0 +1,160 @@
The following list shows the relationship among the different plugins provided
by default by ModemManager. For each of the plugin types, the list of modem
objects created by the plugin is given.
* Altair:
** MMBroadbandModemAltairLte
* Anydata:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemAnydata
* Cinterion:
** MMBroadbandModemQmiCinterion
** MMBroadbandModemCinterion
* Dell:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemFoxconnT77w968 (from the Foxconn utils)
** MMBroadbandModemMbimXmm (from the XMM utils)
** MMBroadbandModemMbim (generic)
** MMBroadbandModemNovatel (from the Novatel utils)
** MMBroadbandModemSierra (from the Sierra Legacy utils)
** MMBroadbandModemTelit (from the Telit utils)
** MMBroadbandModemXmm (from the XMM utils)
** MMBroadbandModem (generic)
* D-Link:
** MMBroadbandModemQmi (generic)
** MMBroadbandModem (generic)
* Fibocom:
** MMBroadbandModemMbimXmm (from the XMM utils)
** MMBroadbandModemMbim (generic)
** MMBroadbandModemXmm (from the XMM utils)
** MMBroadbandModem (generic)
* Foxconn:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemFoxconnT77w968
** MMBroadbandModemMbim (generic)
** MMBroadbandModem (generic)
* Generic:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemMbim (generic)
** MMBroadbandModem (generic)
* Gosuncn:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemMbim (generic)
** MMBroadbandModem (generic)
* Haier:
** MMBroadbandModem (generic)
* Huawei:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemMbim (generic)
** MMBroadbandModemHuawei
* Icera (no explicit plugin):
** MMBroadbandModemIcera
* Iridium:
** MMBroadbandModemIridium
* Linktop:
** MMBroadbandModemLinktop
* Longcheer:
** MMBroadbandModemLongcheer
* MBM:
** MMBroadbandModemMbim (generic)
** MMBroadbandModemMbm
* Motorola:
** MMBroadbandModemMotorola
* Mtk:
** MMBroadbandModemMtk
* Nokia:
** MMBroadbandModemNokia
* Nokia Icera:
** MMBroadbandModemIcera (from the Icera utils)
* Novatel:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemNovatel
* Novatel LTE:
** MMBroadbandModemNovatelLte
* Option:
** MMBroadbandModemOption
* Option HSO:
** MMBroadbandModemHso
* Pantech:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemPantech
* Quectel:
** MMBroadbandModemQmiQuectel
** MMBroadbandModemQuectel
* Samsung:
** MMBroadbandModemSamsung (subclassed from the Icera utils)
* Sierra Legacy:
** MMBroadbandModemSierraIcera (subclassed from the Icera utils)
** MMBroadbandModemSierra
* Sierra:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemMbim (generic)
** MMBroadbandModem (generic)
* Simtech:
** MMBroadbandModemQmiSimtech
** MMBroadbandModemSimtech
* Telit:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemMbimTelit
** MMBroadbandModemTelit
* Thuraya
** MMBroadbandModemThuraya
* TP-Link:
** MMBroadbandModemQmi (generic)
** MMBroadbandModem (generic)
* u-blox:
** MMBroadbandModemUblox
* via:
** MMBroadbandModemVia
* wavecom:
** MMBroadbandModemWavecom
* x22x:
** MMBroadbandModemQmi (generic)
** MMBroadbandModemX22x
* XMM (no explicit plugin):
** MMBroadbandModemMbimXmm
** MMBroadbandModemXmm
* ZTE
** MMBroadbandModemQmi (generic)
** MMBroadbandModemMbim (generic)
** MMBroadbandModemZteIcera (subclassed from the Icera utils)
** MMBroadbandModemZte
@@ -0,0 +1,370 @@
/* -*- 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 Altair Semiconductor
*
* Author: Ori Inbar <ori.inbar@altair-semi.com>
*/
#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-base-modem-at.h"
#include "mm-broadband-bearer-altair-lte.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-log.h"
#include "mm-modem-helpers.h"
#define CONNECTION_CHECK_TIMEOUT_SEC 5
#define STATCM_TAG "%STATCM:"
G_DEFINE_TYPE (MMBroadbandBearerAltairLte, mm_broadband_bearer_altair_lte, MM_TYPE_BROADBAND_BEARER);
/*****************************************************************************/
/* 3GPP Connect sequence */
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
} DetailedConnectContext;
static void
detailed_connect_context_free (DetailedConnectContext *ctx)
{
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
g_free (ctx);
}
static MMBearerConnectResult *
connect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
connect_3gpp_connect_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
DetailedConnectContext *ctx;
const gchar *result;
GError *error = NULL;
MMBearerIpConfig *config;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx = g_task_get_task_data (task);
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
/* Set operation result */
g_task_return_pointer (
task,
mm_bearer_connect_result_new (ctx->data, config, config),
(GDestroyNotify)mm_bearer_connect_result_unref);
g_object_unref (task);
g_object_unref (config);
}
static void
connect_3gpp_apnsettings_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
DetailedConnectContext *ctx;
const gchar *result;
GError *error = NULL;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result) {
g_prefix_error (&error, "setting APN failed: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx = g_task_get_task_data (task);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"%DPDNACT=1",
MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
g_task_get_cancellable (task),
(GAsyncReadyCallback)connect_3gpp_connect_ready,
task); /* user_data */
}
static void
connect_3gpp (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
DetailedConnectContext *ctx;
gchar *command, *apn;
MMBearerProperties *config;
MMModem3gppRegistrationState registration_state;
MMPort *data;
GTask *task;
/* There is a known firmware bug that can leave the modem unusable if a
* connect attempt is made when out of coverage. So, fail without trying.
*/
g_object_get (modem,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &registration_state,
NULL);
if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) {
g_task_report_new_error (self,
callback,
user_data,
connect_3gpp,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK,
"Out of coverage, can't connect.");
return;
}
/* Don't allow a connect while we detach from the network to process SIM
* refresh.
* */
if (mm_broadband_modem_altair_lte_is_sim_refresh_detach_in_progress (modem)) {
mm_obj_dbg (self, "detached from network to process SIM refresh, failing connect request");
g_task_report_new_error (self,
callback,
user_data,
connect_3gpp,
MM_CORE_ERROR,
MM_CORE_ERROR_RETRY,
"Detached from network to process SIM refresh, can't connect.");
return;
}
data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
if (!data) {
g_task_report_new_error (self,
callback,
user_data,
connect_3gpp,
MM_CORE_ERROR,
MM_CORE_ERROR_CONNECTED,
"Couldn't connect: no available net port available");
return;
}
ctx = g_new0 (DetailedConnectContext, 1);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->data = g_object_ref (data);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free);
config = mm_base_bearer_peek_config (MM_BASE_BEARER (self));
apn = mm_port_serial_at_quote_string (mm_bearer_properties_get_apn (config));
command = g_strdup_printf ("%%APNN=%s", apn);
g_free (apn);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
10, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
cancellable,
(GAsyncReadyCallback)connect_3gpp_apnsettings_ready,
task); /* user_data */
g_free (command);
}
/*****************************************************************************/
/* 3GPP Disconnect sequence */
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
} DetailedDisconnectContext;
static gboolean
disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
detailed_disconnect_context_free (DetailedDisconnectContext *ctx)
{
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
g_free (ctx);
}
static void
disconnect_3gpp_check_status (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
const gchar *result;
GError *error = NULL;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
disconnect_3gpp (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
GAsyncReadyCallback callback,
gpointer user_data)
{
DetailedDisconnectContext *ctx;
MMModem3gppRegistrationState registration_state;
GTask *task;
/* There is a known firmware bug that can leave the modem unusable if a
* disconnect attempt is made when out of coverage. So, fail without trying.
*/
g_object_get (modem,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &registration_state,
NULL);
if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) {
g_task_report_new_error (self,
callback,
user_data,
disconnect_3gpp,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK,
"Out of coverage, can't disconnect.");
return;
}
ctx = g_new0 (DetailedDisconnectContext, 1);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->data = g_object_ref (data);
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"%DPDNACT=0",
MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_3gpp_check_status,
task); /* user_data */
}
/*****************************************************************************/
MMBaseBearer *
mm_broadband_bearer_altair_lte_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_altair_lte_new (MMBroadbandModemAltairLte *modem,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (
MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_BEARER_MODEM, modem,
MM_BASE_BEARER_CONFIG, config,
NULL);
}
static void
mm_broadband_bearer_altair_lte_init (MMBroadbandBearerAltairLte *self)
{
}
static void
mm_broadband_bearer_altair_lte_class_init (MMBroadbandBearerAltairLteClass *klass)
{
MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
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->connect_3gpp = connect_3gpp;
broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
}
@@ -0,0 +1,59 @@
/* -*- 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 Altair Semiconductor
*
* Author: Ori Inbar <ori.inbar@altair-semi.com>
*/
#ifndef MM_BROADBAND_BEARER_ALTAIR_LTE_H
#define MM_BROADBAND_BEARER_ALTAIR_LTE_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-altair-lte.h"
#define MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE (mm_broadband_bearer_altair_lte_get_type ())
#define MM_BROADBAND_BEARER_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE, MMBroadbandBearerAltairLte))
#define MM_BROADBAND_BEARER_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE, MMBroadbandBearerAltairLteClass))
#define MM_IS_BROADBAND_BEARER_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE))
#define MM_IS_BROADBAND_BEARER_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE))
#define MM_BROADBAND_BEARER_ALTAIR_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_ALTAIR_LTE, MMBroadbandBearerAltairLteClass))
typedef struct _MMBroadbandBearerAltairLte MMBroadbandBearerAltairLte;
typedef struct _MMBroadbandBearerAltairLteClass MMBroadbandBearerAltairLteClass;
struct _MMBroadbandBearerAltairLte {
MMBroadbandBearer parent;
};
struct _MMBroadbandBearerAltairLteClass {
MMBroadbandBearerClass parent;
};
GType mm_broadband_bearer_altair_lte_get_type (void);
/* Default 3GPP bearer creation implementation */
void mm_broadband_bearer_altair_lte_new (MMBroadbandModemAltairLte *modem,
MMBearerProperties *properties,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MMBaseBearer *mm_broadband_bearer_altair_lte_new_finish (GAsyncResult *res,
GError **error);
#endif /* MM_BROADBAND_BEARER_ALTAIR_LTE_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,53 @@
/* -*- 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 Altair Semiconductor
*
* Author: Ori Inbar <ori.inbar@altair-semi.com>
*/
#ifndef MM_BROADBAND_MODEM_ALTAIR_LTE_H
#define MM_BROADBAND_MODEM_ALTAIR_LTE_H
#include "mm-broadband-modem.h"
#define MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE (mm_broadband_modem_altair_lte_get_type ())
#define MM_BROADBAND_MODEM_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE, MMBroadbandModemAltairLte))
#define MM_BROADBAND_MODEM_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE, MMBroadbandModemAltairLteClass))
#define MM_IS_BROADBAND_MODEM_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE))
#define MM_IS_BROADBAND_MODEM_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE))
#define MM_BROADBAND_MODEM_ALTAIR_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_ALTAIR_LTE, MMBroadbandModemAltairLteClass))
typedef struct _MMBroadbandModemAltairLte MMBroadbandModemAltairLte;
typedef struct _MMBroadbandModemAltairLteClass MMBroadbandModemAltairLteClass;
typedef struct _MMBroadbandModemAltairLtePrivate MMBroadbandModemAltairLtePrivate;
struct _MMBroadbandModemAltairLte {
MMBroadbandModem parent;
MMBroadbandModemAltairLtePrivate *priv;
};
struct _MMBroadbandModemAltairLteClass{
MMBroadbandModemClass parent;
};
GType mm_broadband_modem_altair_lte_get_type (void);
MMBroadbandModemAltairLte *mm_broadband_modem_altair_lte_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
gboolean mm_broadband_modem_altair_lte_is_sim_refresh_detach_in_progress (MMBroadbandModem *self);
#endif /* MM_BROADBAND_MODEM_ALTAIR_LTE_H */
@@ -0,0 +1,259 @@
/* -*- 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 Google Inc.
*
*/
#include <stdlib.h>
#include <string.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-modem-helpers-altair-lte.h"
#define MM_ALTAIR_IMS_PDN_CID 1
#define MM_ALTAIR_INTERNET_PDN_CID 3
/*****************************************************************************/
/* Bands response parser */
GArray *
mm_altair_parse_bands_response (const gchar *response)
{
gchar **split;
GArray *bands;
guint i;
/*
* Response is "<band>[,<band>...]"
*/
split = g_strsplit_set (response, ",", -1);
if (!split)
return NULL;
bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), g_strv_length (split));
for (i = 0; split[i]; i++) {
guint32 band_value;
MMModemBand band;
band_value = (guint32)strtoul (split[i], NULL, 10);
band = MM_MODEM_BAND_EUTRAN_1 - 1 + band_value;
/* Due to a firmware issue, the modem may incorrectly includes 0 in the
* bands response. We thus ignore any band value outside the range of
* E-UTRAN operating bands. */
if (band >= MM_MODEM_BAND_EUTRAN_1 && band <= MM_MODEM_BAND_EUTRAN_44)
g_array_append_val (bands, band);
}
g_strfreev (split);
return bands;
}
/*****************************************************************************/
/* +CEER response parser */
gchar *
mm_altair_parse_ceer_response (const gchar *response,
GError **error)
{
g_autoptr(GRegex) r = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
gchar *ceer_response = NULL;
/* First accept an empty response as the no error case. Sometimes, the only
* response to the AT+CEER query is an OK.
*/
if (g_strcmp0 ("", response) == 0) {
return g_strdup ("");
}
/* The response we are interested in looks so:
* +CEER: EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED
*/
r = g_regex_new ("\\+CEER:\\s*(\\w*)?",
G_REGEX_RAW,
0, NULL);
g_assert (r != NULL);
if (!g_regex_match (r, response, 0, &match_info)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse +CEER response");
return NULL;
}
if (g_match_info_matches (match_info)) {
ceer_response = mm_get_string_unquoted_from_match_info (match_info, 1);
if (!ceer_response)
ceer_response = g_strdup ("");
}
return ceer_response;
}
/*****************************************************************************/
/* %CGINFO="cid",1 response parser */
gint
mm_altair_parse_cid (const gchar *response, GError **error)
{
g_autoptr(GRegex) regex = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
guint cid = -1;
regex = g_regex_new ("\\%CGINFO:\\s*(\\d+)", G_REGEX_RAW, 0, NULL);
g_assert (regex);
if (!g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, error))
return -1;
if (!mm_get_uint_from_match_info (match_info, 1, &cid))
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Failed to parse %%CGINFO=\"cid\",1 response");
return cid;
}
/*****************************************************************************/
/* %PCOINFO response parser */
MMPco *
mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error)
{
g_autoptr(GRegex) regex = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
MMPco *pco = NULL;
gint num_matches;
if (!pco_info || !pco_info[0]) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"No PCO info given");
return NULL;
}
/* Expected %PCOINFO response:
*
* Solicited response: %PCOINFO:<mode>,<cid>[,<pcoid>[,<payload>]]
* Unsolicited response: %PCOINFO:<cid>,<pcoid>[,<payload>]
*/
regex = g_regex_new ("\\%PCOINFO:(?:\\s*\\d+\\s*,)?(\\d+)\\s*(,([^,\\)]*),([0-9A-Fa-f]*))?",
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
0, NULL);
g_assert (regex);
if (!g_regex_match_full (regex, pco_info, strlen (pco_info), 0, 0, &match_info, error))
return NULL;
num_matches = g_match_info_get_match_count (match_info);
if (num_matches != 5) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Failed to parse substrings, number of matches: %d",
num_matches);
return NULL;
}
while (g_match_info_matches (match_info)) {
guint pco_cid;
g_autofree gchar *pco_id = NULL;
g_autofree gchar *pco_payload = NULL;
g_autofree guint8 *pco_payload_bytes = NULL;
gsize pco_payload_bytes_len;
guint8 pco_prefix[6];
GByteArray *pco_raw;
gsize pco_raw_len;
if (!mm_get_uint_from_match_info (match_info, 1, &pco_cid)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse CID from PCO info: '%s'", pco_info);
break;
}
/* We are only interested in IMS and Internet PDN PCO. */
if (pco_cid != MM_ALTAIR_IMS_PDN_CID && pco_cid != MM_ALTAIR_INTERNET_PDN_CID) {
g_match_info_next (match_info, error);
continue;
}
pco_id = mm_get_string_unquoted_from_match_info (match_info, 3);
if (!pco_id) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse PCO ID from PCO info: '%s'", pco_info);
break;
}
if (g_strcmp0 (pco_id, "FF00")) {
g_match_info_next (match_info, error);
continue;
}
pco_payload = mm_get_string_unquoted_from_match_info (match_info, 4);
if (!pco_payload) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse PCO payload from PCO info: '%s'", pco_info);
break;
}
pco_payload_bytes = mm_utils_hexstr2bin (pco_payload, -1, &pco_payload_bytes_len, error);
if (!pco_payload_bytes) {
g_prefix_error (error, "Invalid PCO payload from PCO info '%s': ", pco_info);
break;
}
/* Protocol Configuration Options (PCO) is an information element with an
* identifier (IEI) 0x27 and contains between 3 and 253 octets. See 3GPP TS
* 24.008 for more details on PCO.
*
* NOTE: The standard uses one-based indexing, but to better correlate to the
* code, zero-based indexing is used in the description hereinafter.
*
* Octet | Value
* --------+--------------------------------------------
* 0 | PCO IEI (= 0x27)
* 1 | Length of PCO contents (= total length - 2)
* 2 | bit 7 : ext
* | bit 6 to 3 : spare (= 0b0000)
* | bit 2 to 0 : Configuration protocol
* 3 to 4 | Element 1 ID
* 5 | Length of element 1 contents
* 6 to m | Element 1 contents
* ... |
*/
pco_raw_len = sizeof (pco_prefix) + pco_payload_bytes_len;
pco_prefix[0] = 0x27;
pco_prefix[1] = pco_raw_len - 2;
pco_prefix[2] = 0x80;
/* Verizon uses element ID 0xFF00 for carrier-specific PCO content. */
pco_prefix[3] = 0xFF;
pco_prefix[4] = 0x00;
pco_prefix[5] = pco_payload_bytes_len;
pco_raw = g_byte_array_sized_new (pco_raw_len);
g_byte_array_append (pco_raw, pco_prefix, sizeof (pco_prefix));
g_byte_array_append (pco_raw, pco_payload_bytes, pco_payload_bytes_len);
pco = mm_pco_new ();
mm_pco_set_session_id (pco, pco_cid);
mm_pco_set_complete (pco, TRUE);
mm_pco_set_data (pco, pco_raw->data, pco_raw->len);
break;
}
return pco;
}
@@ -0,0 +1,38 @@
/* -*- 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 Google Inc.
*
*/
#ifndef MM_MODEM_HELPERS_ALTAIR_H
#define MM_MODEM_HELPERS_ALTAIR_H
#include <glib.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
/* Bands response parser */
GArray *mm_altair_parse_bands_response (const gchar *response);
/* +CEER response parser */
gchar *mm_altair_parse_ceer_response (const gchar *response,
GError **error);
/* %CGINFO="cid",1 response parser */
gint mm_altair_parse_cid (const gchar *response, GError **error);
/* %PCOINFO response parser */
MMPco *mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error);
#endif /* MM_MODEM_HELPERS_ALTAIR_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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2013 Altair Semiconductor
*
* Author: Ori Inbar <ori.inbar@altair-semi.com>
*/
#include <string.h>
#include <gmodule.h>
#include "mm-plugin-altair-lte.h"
#include "mm-private-boxed-types.h"
#include "mm-broadband-modem-altair-lte.h"
#include "mm-log.h"
G_DEFINE_TYPE (MMPluginAltairLte, mm_plugin_altair_lte, MM_TYPE_PLUGIN)
MM_PLUGIN_DEFINE_MAJOR_VERSION
MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom commands for AT probing */
/* Increase the response timeout for probe commands since some altair modems
take longer to respond after a reset.
*/
static const MMPortProbeAtCommand custom_at_probe[] = {
{ "AT", 7, mm_port_probe_response_processor_is_at },
{ "AT", 7, mm_port_probe_response_processor_is_at },
{ "AT", 7, mm_port_probe_response_processor_is_at },
{ NULL }
};
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
guint16 subsystem_vendor,
GList *probes,
GError **error)
{
return MM_BASE_MODEM (mm_broadband_modem_altair_lte_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", "net", NULL };
static const mm_uint16_pair products[] = {
{ 0x216f, 0x0047 }, /* Altair NPe */
{ 0, 0 }
};
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_ALTAIR_LTE,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_PRODUCT_IDS, products,
MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe,
MM_PLUGIN_ALLOWED_SINGLE_AT, TRUE,
MM_PLUGIN_SEND_LF, TRUE,
NULL));
}
static void
mm_plugin_altair_lte_init (MMPluginAltairLte *self)
{
}
static void
mm_plugin_altair_lte_class_init (MMPluginAltairLteClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+48
View File
@@ -0,0 +1,48 @@
/* -*- 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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2013 Altair Semiconductor
*
* Author: Ori Inbar <ori.inbar@altair-semi.com>
*/
#ifndef MM_PLUGIN_ALTAIR_LTE_H
#define MM_PLUGIN_ALTAIR_LTE_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_ALTAIR_LTE (mm_plugin_altair_lte_get_type ())
#define MM_PLUGIN_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_ALTAIR_LTE, MMPluginAltairLte))
#define MM_PLUGIN_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_ALTAIR_LTE, MMPluginAltairLteClass))
#define MM_IS_PLUGIN_ALTAIR_LTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_ALTAIR_LTE))
#define MM_IS_PLUGIN_ALTAIR_LTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_ALTAIR_LTE))
#define MM_PLUGIN_ALTAIR_LTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_ALTAIR_LTE, MMPluginAltairLteClass))
typedef struct {
MMPlugin parent;
} MMPluginAltairLte;
typedef struct {
MMPluginClass parent;
} MMPluginAltairLteClass;
GType mm_plugin_altair_lte_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_ALTAIR_LTE_H */
@@ -0,0 +1,189 @@
/* -*- 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 Google Inc.
*
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <glib-object.h>
#include <locale.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-modem-helpers-altair-lte.h"
/*****************************************************************************/
/* Test bands response parsing */
static void
test_parse_bands (void)
{
GArray *bands;
bands = mm_altair_parse_bands_response ("");
g_assert (bands != NULL);
g_assert_cmpuint (bands->len, ==, 0);
g_array_free (bands, TRUE);
/* 0 and 45 are outside the range of E-UTRAN operating bands and should be ignored. */
bands = mm_altair_parse_bands_response ("0, 0, 1, 4,13,44,45");
g_assert (bands != NULL);
g_assert_cmpuint (bands->len, ==, 4);
g_assert_cmpuint (g_array_index (bands, MMModemBand, 0), ==, MM_MODEM_BAND_EUTRAN_1);
g_assert_cmpuint (g_array_index (bands, MMModemBand, 1), ==, MM_MODEM_BAND_EUTRAN_4);
g_assert_cmpuint (g_array_index (bands, MMModemBand, 2), ==, MM_MODEM_BAND_EUTRAN_13);
g_assert_cmpuint (g_array_index (bands, MMModemBand, 3), ==, MM_MODEM_BAND_EUTRAN_44);
g_array_free (bands, TRUE);
}
/*****************************************************************************/
/* Test +CEER responses */
typedef struct {
const gchar *str;
const gchar *result;
} CeerTest;
static const CeerTest ceer_tests[] = {
{ "", "" }, /* Special case, sometimes the response is empty, treat it as a good response. */
{ "+CEER:", "" },
{ "+CEER: EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED", "EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED" },
{ "+CEER: NO_SUITABLE_CELLS_IN_TRACKING_AREA", "NO_SUITABLE_CELLS_IN_TRACKING_AREA" },
{ "WRONG RESPONSE", NULL },
{ NULL, NULL }
};
static void
test_ceer (void)
{
guint i;
for (i = 0; ceer_tests[i].str; ++i) {
GError *error = NULL;
gchar *result;
result = mm_altair_parse_ceer_response (ceer_tests[i].str, &error);
if (ceer_tests[i].result) {
g_assert_cmpstr (ceer_tests[i].result, ==, result);
g_assert_no_error (error);
g_free (result);
}
else {
g_assert (result == NULL);
g_assert (error != NULL);
g_error_free (error);
}
}
}
static void
test_parse_cid (void)
{
g_assert (mm_altair_parse_cid ("%CGINFO: 2", NULL) == 2);
g_assert (mm_altair_parse_cid ("%CGINFO:blah", NULL) == -1);
}
/*****************************************************************************/
/* Test %PCOINFO responses */
typedef struct {
const gchar *pco_info;
guint32 session_id;
gsize pco_data_size;
guint8 pco_data[50];
} TestValidPcoInfo;
static const TestValidPcoInfo good_pco_infos[] = {
/* Valid PCO values */
{ "%PCOINFO: 1,1,FF00,13018400", 1, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x00 } },
{ "%PCOINFO: 1,1,FF00,13018403", 1, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } },
{ "%PCOINFO: 1,1,FF00,13018405", 1, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
{ "%PCOINFO: 1,3,FF00,13018400", 3, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x00 } },
{ "%PCOINFO: 1,3,FF00,13018403", 3, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } },
{ "%PCOINFO: 1,3,FF00,13018405", 3, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
{ "%PCOINFO:1,FF00,13018400", 1, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x00 } },
{ "%PCOINFO:1,FF00,13018403", 1, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } },
{ "%PCOINFO:1,FF00,13018405", 1, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
/* Different payload */
{ "%PCOINFO: 1,3,FF00,130185", 3, 9,
{ 0x27, 0x07, 0x80, 0xFF, 0x00, 0x03, 0x13, 0x01, 0x85 } },
/* Multiline PCO info */
{ "%PCOINFO: 1,2,FF00,13018400\r\n%PCOINFO: 1,3,FF00,13018403", 3, 10,
{ 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } },
};
static const gchar *bad_pco_infos[] = {
/* Different container */
"%PCOINFO: 1,3,F000,13018401",
/* Ingood CID */
"%PCOINFO: 1,2,FF00,13018401",
/* Bad PCO info */
"%PCOINFO: blah,blah,FF00,13018401",
/* Bad PCO payload */
"%PCOINFO: 1,1,FF00,130184011",
};
static void
test_parse_vendor_pco_info (void)
{
MMPco *pco;
guint i;
for (i = 0; i < G_N_ELEMENTS (good_pco_infos); ++i) {
const guint8 *pco_data;
gsize pco_data_size;
pco = mm_altair_parse_vendor_pco_info (good_pco_infos[i].pco_info, NULL);
g_assert (pco != NULL);
g_assert_cmpuint (mm_pco_get_session_id (pco), ==, good_pco_infos[i].session_id);
g_assert (mm_pco_is_complete (pco));
pco_data = mm_pco_get_data (pco, &pco_data_size);
g_assert (pco_data != NULL);
g_assert_cmpuint (pco_data_size, ==, good_pco_infos[i].pco_data_size);
g_assert_cmpint (memcmp (pco_data, good_pco_infos[i].pco_data, pco_data_size), ==, 0);
g_object_unref (pco);
}
for (i = 0; i < G_N_ELEMENTS (bad_pco_infos); ++i) {
pco = mm_altair_parse_vendor_pco_info (bad_pco_infos[i], NULL);
g_assert (pco == NULL);
}
}
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/altair/parse_bands", test_parse_bands);
g_test_add_func ("/MM/altair/ceer", test_ceer);
g_test_add_func ("/MM/altair/parse_cid", test_parse_cid);
g_test_add_func ("/MM/altair/parse_vendor_pco_info", test_parse_vendor_pco_info);
return g_test_run ();
}
@@ -0,0 +1,355 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "ModemManager.h"
#include "mm-serial-parsers.h"
#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-errors-types.h"
#include "mm-base-modem-at.h"
#include "mm-broadband-modem-anydata.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-cdma.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_cdma_init (MMIfaceModemCdma *iface);
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemAnydata, mm_broadband_modem_anydata, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init))
/*****************************************************************************/
/* Detailed registration state (CDMA interface) */
typedef struct {
MMModemCdmaRegistrationState detailed_cdma1x_state;
MMModemCdmaRegistrationState detailed_evdo_state;
} DetailedRegistrationStateResults;
static gboolean
get_detailed_registration_state_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
MMModemCdmaRegistrationState *detailed_cdma1x_state,
MMModemCdmaRegistrationState *detailed_evdo_state,
GError **error)
{
DetailedRegistrationStateResults *results;
results = g_task_propagate_pointer (G_TASK (res), error);
if (!results)
return FALSE;
*detailed_cdma1x_state = results->detailed_cdma1x_state;
*detailed_evdo_state = results->detailed_evdo_state;
g_free (results);
return TRUE;
}
static void
hstate_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
GTask *task)
{
DetailedRegistrationStateResults *results;
GError *error = NULL;
const gchar *response;
g_autoptr(GRegex) r = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
results = g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
/* Leave superclass' reg state alone if AT*HSTATE isn't supported */
g_error_free (error);
g_task_return_pointer (task, g_memdup (results, sizeof (*results)), g_free);
g_object_unref (task);
return;
}
response = mm_strip_tag (response, "*HSTATE:");
/* Format is "<at state>,<session state>,<channel>,<pn>,<EcIo>,<rssi>,..." */
r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([^,\\)]*)\\s*,\\s*([^,\\)]*)\\s*,.*",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
g_assert (r != NULL);
g_regex_match (r, response, 0, &match_info);
if (g_match_info_get_match_count (match_info) >= 6) {
guint val = 0;
gint dbm = 0;
/* dBm is between -106 (worst) and -20.7 (best) */
mm_get_int_from_match_info (match_info, 6, &dbm);
/* Parse the EVDO radio state */
if (mm_get_uint_from_match_info (match_info, 1, &val)) {
switch (val) {
case 3: /* IDLE */
/* If IDLE and the EVDO dBm is -105 or lower, assume no service.
* It may be that IDLE actually means NO SERVICE too; not sure.
*/
if (dbm > -105)
results->detailed_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
break;
case 4: /* ACCESS */
case 5: /* CONNECT */
results->detailed_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
break;
default:
mm_obj_warn (self, "unknown *HSTATE (%d); assuming no service", val);
/* fall through */
case 0: /* NO SERVICE */
case 1: /* ACQUISITION */
case 2: /* SYNC */
break;
}
}
}
g_task_return_pointer (task, g_memdup (results, sizeof (*results)), g_free);
g_object_unref (task);
}
static void
state_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
GTask *task)
{
DetailedRegistrationStateResults *results;
GError *error = NULL;
const gchar *response;
g_autoptr(GRegex) r = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
results = g_task_get_task_data (task);
response = mm_strip_tag (response, "*STATE:");
/* Format is "<channel>,<pn>,<sid>,<nid>,<state>,<rssi>,..." */
r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([^,\\)]*)\\s*,.*",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
g_assert (r != NULL);
g_regex_match (r, response, 0, &match_info);
if (g_match_info_get_match_count (match_info) >= 6) {
guint val = 0;
gint dbm = 0;
/* dBm is between -106 (worst) and -20.7 (best) */
mm_get_int_from_match_info (match_info, 6, &dbm);
/* Parse the 1x radio state */
if (mm_get_uint_from_match_info (match_info, 5, &val)) {
switch (val) {
case 1: /* IDLE */
/* If IDLE and the 1X dBm is -105 or lower, assume no service.
* It may be that IDLE actually means NO SERVICE too; not sure.
*/
if (dbm > -105)
results->detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
break;
case 2: /* ACCESS */
case 3: /* PAGING */
case 4: /* TRAFFIC */
results->detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
break;
default:
mm_obj_warn (self, "unknown *HSTATE (%d); assuming no service", val);
/* fall through */
case 0: /* NO SERVICE */
break;
}
}
}
/* Try for EVDO state too */
mm_base_modem_at_command (MM_BASE_MODEM (self),
"*HSTATE?",
3,
FALSE,
(GAsyncReadyCallback)hstate_ready,
task);
}
static void
get_detailed_registration_state (MMIfaceModemCdma *self,
MMModemCdmaRegistrationState cdma1x_state,
MMModemCdmaRegistrationState evdo_state,
GAsyncReadyCallback callback,
gpointer user_data)
{
DetailedRegistrationStateResults *results;
GTask *task;
results = g_new (DetailedRegistrationStateResults, 1);
results->detailed_cdma1x_state = cdma1x_state;
results->detailed_evdo_state = evdo_state;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, results, g_free);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"*STATE?",
3,
FALSE,
(GAsyncReadyCallback)state_ready,
task);
}
/*****************************************************************************/
/* Reset (Modem interface) */
static gboolean
reset_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
}
static void
reset (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_base_modem_at_command (MM_BASE_MODEM (self),
"*RESET",
3,
FALSE,
callback,
user_data);
}
/*****************************************************************************/
/* Setup ports (Broadband modem class) */
static void
setup_ports (MMBroadbandModem *self)
{
MMPortSerialAt *ports[2];
g_autoptr(GRegex) active_regex = NULL;
g_autoptr(GRegex) inactive_regex = NULL;
g_autoptr(GRegex) dormant_regex = NULL;
g_autoptr(GRegex) offline_regex = NULL;
g_autoptr(GRegex) regreq_regex = NULL;
g_autoptr(GRegex) authreq_regex = NULL;
guint i;
/* Call parent's setup ports first always */
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_anydata_parent_class)->setup_ports (self);
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Data call has connected */
active_regex = g_regex_new ("\\r\\n\\*ACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
/* Data call disconnected */
inactive_regex = g_regex_new ("\\r\\n\\*INACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
/* Modem is now dormant */
dormant_regex = g_regex_new ("\\r\\n\\*DORMANT:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
/* Network acquisition fail */
offline_regex = g_regex_new ("\\r\\n\\*OFFLINE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
/* Registration fail */
regreq_regex = g_regex_new ("\\r\\n\\*REGREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
/* Authentication fail */
authreq_regex = g_regex_new ("\\r\\n\\*AUTHREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
/* Now reset the unsolicited messages */
for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
/* Data state notifications */
mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), active_regex, NULL, NULL, NULL);
mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), inactive_regex, NULL, NULL, NULL);
mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), dormant_regex, NULL, NULL, NULL);
/* Abnormal state notifications
*
* FIXME: set 1X/EVDO registration state to UNKNOWN when these
* notifications are received?
*/
mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), offline_regex, NULL, NULL, NULL);
mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), regreq_regex, NULL, NULL, NULL);
mm_port_serial_at_add_unsolicited_msg_handler (MM_PORT_SERIAL_AT (ports[i]), authreq_regex, NULL, NULL, NULL);
}
}
/*****************************************************************************/
MMBroadbandModemAnydata *
mm_broadband_modem_anydata_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
return g_object_new (MM_TYPE_BROADBAND_MODEM_ANYDATA,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
/* Generic bearer supports TTY only */
MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
static void
mm_broadband_modem_anydata_init (MMBroadbandModemAnydata *self)
{
}
static void
iface_modem_init (MMIfaceModem *iface)
{
iface->reset = reset;
iface->reset_finish = reset_finish;
}
static void
iface_modem_cdma_init (MMIfaceModemCdma *iface)
{
iface->get_cdma1x_serving_system = NULL;
iface->get_cdma1x_serving_system_finish = NULL;
iface->get_detailed_registration_state = get_detailed_registration_state;
iface->get_detailed_registration_state_finish = get_detailed_registration_state_finish;
}
static void
mm_broadband_modem_anydata_class_init (MMBroadbandModemAnydataClass *klass)
{
MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
broadband_modem_class->setup_ports = setup_ports;
}
@@ -0,0 +1,49 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 - Aleksander Morgado <aleksander@gnu.org>
*/
#ifndef MM_BROADBAND_MODEM_ANYDATA_H
#define MM_BROADBAND_MODEM_ANYDATA_H
#include "mm-broadband-modem.h"
#define MM_TYPE_BROADBAND_MODEM_ANYDATA (mm_broadband_modem_anydata_get_type ())
#define MM_BROADBAND_MODEM_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_ANYDATA, MMBroadbandModemAnydata))
#define MM_BROADBAND_MODEM_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_ANYDATA, MMBroadbandModemAnydataClass))
#define MM_IS_BROADBAND_MODEM_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_ANYDATA))
#define MM_IS_BROADBAND_MODEM_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_ANYDATA))
#define MM_BROADBAND_MODEM_ANYDATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_ANYDATA, MMBroadbandModemAnydataClass))
typedef struct _MMBroadbandModemAnydata MMBroadbandModemAnydata;
typedef struct _MMBroadbandModemAnydataClass MMBroadbandModemAnydataClass;
struct _MMBroadbandModemAnydata {
MMBroadbandModem parent;
};
struct _MMBroadbandModemAnydataClass{
MMBroadbandModemClass parent;
};
GType mm_broadband_modem_anydata_get_type (void);
MMBroadbandModemAnydata *mm_broadband_modem_anydata_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_ANYDATA_H */
+97
View File
@@ -0,0 +1,97 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
*/
#include <string.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log-object.h"
#include "mm-plugin-anydata.h"
#include "mm-broadband-modem-anydata.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
#endif
G_DEFINE_TYPE (MMPluginAnydata, mm_plugin_anydata, 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_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered AnyDATA modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
return MM_BASE_MODEM (mm_broadband_modem_anydata_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 guint16 vendor_ids[] = { 0x16d5, 0 };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_ANYDATA,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_REQUIRED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
NULL));
}
static void
mm_plugin_anydata_init (MMPluginAnydata *self)
{
}
static void
mm_plugin_anydata_class_init (MMPluginAnydataClass *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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
*/
#ifndef MM_PLUGIN_ANYDATA_H
#define MM_PLUGIN_ANYDATA_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_ANYDATA (mm_plugin_anydata_get_type ())
#define MM_PLUGIN_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_ANYDATA, MMPluginAnydata))
#define MM_PLUGIN_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_ANYDATA, MMPluginAnydataClass))
#define MM_IS_PLUGIN_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_ANYDATA))
#define MM_IS_PLUGIN_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_ANYDATA))
#define MM_PLUGIN_ANYDATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_ANYDATA, MMPluginAnydataClass))
typedef struct {
MMPlugin parent;
} MMPluginAnydata;
typedef struct {
MMPluginClass parent;
} MMPluginAnydataClass;
GType mm_plugin_anydata_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_ANYDATA_H */
@@ -0,0 +1,16 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_broadmobi_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2020", GOTO="mm_broadmobi_port_types"
GOTO="mm_broadmobi_port_types_end"
LABEL="mm_broadmobi_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# BroadMobi BM818
ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
LABEL="mm_broadmobi_port_types_end"
@@ -0,0 +1,95 @@
/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <string.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log-object.h"
#include "mm-plugin-broadmobi.h"
#include "mm-broadband-modem.h"
#if defined WITH_QMI
# include "mm-broadband-modem-qmi.h"
#endif
G_DEFINE_TYPE (MMPluginBroadmobi, mm_plugin_broadmobi, 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_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered BroadMobi modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
return MM_BASE_MODEM (mm_broadband_modem_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 guint16 vendor_ids[] = { 0x2020, 0 };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_BROADMOBI,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_REQUIRED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
NULL));
}
static void
mm_plugin_broadmobi_init (MMPluginBroadmobi *self)
{
}
static void
mm_plugin_broadmobi_class_init (MMPluginBroadmobiClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
@@ -0,0 +1,40 @@
/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_PLUGIN_BROADMOBI_H
#define MM_PLUGIN_BROADMOBI_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_BROADMOBI (mm_plugin_broadmobi_get_type ())
#define MM_PLUGIN_BROADMOBI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BROADMOBI, MMPluginBroadmobi))
#define MM_PLUGIN_BROADMOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_BROADMOBI, MMPluginBroadmobiClass))
#define MM_IS_PLUGIN_BROADMOBI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_BROADMOBI))
#define MM_IS_PLUGIN_BROADMOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_BROADMOBI))
#define MM_PLUGIN_BROADMOBI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_BROADMOBI, MMPluginBroadmobiClass))
typedef struct {
MMPlugin parent;
} MMPluginBroadmobi;
typedef struct {
MMPluginClass parent;
} MMPluginBroadmobiClass;
GType mm_plugin_broadmobi_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_BROADMOBI_H */
@@ -0,0 +1,71 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_cinterion_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1e2d", GOTO="mm_cinterion_port_types"
GOTO="mm_cinterion_port_types_end"
LABEL="mm_cinterion_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# PHS8
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0053", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
# PLS8 port types
# ttyACM0 (if #0): AT port
# ttyACM1 (if #2): AT port
# ttyACM2 (if #4): GPS data port
# ttyACM3 (if #6): unknown
# ttyACM4 (if #8): unknown
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1"
# PLS62 family non-mbim enumeration uses alternate settings for 2G band management
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{ID_MM_CINTERION_MODEM_FAMILY}="imt"
# PLS62 family non-mbim enumeration
# ttyACM0 (if #0): AT port
# ttyACM1 (if #2): AT port
# ttyACM2 (if #4): can be AT or GNSS in some models
# ttyACM3 (if #6): AT port (but just ignore)
# ttyACM4 (if #8): DIAG/QCDM
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="08", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
# PLS62 family mbim enumeration
# ttyACM0 (if #0): AT port
# ttyACM1 (if #2): AT port
# ttyACM2 (if #4): can be AT or GNSS in some models
# ttyACM3 (if #6): AT port (but just ignore)
# ttyACM4 (if #8): DIAG/QCDM
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="08", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
# PLS63
# ttyACM0 (if #0): AT port
# ttyACM1 (if #2): AT port
# ttyACM2 (if #4): GPS data port
# ttyACM3 (if #6): DIAG/QCDM
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0069", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
# PLS83
# ttyACM0 (if #0): AT port
# ttyACM1 (if #2): AT port
# ttyACM2 (if #4): GPS data port
# ttyACM3 (if #6): DIAG/QCDM
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
LABEL="mm_cinterion_port_types_end"
@@ -0,0 +1,796 @@
/* -*- 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) 2016 Trimble Navigation Limited
* Author: Matthew Stanger <matthew_stanger@trimble.com>
*/
#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>
#include "mm-base-modem-at.h"
#include "mm-broadband-bearer-cinterion.h"
#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-cinterion.h"
#include "mm-daemon-enums-types.h"
G_DEFINE_TYPE (MMBroadbandBearerCinterion, mm_broadband_bearer_cinterion, MM_TYPE_BROADBAND_BEARER)
/*****************************************************************************/
/* WWAN interface mapping */
typedef struct {
guint swwan_index;
guint usb_iface_num;
} UsbInterfaceConfig;
/* Map SWWAN index, USB interface number and preferred PDP context.
*
* The expected USB interface mapping is:
* INTERFACE=usb0 -> ID_USB_INTERFACE_NUM=0a
* INTERFACE=usb1 -> ID_USB_INTERFACE_NUM=0c
* INTERFACE=usb0 -> ID_USB_INTERFACE_NUM=08 (PLSx3w)
*/
static const UsbInterfaceConfig usb_interface_configs[] = {
{
.swwan_index = 1,
.usb_iface_num = 0x0a,
},
{
.swwan_index = 2,
.usb_iface_num = 0x0c,
},
{
.swwan_index = 1,
.usb_iface_num = 0x08,
},
};
static gint
get_usb_interface_config_index (MMPort *data,
GError **error)
{
guint usb_iface_num;
guint i;
usb_iface_num = (guint) mm_kernel_device_get_interface_number (mm_port_peek_kernel_device (data));
for (i = 0; i < G_N_ELEMENTS (usb_interface_configs); i++) {
if (usb_interface_configs[i].usb_iface_num == usb_iface_num)
return (gint) i;
}
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Unsupported WWAN interface: unexpected interface number: 0x%02x", usb_iface_num);
return -1;
}
/*****************************************************************************/
/* Connection status loading
* NOTE: only CONNECTED or DISCONNECTED should be reported here.
*/
static MMBearerConnectionStatus
load_connection_status_finish (MMBaseBearer *bearer,
GAsyncResult *res,
GError **error)
{
GError *inner_error = NULL;
gssize aux;
aux = g_task_propagate_int (G_TASK (res), &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
}
return (MMBearerConnectionStatus) aux;
}
typedef struct {
guint cid;
guint retries;
gboolean delay;
gboolean retry;
} LoadConnectionContext;
static void
load_connection_context_free (LoadConnectionContext *ctx)
{
g_slice_free (LoadConnectionContext, ctx);
}
static gboolean swwan_check_status (GTask *task);
static void
swwan_check_status_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBroadbandBearerCinterion *self;
const gchar *response;
GError *error = NULL;
MMBearerConnectionStatus status;
LoadConnectionContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (modem, res, &error);
if (!response) {
g_task_return_error (task, error);
goto out;
}
status = mm_cinterion_parse_swwan_response (response, ctx->cid, self, &error);
if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) {
g_task_return_error (task, error);
goto out;
} else if (ctx->retry && status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
mm_obj_dbg (self, "check status retry");
if (ctx->retries == 0) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"CID %u status check retry exceeded", ctx->cid);
goto out;
} else {
if (ctx->delay) {
g_timeout_add_seconds (1, (GSourceFunc)swwan_check_status, task);
} else {
g_idle_add ((GSourceFunc)swwan_check_status, task);
}
ctx->retries--;
return;
}
}
g_assert (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED ||
status == MM_BEARER_CONNECTION_STATUS_CONNECTED);
g_task_return_int (task, (gssize) status);
out:
g_object_unref (task);
}
static gboolean
swwan_check_status (GTask *task)
{
MMBroadbandBearerCinterion *bearer;
g_autoptr(MMBaseModem) modem = NULL;
bearer = g_task_get_source_object (task);
g_object_get (bearer,
MM_BASE_BEARER_MODEM, &modem,
NULL);
mm_base_modem_at_command (modem,
"^SWWAN?",
5,
FALSE,
(GAsyncReadyCallback) swwan_check_status_ready,
task);
return G_SOURCE_REMOVE;
}
static void
load_connection_status_by_cid (MMBroadbandBearerCinterion *bearer,
gint cid,
gboolean delay,
gboolean retry,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
LoadConnectionContext *ctx;
task = g_task_new (bearer, NULL, callback, user_data);
if (cid == MM_3GPP_PROFILE_ID_UNKNOWN) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Unknown profile id to check connection status");
g_object_unref (task);
return;
}
ctx = g_slice_new0 (LoadConnectionContext);
g_task_set_task_data (task, ctx, (GDestroyNotify) load_connection_context_free);
/* Setup context */
ctx->cid = cid;
ctx->retries = 5;
ctx->delay = delay;
ctx->retry = retry;
/* Some modems require a delay before querying the SWWAN status
* This is only needed for step DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION
* and DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS. */
if (delay) {
g_timeout_add_seconds (1, (GSourceFunc)swwan_check_status, task);
} else {
g_idle_add ((GSourceFunc)swwan_check_status, task);
}
}
static void
load_connection_status (MMBaseBearer *bearer,
GAsyncReadyCallback callback,
gpointer user_data)
{
load_connection_status_by_cid (MM_BROADBAND_BEARER_CINTERION (bearer),
mm_base_bearer_get_profile_id (bearer),
FALSE,
FALSE,
callback,
user_data);
}
/******************************************************************************/
/* Dial 3GPP */
typedef enum {
DIAL_3GPP_CONTEXT_STEP_FIRST = 0,
DIAL_3GPP_CONTEXT_STEP_AUTH,
DIAL_3GPP_CONTEXT_STEP_START_SWWAN,
DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION,
DIAL_3GPP_CONTEXT_STEP_LAST,
} Dial3gppContextStep;
typedef struct {
MMBroadbandBearerCinterion *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
guint cid;
MMPort *data;
gint usb_interface_config_index;
Dial3gppContextStep step;
} Dial3gppContext;
static void
dial_3gpp_context_free (Dial3gppContext *ctx)
{
g_object_unref (ctx->modem);
g_object_unref (ctx->self);
g_object_unref (ctx->primary);
g_clear_object (&ctx->data);
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 dial_3gpp_context_step (GTask *task);
static void
dial_connection_status_ready (MMBroadbandBearerCinterion *self,
GAsyncResult *res,
GTask *task)
{
MMBearerConnectionStatus status;
Dial3gppContext *ctx;
GError *error = NULL;
ctx = (Dial3gppContext *) g_task_get_task_data (task);
status = load_connection_status_finish (MM_BASE_BEARER (self), res, &error);
if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"CID %u is reported disconnected", ctx->cid);
g_object_unref (task);
return;
}
g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED);
/* Go to next step */
ctx->step++;
dial_3gpp_context_step (task);
}
static void
common_dial_operation_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
Dial3gppContext *ctx;
GError *error = NULL;
ctx = (Dial3gppContext *) g_task_get_task_data (task);
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Go to next step */
ctx->step++;
dial_3gpp_context_step (task);
}
static void
swwan_dial_operation_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerCinterion *self) /* full ref! */
{
GError *error = NULL;
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
mm_obj_warn (self, "data connection attempt failed: %s", error->message);
mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),
MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
g_error_free (error);
}
g_object_unref (self);
}
static void
handle_cancel_dial (GTask *task)
{
Dial3gppContext *ctx;
gchar *command;
ctx = (Dial3gppContext *) g_task_get_task_data (task);
/* Disconnect, may not succeed. Will not check response on cancel */
command = g_strdup_printf ("^SWWAN=0,%u,%u",
ctx->cid, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
3,
FALSE,
FALSE,
NULL,
NULL,
NULL);
g_free (command);
}
static void
dial_3gpp_context_step (GTask *task)
{
MMBroadbandBearerCinterion *self;
Dial3gppContext *ctx;
MMCinterionModemFamily modem_family;
gboolean default_swwan_behavior;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
/* Check for cancellation */
if (g_task_return_error_if_cancelled (task)) {
handle_cancel_dial (task);
g_object_unref (task);
return;
}
modem_family = mm_broadband_modem_cinterion_get_family (MM_BROADBAND_MODEM_CINTERION (ctx->modem));
default_swwan_behavior = modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT;
switch (ctx->step) {
case DIAL_3GPP_CONTEXT_STEP_FIRST:
ctx->step++;
/* fall through */
case DIAL_3GPP_CONTEXT_STEP_AUTH: {
g_autofree gchar *command = NULL;
command = mm_cinterion_build_auth_string (self,
modem_family,
mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)),
ctx->cid);
if (command) {
mm_obj_dbg (self, "dial step %u/%u: authenticating...", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST);
/* Send SGAUTH write, if User & Pass are provided.
* advance to next state by callback */
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
10,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback) common_dial_operation_ready,
task);
return;
}
mm_obj_dbg (self, "dial step %u/%u: authentication not required", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST);
ctx->step++;
} /* fall through */
case DIAL_3GPP_CONTEXT_STEP_START_SWWAN: {
g_autofree gchar *command = NULL;
mm_obj_dbg (self, "dial step %u/%u: starting SWWAN interface %u connection...",
ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
command = g_strdup_printf ("^SWWAN=1,%u,%u",
ctx->cid,
usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
if (default_swwan_behavior) {
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback) common_dial_operation_ready,
task);
return;
}
/* We "jump" to the last step here here since the modem expects the
* DHCP discover packet while ^SWWAN runs. If the command fails,
* we'll mark the bearer disconnected later in the callback.
*/
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback) swwan_dial_operation_ready,
g_object_ref (self));
ctx->step = DIAL_3GPP_CONTEXT_STEP_LAST;
dial_3gpp_context_step (task);
return;
}
case DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION:
g_assert (default_swwan_behavior);
mm_obj_dbg (self, "dial step %u/%u: checking SWWAN interface %u status...",
ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
load_connection_status_by_cid (ctx->self,
(gint) ctx->cid,
TRUE,
TRUE,
(GAsyncReadyCallback) dial_connection_status_ready,
task);
return;
case DIAL_3GPP_CONTEXT_STEP_LAST:
mm_obj_dbg (self, "dial step %u/%u: finished", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST);
g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
g_object_unref (task);
return;
default:
g_assert_not_reached ();
}
}
static void
dial_3gpp (MMBroadbandBearer *self,
MMBaseModem *modem,
MMPortSerialAt *primary,
guint cid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
Dial3gppContext *ctx;
GError *error = NULL;
g_assert (primary != NULL);
/* Setup task and create connection context */
task = g_task_new (self, cancellable, callback, user_data);
ctx = g_slice_new0 (Dial3gppContext);
g_task_set_task_data (task, ctx, (GDestroyNotify) dial_3gpp_context_free);
/* Setup context */
ctx->self = MM_BROADBAND_BEARER_CINTERION (g_object_ref (self));
ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
ctx->cid = cid;
ctx->step = DIAL_3GPP_CONTEXT_STEP_FIRST;
/* Get a net port to setup the connection on */
ctx->data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (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;
}
g_object_ref (ctx->data);
/* Validate configuration */
ctx->usb_interface_config_index = get_usb_interface_config_index (ctx->data, &error);
if (ctx->usb_interface_config_index < 0) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Run! */
dial_3gpp_context_step (task);
}
/*****************************************************************************/
/* Disconnect 3GPP */
typedef enum {
DISCONNECT_3GPP_CONTEXT_STEP_FIRST,
DISCONNECT_3GPP_CONTEXT_STEP_STOP_SWWAN,
DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS,
DISCONNECT_3GPP_CONTEXT_STEP_LAST,
} Disconnect3gppContextStep;
typedef struct {
MMBroadbandBearerCinterion *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
guint cid;
gint usb_interface_config_index;
Disconnect3gppContextStep step;
} Disconnect3gppContext;
static void
disconnect_3gpp_context_free (Disconnect3gppContext *ctx)
{
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->self);
g_object_unref (ctx->modem);
g_slice_free (Disconnect3gppContext, ctx);
}
static gboolean
disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void disconnect_3gpp_context_step (GTask *task);
static void
disconnect_connection_status_ready (MMBroadbandBearerCinterion *self,
GAsyncResult *res,
GTask *task)
{
MMBearerConnectionStatus status;
Disconnect3gppContext *ctx;
GError *error = NULL;
ctx = (Disconnect3gppContext *) g_task_get_task_data (task);
status = load_connection_status_finish (MM_BASE_BEARER (self), res, &error);
switch (status) {
case MM_BEARER_CONNECTION_STATUS_UNKNOWN:
/* Assume disconnected */
mm_obj_dbg (self, "couldn't get CID %u status, assume disconnected: %s", ctx->cid, error->message);
g_clear_error (&error);
break;
case MM_BEARER_CONNECTION_STATUS_DISCONNECTED:
break;
case MM_BEARER_CONNECTION_STATUS_CONNECTED:
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"CID %u is reported connected", ctx->cid);
g_object_unref (task);
return;
case MM_BEARER_CONNECTION_STATUS_DISCONNECTING:
case MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED:
default:
g_assert_not_reached ();
}
/* Go on to next step */
ctx->step++;
disconnect_3gpp_context_step (task);
}
static void
swwan_disconnect_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
Disconnect3gppContext *ctx;
ctx = (Disconnect3gppContext *) g_task_get_task_data (task);
/* We don't bother to check error or response here since, ctx flow's
* next step checks it */
mm_base_modem_at_command_full_finish (modem, res, NULL);
/* Go on to next step */
ctx->step++;
disconnect_3gpp_context_step (task);
}
static void
disconnect_3gpp_context_step (GTask *task)
{
MMBroadbandBearerCinterion *self;
Disconnect3gppContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case DISCONNECT_3GPP_CONTEXT_STEP_FIRST:
ctx->step++;
/* fall through */
case DISCONNECT_3GPP_CONTEXT_STEP_STOP_SWWAN: {
gchar *command;
command = g_strdup_printf ("^SWWAN=0,%u,%u",
ctx->cid, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
mm_obj_dbg (self, "disconnect step %u/%u: disconnecting PDP CID %u...",
ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST, ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback) swwan_disconnect_ready,
task);
g_free (command);
return;
}
case DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS:
mm_obj_dbg (self, "disconnect step %u/%u: checking SWWAN interface %u status...",
ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST,
usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
load_connection_status_by_cid (MM_BROADBAND_BEARER_CINTERION (ctx->self),
(gint) ctx->cid,
TRUE,
FALSE,
(GAsyncReadyCallback) disconnect_connection_status_ready,
task);
return;
case DISCONNECT_3GPP_CONTEXT_STEP_LAST:
mm_obj_dbg (self, "disconnect step %u/%u: finished",
ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
g_assert_not_reached ();
}
}
static void
disconnect_3gpp (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
Disconnect3gppContext *ctx;
GError *error = NULL;
g_assert (primary != NULL);
g_assert (data != NULL);
/* Setup task and create connection context */
task = g_task_new (self, NULL, callback, user_data);
ctx = g_slice_new0 (Disconnect3gppContext);
g_task_set_task_data (task, ctx, (GDestroyNotify) disconnect_3gpp_context_free);
/* Setup context */
ctx->self = MM_BROADBAND_BEARER_CINTERION (g_object_ref (self));
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->data = g_object_ref (data);
ctx->cid = cid;
ctx->step = DISCONNECT_3GPP_CONTEXT_STEP_FIRST;
/* Validate configuration */
ctx->usb_interface_config_index = get_usb_interface_config_index (data, &error);
if (ctx->usb_interface_config_index < 0) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Start */
disconnect_3gpp_context_step (task);
}
/*****************************************************************************/
/* Setup and Init Bearers */
MMBaseBearer *
mm_broadband_bearer_cinterion_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_cinterion_new (MMBroadbandModemCinterion *modem,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (
MM_TYPE_BROADBAND_BEARER_CINTERION,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_BEARER_MODEM, modem,
MM_BASE_BEARER_CONFIG, config,
NULL);
}
static void
mm_broadband_bearer_cinterion_init (MMBroadbandBearerCinterion *self)
{
}
static void
mm_broadband_bearer_cinterion_class_init (MMBroadbandBearerCinterionClass *klass)
{
MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
base_bearer_class->load_connection_status = load_connection_status;
base_bearer_class->load_connection_status_finish = load_connection_status_finish;
#if defined WITH_SUSPEND_RESUME
base_bearer_class->reload_connection_status = load_connection_status;
base_bearer_class->reload_connection_status_finish = load_connection_status_finish;
#endif
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
}
@@ -0,0 +1,54 @@
/* -*- 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) 2016 Trimble Navigation Limited
* Author: Matthew Stanger <Matthew_Stanger@trimble.com>
*/
#ifndef MM_BROADBAND_BEARER_CINTERION_H
#define MM_BROADBAND_BEARER_CINTERION_H
#include <glib.h>
#include <glib-object.h>
#include "mm-broadband-bearer.h"
#include "mm-broadband-modem-cinterion.h"
#define MM_TYPE_BROADBAND_BEARER_CINTERION (mm_broadband_bearer_cinterion_get_type ())
#define MM_BROADBAND_BEARER_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterion))
#define MM_BROADBAND_BEARER_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass))
#define MM_IS_BROADBAND_BEARER_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION))
#define MM_IS_BROADBAND_BEARER_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_CINTERION))
#define MM_BROADBAND_BEARER_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass))
typedef struct _MMBroadbandBearerCinterion MMBroadbandBearerCinterion;
typedef struct _MMBroadbandBearerCinterionClass MMBroadbandBearerCinterionClass;
struct _MMBroadbandBearerCinterion {
MMBroadbandBearer parent;
};
struct _MMBroadbandBearerCinterionClass {
MMBroadbandBearerClass parent;
};
GType mm_broadband_bearer_cinterion_get_type (void);
void mm_broadband_bearer_cinterion_new (MMBroadbandModemCinterion *modem,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MMBaseBearer *mm_broadband_bearer_cinterion_new_finish (GAsyncResult *res,
GError **error);
#endif /* MM_BROADBAND_BEARER_CINTERION_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
/* -*- 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) 2011 Ammonit Measurement GmbH
* Copyright (C) 2011 Google Inc.
* Author: Aleksander Morgado <aleksander@lanedo.com>
*/
#ifndef MM_BROADBAND_MODEM_CINTERION_H
#define MM_BROADBAND_MODEM_CINTERION_H
#include "mm-broadband-modem.h"
#include "mm-modem-helpers-cinterion.h"
#define MM_TYPE_BROADBAND_MODEM_CINTERION (mm_broadband_modem_cinterion_get_type ())
#define MM_BROADBAND_MODEM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterion))
#define MM_BROADBAND_MODEM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterionClass))
#define MM_IS_BROADBAND_MODEM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION))
#define MM_IS_BROADBAND_MODEM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_CINTERION))
#define MM_BROADBAND_MODEM_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterionClass))
typedef struct _MMBroadbandModemCinterion MMBroadbandModemCinterion;
typedef struct _MMBroadbandModemCinterionClass MMBroadbandModemCinterionClass;
typedef struct _MMBroadbandModemCinterionPrivate MMBroadbandModemCinterionPrivate;
struct _MMBroadbandModemCinterion {
MMBroadbandModem parent;
MMBroadbandModemCinterionPrivate *priv;
};
struct _MMBroadbandModemCinterionClass{
MMBroadbandModemClass parent;
};
GType mm_broadband_modem_cinterion_get_type (void);
MMBroadbandModemCinterion *mm_broadband_modem_cinterion_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
MMCinterionModemFamily mm_broadband_modem_cinterion_get_family (MMBroadbandModemCinterion * modem);
#endif /* MM_BROADBAND_MODEM_CINTERION_H */
@@ -0,0 +1,167 @@
/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "ModemManager.h"
#include "mm-log.h"
#include "mm-errors-types.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-location.h"
#include "mm-iface-modem-voice.h"
#include "mm-broadband-modem-mbim-cinterion.h"
#include "mm-shared-cinterion.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_location_init (MMIfaceModemLocation *iface);
static void iface_modem_voice_init (MMIfaceModemVoice *iface);
static void iface_modem_time_init (MMIfaceModemTime *iface);
static void shared_cinterion_init (MMSharedCinterion *iface);
static MMIfaceModem *iface_modem_parent;
static MMIfaceModemLocation *iface_modem_location_parent;
static MMIfaceModemVoice *iface_modem_voice_parent;
static MMIfaceModemTime *iface_modem_time_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimCinterion, mm_broadband_modem_mbim_cinterion, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init))
/*****************************************************************************/
MMBroadbandModemMbimCinterion *
mm_broadband_modem_mbim_cinterion_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
/* MBIM bearer supports NET only */
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
MM_BROADBAND_MODEM_MBIM_INTEL_FIRMWARE_UPDATE_UNSUPPORTED, TRUE,
NULL);
}
static void
mm_broadband_modem_mbim_cinterion_init (MMBroadbandModemMbimCinterion *self)
{
}
static void
iface_modem_init (MMIfaceModem *iface)
{
iface_modem_parent = g_type_interface_peek_parent (iface);
iface->reset = mm_shared_cinterion_modem_reset;
iface->reset_finish = mm_shared_cinterion_modem_reset_finish;
}
static MMIfaceModem *
peek_parent_interface (MMSharedCinterion *self)
{
return iface_modem_parent;
}
static void
iface_modem_location_init (MMIfaceModemLocation *iface)
{
iface_modem_location_parent = g_type_interface_peek_parent (iface);
iface->load_capabilities = mm_shared_cinterion_location_load_capabilities;
iface->load_capabilities_finish = mm_shared_cinterion_location_load_capabilities_finish;
iface->enable_location_gathering = mm_shared_cinterion_enable_location_gathering;
iface->enable_location_gathering_finish = mm_shared_cinterion_enable_location_gathering_finish;
iface->disable_location_gathering = mm_shared_cinterion_disable_location_gathering;
iface->disable_location_gathering_finish = mm_shared_cinterion_disable_location_gathering_finish;
}
static MMIfaceModemLocation *
peek_parent_location_interface (MMSharedCinterion *self)
{
return iface_modem_location_parent;
}
static void
iface_modem_voice_init (MMIfaceModemVoice *iface)
{
iface_modem_voice_parent = g_type_interface_peek_parent (iface);
iface->create_call = mm_shared_cinterion_create_call;
iface->check_support = mm_shared_cinterion_voice_check_support;
iface->check_support_finish = mm_shared_cinterion_voice_check_support_finish;
iface->enable_unsolicited_events = mm_shared_cinterion_voice_enable_unsolicited_events;
iface->enable_unsolicited_events_finish = mm_shared_cinterion_voice_enable_unsolicited_events_finish;
iface->disable_unsolicited_events = mm_shared_cinterion_voice_disable_unsolicited_events;
iface->disable_unsolicited_events_finish = mm_shared_cinterion_voice_disable_unsolicited_events_finish;
iface->setup_unsolicited_events = mm_shared_cinterion_voice_setup_unsolicited_events;
iface->setup_unsolicited_events_finish = mm_shared_cinterion_voice_setup_unsolicited_events_finish;
iface->cleanup_unsolicited_events = mm_shared_cinterion_voice_cleanup_unsolicited_events;
iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_voice_cleanup_unsolicited_events_finish;
}
static MMIfaceModemVoice *
peek_parent_voice_interface (MMSharedCinterion *self)
{
return iface_modem_voice_parent;
}
static void
iface_modem_time_init (MMIfaceModemTime *iface)
{
iface_modem_time_parent = g_type_interface_peek_parent (iface);
iface->setup_unsolicited_events = mm_shared_cinterion_time_setup_unsolicited_events;
iface->setup_unsolicited_events_finish = mm_shared_cinterion_time_setup_unsolicited_events_finish;
iface->cleanup_unsolicited_events = mm_shared_cinterion_time_cleanup_unsolicited_events;
iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_time_cleanup_unsolicited_events_finish;
}
static MMIfaceModemTime *
peek_parent_time_interface (MMSharedCinterion *self)
{
return iface_modem_time_parent;
}
static void
shared_cinterion_init (MMSharedCinterion *iface)
{
iface->peek_parent_interface = peek_parent_interface;
iface->peek_parent_location_interface = peek_parent_location_interface;
iface->peek_parent_voice_interface = peek_parent_voice_interface;
iface->peek_parent_time_interface = peek_parent_time_interface;
}
static void
mm_broadband_modem_mbim_cinterion_class_init (MMBroadbandModemMbimCinterionClass *klass)
{
}
@@ -0,0 +1,47 @@
/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_BROADBAND_MODEM_MBIM_CINTERION_MBIM_H
#define MM_BROADBAND_MODEM_MBIM_CINTERION_MBIM_H
#include "mm-broadband-modem-mbim.h"
#define MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION (mm_broadband_modem_mbim_cinterion_get_type ())
#define MM_BROADBAND_MODEM_MBIM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MMBroadbandModemMbimCinterion))
#define MM_BROADBAND_MODEM_MBIM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MMBroadbandModemMbimCinterionClass))
#define MM_IS_BROADBAND_MODEM_MBIM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION))
#define MM_IS_BROADBAND_MODEM_MBIM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION))
#define MM_BROADBAND_MODEM_MBIM_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MMBroadbandModemMbimCinterionClass))
typedef struct _MMBroadbandModemMbimCinterion MMBroadbandModemMbimCinterion;
typedef struct _MMBroadbandModemMbimCinterionClass MMBroadbandModemMbimCinterionClass;
struct _MMBroadbandModemMbimCinterion {
MMBroadbandModemMbim parent;
};
struct _MMBroadbandModemMbimCinterionClass{
MMBroadbandModemMbimClass parent;
};
GType mm_broadband_modem_mbim_cinterion_get_type (void);
MMBroadbandModemMbimCinterion *mm_broadband_modem_mbim_cinterion_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_MBIM_CINTERION_H */
@@ -0,0 +1,167 @@
/* -*- 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 Ammonit Measurement GmbH
* Author: Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "ModemManager.h"
#include "mm-log.h"
#include "mm-errors-types.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-location.h"
#include "mm-iface-modem-voice.h"
#include "mm-broadband-modem-qmi-cinterion.h"
#include "mm-shared-cinterion.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_location_init (MMIfaceModemLocation *iface);
static void iface_modem_voice_init (MMIfaceModemVoice *iface);
static void iface_modem_time_init (MMIfaceModemTime *iface);
static void shared_cinterion_init (MMSharedCinterion *iface);
static MMIfaceModem *iface_modem_parent;
static MMIfaceModemLocation *iface_modem_location_parent;
static MMIfaceModemVoice *iface_modem_voice_parent;
static MMIfaceModemTime *iface_modem_time_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmiCinterion, mm_broadband_modem_qmi_cinterion, MM_TYPE_BROADBAND_MODEM_QMI, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init))
/*****************************************************************************/
MMBroadbandModemQmiCinterion *
mm_broadband_modem_qmi_cinterion_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI_CINTERION,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
/* QMI bearer supports NET only */
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
NULL);
}
static void
mm_broadband_modem_qmi_cinterion_init (MMBroadbandModemQmiCinterion *self)
{
}
static void
iface_modem_init (MMIfaceModem *iface)
{
iface_modem_parent = g_type_interface_peek_parent (iface);
iface->reset = mm_shared_cinterion_modem_reset;
iface->reset_finish = mm_shared_cinterion_modem_reset_finish;
}
static MMIfaceModem *
peek_parent_interface (MMSharedCinterion *self)
{
return iface_modem_parent;
}
static void
iface_modem_location_init (MMIfaceModemLocation *iface)
{
iface_modem_location_parent = g_type_interface_peek_parent (iface);
iface->load_capabilities = mm_shared_cinterion_location_load_capabilities;
iface->load_capabilities_finish = mm_shared_cinterion_location_load_capabilities_finish;
iface->enable_location_gathering = mm_shared_cinterion_enable_location_gathering;
iface->enable_location_gathering_finish = mm_shared_cinterion_enable_location_gathering_finish;
iface->disable_location_gathering = mm_shared_cinterion_disable_location_gathering;
iface->disable_location_gathering_finish = mm_shared_cinterion_disable_location_gathering_finish;
}
static MMIfaceModemLocation *
peek_parent_location_interface (MMSharedCinterion *self)
{
return iface_modem_location_parent;
}
static void
iface_modem_voice_init (MMIfaceModemVoice *iface)
{
iface_modem_voice_parent = g_type_interface_peek_parent (iface);
iface->create_call = mm_shared_cinterion_create_call;
iface->check_support = mm_shared_cinterion_voice_check_support;
iface->check_support_finish = mm_shared_cinterion_voice_check_support_finish;
iface->enable_unsolicited_events = mm_shared_cinterion_voice_enable_unsolicited_events;
iface->enable_unsolicited_events_finish = mm_shared_cinterion_voice_enable_unsolicited_events_finish;
iface->disable_unsolicited_events = mm_shared_cinterion_voice_disable_unsolicited_events;
iface->disable_unsolicited_events_finish = mm_shared_cinterion_voice_disable_unsolicited_events_finish;
iface->setup_unsolicited_events = mm_shared_cinterion_voice_setup_unsolicited_events;
iface->setup_unsolicited_events_finish = mm_shared_cinterion_voice_setup_unsolicited_events_finish;
iface->cleanup_unsolicited_events = mm_shared_cinterion_voice_cleanup_unsolicited_events;
iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_voice_cleanup_unsolicited_events_finish;
}
static MMIfaceModemVoice *
peek_parent_voice_interface (MMSharedCinterion *self)
{
return iface_modem_voice_parent;
}
static void
iface_modem_time_init (MMIfaceModemTime *iface)
{
iface_modem_time_parent = g_type_interface_peek_parent (iface);
iface->setup_unsolicited_events = mm_shared_cinterion_time_setup_unsolicited_events;
iface->setup_unsolicited_events_finish = mm_shared_cinterion_time_setup_unsolicited_events_finish;
iface->cleanup_unsolicited_events = mm_shared_cinterion_time_cleanup_unsolicited_events;
iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_time_cleanup_unsolicited_events_finish;
}
static MMIfaceModemTime *
peek_parent_time_interface (MMSharedCinterion *self)
{
return iface_modem_time_parent;
}
static void
shared_cinterion_init (MMSharedCinterion *iface)
{
iface->peek_parent_interface = peek_parent_interface;
iface->peek_parent_location_interface = peek_parent_location_interface;
iface->peek_parent_voice_interface = peek_parent_voice_interface;
iface->peek_parent_time_interface = peek_parent_time_interface;
}
static void
mm_broadband_modem_qmi_cinterion_class_init (MMBroadbandModemQmiCinterionClass *klass)
{
}
@@ -0,0 +1,48 @@
/* -*- 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 Ammonit Measurement GmbH
* Author: Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_BROADBAND_MODEM_QMI_CINTERION_QMI_H
#define MM_BROADBAND_MODEM_QMI_CINTERION_QMI_H
#include "mm-broadband-modem-qmi.h"
#define MM_TYPE_BROADBAND_MODEM_QMI_CINTERION (mm_broadband_modem_qmi_cinterion_get_type ())
#define MM_BROADBAND_MODEM_QMI_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION, MMBroadbandModemQmiCinterion))
#define MM_BROADBAND_MODEM_QMI_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION, MMBroadbandModemQmiCinterionClass))
#define MM_IS_BROADBAND_MODEM_QMI_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION))
#define MM_IS_BROADBAND_MODEM_QMI_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION))
#define MM_BROADBAND_MODEM_QMI_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI_CINTERION, MMBroadbandModemQmiCinterionClass))
typedef struct _MMBroadbandModemQmiCinterion MMBroadbandModemQmiCinterion;
typedef struct _MMBroadbandModemQmiCinterionClass MMBroadbandModemQmiCinterionClass;
struct _MMBroadbandModemQmiCinterion {
MMBroadbandModemQmi parent;
};
struct _MMBroadbandModemQmiCinterionClass{
MMBroadbandModemQmiClass parent;
};
GType mm_broadband_modem_qmi_cinterion_get_type (void);
MMBroadbandModemQmiCinterion *mm_broadband_modem_qmi_cinterion_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_QMI_CINTERION_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,211 @@
/* -*- 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 Aleksander Morgado <aleksander@aleksander.es>
* Copyright (C) 2016 Trimble Navigation Limited
* Copyright (C) 2016 Matthew Stanger <matthew_stanger@trimble.com>
* Copyright (C) 2019 Purism SPC
*/
#ifndef MM_MODEM_HELPERS_CINTERION_H
#define MM_MODEM_HELPERS_CINTERION_H
#include <glib.h>
#include <ModemManager.h>
#include <mm-base-bearer.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
typedef enum {
MM_CINTERION_MODEM_FAMILY_DEFAULT = 0,
MM_CINTERION_MODEM_FAMILY_IMT = 1,
} MMCinterionModemFamily;
typedef enum {
MM_CINTERION_RADIO_BAND_FORMAT_SINGLE = 0,
MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE = 1,
} MMCinterionRadioBandFormat;
typedef enum {
MM_CINTERION_RB_BLOCK_LEGACY = 0,
MM_CINTERION_RB_BLOCK_GSM = 0,
MM_CINTERION_RB_BLOCK_UMTS = 1,
MM_CINTERION_RB_BLOCK_LTE_LOW = 2,
MM_CINTERION_RB_BLOCK_LTE_HIGH = 3,
MM_CINTERION_RB_BLOCK_N = 4
} MMCinterionRbBlock;
typedef enum {
MM_CINTERION_RADIO_GEN_NONE = 0,
MM_CINTERION_RADIO_GEN_2G = 2,
MM_CINTERION_RADIO_GEN_3G = 3,
MM_CINTERION_RADIO_GEN_4G = 4,
} MMCinterionRadioGen;
/*****************************************************************************/
/* ^SCFG test parser */
gboolean mm_cinterion_parse_scfg_test (const gchar *response,
MMCinterionModemFamily modem_family,
MMModemCharset charset,
GArray **supported_bands,
MMCinterionRadioBandFormat *format,
GError **error);
/*****************************************************************************/
/* ^SCFG response parser */
gboolean mm_cinterion_parse_scfg_response (const gchar *response,
MMCinterionModemFamily modem_family,
MMModemCharset charset,
GArray **bands,
MMCinterionRadioBandFormat format,
GError **error);
/*****************************************************************************/
/* +CNMI test parser */
gboolean mm_cinterion_parse_cnmi_test (const gchar *response,
GArray **supported_mode,
GArray **supported_mt,
GArray **supported_bm,
GArray **supported_ds,
GArray **supported_bfr,
GError **error);
/*****************************************************************************/
/* ^SXRAT test parser */
gboolean mm_cinterion_parse_sxrat_test (const gchar *response,
GArray **supported_rat,
GArray **supported_pref1,
GArray **supported_pref2,
GError **error);
/*****************************************************************************/
/* Build Cinterion-specific band value */
gboolean mm_cinterion_build_band (GArray *bands,
guint *supported,
gboolean only_2g,
MMCinterionRadioBandFormat format,
MMCinterionModemFamily modem_family,
guint *out_band,
GError **error);
/*****************************************************************************/
/* Single ^SIND response parser */
gboolean mm_cinterion_parse_sind_response (const gchar *response,
gchar **description,
guint *mode,
guint *value,
GError **error);
/*****************************************************************************/
/* ^SWWAN response parser */
MMBearerConnectionStatus mm_cinterion_parse_swwan_response (const gchar *response,
guint swwan_index,
gpointer log_object,
GError **error);
/*****************************************************************************/
/* ^SGAUTH response parser */
gboolean mm_cinterion_parse_sgauth_response (const gchar *response,
guint cid,
MMBearerAllowedAuth *out_auth,
gchar **out_username,
GError **error);
/*****************************************************************************/
/* ^SMONG response parser */
gboolean mm_cinterion_parse_smong_response (const gchar *response,
MMModemAccessTechnology *access_tech,
GError **error);
/*****************************************************************************/
/* ^SIND psinfo helper */
MMModemAccessTechnology mm_cinterion_get_access_technology_from_sind_psinfo (guint val,
gpointer log_object);
/*****************************************************************************/
/* ^SLCC URC helpers */
GRegex *mm_cinterion_get_slcc_regex (void);
/* MMCallInfo list management */
gboolean mm_cinterion_parse_slcc_list (const gchar *str,
gpointer log_object,
GList **out_list,
GError **error);
void mm_cinterion_call_info_list_free (GList *call_info_list);
/*****************************************************************************/
/* +CTZU URC helpers */
GRegex *mm_cinterion_get_ctzu_regex (void);
gboolean mm_cinterion_parse_ctzu_urc (GMatchInfo *match_info,
gchar **iso8601p,
MMNetworkTimezone **tzp,
GError **error);
/*****************************************************************************/
/* ^SMONI helper */
gboolean mm_cinterion_parse_smoni_query_response (const gchar *response,
MMCinterionRadioGen *out_tech,
gdouble *out_rssi,
gdouble *out_ecn0,
gdouble *out_rscp,
gdouble *out_rsrp,
gdouble *out_rsrq,
GError **error);
gboolean mm_cinterion_smoni_response_to_signal_info (const gchar *response,
MMSignal **out_gsm,
MMSignal **out_umts,
MMSignal **out_lte,
GError **error);
/*****************************************************************************/
/* ^SCFG="MEopMode/Prov/Cfg" helper */
gboolean mm_cinterion_provcfg_response_to_cid (const gchar *response,
MMCinterionModemFamily modem_family,
MMModemCharset charset,
gpointer log_object,
gint *cid,
GError **error);
/*****************************************************************************/
/* Auth related helpers */
MMBearerAllowedAuth mm_auth_type_from_cinterion_auth_type (guint cinterion_auth);
gchar *mm_cinterion_build_auth_string (gpointer log_object,
MMCinterionModemFamily modem_family,
MMBearerProperties *config,
guint cid);
/*****************************************************************************/
/* ^SXRAT set command helper */
gchar *mm_cinterion_build_sxrat_set_command (MMModemMode allowed,
MMModemMode preferred,
GError **error);
#endif /* MM_MODEM_HELPERS_CINTERION_H */
+218
View File
@@ -0,0 +1,218 @@
/* -*- 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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2011 Ammonit Measurement GmbH
* Copyright (C) 2011 - 2012 Google Inc.
* Author: Aleksander Morgado <aleksander@lanedo.com>
*/
#include <string.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-plugin-cinterion.h"
#include "mm-broadband-modem-cinterion.h"
#include "mm-log-object.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi-cinterion.h"
#endif
#if defined WITH_MBIM
#include "mm-broadband-modem-mbim-cinterion.h"
#endif
G_DEFINE_TYPE (MMPluginCinterion, mm_plugin_cinterion, MM_TYPE_PLUGIN)
MM_PLUGIN_DEFINE_MAJOR_VERSION
MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom init */
#define TAG_CINTERION_APP_PORT "cinterion-app-port"
#define TAG_CINTERION_MODEM_PORT "cinterion-modem-port"
static gboolean
cinterion_custom_init_finish (MMPortProbe *probe,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_boolean (G_TASK (result), error);
}
static void
sqport_ready (MMPortSerialAt *port,
GAsyncResult *res,
GTask *task)
{
MMPortProbe *probe;
const gchar *response;
probe = g_task_get_source_object (task);
/* Ignore errors, just avoid tagging */
response = mm_port_serial_at_command_finish (port, res, NULL);
if (response) {
/* A valid reply to AT^SQPORT tells us this is an AT port already */
mm_port_probe_set_result_at (probe, TRUE);
if (strstr (response, "Application"))
g_object_set_data (G_OBJECT (probe), TAG_CINTERION_APP_PORT, GUINT_TO_POINTER (TRUE));
else if (strstr (response, "Modem"))
g_object_set_data (G_OBJECT (probe), TAG_CINTERION_MODEM_PORT, GUINT_TO_POINTER (TRUE));
}
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
cinterion_custom_init (MMPortProbe *probe,
MMPortSerialAt *port,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (probe, cancellable, callback, user_data);
mm_port_serial_at_command (
port,
"AT^SQPORT?",
3,
FALSE, /* raw */
FALSE, /* allow cached */
cancellable,
(GAsyncReadyCallback) sqport_ready,
task);
}
/*****************************************************************************/
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_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered Cinterion modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_cinterion_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
mm_obj_dbg (self, "MBIM-powered Cinterion modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_cinterion_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
return MM_BASE_MODEM (mm_broadband_modem_cinterion_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
static gboolean
grab_port (MMPlugin *self,
MMBaseModem *modem,
MMPortProbe *probe,
GError **error)
{
MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
MMPortType ptype;
ptype = mm_port_probe_get_port_type (probe);
if (g_object_get_data (G_OBJECT (probe), TAG_CINTERION_APP_PORT)) {
mm_obj_dbg (self, "port '%s/%s' flagged as primary",
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe));
pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
} else if (g_object_get_data (G_OBJECT (probe), TAG_CINTERION_MODEM_PORT)) {
mm_obj_dbg (self, "port '%s/%s' flagged as PPP",
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe));
pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
}
return mm_base_modem_grab_port (modem,
mm_port_probe_peek_port (probe),
ptype,
pflags,
error);
}
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL };
static const gchar *vendor_strings[] = { "cinterion", "siemens", NULL };
static const guint16 vendor_ids[] = { 0x1e2d, 0x0681, 0x1269, 0 };
static const MMAsyncMethod custom_init = {
.async = G_CALLBACK (cinterion_custom_init),
.finish = G_CALLBACK (cinterion_custom_init_finish),
};
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_CINTERION,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
MM_PLUGIN_CUSTOM_INIT, &custom_init,
NULL));
}
static void
mm_plugin_cinterion_init (MMPluginCinterion *self)
{
}
static void
mm_plugin_cinterion_class_init (MMPluginCinterionClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
plugin_class->grab_port = grab_port;
}
@@ -0,0 +1,47 @@
/* -*- 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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2011 Ammonit Measurement GmbH
* Author: Aleksander Morgado <aleksander@lanedo.com>
*/
#ifndef MM_PLUGIN_CINTERION_H
#define MM_PLUGIN_CINTERION_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_CINTERION (mm_plugin_cinterion_get_type ())
#define MM_PLUGIN_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_CINTERION, MMPluginCinterion))
#define MM_PLUGIN_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_CINTERION, MMPluginCinterionClass))
#define MM_IS_PLUGIN_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_CINTERION))
#define MM_IS_PLUGIN_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_CINTERION))
#define MM_PLUGIN_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_CINTERION, MMPluginCinterionClass))
typedef struct {
MMPlugin parent;
} MMPluginCinterion;
typedef struct {
MMPluginClass parent;
} MMPluginCinterionClass;
GType mm_plugin_cinterion_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_CINTERION_H */
File diff suppressed because it is too large Load Diff
+153
View File
@@ -0,0 +1,153 @@
/* -*- 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 Ammonit Measurement GmbH
* Copyright (C) 2014 - 2018 Aleksander Morgado <aleksander@aleksander.es>
* Copyright (C) 2019 Purism SPC
*/
#ifndef MM_SHARED_CINTERION_H
#define MM_SHARED_CINTERION_H
#include <glib-object.h>
#include <gio/gio.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-broadband-modem.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-location.h"
#include "mm-iface-modem-voice.h"
#include "mm-iface-modem-time.h"
#define MM_TYPE_SHARED_CINTERION (mm_shared_cinterion_get_type ())
#define MM_SHARED_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_CINTERION, MMSharedCinterion))
#define MM_IS_SHARED_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_CINTERION))
#define MM_SHARED_CINTERION_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_CINTERION, MMSharedCinterion))
typedef struct _MMSharedCinterion MMSharedCinterion;
struct _MMSharedCinterion {
GTypeInterface g_iface;
/* Peek modem interface of the parent class of the object */
MMIfaceModem * (* peek_parent_interface) (MMSharedCinterion *self);
/* Peek location interface of the parent class of the object */
MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedCinterion *self);
/* Peek voice interface of the parent class of the object */
MMIfaceModemVoice * (* peek_parent_voice_interface) (MMSharedCinterion *self);
/* Peek time interface of the parent class of the object */
MMIfaceModemTime * (* peek_parent_time_interface) (MMSharedCinterion *self);
};
GType mm_shared_cinterion_get_type (void);
/*****************************************************************************/
/* Modem interface */
void mm_shared_cinterion_modem_reset (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_modem_reset_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
/*****************************************************************************/
/* Location interface */
void mm_shared_cinterion_location_load_capabilities (MMIfaceModemLocation *self,
GAsyncReadyCallback callback,
gpointer user_data);
MMModemLocationSource mm_shared_cinterion_location_load_capabilities_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error);
void mm_shared_cinterion_enable_location_gathering (MMIfaceModemLocation *self,
MMModemLocationSource source,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_enable_location_gathering_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error);
void mm_shared_cinterion_disable_location_gathering (MMIfaceModemLocation *self,
MMModemLocationSource source,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_disable_location_gathering_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error);
/*****************************************************************************/
/* Voice interface */
MMBaseCall *mm_shared_cinterion_create_call (MMIfaceModemVoice *self,
MMCallDirection direction,
const gchar *number);
void mm_shared_cinterion_voice_check_support (MMIfaceModemVoice *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_voice_check_support_finish (MMIfaceModemVoice *self,
GAsyncResult *res,
GError **error);
void mm_shared_cinterion_voice_setup_unsolicited_events (MMIfaceModemVoice *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self,
GAsyncResult *res,
GError **error);
void mm_shared_cinterion_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self,
GAsyncResult *res,
GError **error);
void mm_shared_cinterion_voice_enable_unsolicited_events (MMIfaceModemVoice *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self,
GAsyncResult *res,
GError **error);
void mm_shared_cinterion_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self,
GAsyncResult *res,
GError **error);
/*****************************************************************************/
/* Time interface */
void mm_shared_cinterion_time_setup_unsolicited_events (MMIfaceModemTime *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_time_setup_unsolicited_events_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error);
void mm_shared_cinterion_time_cleanup_unsolicited_events (MMIfaceModemTime *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_cinterion_time_cleanup_unsolicited_events_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error);
#endif /* MM_SHARED_CINTERION_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,32 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_dell_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="413c", GOTO="mm_dell_vendorcheck"
GOTO="mm_dell_port_types_end"
LABEL="mm_dell_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Dell DW5821e (default 0x81d7, with esim support 0x81e0)
# if 02: primary port
# if 03: secondary port
# if 04: raw NMEA port
# if 05: diag/qcdm port
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
# Dell DW5820e
# if 02: AT port
# if 04: debug port (ignore)
# if 06: AT port
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d9", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
GOTO="mm_dell_port_types_end"
LABEL="mm_dell_port_types_end"
+528
View File
@@ -0,0 +1,528 @@
/* -*- 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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2015-2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <string.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-plugin-dell.h"
#include "mm-common-novatel.h"
#include "mm-private-boxed-types.h"
#include "mm-broadband-modem.h"
#include "mm-broadband-modem-novatel.h"
#include "mm-common-novatel.h"
#include "mm-broadband-modem-sierra.h"
#include "mm-common-sierra.h"
#include "mm-broadband-modem-telit.h"
#include "mm-broadband-modem-xmm.h"
#include "mm-common-telit.h"
#include "mm-log-object.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
#endif
#if defined WITH_MBIM
#include "mm-broadband-modem-mbim.h"
#include "mm-broadband-modem-mbim-xmm.h"
#include "mm-broadband-modem-mbim-foxconn.h"
#endif
#define MAX_PORT_PROBE_TIMEOUTS 3
G_DEFINE_TYPE (MMPluginDell, mm_plugin_dell, MM_TYPE_PLUGIN)
MM_PLUGIN_DEFINE_MAJOR_VERSION
MM_PLUGIN_DEFINE_MINOR_VERSION
#define TAG_DELL_MANUFACTURER "dell-manufacturer"
typedef enum {
DELL_MANUFACTURER_UNKNOWN = 0,
DELL_MANUFACTURER_NOVATEL = 1,
DELL_MANUFACTURER_SIERRA = 2,
DELL_MANUFACTURER_ERICSSON = 3,
DELL_MANUFACTURER_TELIT = 4
} DellManufacturer;
/*****************************************************************************/
/* Custom init */
typedef struct {
MMPortSerialAt *port;
guint gmi_retries;
guint cgmi_retries;
guint ati_retries;
guint timeouts;
} CustomInitContext;
static void
custom_init_context_free (CustomInitContext *ctx)
{
g_object_unref (ctx->port);
g_slice_free (CustomInitContext, ctx);
}
static gboolean
dell_custom_init_finish (MMPortProbe *probe,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
novatel_custom_init_ready (MMPortProbe *probe,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_common_novatel_custom_init_finish (probe, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
sierra_custom_init_ready (MMPortProbe *probe,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_common_sierra_custom_init_finish (probe, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
telit_custom_init_ready (MMPortProbe *probe,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!telit_custom_init_finish (probe, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void custom_init_step (GTask *task);
static void
custom_init_step_next_command (GTask *task)
{
CustomInitContext *ctx;
ctx = g_task_get_task_data (task);
ctx->timeouts = 0;
if (ctx->gmi_retries > 0)
ctx->gmi_retries = 0;
else if (ctx->cgmi_retries > 0)
ctx->cgmi_retries = 0;
else if (ctx->ati_retries > 0)
ctx->ati_retries = 0;
custom_init_step (task);
}
static void
response_ready (MMPortSerialAt *port,
GAsyncResult *res,
GTask *task)
{
CustomInitContext *ctx;
MMPortProbe *probe;
const gchar *response;
GError *error = NULL;
gchar *lower;
DellManufacturer manufacturer;
probe = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
response = mm_port_serial_at_command_finish (port, res, &error);
if (error) {
/* Non-timeout error, jump to next command */
if (!g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
mm_obj_dbg (probe, "error probing AT port: %s", error->message);
g_error_free (error);
custom_init_step_next_command (task);
return;
}
/* Directly retry same command on timeout */
ctx->timeouts++;
custom_init_step (task);
g_error_free (error);
return;
}
/* Guess manufacturer from response */
lower = g_ascii_strdown (response, -1);
if (strstr (lower, "novatel"))
manufacturer = DELL_MANUFACTURER_NOVATEL;
else if (strstr (lower, "sierra"))
manufacturer = DELL_MANUFACTURER_SIERRA;
else if (strstr (lower, "ericsson"))
manufacturer = DELL_MANUFACTURER_ERICSSON;
else if (strstr (lower, "telit"))
manufacturer = DELL_MANUFACTURER_TELIT;
else
manufacturer = DELL_MANUFACTURER_UNKNOWN;
g_free (lower);
/* Tag based on manufacturer */
if (manufacturer != DELL_MANUFACTURER_UNKNOWN) {
g_object_set_data (G_OBJECT (probe), TAG_DELL_MANUFACTURER, GUINT_TO_POINTER (manufacturer));
/* Run additional custom init, if needed */
if (manufacturer == DELL_MANUFACTURER_NOVATEL) {
mm_common_novatel_custom_init (probe,
ctx->port,
g_task_get_cancellable (task),
(GAsyncReadyCallback) novatel_custom_init_ready,
task);
return;
}
if (manufacturer == DELL_MANUFACTURER_SIERRA) {
mm_common_sierra_custom_init (probe,
ctx->port,
g_task_get_cancellable (task),
(GAsyncReadyCallback) sierra_custom_init_ready,
task);
return;
}
if (manufacturer == DELL_MANUFACTURER_TELIT) {
telit_custom_init (probe,
ctx->port,
g_task_get_cancellable (task),
(GAsyncReadyCallback) telit_custom_init_ready,
task);
return;
}
/* Finish custom_init */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* If we got a response, but we didn't get an expected string, try with next command */
custom_init_step_next_command (task);
}
static void
custom_init_step (GTask *task)
{
CustomInitContext *ctx;
MMPortProbe *probe;
probe = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
/* If cancelled, end without error right away */
if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
mm_obj_dbg (probe, "no need to keep on running custom init: cancelled");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
#if defined WITH_QMI
/* If device has a QMI port, don't run anything else, as we don't care */
if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (probe)))) {
mm_obj_dbg (probe, "no need to run custom init: device has QMI port");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
#endif
#if defined WITH_MBIM
/* If device has a MBIM port, don't run anything else, as we don't care */
if (mm_port_probe_list_has_mbim_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (probe)))) {
mm_obj_dbg (probe, "no need to run custom init: device has MBIM port");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
#endif
if (ctx->timeouts >= MAX_PORT_PROBE_TIMEOUTS) {
mm_obj_dbg (probe, "couldn't detect real manufacturer: too many timeouts");
mm_port_probe_set_result_at (probe, FALSE);
goto out;
}
if (ctx->gmi_retries > 0) {
ctx->gmi_retries--;
mm_port_serial_at_command (ctx->port,
"AT+GMI",
3,
FALSE, /* raw */
FALSE, /* allow_cached */
g_task_get_cancellable (task),
(GAsyncReadyCallback)response_ready,
task);
return;
}
if (ctx->cgmi_retries > 0) {
ctx->cgmi_retries--;
mm_port_serial_at_command (ctx->port,
"AT+CGMI",
3,
FALSE, /* raw */
FALSE, /* allow_cached */
g_task_get_cancellable (task),
(GAsyncReadyCallback)response_ready,
task);
return;
}
if (ctx->ati_retries > 0) {
ctx->ati_retries--;
/* Note: in Ericsson devices, ATI3 seems to reply the vendor string */
mm_port_serial_at_command (ctx->port,
"ATI1I2I3",
3,
FALSE, /* raw */
FALSE, /* allow_cached */
g_task_get_cancellable (task),
(GAsyncReadyCallback)response_ready,
task);
return;
}
mm_obj_dbg (probe, "couldn't detect real manufacturer: all retries consumed");
out:
/* Finish custom_init */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
dell_custom_init (MMPortProbe *probe,
MMPortSerialAt *port,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
CustomInitContext *ctx;
ctx = g_slice_new0 (CustomInitContext);
ctx->port = g_object_ref (port);
ctx->gmi_retries = 3;
ctx->cgmi_retries = 1;
ctx->ati_retries = 1;
task = g_task_new (probe, cancellable, callback, user_data);
g_task_set_check_cancellable (task, FALSE);
g_task_set_task_data (task, ctx, (GDestroyNotify) custom_init_context_free);
custom_init_step (task);
}
/*****************************************************************************/
static gboolean
port_probe_list_has_manufacturer_port (GList *probes,
DellManufacturer manufacturer)
{
GList *l;
for (l = probes; l; l = g_list_next (l)) {
if (GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (l->data), TAG_DELL_MANUFACTURER)) == manufacturer)
return TRUE;
}
return FALSE;
}
static MMBaseModem *
create_modem (MMPlugin *self,
const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
guint16 subsystem_vendor,
GList *probes,
GError **error)
{
/* Note: at this point we don't make any difference between different
* Dell-branded QMI or MBIM modems; they may come from Novatel, Ericsson or
* Sierra. */
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered Dell-branded modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
/* Specific implementation for the DW5821e and DW5829e */
if (vendor == 0x413c && (product == 0x81d7 || product == 0x81e0 || product == 0x81e4 || product == 0x81e6)) {
mm_obj_dbg (self, "MBIM-powered DW5821e/DW5829e (T77W968) modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_foxconn_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
if (mm_port_probe_list_is_xmm (probes)) {
mm_obj_dbg (self, "MBIM-powered XMM-based modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_xmm_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
mm_obj_dbg (self, "MBIM-powered Dell-branded modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_NOVATEL)) {
mm_obj_dbg (self, "Novatel-powered Dell-branded modem found...");
return MM_BASE_MODEM (mm_broadband_modem_novatel_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_SIERRA)) {
mm_obj_dbg (self, "Sierra-powered Dell-branded modem found...");
return MM_BASE_MODEM (mm_broadband_modem_sierra_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_TELIT)) {
mm_obj_dbg (self, "Telit-powered Dell-branded modem found...");
return MM_BASE_MODEM (mm_broadband_modem_telit_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
if (mm_port_probe_list_is_xmm (probes)) {
mm_obj_dbg (self, "XMM-based modem found...");
return MM_BASE_MODEM (mm_broadband_modem_xmm_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
mm_obj_dbg (self, "Dell-branded generic modem found...");
return MM_BASE_MODEM (mm_broadband_modem_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
/*****************************************************************************/
static gboolean
grab_port (MMPlugin *self,
MMBaseModem *modem,
MMPortProbe *probe,
GError **error)
{
if (MM_IS_BROADBAND_MODEM_SIERRA (modem))
return mm_common_sierra_grab_port (self, modem, probe, error);
if (MM_IS_BROADBAND_MODEM_TELIT (modem))
return telit_grab_port (self, modem, probe, error);
return mm_base_modem_grab_port (modem,
mm_port_probe_peek_port (probe),
mm_port_probe_get_port_type (probe),
MM_PORT_SERIAL_AT_FLAG_NONE,
error);
}
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const guint16 vendors[] = { 0x413c, 0 };
static const MMAsyncMethod custom_init = {
.async = G_CALLBACK (dell_custom_init),
.finish = G_CALLBACK (dell_custom_init_finish),
};
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_DELL,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendors,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_CUSTOM_INIT, &custom_init,
MM_PLUGIN_ALLOWED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
MM_PLUGIN_XMM_PROBE, TRUE,
NULL));
}
static void
mm_plugin_dell_init (MMPluginDell *self)
{
}
static void
mm_plugin_dell_class_init (MMPluginDellClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
plugin_class->grab_port = grab_port;
}
+46
View File
@@ -0,0 +1,46 @@
/* -*- 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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2015 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_PLUGIN_DELL_H
#define MM_PLUGIN_DELL_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_DELL (mm_plugin_dell_get_type ())
#define MM_PLUGIN_DELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_DELL, MMPluginDell))
#define MM_PLUGIN_DELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_DELL, MMPluginDellClass))
#define MM_IS_PLUGIN_DELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_DELL))
#define MM_IS_PLUGIN_DELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_DELL))
#define MM_PLUGIN_DELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_DELL, MMPluginDellClass))
typedef struct {
MMPlugin parent;
} MMPluginDell;
typedef struct {
MMPluginClass parent;
} MMPluginDellClass;
GType mm_plugin_dell_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_DELL_H */
@@ -0,0 +1,16 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_dlink_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2001", GOTO="mm_dlink_port_types"
GOTO="mm_dlink_port_types_end"
LABEL="mm_dlink_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# D-Link DWM-222
ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
LABEL="mm_dlink_port_types_end"
+94
View File
@@ -0,0 +1,94 @@
/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <string.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log-object.h"
#include "mm-plugin-dlink.h"
#include "mm-broadband-modem.h"
#if defined WITH_QMI
# include "mm-broadband-modem-qmi.h"
#endif
G_DEFINE_TYPE (MMPluginDlink, mm_plugin_dlink, 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_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered D-Link modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
return MM_BASE_MODEM (mm_broadband_modem_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 guint16 vendor_ids[] = { 0x2001, 0 };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_DLINK,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
NULL));
}
static void
mm_plugin_dlink_init (MMPluginDlink *self)
{
}
static void
mm_plugin_dlink_class_init (MMPluginDlinkClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+40
View File
@@ -0,0 +1,40 @@
/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_PLUGIN_DLINK_H
#define MM_PLUGIN_DLINK_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_DLINK (mm_plugin_dlink_get_type ())
#define MM_PLUGIN_DLINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_DLINK, MMPluginDlink))
#define MM_PLUGIN_DLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_DLINK, MMPluginDlinkClass))
#define MM_IS_PLUGIN_DLINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_DLINK))
#define MM_IS_PLUGIN_DLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_DLINK))
#define MM_PLUGIN_DLINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_DLINK, MMPluginDlinkClass))
typedef struct {
MMPlugin parent;
} MMPluginDlink;
typedef struct {
MMPluginClass parent;
} MMPluginDlinkClass;
GType mm_plugin_dlink_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_DLINK_H */
@@ -0,0 +1,109 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_fibocom_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2cb7", GOTO="mm_fibocom_port_types"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1782", GOTO="mm_fibocom_port_types"
GOTO="mm_fibocom_port_types_end"
LABEL="mm_fibocom_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Fibocom L850-GL attach APN with toggle modem power
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0007", ENV{ID_MM_FIBOCOM_INITIAL_EPS_OFF_ON}="1"
# Fibocom L850-GL
# ttyACM0 (if #2): AT port
# ttyACM1 (if #4): debug port (ignore)
# ttyACM2 (if #6): AT port
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
# Fibocom NL668-AM
# ttyACM0 (if #2): AT port
# ttyACM1 (if #3): AT port
# ttyACM2 (if #4): debug port (ignore)
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
# Fibocom FM150
# ttyUSB0 (if #0): QCDM port
# ttyUSB1 (if #1): AT port
# ttyUSB2 (if #2): AT port
# ttyUSB2 (if #3): Ignore
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
# Fibocom FM101-GL (MBIM)
# ttyUSB0 (if #2): AT port
# ttyUSB1 (if #3): AT port
# ttyUSB2 (if #4): debug port (ignore)
# ttyUSB3 (if #5): debug port (ignore)
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1"
# Fibocom FM101-GL (ADB)
# ttyUSB0 (if #2): debug port (ignore)
# ttyUSB1 (if #3): AT port
# ttyUSB2 (if #4): debug port (ignore)
# ttyUSB3 (if #5): debug port (ignore)
# ttyUSB4 (if #6): debug port (ignore)
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a4", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
# Fibocom MA510-GL (GTUSBMODE=31)
# ttyUSB0 (if #0): debug port (ignore)
# ttyUSB1 (if #1): AT port
# ttyUSB2 (if #2): AT port
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0106", ENV{ID_MM_FIBOCOM_INITIAL_EPS_CID}="1"
# Fibocom MA510-GL (GTUSBMODE=32)
# ttyUSB1 (if #0): AT port
# ttyUSB2 (if #1): AT port
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="010a", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="010a", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="010a", ENV{ID_MM_FIBOCOM_INITIAL_EPS_CID}="1"
# Fibocom L610 (GTUSBMODE=31)
# ttyUSB0 (if #0): AT port
# ttyUSB1 (if #1): NV
# ttyUSB2 (if #2): MOS
# ttyUSB3 (if #3): Diagnostic
# ttyUSB4 (if #4): Logging
# ttyUSB5 (if #5): AT port
# ttyUSB6 (if #6): AT port
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d10", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
# Fibocom L610 (GTUSBMODE={32,33} - ECM/RNDIS)
# ttyUSB0 (if #2): AT port
# ttyUSB1 (if #3): NV
# ttyUSB2 (if #4): MOS
# ttyUSB3 (if #5): Diagnostic
# ttyUSB4 (if #6): Logging
# ttyUSB5 (if #7): AT port
# ttyUSB6 (if #8): AT port
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="07", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1782", ATTRS{idProduct}=="4d11", ENV{.MM_USBIFNUM}=="08", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
LABEL="mm_fibocom_port_types_end"
@@ -0,0 +1,540 @@
/* -*- 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) 2022 Disruptive Technologies Research AS
*/
#include <config.h>
#include "mm-broadband-bearer-fibocom-ecm.h"
#include "mm-broadband-modem-fibocom.h"
#include "mm-base-modem-at.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-log.h"
G_DEFINE_TYPE (MMBroadbandBearerFibocomEcm, mm_broadband_bearer_fibocom_ecm, MM_TYPE_BROADBAND_BEARER)
/*****************************************************************************/
/* Common helper functions */
static gboolean
parse_gtrndis_read_response (const gchar *response,
guint *state,
guint *cid,
GError **error)
{
g_autoptr(GRegex) r = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
r = g_regex_new ("\\+GTRNDIS:\\s*(\\d+)(?:,(\\d+))?",
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
g_assert (r != NULL);
if (!g_regex_match (r, response, 0, &match_info)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Invalid +GTRNDIS response: %s", response);
return FALSE;
}
if (!mm_get_uint_from_match_info (match_info, 1, state)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Failed to match state in +GTRNDIS response: %s", response);
return FALSE;
}
if (*state) {
if (!mm_get_uint_from_match_info (match_info, 2, cid)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Failed to match cid in +GTRNDIS response: %s", response);
return FALSE;
}
} else {
*cid = 0;
}
return TRUE;
}
/*****************************************************************************/
/* Connection status monitoring */
static MMBearerConnectionStatus
load_connection_status_finish (MMBaseBearer *bearer,
GAsyncResult *res,
GError **error)
{
GError *inner_error = NULL;
gssize value;
value = g_task_propagate_int (G_TASK (res), &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
}
return (MMBearerConnectionStatus) value;
}
static void
gtrndis_query_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBaseBearer *bearer;
GError *error = NULL;
const gchar *result;
guint state;
guint cid;
bearer = g_task_get_source_object (task);
result = mm_base_modem_at_command_finish (modem, res, &error);
if (!result)
g_task_return_error (task, error);
else if (!parse_gtrndis_read_response (result, &state, &cid, &error))
g_task_return_error (task, error);
else if (!state || (gint) cid != mm_base_bearer_get_profile_id (bearer))
g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
else
g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_CONNECTED);
g_object_unref (task);
}
static void
load_connection_status (MMBaseBearer *bearer,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
MMBaseModem *modem = NULL;
task = g_task_new (bearer, NULL, callback, user_data);
g_object_get (MM_BASE_BEARER (bearer),
MM_BASE_BEARER_MODEM, &modem,
NULL);
mm_base_modem_at_command (modem,
"+GTRNDIS?",
3,
FALSE,
(GAsyncReadyCallback) gtrndis_query_ready,
task);
g_object_unref (modem);
}
/*****************************************************************************/
/* 3GPP Connect */
typedef struct {
MMBroadbandModem *modem;
MMPortSerialAt *primary;
MMPortSerialAt *secondary;
MMBearerIpFamily ip_family;
} ConnectContext;
static void
connect_context_free (ConnectContext *ctx)
{
g_clear_object (&ctx->modem);
g_clear_object (&ctx->primary);
g_clear_object (&ctx->secondary);
g_slice_free (ConnectContext, ctx);
}
static MMBearerConnectResult *
connect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_connect_3gpp_ready (MMBroadbandBearer *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
MMBearerConnectResult *result;
result = MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_fibocom_ecm_parent_class)->connect_3gpp_finish (self, res, &error);
if (result)
g_task_return_pointer (task, result, (GDestroyNotify) mm_bearer_connect_result_unref);
else
g_task_return_error (task, error);
g_object_unref (task);
}
static void
disconnect_3gpp_ready (MMBroadbandBearer *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
gboolean result;
ConnectContext *ctx;
result = MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self, res, &error);
if (!result) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx = g_task_get_task_data (task);
MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_fibocom_ecm_parent_class)->connect_3gpp (
self,
ctx->modem,
ctx->primary,
ctx->secondary,
g_task_get_cancellable (task),
(GAsyncReadyCallback) parent_connect_3gpp_ready,
task);
}
static void
gtrndis_check_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBroadbandBearer *self;
ConnectContext *ctx;
GError *error = NULL;
const gchar *response;
guint state;
guint cid;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (modem, res, &error);
if (!response) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (!parse_gtrndis_read_response (response, &state, &cid, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (state) {
/* RNDIS is already active, disconnect first. */
mm_obj_dbg (self, "RNDIS active, tearing down existing connection...");
MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp (
MM_BROADBAND_BEARER (self),
ctx->modem,
ctx->primary,
ctx->secondary,
NULL, /* data port */
cid,
(GAsyncReadyCallback) disconnect_3gpp_ready,
task);
return;
}
/* Execute the regular connection flow if RNDIS is inactive. */
mm_obj_dbg (self, "RNDIS inactive");
MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_fibocom_ecm_parent_class)->connect_3gpp (
MM_BROADBAND_BEARER (self),
ctx->modem,
ctx->primary,
ctx->secondary,
g_task_get_cancellable (task),
(GAsyncReadyCallback) parent_connect_3gpp_ready,
task);
}
static void
connect_3gpp (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
ConnectContext *ctx;
GTask *task;
ctx = g_slice_new0 (ConnectContext);
ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
ctx->secondary = secondary ? g_object_ref (secondary) : NULL;
ctx->ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
mm_3gpp_normalize_ip_family (&ctx->ip_family);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify) connect_context_free);
/* First, we must check whether RNDIS is already active */
mm_base_modem_at_command (MM_BASE_MODEM (modem),
"+GTRNDIS?",
3,
FALSE, /* allow_cached */
(GAsyncReadyCallback) gtrndis_check_ready,
task);
}
/*****************************************************************************/
/* Dial context and task */
typedef struct {
MMBroadbandModem *modem;
MMPortSerialAt *primary;
guint cid;
MMPort *data;
} DialContext;
static void
dial_task_free (DialContext *ctx)
{
g_object_unref (ctx->modem);
g_object_unref (ctx->primary);
if (ctx->data)
g_object_unref (ctx->data);
g_slice_free (DialContext, ctx);
}
static GTask *
dial_task_new (MMBroadbandBearerFibocomEcm *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
guint cid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
DialContext *ctx;
GTask *task;
ctx = g_slice_new0 (DialContext);
ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
ctx->cid = cid;
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify) dial_task_free);
ctx->data = mm_base_modem_get_best_data_port (MM_BASE_MODEM (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 NULL;
}
return task;
}
/*****************************************************************************/
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
static MMPort *
dial_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
gtrndis_verify_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
DialContext *ctx;
GError *error = NULL;
const gchar *response;
ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (modem, res, &error);
if (!response)
g_task_return_error (task, error);
else {
response = mm_strip_tag (response, "+GTRNDIS:");
if (strtol (response, NULL, 10) != 1)
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Connection status verification failed");
else
g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
}
g_object_unref (task);
}
static void
gtrndis_activate_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (modem, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mm_base_modem_at_command (modem,
"+GTRNDIS?",
6, /* timeout [s] */
FALSE, /* allow_cached */
(GAsyncReadyCallback) gtrndis_verify_ready,
task);
}
static void
dial_3gpp (MMBroadbandBearer *self,
MMBaseModem *modem,
MMPortSerialAt *primary,
guint cid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_autofree gchar *cmd = NULL;
task = dial_task_new (MM_BROADBAND_BEARER_FIBOCOM_ECM (self),
MM_BROADBAND_MODEM (modem),
primary,
cid,
cancellable,
callback,
user_data);
if (!task)
return;
cmd = g_strdup_printf ("+GTRNDIS=1,%u", cid);
mm_base_modem_at_command (modem,
cmd,
MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE, /* allow_cached */
(GAsyncReadyCallback) gtrndis_activate_ready,
task);
}
/*****************************************************************************/
/* 3GPP Disconnect sequence */
static gboolean
disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
gtrndis_deactivate_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (modem, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
disconnect_3gpp (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_autofree gchar *cmd = NULL;
task = g_task_new (self, NULL, callback, user_data);
cmd = g_strdup_printf ("+GTRNDIS=0,%u", cid);
mm_base_modem_at_command (MM_BASE_MODEM (modem),
cmd,
MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE, /* allow_cached */
(GAsyncReadyCallback) gtrndis_deactivate_ready,
task);
}
/*****************************************************************************/
MMBaseBearer *
mm_broadband_bearer_fibocom_ecm_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_fibocom_ecm_new (MMBroadbandModemFibocom *modem,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (
MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_BEARER_MODEM, modem,
MM_BASE_BEARER_CONFIG, config,
NULL);
}
static void
mm_broadband_bearer_fibocom_ecm_init (MMBroadbandBearerFibocomEcm *self)
{
}
static void
mm_broadband_bearer_fibocom_ecm_class_init (MMBroadbandBearerFibocomEcmClass *klass)
{
MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
base_bearer_class->load_connection_status = load_connection_status;
base_bearer_class->load_connection_status_finish = load_connection_status_finish;
broadband_bearer_class->connect_3gpp = connect_3gpp;
broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
}
@@ -0,0 +1,50 @@
/* -*- 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) 2022 Disruptive Technologies Research AS
*/
#ifndef MM_BROADBAND_BEARER_FIBOCOM_ECM_H
#define MM_BROADBAND_BEARER_FIBOCOM_ECM_H
#include "mm-broadband-bearer.h"
#include "mm-broadband-modem-fibocom.h"
#define MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM (mm_broadband_bearer_fibocom_ecm_get_type ())
#define MM_BROADBAND_BEARER_FIBOCOM_ECM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM, MMBroadbandBearerFibocomEcm))
#define MM_BROADBAND_BEARER_FIBOCOM_ECM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM, MMBroadbandBearerFibocomEcmClass))
#define MM_IS_BROADBAND_BEARER_FIBOCOM_ECM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM))
#define MM_IS_BROADBAND_BEARER_FIBOCOM_ECM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM))
#define MM_BROADBAND_BEARER_FIBOCOM_ECM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_FIBOCOM_ECM, MMBroadbandBearerFibocomEcmClass))
typedef struct _MMBroadbandBearerFibocomEcm MMBroadbandBearerFibocomEcm;
typedef struct _MMBroadbandBearerFibocomEcmClass MMBroadbandBearerFibocomEcmClass;
struct _MMBroadbandBearerFibocomEcm {
MMBroadbandBearer parent;
};
struct _MMBroadbandBearerFibocomEcmClass {
MMBroadbandBearerClass parent;
};
GType mm_broadband_bearer_fibocom_ecm_get_type (void);
void mm_broadband_bearer_fibocom_ecm_new (MMBroadbandModemFibocom *modem,
MMBearerProperties *properties,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MMBaseBearer *mm_broadband_bearer_fibocom_ecm_new_finish (GAsyncResult *res,
GError **error);
#endif /* MM_BROADBAND_BEARER_FIBOCOM_ECM_H */
@@ -0,0 +1,763 @@
/* -*- 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) 2022 Disruptive Technologies Research AS
*/
#include <config.h>
#include "mm-broadband-modem-fibocom.h"
#include "mm-broadband-bearer-fibocom-ecm.h"
#include "mm-broadband-modem.h"
#include "mm-base-modem-at.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem-3gpp-profile-manager.h"
#include "mm-log.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface);
static MMIfaceModem3gppProfileManager *iface_modem_3gpp_profile_manager_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemFibocom, mm_broadband_modem_fibocom, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init))
typedef enum {
FEATURE_SUPPORT_UNKNOWN,
FEATURE_NOT_SUPPORTED,
FEATURE_SUPPORTED,
} FeatureSupport;
struct _MMBroadbandModemFibocomPrivate {
FeatureSupport gtrndis_support;
GRegex *sim_ready_regex;
FeatureSupport initial_eps_bearer_support;
gint initial_eps_bearer_cid;
};
/*****************************************************************************/
/* Create Bearer (Modem interface) */
static MMBaseBearer *
modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_fibocom_ecm_new_ready (GObject *source,
GAsyncResult *res,
GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_fibocom_ecm_new_finish (res, &error);
if (!bearer)
g_task_return_error (task, error);
else
g_task_return_pointer (task, bearer, g_object_unref);
g_object_unref (task);
}
static void
broadband_bearer_new_ready (GObject *source,
GAsyncResult *res,
GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_new_finish (res, &error);
if (!bearer)
g_task_return_error (task, error);
else
g_task_return_pointer (task, bearer, g_object_unref);
g_object_unref (task);
}
static void
common_create_bearer (GTask *task)
{
MMBroadbandModemFibocom *self;
self = g_task_get_source_object (task);
switch (self->priv->gtrndis_support) {
case FEATURE_SUPPORTED:
mm_obj_dbg (self, "+GTRNDIS supported, creating Fibocom ECM bearer");
mm_broadband_bearer_fibocom_ecm_new (self,
g_task_get_task_data (task),
NULL, /* cancellable */
(GAsyncReadyCallback) broadband_bearer_fibocom_ecm_new_ready,
task);
return;
case FEATURE_NOT_SUPPORTED:
mm_obj_dbg (self, "+GTRNDIS not supported, creating generic PPP bearer");
mm_broadband_bearer_new (MM_BROADBAND_MODEM (self),
g_task_get_task_data (task),
NULL, /* cancellable */
(GAsyncReadyCallback) broadband_bearer_new_ready,
task);
return;
case FEATURE_SUPPORT_UNKNOWN:
default:
g_assert_not_reached ();
}
}
static void
gtrndis_test_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
if (!mm_base_modem_at_command_finish (_self, res, NULL)) {
mm_obj_dbg (self, "+GTRNDIS unsupported");
self->priv->gtrndis_support = FEATURE_NOT_SUPPORTED;
} else {
mm_obj_dbg (self, "+GTRNDIS supported");
self->priv->gtrndis_support = FEATURE_SUPPORTED;
}
/* Go on and create the bearer */
common_create_bearer (task);
}
static void
modem_create_bearer (MMIfaceModem *_self,
MMBearerProperties *properties,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, g_object_ref (properties), g_object_unref);
if (self->priv->gtrndis_support != FEATURE_SUPPORT_UNKNOWN) {
common_create_bearer (task);
return;
}
if (!mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET)) {
mm_obj_dbg (self, "skipping +GTRNDIS check as no data port is available");
self->priv->gtrndis_support = FEATURE_NOT_SUPPORTED;
common_create_bearer (task);
return;
}
mm_obj_dbg (self, "checking +GTRNDIS support...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+GTRNDIS=?",
6, /* timeout [s] */
TRUE, /* allow_cached */
(GAsyncReadyCallback) gtrndis_test_ready,
task);
}
/*****************************************************************************/
/* Reset / Power (Modem interface) */
static gboolean
modem_common_power_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
}
static void
modem_reset (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CFUN=15",
15,
FALSE,
callback,
user_data);
}
static void
modem_power_down (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CFUN=4",
15,
FALSE,
callback,
user_data);
}
static void
modem_power_off (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CPWROFF",
3,
FALSE,
callback,
user_data);
}
/*****************************************************************************/
/* Load initial EPS bearer properties (as agreed with network) */
static MMBearerProperties *
modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
}
static void
load_initial_eps_cgcontrdp_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
const gchar *response;
g_autofree gchar *apn = NULL;
MMBearerProperties *properties;
response = mm_base_modem_at_command_finish (self, res, &error);
if (!response || !mm_3gpp_parse_cgcontrdp_response (response, NULL, NULL, &apn, NULL, NULL, NULL, NULL, NULL, &error))
g_task_return_error (task, error);
else {
properties = mm_bearer_properties_new ();
mm_bearer_properties_set_apn (properties, apn);
g_task_return_pointer (task, properties, g_object_unref);
}
g_object_unref (task);
}
static void
modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
GTask *task;
g_autofree gchar *cmd = NULL;
task = g_task_new (self, NULL, callback, user_data);
if (self->priv->initial_eps_bearer_support != FEATURE_SUPPORTED) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Initial EPS bearer context ID unknown");
g_object_unref (task);
return;
}
g_assert (self->priv->initial_eps_bearer_cid >= 0);
cmd = g_strdup_printf ("+CGCONTRDP=%d", self->priv->initial_eps_bearer_cid);
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
3,
FALSE,
(GAsyncReadyCallback) load_initial_eps_cgcontrdp_ready,
task);
}
/*****************************************************************************/
/* Load initial EPS bearer settings (currently configured in modem) */
static MMBearerProperties *
modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
}
static void
load_initial_eps_bearer_get_profile_ready (MMIfaceModem3gppProfileManager *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
g_autoptr(MM3gppProfile) profile = NULL;
MMBearerProperties *properties;
profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (self, res, &error);
if (!profile) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
properties = mm_bearer_properties_new_from_profile (profile, &error);
if (!properties)
g_task_return_error (task, error);
else
g_task_return_pointer (task, properties, g_object_unref);
g_object_unref (task);
}
static void
modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
MMPortSerialAt *port;
MMKernelDevice *device;
GTask *task;
/* Initial EPS bearer CID initialization run once only */
if (G_UNLIKELY (self->priv->initial_eps_bearer_support == FEATURE_SUPPORT_UNKNOWN)) {
/* There doesn't seem to be a programmatic way to find the initial EPS
* bearer's CID, so we'll use a udev variable. */
port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
device = mm_port_peek_kernel_device (MM_PORT (port));
if (mm_kernel_device_has_global_property (device, "ID_MM_FIBOCOM_INITIAL_EPS_CID")) {
self->priv->initial_eps_bearer_support = FEATURE_SUPPORTED;
self->priv->initial_eps_bearer_cid = mm_kernel_device_get_global_property_as_int (
device, "ID_MM_FIBOCOM_INITIAL_EPS_CID");
}
else
self->priv->initial_eps_bearer_support = FEATURE_NOT_SUPPORTED;
}
task = g_task_new (self, NULL, callback, user_data);
if (self->priv->initial_eps_bearer_support != FEATURE_SUPPORTED) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Initial EPS bearer context ID unknown");
g_object_unref (task);
return;
}
g_assert (self->priv->initial_eps_bearer_cid >= 0);
mm_iface_modem_3gpp_profile_manager_get_profile (
MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
self->priv->initial_eps_bearer_cid,
(GAsyncReadyCallback) load_initial_eps_bearer_get_profile_ready,
task);
}
/*****************************************************************************/
/* Set initial EPS bearer settings */
typedef enum {
SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE = 0,
SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN,
SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE,
SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP,
SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FINISH,
} SetInitialEpsStep;
typedef struct {
MM3gppProfile *profile;
SetInitialEpsStep step;
MMModemPowerState power_state;
} SetInitialEpsContext;
static void
set_initial_eps_context_free (SetInitialEpsContext *ctx)
{
g_object_unref (ctx->profile);
g_slice_free (SetInitialEpsContext, ctx);
}
static gboolean
modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void set_initial_eps_step (GTask *task);
static void
set_initial_eps_bearer_power_up_ready (MMBaseModem *_self,
GAsyncResult *res,
GTask *task)
{
MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
SetInitialEpsContext *ctx;
GError *error = NULL;
ctx = g_task_get_task_data (task);
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish (MM_IFACE_MODEM (self), res, &error)) {
g_prefix_error (&error, "Couldn't power up modem: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx->step++;
set_initial_eps_step (task);
}
static void
set_initial_eps_bearer_modify_profile_ready (MMIfaceModem3gppProfileManager *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
SetInitialEpsContext *ctx;
g_autoptr(MM3gppProfile) stored = NULL;
ctx = g_task_get_task_data (task);
stored = mm_iface_modem_3gpp_profile_manager_set_profile_finish (self, res, &error);
if (!stored) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx->step++;
set_initial_eps_step (task);
}
static void
set_initial_eps_bearer_power_down_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
SetInitialEpsContext *ctx;
GError *error = NULL;
ctx = g_task_get_task_data (task);
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish (MM_IFACE_MODEM (self), res, &error)) {
g_prefix_error (&error, "Couldn't power down modem: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx->step++;
set_initial_eps_step (task);
}
static void
set_initial_eps_bearer_load_power_state_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
SetInitialEpsContext *ctx;
GError *error = NULL;
ctx = g_task_get_task_data (task);
ctx->power_state = MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish (MM_IFACE_MODEM (self), res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ctx->step++;
set_initial_eps_step (task);
}
static void
set_initial_eps_step (GTask *task)
{
MMBroadbandModemFibocom *self;
SetInitialEpsContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE:
mm_obj_dbg (self, "querying current power state...");
g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state);
g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish);
MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback) set_initial_eps_bearer_load_power_state_ready,
task);
return;
case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN:
if (ctx->power_state == MM_MODEM_POWER_STATE_ON) {
mm_obj_dbg (self, "powering down before changing initial EPS bearer settings...");
g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down);
g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish);
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback) set_initial_eps_bearer_power_down_ready,
task);
return;
}
ctx->step++;
/* fall through */
case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE:
mm_obj_dbg (self, "modifying initial EPS bearer settings profile...");
mm_iface_modem_3gpp_profile_manager_set_profile (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
ctx->profile,
"profile-id",
TRUE,
(GAsyncReadyCallback) set_initial_eps_bearer_modify_profile_ready,
task);
return;
case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP:
if (ctx->power_state == MM_MODEM_POWER_STATE_ON) {
mm_obj_dbg (self, "powering up after changing initial EPS bearer settings...");
g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up);
g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish);
MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback) set_initial_eps_bearer_power_up_ready,
task);
return;
}
ctx->step++;
/* fall through */
case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FINISH:
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
g_assert_not_reached ();
}
}
static void
modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *_self,
MMBearerProperties *properties,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
GTask *task;
MM3gppProfile *profile;
MMBearerIpFamily ip_family;
SetInitialEpsContext *ctx;
task = g_task_new (self, NULL, callback, user_data);
if (self->priv->initial_eps_bearer_support != FEATURE_SUPPORTED) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Initial EPS bearer context ID unknown");
g_object_unref (task);
return;
}
profile = mm_bearer_properties_peek_3gpp_profile (properties);
g_assert (self->priv->initial_eps_bearer_cid >= 0);
mm_3gpp_profile_set_profile_id (profile, self->priv->initial_eps_bearer_cid);
ip_family = mm_3gpp_profile_get_ip_type (profile);
if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY)
mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4);
/* Setup context */
ctx = g_slice_new0 (SetInitialEpsContext);
ctx->profile = g_object_ref (profile);
ctx->step = SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE;
g_task_set_task_data (task, ctx, (GDestroyNotify) set_initial_eps_context_free);
set_initial_eps_step (task);
}
/*****************************************************************************/
/* Deactivate profile (3GPP profile management interface) */
static gboolean
modem_3gpp_profile_manager_deactivate_profile_finish (MMIfaceModem3gppProfileManager *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
profile_manager_parent_deactivate_profile_ready (MMIfaceModem3gppProfileManager *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (iface_modem_3gpp_profile_manager_parent->deactivate_profile_finish(self, res, &error))
g_task_return_boolean (task, TRUE);
else
g_task_return_error (task, error);
g_object_unref (task);
}
static void
modem_3gpp_profile_manager_deactivate_profile (MMIfaceModem3gppProfileManager *_self,
MM3gppProfile *profile,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (_self);
GTask *task;
gint profile_id;
task = g_task_new (self, NULL, callback, user_data);
profile_id = mm_3gpp_profile_get_profile_id (profile);
if (self->priv->initial_eps_bearer_support == FEATURE_SUPPORTED) {
g_assert (self->priv->initial_eps_bearer_cid >= 0);
if (self->priv->initial_eps_bearer_cid == profile_id) {
mm_obj_dbg (self, "skipping profile deactivation (initial EPS bearer)");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
}
iface_modem_3gpp_profile_manager_parent->deactivate_profile (
_self,
profile,
(GAsyncReadyCallback) profile_manager_parent_deactivate_profile_ready,
task);
}
/*****************************************************************************/
static void
setup_ports (MMBroadbandModem *_self)
{
MMBroadbandModemFibocom *self = (MM_BROADBAND_MODEM_FIBOCOM (_self));
MMPortSerialAt *ports[2];
guint i;
/* Call parent's setup ports first always */
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_fibocom_parent_class)->setup_ports (_self);
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
mm_port_serial_at_add_unsolicited_msg_handler (
ports[i],
self->priv->sim_ready_regex,
NULL, NULL, NULL);
}
}
/*****************************************************************************/
MMBroadbandModemFibocom *
mm_broadband_modem_fibocom_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
return g_object_new (MM_TYPE_BROADBAND_MODEM_FIBOCOM,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
static void
mm_broadband_modem_fibocom_init (MMBroadbandModemFibocom *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_BROADBAND_MODEM_FIBOCOM,
MMBroadbandModemFibocomPrivate);
self->priv->gtrndis_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->sim_ready_regex = g_regex_new ("\\r\\n\\+SIM READY\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->initial_eps_bearer_support = FEATURE_SUPPORT_UNKNOWN;
}
static void
finalize (GObject *object)
{
MMBroadbandModemFibocom *self = MM_BROADBAND_MODEM_FIBOCOM (object);
g_regex_unref (self->priv->sim_ready_regex);
G_OBJECT_CLASS (mm_broadband_modem_fibocom_parent_class)->finalize (object);
}
static void
iface_modem_init (MMIfaceModem *iface)
{
iface->create_bearer = modem_create_bearer;
iface->create_bearer_finish = modem_create_bearer_finish;
iface->reset = modem_reset;
iface->reset_finish = modem_common_power_finish;
iface->modem_power_down = modem_power_down;
iface->modem_power_down_finish = modem_common_power_finish;
iface->modem_power_off = modem_power_off;
iface->modem_power_off_finish = modem_common_power_finish;
}
static void
iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
{
iface->load_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer;
iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish;
iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings;
iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish;
iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings;
iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish;
}
static void
iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface)
{
iface_modem_3gpp_profile_manager_parent = g_type_interface_peek_parent (iface);
iface->deactivate_profile = modem_3gpp_profile_manager_deactivate_profile;
iface->deactivate_profile_finish = modem_3gpp_profile_manager_deactivate_profile_finish;
}
static void
mm_broadband_modem_fibocom_class_init (MMBroadbandModemFibocomClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
g_type_class_add_private (G_OBJECT_CLASS (klass),
sizeof (MMBroadbandModemFibocomPrivate));
/* Virtual methods */
object_class->finalize = finalize;
broadband_modem_class->setup_ports = setup_ports;
}
@@ -0,0 +1,49 @@
/* -*- 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) 2022 Disruptive Technologies Research AS
*/
#ifndef MM_BROADBAND_MODEM_FIBOCOM_H
#define MM_BROADBAND_MODEM_FIBOCOM_H
#include "mm-broadband-modem.h"
#define MM_TYPE_BROADBAND_MODEM_FIBOCOM (mm_broadband_modem_fibocom_get_type ())
#define MM_BROADBAND_MODEM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_FIBOCOM, MMBroadbandModemFibocom))
#define MM_BROADBAND_MODEM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_FIBOCOM, MMBroadbandModemFibocomClass))
#define MM_IS_BROADBAND_MODEM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_FIBOCOM))
#define MM_IS_BROADBAND_MODEM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_FIBOCOM))
#define MM_BROADBAND_MODEM_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_FIBOCOM, MMBroadbandModemFibocomClass))
typedef struct _MMBroadbandModemFibocom MMBroadbandModemFibocom;
typedef struct _MMBroadbandModemFibocomClass MMBroadbandModemFibocomClass;
typedef struct _MMBroadbandModemFibocomPrivate MMBroadbandModemFibocomPrivate;
struct _MMBroadbandModemFibocom {
MMBroadbandModem parent;
MMBroadbandModemFibocomPrivate *priv;
};
struct _MMBroadbandModemFibocomClass{
MMBroadbandModemClass parent;
};
GType mm_broadband_modem_fibocom_get_type (void);
MMBroadbandModemFibocom *mm_broadband_modem_fibocom_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_FIBOCOM_H */
@@ -0,0 +1,95 @@
/* -*- 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) 2022 Fibocom Wireless Inc.
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "ModemManager.h"
#include "mm-log-object.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-broadband-modem-mbim-fibocom.h"
#include "mm-shared-fibocom.h"
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
static void shared_fibocom_init (MMSharedFibocom *iface);
static MMIfaceModem3gpp *iface_modem_3gpp_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimFibocom, mm_broadband_modem_mbim_fibocom, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_FIBOCOM, shared_fibocom_init))
/******************************************************************************/
MMBroadbandModemMbimFibocom *
mm_broadband_modem_mbim_fibocom_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
/* MBIM bearer supports NET only */
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE,
#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, TRUE,
#endif
NULL);
}
static void
mm_broadband_modem_mbim_fibocom_init (MMBroadbandModemMbimFibocom *self)
{
}
static void
iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
{
iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
iface->set_initial_eps_bearer_settings = mm_shared_fibocom_set_initial_eps_bearer_settings;
iface->set_initial_eps_bearer_settings_finish = mm_shared_fibocom_set_initial_eps_bearer_settings_finish;
}
static MMIfaceModem3gpp *
peek_parent_3gpp_interface (MMSharedFibocom *self)
{
return iface_modem_3gpp_parent;
}
static void
shared_fibocom_init (MMSharedFibocom *iface)
{
iface->peek_parent_3gpp_interface = peek_parent_3gpp_interface;
}
static void
mm_broadband_modem_mbim_fibocom_class_init (MMBroadbandModemMbimFibocomClass *klass)
{
}
@@ -0,0 +1,47 @@
/* -*- 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) 2022 Fibocom Wireless Inc.
*/
#ifndef MM_BROADBAND_MODEM_MBIM_FIBOCOM_H
#define MM_BROADBAND_MODEM_MBIM_FIBOCOM_H
#include "mm-broadband-modem-mbim.h"
#define MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM (mm_broadband_modem_mbim_fibocom_get_type ())
#define MM_BROADBAND_MODEM_MBIM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM, MMBroadbandModemMbimFibocom))
#define MM_BROADBAND_MODEM_MBIM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM, MMBroadbandModemMbimFibocomClass))
#define MM_IS_BROADBAND_MODEM_MBIM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM))
#define MM_IS_BROADBAND_MODEM_MBIM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM))
#define MM_BROADBAND_MODEM_MBIM_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FIBOCOM, MMBroadbandModemMbimFibocomClass))
typedef struct _MMBroadbandModemMbimFibocom MMBroadbandModemMbimFibocom;
typedef struct _MMBroadbandModemMbimFibocomClass MMBroadbandModemMbimFibocomClass;
struct _MMBroadbandModemMbimFibocom {
MMBroadbandModemMbim parent;
};
struct _MMBroadbandModemMbimFibocomClass{
MMBroadbandModemMbimClass parent;
};
GType mm_broadband_modem_mbim_fibocom_get_type (void);
MMBroadbandModemMbimFibocom *mm_broadband_modem_mbim_fibocom_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_MBIM_FIBOCOM_H */
@@ -0,0 +1,95 @@
/* -*- 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) 2022 Fibocom Wireless Inc.
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "ModemManager.h"
#include "mm-log-object.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-broadband-modem-mbim-xmm-fibocom.h"
#include "mm-shared-fibocom.h"
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
static void shared_fibocom_init (MMSharedFibocom *iface);
static MMIfaceModem3gpp *iface_modem_3gpp_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimXmmFibocom, mm_broadband_modem_mbim_xmm_fibocom, MM_TYPE_BROADBAND_MODEM_MBIM_XMM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_FIBOCOM, shared_fibocom_init))
/******************************************************************************/
MMBroadbandModemMbimXmmFibocom *
mm_broadband_modem_mbim_xmm_fibocom_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
/* MBIM bearer supports NET only */
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE,
#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, TRUE,
#endif
NULL);
}
static void
mm_broadband_modem_mbim_xmm_fibocom_init (MMBroadbandModemMbimXmmFibocom *self)
{
}
static void
iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
{
iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
iface->set_initial_eps_bearer_settings = mm_shared_fibocom_set_initial_eps_bearer_settings;
iface->set_initial_eps_bearer_settings_finish = mm_shared_fibocom_set_initial_eps_bearer_settings_finish;
}
static MMIfaceModem3gpp *
peek_parent_3gpp_interface (MMSharedFibocom *self)
{
return iface_modem_3gpp_parent;
}
static void
shared_fibocom_init (MMSharedFibocom *iface)
{
iface->peek_parent_3gpp_interface = peek_parent_3gpp_interface;
}
static void
mm_broadband_modem_mbim_xmm_fibocom_class_init (MMBroadbandModemMbimXmmFibocomClass *klass)
{
}
@@ -0,0 +1,47 @@
/* -*- 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) 2022 Fibocom Wireless Inc.
*/
#ifndef MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_H
#define MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_H
#include "mm-broadband-modem-mbim-xmm.h"
#define MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM (mm_broadband_modem_mbim_xmm_fibocom_get_type ())
#define MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM, MMBroadbandModemMbimXmmFibocom))
#define MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM, MMBroadbandModemMbimXmmFibocomClass))
#define MM_IS_BROADBAND_MODEM_MBIM_XMM_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM))
#define MM_IS_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM))
#define MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM_FIBOCOM, MMBroadbandModemMbimXmmFibocomClass))
typedef struct _MMBroadbandModemMbimXmmFibocom MMBroadbandModemMbimXmmFibocom;
typedef struct _MMBroadbandModemMbimXmmFibocomClass MMBroadbandModemMbimXmmFibocomClass;
struct _MMBroadbandModemMbimXmmFibocom {
MMBroadbandModemMbimXmm parent;
};
struct _MMBroadbandModemMbimXmmFibocomClass{
MMBroadbandModemMbimXmmClass parent;
};
GType mm_broadband_modem_mbim_xmm_fibocom_get_type (void);
MMBroadbandModemMbimXmmFibocom *mm_broadband_modem_mbim_xmm_fibocom_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_MBIM_XMM_FIBOCOM_H */
+136
View File
@@ -0,0 +1,136 @@
/* -*- 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) 2018-2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <stdlib.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log-object.h"
#include "mm-plugin-fibocom.h"
#include "mm-broadband-modem.h"
#include "mm-broadband-modem-xmm.h"
#include "mm-broadband-modem-fibocom.h"
#if defined WITH_MBIM
#include "mm-broadband-modem-mbim.h"
#include "mm-broadband-modem-mbim-fibocom.h"
#include "mm-broadband-modem-mbim-xmm.h"
#include "mm-broadband-modem-mbim-xmm-fibocom.h"
#endif
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
#endif
G_DEFINE_TYPE (MMPluginFibocom, mm_plugin_fibocom, 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)) {
if (mm_port_probe_list_is_xmm (probes)) {
mm_obj_dbg (self, "MBIM-powered XMM-based Fibocom modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_xmm_fibocom_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
mm_obj_dbg (self, "MBIM-powered Fibocom modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_fibocom_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered Fibocom modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
if (mm_port_probe_list_is_xmm (probes)) {
mm_obj_dbg (self, "XMM-based Fibocom modem found...");
return MM_BASE_MODEM (mm_broadband_modem_xmm_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
mm_obj_dbg (self, "Fibocom modem found...");
return MM_BASE_MODEM (mm_broadband_modem_fibocom_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 guint16 vendor_ids[] = { 0x2cb7, 0x1782, 0 };
static const gchar *drivers[] = { "cdc_mbim", "qmi_wwan", "cdc_ether", "option", NULL };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_FIBOCOM,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_DRIVERS, drivers,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
MM_PLUGIN_XMM_PROBE, TRUE,
NULL));
}
static void
mm_plugin_fibocom_init (MMPluginFibocom *self)
{
}
static void
mm_plugin_fibocom_class_init (MMPluginFibocomClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+40
View File
@@ -0,0 +1,40 @@
/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_PLUGIN_FIBOCOM_H
#define MM_PLUGIN_FIBOCOM_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_FIBOCOM (mm_plugin_fibocom_get_type ())
#define MM_PLUGIN_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_FIBOCOM, MMPluginFibocom))
#define MM_PLUGIN_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_FIBOCOM, MMPluginFibocomClass))
#define MM_IS_PLUGIN_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_FIBOCOM))
#define MM_IS_PLUGIN_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_FIBOCOM))
#define MM_PLUGIN_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_FIBOCOM, MMPluginFibocomClass))
typedef struct {
MMPlugin parent;
} MMPluginFibocom;
typedef struct {
MMPluginClass parent;
} MMPluginFibocomClass;
GType mm_plugin_fibocom_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_FIBOCOM_H */
+246
View File
@@ -0,0 +1,246 @@
/* -*- 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) 2022 Fibocom Wireless Inc.
*/
#include <config.h>
#include <arpa/inet.h>
#include <glib-object.h>
#include <gio/gio.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log-object.h"
#include "mm-broadband-modem.h"
#include "mm-broadband-modem-mbim.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-shared-fibocom.h"
/*****************************************************************************/
/* Private data context */
#define PRIVATE_TAG "shared-intel-private-tag"
static GQuark private_quark;
typedef struct {
/* 3GPP interface support */
MMIfaceModem3gpp *iface_modem_3gpp_parent;
} Private;
static void
private_free (Private *priv)
{
g_slice_free (Private, priv);
}
static Private *
get_private (MMSharedFibocom *self)
{
Private *priv;
if (G_UNLIKELY (!private_quark))
private_quark = g_quark_from_static_string (PRIVATE_TAG);
priv = g_object_get_qdata (G_OBJECT (self), private_quark);
if (!priv) {
priv = g_slice_new0 (Private);
/* Setup parent class' MMIfaceModem3gpp */
g_assert (MM_SHARED_FIBOCOM_GET_INTERFACE (self)->peek_parent_3gpp_interface);
priv->iface_modem_3gpp_parent = MM_SHARED_FIBOCOM_GET_INTERFACE (self)->peek_parent_3gpp_interface (self);
g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
}
return priv;
}
/*****************************************************************************/
typedef struct {
MMBearerProperties *config;
gboolean initial_eps_off_on;
} SetInitialEpsBearerSettingsContext;
static void
set_initial_eps_bearer_settings_context_free (SetInitialEpsBearerSettingsContext *ctx)
{
g_clear_object (&ctx->config);
g_slice_free (SetInitialEpsBearerSettingsContext, ctx);
}
gboolean
mm_shared_fibocom_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
after_attach_apn_modem_power_up_ready (MMIfaceModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_iface_modem_set_power_state_finish (self, res, &error)) {
mm_obj_warn (self, "failed to power up modem after attach APN settings update: %s", error->message);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mm_obj_dbg (self, "success toggling modem power up after attach APN");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
parent_set_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
SetInitialEpsBearerSettingsContext *ctx;
Private *priv;
GError *error = NULL;
ctx = g_task_get_task_data (task);
priv = get_private (MM_SHARED_FIBOCOM (self));
if (!priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings_finish (self, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (ctx->initial_eps_off_on) {
mm_obj_dbg (self, "toggle modem power up after attach APN");
mm_iface_modem_set_power_state (MM_IFACE_MODEM (self),
MM_MODEM_POWER_STATE_ON,
(GAsyncReadyCallback) after_attach_apn_modem_power_up_ready,
task);
return;
}
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
parent_set_initial_eps_bearer_settings (GTask *task)
{
MMSharedFibocom *self;
SetInitialEpsBearerSettingsContext *ctx;
Private *priv;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
priv = get_private (self);
g_assert (priv->iface_modem_3gpp_parent);
g_assert (priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings);
g_assert (priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings_finish);
priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings (MM_IFACE_MODEM_3GPP (self),
ctx->config,
(GAsyncReadyCallback)parent_set_initial_eps_bearer_settings_ready,
task);
}
static void
before_attach_apn_modem_power_down_ready (MMIfaceModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_iface_modem_set_power_state_finish (self, res, &error)) {
mm_obj_warn (self, "failed to power down modem before attach APN settings update: %s", error->message);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mm_obj_dbg (self, "success toggling modem power down before attach APN");
parent_set_initial_eps_bearer_settings (task);
}
void
mm_shared_fibocom_set_initial_eps_bearer_settings (MMIfaceModem3gpp *self,
MMBearerProperties *config,
GAsyncReadyCallback callback,
gpointer user_data)
{
SetInitialEpsBearerSettingsContext *ctx;
GTask *task;
MMPortMbim *port;
task = g_task_new (self, NULL, callback, user_data);
/* This shared logic is only expected in MBIM capable devices */
g_assert (MM_IS_BROADBAND_MODEM_MBIM (self));
port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self));
if (!port) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"No valid MBIM port found");
g_object_unref (task);
return;
}
ctx = g_slice_new0 (SetInitialEpsBearerSettingsContext);
ctx->config = g_object_ref (config);
ctx->initial_eps_off_on = mm_kernel_device_get_property_as_boolean (mm_port_peek_kernel_device (MM_PORT (port)), "ID_MM_FIBOCOM_INITIAL_EPS_OFF_ON");
g_task_set_task_data (task, ctx, (GDestroyNotify)set_initial_eps_bearer_settings_context_free);
if (ctx->initial_eps_off_on) {
mm_obj_dbg (self, "toggle modem power down before attach APN");
mm_iface_modem_set_power_state (MM_IFACE_MODEM (self),
MM_MODEM_POWER_STATE_LOW,
(GAsyncReadyCallback) before_attach_apn_modem_power_down_ready,
task);
return;
}
parent_set_initial_eps_bearer_settings (task);
}
/*****************************************************************************/
static void
shared_fibocom_init (gpointer g_iface)
{
}
GType
mm_shared_fibocom_get_type (void)
{
static GType shared_fibocom_type = 0;
if (!G_UNLIKELY (shared_fibocom_type)) {
static const GTypeInfo info = {
sizeof (MMSharedFibocom), /* class_size */
shared_fibocom_init, /* base_init */
NULL, /* base_finalize */
};
shared_fibocom_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedFibocom", &info, 0);
g_type_interface_add_prerequisite (shared_fibocom_type, MM_TYPE_IFACE_MODEM);
g_type_interface_add_prerequisite (shared_fibocom_type, MM_TYPE_IFACE_MODEM_3GPP);
}
return shared_fibocom_type;
}
+53
View File
@@ -0,0 +1,53 @@
/* -*- 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) 2022 Fibocom Wireless Inc.
*/
#ifndef MM_SHARED_FIBOCOM_H
#define MM_SHARED_FIBOCOM_H
#include <glib-object.h>
#include <gio/gio.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-broadband-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem.h"
#define MM_TYPE_SHARED_FIBOCOM (mm_shared_fibocom_get_type ())
#define MM_SHARED_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_FIBOCOM, MMSharedFibocom))
#define MM_IS_SHARED_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_FIBOCOM))
#define MM_SHARED_FIBOCOM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_FIBOCOM, MMSharedFibocom))
typedef struct _MMSharedFibocom MMSharedFibocom;
struct _MMSharedFibocom {
GTypeInterface g_iface;
/* Peek 3GPP interface of the parent class of the object */
MMIfaceModem3gpp * (* peek_parent_3gpp_interface) (MMSharedFibocom *self);
};
GType mm_shared_fibocom_get_type (void);
void mm_shared_fibocom_set_initial_eps_bearer_settings (MMIfaceModem3gpp *self,
MMBearerProperties *config,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_shared_fibocom_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error);
#endif /* MM_SHARED_FIBOCOM_H */
+20
View File
@@ -0,0 +1,20 @@
/* -*- 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) 2022 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "mm-shared.h"
MM_SHARED_DEFINE_MAJOR_VERSION
MM_SHARED_DEFINE_MINOR_VERSION
MM_SHARED_DEFINE_NAME(Intel)
@@ -0,0 +1,26 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_foxconn_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0489", GOTO="mm_foxconn_vendorcheck"
GOTO="mm_foxconn_port_types_end"
LABEL="mm_foxconn_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Foxconn T77w968 (default 0xe0b4, with esim support 0xe0b5)
# if 02: primary port
# if 03: secondary port
# if 04: raw NMEA port
# if 05: diag/qcdm port
ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
GOTO="mm_foxconn_port_types_end"
LABEL="mm_foxconn_port_types_end"
@@ -0,0 +1,612 @@
/* -*- 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) 2018-2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-errors-types.h"
#include "mm-modem-helpers.h"
#include "mm-base-modem-at.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-location.h"
#include "mm-broadband-modem-mbim-foxconn.h"
#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
# include "mm-iface-modem-firmware.h"
# include "mm-shared-qmi.h"
# include "mm-log.h"
#endif
static void iface_modem_location_init (MMIfaceModemLocation *iface);
#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
#endif
static MMIfaceModemLocation *iface_modem_location_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimFoxconn, mm_broadband_modem_mbim_foxconn, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)
#endif
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))
typedef enum {
FEATURE_SUPPORT_UNKNOWN,
FEATURE_NOT_SUPPORTED,
FEATURE_SUPPORTED
} FeatureSupport;
struct _MMBroadbandModemMbimFoxconnPrivate {
FeatureSupport unmanaged_gps_support;
};
#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
/*****************************************************************************/
/* Firmware update settings
*
* We only support reporting firmware update settings when QMI support is built,
* because this is the only clean way to get the expected firmware version to
* report.
*/
static MMFirmwareUpdateSettings *
firmware_load_update_settings_finish (MMIfaceModemFirmware *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static gboolean
needs_qdu_and_mcfg_apps_version (MMIfaceModemFirmware *self)
{
guint vendor_id;
guint product_id;
/* 0x105b is the T99W175 module, T99W175 supports QDU and requires MCFG+APPS version.
* T99W265(0x0489:0xe0da ; 0x0489:0xe0db): supports QDU and requires MCFG+APPS version.
* else support FASTBOOT and QMI PDC, and require only MCFG version.
*/
vendor_id = mm_base_modem_get_vendor_id (MM_BASE_MODEM (self));
product_id = mm_base_modem_get_product_id (MM_BASE_MODEM (self));
return (vendor_id == 0x105b || (vendor_id == 0x0489 && (product_id == 0xe0da || product_id == 0xe0db)));
}
/*****************************************************************************/
/* Need APPS version for the development of different functions when T77W968 support FASTBOOT and QMI PDC.
* Such as: T77W968.F1.0.0.5.2.GC.013.037 and T77W968.F1.0.0.5.2.GC.013.049, the MCFG version(T77W968.F1.0.0.5.2.GC.013) is same,
* but the APPS version(037 and 049) is different.
*
* For T77W968.F1.0.0.5.2.GC.013.049, before the change, "fwupdmgr get-devices" can obtain Current version is T77W968.F1.0.0.5.2.GC.013,
* it only include the MCFG version.
* After add need APPS version, it shows Current version is T77W968.F1.0.0.5.2.GC.013.049, including the MCFG+APPS version.
*/
static gboolean
needs_fastboot_and_qmi_pdc_mcfg_apps_version (MMIfaceModemFirmware *self)
{
guint vendor_id;
guint product_id;
/* T77W968(0x413c:0x81d7 ; 0x413c:0x81e0 ; 0x413c:0x81e4 ; 0x413c:0x81e6): supports FASTBOOT and QMI PDC,
* and requires MCFG+APPS version.
* else support FASTBOOT and QMI PDC, and require only MCFG version.
*/
vendor_id = mm_base_modem_get_vendor_id (MM_BASE_MODEM (self));
product_id = mm_base_modem_get_product_id (MM_BASE_MODEM (self));
return (vendor_id == 0x413c && (product_id == 0x81d7 || product_id == 0x81e0 || product_id == 0x81e4 || product_id == 0x81e6));
}
static MMFirmwareUpdateSettings *
create_update_settings (MMIfaceModemFirmware *self,
const gchar *version_str)
{
MMModemFirmwareUpdateMethod methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE;
MMFirmwareUpdateSettings *update_settings = NULL;
if (needs_qdu_and_mcfg_apps_version (self))
methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU;
else
methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT | MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC;
update_settings = mm_firmware_update_settings_new (methods);
if (methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT)
mm_firmware_update_settings_set_fastboot_at (update_settings, "AT^FASTBOOT");
mm_firmware_update_settings_set_version (update_settings, version_str);
return update_settings;
}
static void
dms_foxconn_get_firmware_version_ready (QmiClientDms *client,
GAsyncResult *res,
GTask *task)
{
g_autoptr(QmiMessageDmsFoxconnGetFirmwareVersionOutput) output = NULL;
GError *error = NULL;
const gchar *str;
output = qmi_client_dms_foxconn_get_firmware_version_finish (client, res, &error);
if (!output || !qmi_message_dms_foxconn_get_firmware_version_output_get_result (output, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
qmi_message_dms_foxconn_get_firmware_version_output_get_version (output, &str, NULL);
g_task_return_pointer (task,
create_update_settings (g_task_get_source_object (task), str),
g_object_unref);
g_object_unref (task);
}
static void
fox_get_firmware_version_ready (QmiClientFox *client,
GAsyncResult *res,
GTask *task)
{
g_autoptr(QmiMessageFoxGetFirmwareVersionOutput) output = NULL;
GError *error = NULL;
const gchar *str;
output = qmi_client_fox_get_firmware_version_finish (client, res, &error);
if (!output || !qmi_message_fox_get_firmware_version_output_get_result (output, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
qmi_message_fox_get_firmware_version_output_get_version (output, &str, NULL);
g_task_return_pointer (task,
create_update_settings (g_task_get_source_object (task), str),
g_object_unref);
g_object_unref (task);
}
static void
mbim_port_allocate_qmi_client_ready (MMPortMbim *mbim,
GAsyncResult *res,
GTask *task)
{
MMIfaceModemFirmware *self;
QmiClient *fox_client = NULL;
QmiClient *dms_client = NULL;
g_autoptr(GError) error = NULL;
self = g_task_get_source_object (task);
if (!mm_port_mbim_allocate_qmi_client_finish (mbim, res, &error))
mm_obj_dbg (self, "Allocate FOX client failed: %s", error->message);
/* Try to get firmware version over fox service, if it failed to peek client, try dms service. */
fox_client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_FOX, MM_PORT_QMI_FLAG_DEFAULT, NULL);
if (!fox_client) {
dms_client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_DMS, MM_PORT_QMI_FLAG_DEFAULT, NULL);
if (!dms_client) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Unable to load version info: no FOX or DMS client available");
g_object_unref (task);
return;
}
}
if (fox_client) {
g_autoptr(QmiMessageFoxGetFirmwareVersionInput) input = NULL;
input = qmi_message_fox_get_firmware_version_input_new ();
qmi_message_fox_get_firmware_version_input_set_version_type (input,
(needs_qdu_and_mcfg_apps_version (self) ?
QMI_FOX_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG_APPS :
QMI_FOX_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG),
NULL);
qmi_client_fox_get_firmware_version (QMI_CLIENT_FOX (fox_client),
input,
10,
NULL,
(GAsyncReadyCallback)fox_get_firmware_version_ready,
task);
return;
}
if (dms_client) {
g_autoptr(QmiMessageDmsFoxconnGetFirmwareVersionInput) input = NULL;
input = qmi_message_dms_foxconn_get_firmware_version_input_new ();
qmi_message_dms_foxconn_get_firmware_version_input_set_version_type (input,
((needs_qdu_and_mcfg_apps_version (self) || needs_fastboot_and_qmi_pdc_mcfg_apps_version (self)) ?
QMI_DMS_FOXCONN_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG_APPS:
QMI_DMS_FOXCONN_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG),
NULL);
qmi_client_dms_foxconn_get_firmware_version (QMI_CLIENT_DMS (dms_client),
input,
10,
NULL,
(GAsyncReadyCallback)dms_foxconn_get_firmware_version_ready,
task);
return;
}
g_assert_not_reached ();
}
static void
firmware_load_update_settings (MMIfaceModemFirmware *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
MMPortMbim *mbim;
task = g_task_new (self, NULL, callback, user_data);
mbim = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self));
mm_port_mbim_allocate_qmi_client (mbim,
QMI_SERVICE_FOX,
NULL,
(GAsyncReadyCallback)mbim_port_allocate_qmi_client_ready,
task);
}
#endif
/*****************************************************************************/
/* Location capabilities loading (Location interface) */
static MMModemLocationSource
location_load_capabilities_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
GError *inner_error = NULL;
gssize value;
value = g_task_propagate_int (G_TASK (res), &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return MM_MODEM_LOCATION_SOURCE_NONE;
}
return (MMModemLocationSource)value;
}
static void
custom_location_load_capabilities (GTask *task,
MMModemLocationSource sources)
{
MMBroadbandModemMbimFoxconn *self;
self = g_task_get_source_object (task);
/* If we have a GPS port and an AT port, enable unmanaged GPS support */
if (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)) &&
mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) {
self->priv->unmanaged_gps_support = FEATURE_SUPPORTED;
sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED;
}
/* So we're done, complete */
g_task_return_int (task, sources);
g_object_unref (task);
}
static void
parent_load_capabilities_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
GTask *task)
{
MMModemLocationSource sources;
GError *error = NULL;
sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
custom_location_load_capabilities (task, sources);
}
static void
location_load_capabilities (MMIfaceModemLocation *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's setup, if any. If MM is built without QMI support,
* the MBIM modem won't have any location capabilities. */
if (iface_modem_location_parent &&
iface_modem_location_parent->load_capabilities &&
iface_modem_location_parent->load_capabilities_finish) {
iface_modem_location_parent->load_capabilities (self,
(GAsyncReadyCallback)parent_load_capabilities_ready,
task);
return;
}
custom_location_load_capabilities (task, MM_MODEM_LOCATION_SOURCE_NONE);
}
/*****************************************************************************/
/* Disable location gathering (Location interface) */
static gboolean
disable_location_gathering_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_disable_location_gathering_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!iface_modem_location_parent->disable_location_gathering_finish (self, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
parent_disable_location_gathering (GTask *task)
{
MMIfaceModemLocation *self;
MMModemLocationSource source;
self = MM_IFACE_MODEM_LOCATION (g_task_get_source_object (task));
source = GPOINTER_TO_UINT (g_task_get_task_data (task));
if (iface_modem_location_parent &&
iface_modem_location_parent->disable_location_gathering &&
iface_modem_location_parent->disable_location_gathering_finish) {
iface_modem_location_parent->disable_location_gathering (
self,
source,
(GAsyncReadyCallback)parent_disable_location_gathering_ready,
task);
return;
}
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
unmanaged_gps_disabled_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (self, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
parent_disable_location_gathering (task);
}
static void
disable_location_gathering (MMIfaceModemLocation *_self,
MMModemLocationSource source,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandModemMbimFoxconn *self = MM_BROADBAND_MODEM_MBIM_FOXCONN (_self);
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
/* We only support Unmanaged GPS at this level */
if ((self->priv->unmanaged_gps_support != FEATURE_SUPPORTED) ||
(source != MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
parent_disable_location_gathering (task);
return;
}
mm_base_modem_at_command (MM_BASE_MODEM (_self),
"^NV=30007,01,\"00\"",
3,
FALSE,
(GAsyncReadyCallback)unmanaged_gps_disabled_ready,
task);
}
/*****************************************************************************/
/* Enable location gathering (Location interface) */
static gboolean
enable_location_gathering_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
unmanaged_gps_enabled_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (self, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
custom_enable_location_gathering (GTask *task)
{
MMBroadbandModemMbimFoxconn *self;
MMModemLocationSource source;
self = g_task_get_source_object (task);
source = GPOINTER_TO_UINT (g_task_get_task_data (task));
/* We only support Unmanaged GPS at this level */
if ((self->priv->unmanaged_gps_support != FEATURE_SUPPORTED) ||
(source != MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^NV=30007,01,\"01\"",
3,
FALSE,
(GAsyncReadyCallback)unmanaged_gps_enabled_ready,
task);
}
static void
parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
custom_enable_location_gathering (task);
}
static void
enable_location_gathering (MMIfaceModemLocation *self,
MMModemLocationSource source,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
/* Chain up parent's gathering enable */
if (iface_modem_location_parent &&
iface_modem_location_parent->enable_location_gathering &&
iface_modem_location_parent->enable_location_gathering_finish) {
iface_modem_location_parent->enable_location_gathering (
self,
source,
(GAsyncReadyCallback)parent_enable_location_gathering_ready,
task);
return;
}
custom_enable_location_gathering (task);
}
/*****************************************************************************/
MMBroadbandModemMbimFoxconn *
mm_broadband_modem_mbim_foxconn_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
const gchar *carrier_config_mapping = NULL;
/* T77W968 (DW5821e/DW5829e is also T77W968) modules use t77w968 carrier mapping table. */
if ((vendor_id == 0x0489 && (product_id == 0xe0b4 || product_id == 0xe0b5)) ||
(vendor_id == 0x413c && (product_id == 0x81d7 || product_id == 0x81e0 || product_id == 0x81e4 || product_id == 0x81e6)))
carrier_config_mapping = PKGDATADIR "/mm-foxconn-t77w968-carrier-mapping.conf";
return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
/* MBIM bearer supports NET only */
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE,
MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, TRUE,
MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING, carrier_config_mapping,
NULL);
}
static void
mm_broadband_modem_mbim_foxconn_init (MMBroadbandModemMbimFoxconn *self)
{
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconnPrivate);
self->priv->unmanaged_gps_support = FEATURE_SUPPORT_UNKNOWN;
}
static void
iface_modem_location_init (MMIfaceModemLocation *iface)
{
iface_modem_location_parent = g_type_interface_peek_parent (iface);
iface->load_capabilities = location_load_capabilities;
iface->load_capabilities_finish = location_load_capabilities_finish;
iface->enable_location_gathering = enable_location_gathering;
iface->enable_location_gathering_finish = enable_location_gathering_finish;
iface->disable_location_gathering = disable_location_gathering;
iface->disable_location_gathering_finish = disable_location_gathering_finish;
}
#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
static void
iface_modem_firmware_init (MMIfaceModemFirmware *iface)
{
iface->load_update_settings = firmware_load_update_settings;
iface->load_update_settings_finish = firmware_load_update_settings_finish;
}
#endif
static void
mm_broadband_modem_mbim_foxconn_class_init (MMBroadbandModemMbimFoxconnClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMBroadbandModemMbimFoxconnPrivate));
}
@@ -0,0 +1,49 @@
/* -*- 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) 2018-2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_BROADBAND_MODEM_MBIM_FOXCONN_H
#define MM_BROADBAND_MODEM_MBIM_FOXCONN_H
#include "mm-broadband-modem-mbim.h"
#define MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN (mm_broadband_modem_mbim_foxconn_get_type ())
#define MM_BROADBAND_MODEM_MBIM_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconn))
#define MM_BROADBAND_MODEM_MBIM_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconnClass))
#define MM_IS_BROADBAND_MODEM_MBIM_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN))
#define MM_IS_BROADBAND_MODEM_MBIM_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN))
#define MM_BROADBAND_MODEM_MBIM_FOXCONN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconnClass))
typedef struct _MMBroadbandModemMbimFoxconn MMBroadbandModemMbimFoxconn;
typedef struct _MMBroadbandModemMbimFoxconnClass MMBroadbandModemMbimFoxconnClass;
typedef struct _MMBroadbandModemMbimFoxconnPrivate MMBroadbandModemMbimFoxconnPrivate;
struct _MMBroadbandModemMbimFoxconn {
MMBroadbandModemMbim parent;
MMBroadbandModemMbimFoxconnPrivate *priv;
};
struct _MMBroadbandModemMbimFoxconnClass{
MMBroadbandModemMbimClass parent;
};
GType mm_broadband_modem_mbim_foxconn_get_type (void);
MMBroadbandModemMbimFoxconn *mm_broadband_modem_mbim_foxconn_new (const gchar *device,
const gchar **driver,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_MBIM_FOXCONN_H */
@@ -0,0 +1,319 @@
#
# T77W968 carrier mapping table
#
# This table maps the MCCMNC of the SIM card with the corresponding
# configuration description as reported by the QMI PDC service in
# this module.
#
[foxconn t77w968]
# AT&T
302220=ATT
302221=ATT
31030=ATT
31070=ATT
31090=ATT
310150=ATT
310170=ATT
310280=ATT
310380=ATT
310410=ATT
310560=ATT
310650=ATT
310680=ATT
310980=ATT
311180=ATT
90118=ATT
# FirstNet
312670=A2
313100=A2
313110=A2
313120=A2
313130=A2
313140=A2
# Verizon
310590=Verizon
310890=Verizon
311270=Verizon
311480=Verizon
312770=Verizon
# Vodafone
20205=Vodafone
20404=Vodafone
20601=Vodafone
20810=Vodafone
21401=Vodafone
21670=Vodafone
21910=Vodafone
22005=Vodafone
22210=Vodafone
22601=Vodafone
23003=Vodafone
23201=Vodafone
23415=Vodafone
23801=Vodafone
24405=Vodafone
24602=Vodafone
24705=Vodafone
24802=Vodafone
25001=Vodafone
26202=Vodafone
26209=Vodafone
26801=Vodafone
27077=Vodafone
27201=Vodafone
27402=Vodafone
27602=Vodafone
27801=Vodafone
28001=Vodafone
28401=Vodafone
28602=Vodafone
28802=Vodafone
29340=Vodafone
29403=Vodafone
40004=Vodafone
40401=Vodafone
40405=Vodafone
40411=Vodafone
40413=Vodafone
40415=Vodafone
40420=Vodafone
40427=Vodafone
40430=Vodafone
40443=Vodafone
40446=Vodafone
40460=Vodafone
40484=Vodafone
40486=Vodafone
40488=Vodafone
40566=Vodafone
40567=Vodafone
405750=Vodafone
405751=Vodafone
405752=Vodafone
405753=Vodafone
405754=Vodafone
405755=Vodafone
405756=Vodafone
41302=Vodafone
42403=Vodafone
42602=Vodafone
42702=Vodafone
46601=Vodafone
46603=Vodafone
50213=Vodafone
50219=Vodafone
50503=Vodafone
52503=Vodafone
52505=Vodafone
53001=Vodafone
54201=Vodafone
60202=Vodafone
62002=Vodafone
63001=Vodafone
63902=Vodafone
64004=Vodafone
64304=Vodafone
64710=Vodafone
65101=Vodafone
65501=Vodafone
73001=Vodafone
90128=Vodafone
# Orange
20610=Orange
20801=Orange
20802=Orange
21403=Orange
21409=Orange
22610=Orange
23101=Orange
23105=Orange
23430=Orange
23433=Orange
23434=Orange
25901=Orange
26003=Orange
26005=Orange
27099=Orange
28310=Orange
# Telefonica Movistar
21405=Telefonica
21407=Telefonica
# Swisscom
22801=Swisscom
29501=Swisscom
# Telstra
50501=Telstra
50506=Telstra
50571=Telstra
50572=Telstra
# Sprint
310120=Sprint
# Optus
50202=Optus
# NTT DoCoMo
44002=Docomo
44003=Docomo
44009=Docomo
44010=Docomo
44011=Docomo
44012=Docomo
44013=Docomo
44014=Docomo
44015=Docomo
44016=Docomo
44017=Docomo
44018=Docomo
44019=Docomo
44022=Docomo
44023=Docomo
44024=Docomo
44025=Docomo
44026=Docomo
44027=Docomo
44028=Docomo
44029=Docomo
44030=Docomo
44031=Docomo
44032=Docomo
44033=Docomo
44034=Docomo
44035=Docomo
44036=Docomo
44037=Docomo
44038=Docomo
44039=Docomo
44049=Docomo
44058=Docomo
44060=Docomo
44061=Docomo
44062=Docomo
44063=Docomo
44064=Docomo
44065=Docomo
44066=Docomo
44067=Docomo
44068=Docomo
44069=Docomo
44087=Docomo
44099=Docomo
44140=Docomo
44141=Docomo
44142=Docomo
44143=Docomo
44144=Docomo
44145=Docomo
44190=Docomo
44101=Docomo
44192=Docomo
44193=Docomo
44194=Docomo
44198=Docomo
44199=Docomo
# KDDI
44007=KDDI
44008=KDDI
44050=KDDI
44051=KDDI
44052=KDDI
44053=KDDI
44054=KDDI
44055=KDDI
44056=KDDI
44070=KDDI
44071=KDDI
44072=KDDI
44073=KDDI
44074=KDDI
44075=KDDI
44076=KDDI
44077=KDDI
44078=KDDI
44079=KDDI
44080=KDDI
44081=KDDI
44082=KDDI
44083=KDDI
44084=KDDI
44085=KDDI
44086=KDDI
44088=KDDI
44089=KDDI
44150=KDDI
44151=KDDI
44170=KDDI
# SoftBank
44000=SBM
44004=SBM
44006=SBM
44020=SBM
44021=SBM
44040=SBM
44041=SBM
44042=SBM
44043=SBM
44044=SBM
44045=SBM
44046=SBM
44047=SBM
44048=SBM
44090=SBM
44092=SBM
44093=SBM
44094=SBM
44095=SBM
44096=SBM
44097=SBM
44098=SBM
44101=SBM
44161=SBM
44162=SBM
44163=SBM
44164=SBM
44165=SBM
# EE UK
23430=EE
23431=EE
23432=EE
23433=EE
23434=EE
23476=EE
23501=EE
23502=EE
23577=EE
# Deutsche Telekom
20201=DT
20416=DT
20420=DT
21630=DT
21901=DT
22603=DT
22606=DT
23001=DT
23102=DT
23203=DT
23207=DT
26002=DT
26201=DT
27601=DT
29401=DT
29702=DT
# Others
generic=GCF
+121
View File
@@ -0,0 +1,121 @@
/* -*- 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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <string.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-plugin-foxconn.h"
#include "mm-log-object.h"
#include "mm-broadband-modem.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
#endif
#if defined WITH_MBIM
#include "mm-broadband-modem-mbim-foxconn.h"
#endif
G_DEFINE_TYPE (MMPluginFoxconn, mm_plugin_foxconn, 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_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered Foxconn-branded modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
mm_obj_dbg (self, "MBIM-powered Foxconn-branded modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_foxconn_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
mm_obj_dbg (self, "Foxconn-branded generic modem found...");
return MM_BASE_MODEM (mm_broadband_modem_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", "wwan", NULL };
static const guint16 vendor_ids[] = {
0x0489, /* usb vid */
0x105b, /* pci vid */
0 };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_FOXCONN,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
NULL));
}
static void
mm_plugin_foxconn_init (MMPluginFoxconn *self)
{
}
static void
mm_plugin_foxconn_class_init (MMPluginFoxconnClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+46
View File
@@ -0,0 +1,46 @@
/* -*- 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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_PLUGIN_FOXCONN_H
#define MM_PLUGIN_FOXCONN_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_FOXCONN (mm_plugin_foxconn_get_type ())
#define MM_PLUGIN_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_FOXCONN, MMPluginFoxconn))
#define MM_PLUGIN_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_FOXCONN, MMPluginFoxconnClass))
#define MM_IS_PLUGIN_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_FOXCONN))
#define MM_IS_PLUGIN_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_FOXCONN))
#define MM_PLUGIN_FOXCONN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_FOXCONN, MMPluginFoxconnClass))
typedef struct {
MMPlugin parent;
} MMPluginFoxconn;
typedef struct {
MMPluginClass parent;
} MMPluginFoxconnClass;
GType mm_plugin_foxconn_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_FOXCONN_H */
+20
View File
@@ -0,0 +1,20 @@
/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "mm-shared.h"
MM_SHARED_DEFINE_MAJOR_VERSION
MM_SHARED_DEFINE_MINOR_VERSION
MM_SHARED_DEFINE_NAME(Foxconn)
+120
View File
@@ -0,0 +1,120 @@
/* -*- 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 Dan Williams <dcbw@redhat.com>
*/
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <time.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-plugin-generic.h"
#include "mm-broadband-modem.h"
#include "mm-serial-parsers.h"
#include "mm-log-object.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
#endif
#if defined WITH_MBIM
#include "mm-broadband-modem-mbim.h"
#endif
G_DEFINE_TYPE (MMPluginGeneric, mm_plugin_generic, 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_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered generic modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
mm_obj_dbg (self, "MBIM-powered generic 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_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", "wwan", NULL };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_GENERIC,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_IS_GENERIC, TRUE,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_REQUIRED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
NULL));
}
static void
mm_plugin_generic_init (MMPluginGeneric *self)
{
}
static void
mm_plugin_generic_class_init (MMPluginGenericClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+40
View File
@@ -0,0 +1,40 @@
/* -*- 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) 2009 Red Hat, Inc.
*/
#ifndef MM_PLUGIN_GENERIC_H
#define MM_PLUGIN_GENERIC_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_GENERIC (mm_plugin_generic_get_type ())
#define MM_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGeneric))
#define MM_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass))
#define MM_IS_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GENERIC))
#define MM_IS_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GENERIC))
#define MM_PLUGIN_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass))
typedef struct {
MMPlugin parent;
} MMPluginGeneric;
typedef struct {
MMPluginClass parent;
} MMPluginGenericClass;
GType mm_plugin_generic_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_GENERIC_H */
@@ -0,0 +1,90 @@
/* -*- 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) 2016 Aleksander Morgado <aleksander@gnu.org>
*/
#include <sys/types.h>
#include <unistd.h>
#include <glib.h>
#include <glib-object.h>
#include <libmm-glib.h>
#include "test-port-context.h"
#include "test-fixture.h"
/*****************************************************************************/
static void
test_enable_disable (TestFixture *fixture)
{
GError *error = NULL;
MMObject *obj;
MMModem *modem;
TestPortContext *port0;
gchar *ports [] = { NULL, NULL };
/* Create port name, and add process ID so that multiple runs of this test
* in the same system don't clash with each other */
ports[0] = g_strdup_printf ("abstract:port0:%ld", (glong) getpid ());
g_debug ("test service generic: using abstract port at '%s'", ports[0]);
/* Setup new port context */
port0 = test_port_context_new (ports[0]);
test_port_context_load_commands (port0, COMMON_GSM_PORT_CONF);
test_port_context_start (port0);
/* Ensure no modem is modem exported */
test_fixture_no_modem (fixture);
/* Set the test profile */
test_fixture_set_profile (fixture,
"test-enable-disable",
"generic",
(const gchar *const *)ports);
/* Wait and get the modem object */
obj = test_fixture_get_modem (fixture);
/* Get Modem interface, and enable */
modem = mm_object_get_modem (obj);
g_assert (modem != NULL);
mm_modem_enable_sync (modem, NULL, &error);
g_assert_no_error (error);
/* And disable */
mm_modem_disable_sync (modem, NULL, &error);
g_assert_no_error (error);
g_object_unref (modem);
g_object_unref (obj);
/* Stop port context */
test_port_context_stop (port0);
test_port_context_free (port0);
g_free (ports[0]);
}
/*****************************************************************************/
int main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
TEST_ADD ("/MM/Service/Generic/enable-disable", test_enable_disable);
return g_test_run ();
}
@@ -0,0 +1,17 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_gosuncn_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="305a", GOTO="mm_gosuncn_port_types"
GOTO="mm_gosuncn_port_types_end"
LABEL="mm_gosuncn_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Gosuncn GM800
# Interfaces #3 and #4 are MBIM
ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1"
LABEL="mm_gosuncn_port_types_end"
+114
View File
@@ -0,0 +1,114 @@
/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <stdlib.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-log-object.h"
#include "mm-plugin-gosuncn.h"
#include "mm-broadband-modem.h"
#if defined WITH_QMI
# include "mm-broadband-modem-qmi.h"
#endif
#if defined WITH_MBIM
# include "mm-broadband-modem-mbim.h"
#endif
G_DEFINE_TYPE (MMPluginGosuncn, mm_plugin_gosuncn, 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_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered Gosuncn modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
mm_obj_dbg (self, "MBIM-powered Gosuncn modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
/* Fallback to default modem in the worst case */
return MM_BASE_MODEM (mm_broadband_modem_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 guint16 vendor_ids[] = { 0x305a, 0 };
static const gchar *drivers[] = { "qmi_wwan", "cdc_mbim", NULL };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_GOSUNCN,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_DRIVERS, drivers,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
NULL));
}
static void
mm_plugin_gosuncn_init (MMPluginGosuncn *self)
{
}
static void
mm_plugin_gosuncn_class_init (MMPluginGosuncnClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+40
View File
@@ -0,0 +1,40 @@
/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_PLUGIN_GOSUNCN_H
#define MM_PLUGIN_GOSUNCN_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_GOSUNCN (mm_plugin_gosuncn_get_type ())
#define MM_PLUGIN_GOSUNCN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GOSUNCN, MMPluginGosuncn))
#define MM_PLUGIN_GOSUNCN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GOSUNCN, MMPluginGosuncnClass))
#define MM_IS_PLUGIN_GOSUNCN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GOSUNCN))
#define MM_IS_PLUGIN_GOSUNCN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GOSUNCN))
#define MM_PLUGIN_GOSUNCN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GOSUNCN, MMPluginGosuncnClass))
typedef struct {
MMPlugin parent;
} MMPluginGosuncn;
typedef struct {
MMPluginClass parent;
} MMPluginGosuncnClass;
GType mm_plugin_gosuncn_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_GOSUNCN_H */
@@ -0,0 +1,13 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_haier_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="201e", GOTO="mm_haier_port_types"
GOTO="mm_haier_port_types_end"
LABEL="mm_haier_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Haier CE81B
ATTRS{idVendor}=="201e", ATTRS{idProduct}=="10f8", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
LABEL="mm_haier_port_types_end"
+76
View File
@@ -0,0 +1,76 @@
/* -*- 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 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <string.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-broadband-modem.h"
#include "mm-plugin-haier.h"
G_DEFINE_TYPE (MMPluginHaier, mm_plugin_haier, 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)
{
return MM_BASE_MODEM (mm_broadband_modem_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", NULL };
static const guint16 vendor_ids[] = { 0x201e, 0 };
return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_HAIER,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
NULL));
}
static void
mm_plugin_haier_init (MMPluginHaier *self)
{
}
static void
mm_plugin_haier_class_init (MMPluginHaierClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+40
View File
@@ -0,0 +1,40 @@
/* -*- 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 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_PLUGIN_HAIER_H
#define MM_PLUGIN_HAIER_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_HAIER (mm_plugin_haier_get_type ())
#define MM_PLUGIN_HAIER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_HAIER, MMPluginHaier))
#define MM_PLUGIN_HAIER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_HAIER, MMPluginHaierClass))
#define MM_IS_PLUGIN_HAIER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_HAIER))
#define MM_IS_PLUGIN_HAIER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_HAIER))
#define MM_PLUGIN_HAIER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_HAIER, MMPluginHaierClass))
typedef struct {
MMPlugin parent;
} MMPluginHaier;
typedef struct {
MMPluginClass parent;
} MMPluginHaierClass;
GType mm_plugin_haier_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_HAIER_H */
@@ -0,0 +1,37 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change|move|bind", GOTO="mm_huawei_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="12d1", GOTO="mm_huawei_port_types"
GOTO="mm_huawei_port_types_end"
LABEL="mm_huawei_port_types"
# MU609 does not support getportmode (crashes modem with default firmware)
ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1573", ENV{ID_MM_HUAWEI_DISABLE_GETPORTMODE}="1"
# Mark the modem and at port flags for ModemManager
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1"
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1"
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
# GPS NMEA port on MU609
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
# GPS NMEA port on MU909
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="14", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
# GPS NMEA port on MU906e
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="06", ATTRS{bInterfaceProtocol}=="14", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
# Only the standard ECM or NCM port can support dial-up with AT NDISDUP through AT port
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="02", ATTRS{bInterfaceSubClass}=="06",ATTRS{bInterfaceProtocol}=="00", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1"
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="02", ATTRS{bInterfaceSubClass}=="0d",ATTRS{bInterfaceProtocol}=="00", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1"
# Airtel branded E3372h-607, using huawei-cdc-ncm driver but with unresponsive cdc-wdm port
ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1506", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1"
# R215, Disable CPOL based features
ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1588", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1"
# E226, Disable CPOL based features
ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1003", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1"
LABEL="mm_huawei_port_types_end"
@@ -0,0 +1,879 @@
/* -*- 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) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
* Copyright (C) 2012 Huawei Technologies Co., Ltd
*
* Author: Franko fang <huanahu@huawei.com>
*/
#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>
#include "mm-base-modem-at.h"
#include "mm-broadband-bearer-huawei.h"
#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-huawei.h"
#include "mm-daemon-enums-types.h"
G_DEFINE_TYPE (MMBroadbandBearerHuawei, mm_broadband_bearer_huawei, MM_TYPE_BROADBAND_BEARER)
struct _MMBroadbandBearerHuaweiPrivate {
gpointer connect_pending;
gpointer disconnect_pending;
};
/*****************************************************************************/
static MMPortSerialAt *
get_dial_port (MMBroadbandModemHuawei *modem,
MMPort *data,
MMPortSerialAt *primary)
{
MMPortSerialAt *dial_port;
/* See if we have a cdc-wdm AT port for the interface */
dial_port = (mm_broadband_modem_huawei_peek_port_at_for_data (
MM_BROADBAND_MODEM_HUAWEI (modem), data));
if (dial_port)
return g_object_ref (dial_port);
/* Otherwise, fallback to using the primary port for dialing */
return g_object_ref (primary);
}
/*****************************************************************************/
/* Connect 3GPP */
typedef enum {
CONNECT_3GPP_CONTEXT_STEP_FIRST = 0,
CONNECT_3GPP_CONTEXT_STEP_NDISDUP,
CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY,
CONNECT_3GPP_CONTEXT_STEP_IP_CONFIG,
CONNECT_3GPP_CONTEXT_STEP_LAST
} Connect3gppContextStep;
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
Connect3gppContextStep step;
guint check_count;
guint failed_ndisstatqry_count;
MMBearerIpConfig *ipv4_config;
} Connect3gppContext;
static void
connect_3gpp_context_free (Connect3gppContext *ctx)
{
g_object_unref (ctx->modem);
g_clear_object (&ctx->ipv4_config);
g_clear_object (&ctx->data);
g_clear_object (&ctx->primary);
g_slice_free (Connect3gppContext, ctx);
}
static MMBearerConnectResult *
connect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void connect_3gpp_context_step (GTask *task);
static void
connect_dhcp_check_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerHuawei *self)
{
GTask *task;
Connect3gppContext *ctx;
const gchar *response;
GError *error = NULL;
task = self->priv->connect_pending;
g_assert (task != NULL);
ctx = g_task_get_task_data (task);
/* Balance refcount */
g_object_unref (self);
/* Cache IPv4 details if available, otherwise clients will have to use DHCP */
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (response) {
guint address = 0;
guint prefix = 0;
guint gateway = 0;
guint dns1 = 0;
guint dns2 = 0;
if (mm_huawei_parse_dhcp_response (response,
&address,
&prefix,
&gateway,
&dns1,
&dns2,
&error)) {
GInetAddress *addr;
gchar *strarr[3] = { NULL, NULL, NULL };
guint n = 0;
gchar *str;
mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_STATIC);
addr = g_inet_address_new_from_bytes ((guint8 *)&address, G_SOCKET_FAMILY_IPV4);
str = g_inet_address_to_string (addr);
mm_bearer_ip_config_set_address (ctx->ipv4_config, str);
g_free (str);
g_object_unref (addr);
/* Netmask */
mm_bearer_ip_config_set_prefix (ctx->ipv4_config, prefix);
/* Gateway */
addr = g_inet_address_new_from_bytes ((guint8 *)&gateway, G_SOCKET_FAMILY_IPV4);
str = g_inet_address_to_string (addr);
mm_bearer_ip_config_set_gateway (ctx->ipv4_config, str);
g_free (str);
g_object_unref (addr);
/* DNS */
if (dns1) {
addr = g_inet_address_new_from_bytes ((guint8 *)&dns1, G_SOCKET_FAMILY_IPV4);
strarr[n++] = g_inet_address_to_string (addr);
g_object_unref (addr);
}
if (dns2) {
addr = g_inet_address_new_from_bytes ((guint8 *)&dns2, G_SOCKET_FAMILY_IPV4);
strarr[n++] = g_inet_address_to_string (addr);
g_object_unref (addr);
}
mm_bearer_ip_config_set_dns (ctx->ipv4_config, (const gchar **)strarr);
g_free (strarr[0]);
g_free (strarr[1]);
} else {
mm_obj_dbg (self, "unexpected response to ^DHCP command: %s", error->message);
}
}
g_clear_error (&error);
ctx->step++;
connect_3gpp_context_step (task);
}
static gboolean
connect_retry_ndisstatqry_check_cb (MMBroadbandBearerHuawei *self)
{
GTask *task;
/* Recover context */
task = self->priv->connect_pending;
g_assert (task != NULL);
/* Balance refcount */
g_object_unref (self);
/* Retry same step */
connect_3gpp_context_step (task);
return G_SOURCE_REMOVE;
}
static void
connect_ndisstatqry_check_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerHuawei *self)
{
GTask *task;
Connect3gppContext *ctx;
const gchar *response;
GError *error = NULL;
gboolean ipv4_available = FALSE;
gboolean ipv4_connected = FALSE;
gboolean ipv6_available = FALSE;
gboolean ipv6_connected = FALSE;
task = self->priv->connect_pending;
g_assert (task != NULL);
ctx = g_task_get_task_data (task);
/* Balance refcount */
g_object_unref (self);
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!response ||
!mm_huawei_parse_ndisstatqry_response (response,
&ipv4_available,
&ipv4_connected,
&ipv6_available,
&ipv6_connected,
&error)) {
ctx->failed_ndisstatqry_count++;
mm_obj_dbg (self, "unexpected response to ^NDISSTATQRY command: %s (%u attempts so far)",
error->message, ctx->failed_ndisstatqry_count);
g_error_free (error);
}
/* Connected in IPv4? */
if (ipv4_available && ipv4_connected) {
/* Success! */
ctx->step++;
connect_3gpp_context_step (task);
return;
}
/* Setup timeout to retry the same step */
g_timeout_add_seconds (1,
(GSourceFunc)connect_retry_ndisstatqry_check_cb,
g_object_ref (self));
}
static void
connect_ndisdup_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerHuawei *self)
{
GTask *task;
Connect3gppContext *ctx;
GError *error = NULL;
task = self->priv->connect_pending;
g_assert (task != NULL);
ctx = g_task_get_task_data (task);
/* Balance refcount */
g_object_unref (self);
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
/* Clear task */
self->priv->connect_pending = NULL;
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Go to next step */
ctx->step++;
connect_3gpp_context_step (task);
}
typedef enum {
MM_BEARER_HUAWEI_AUTH_UNKNOWN = -1,
MM_BEARER_HUAWEI_AUTH_NONE = 0,
MM_BEARER_HUAWEI_AUTH_PAP = 1,
MM_BEARER_HUAWEI_AUTH_CHAP = 2,
MM_BEARER_HUAWEI_AUTH_MSCHAPV2 = 3,
} MMBearerHuaweiAuthPref;
static gint
huawei_parse_auth_type (MMBearerAllowedAuth mm_auth)
{
switch (mm_auth) {
case MM_BEARER_ALLOWED_AUTH_NONE:
return MM_BEARER_HUAWEI_AUTH_NONE;
case MM_BEARER_ALLOWED_AUTH_PAP:
return MM_BEARER_HUAWEI_AUTH_PAP;
case MM_BEARER_ALLOWED_AUTH_CHAP:
return MM_BEARER_HUAWEI_AUTH_CHAP;
case MM_BEARER_ALLOWED_AUTH_MSCHAPV2:
return MM_BEARER_HUAWEI_AUTH_MSCHAPV2;
default:
case MM_BEARER_ALLOWED_AUTH_UNKNOWN:
case MM_BEARER_ALLOWED_AUTH_MSCHAP:
case MM_BEARER_ALLOWED_AUTH_EAP:
return MM_BEARER_HUAWEI_AUTH_UNKNOWN;
}
}
static void
connect_3gpp_context_step (GTask *task)
{
MMBroadbandBearerHuawei *self;
Connect3gppContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
/* Check for cancellation */
if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
/* Clear task */
self->priv->connect_pending = NULL;
/* If we already sent the connetion command, send the disconnection one */
if (ctx->step > CONNECT_3GPP_CONTEXT_STEP_NDISDUP)
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"^NDISDUP=1,0",
MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE,
FALSE,
NULL,
NULL, /* Do not care the AT response */
NULL);
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED,
"Huawei connection operation has been cancelled");
g_object_unref (task);
return;
}
switch (ctx->step) {
case CONNECT_3GPP_CONTEXT_STEP_FIRST: {
MMBearerIpFamily ip_family;
ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
mm_3gpp_normalize_ip_family (&ip_family);
if (ip_family != MM_BEARER_IP_FAMILY_IPV4) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Only IPv4 is supported by this modem");
g_object_unref (task);
return;
}
/* Store the task */
self->priv->connect_pending = task;
ctx->step++;
} /* fall through */
case CONNECT_3GPP_CONTEXT_STEP_NDISDUP: {
const gchar *apn;
const gchar *user;
const gchar *passwd;
MMBearerAllowedAuth auth;
gint encoded_auth = MM_BEARER_HUAWEI_AUTH_UNKNOWN;
gchar *command;
apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
passwd = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
encoded_auth = huawei_parse_auth_type (auth);
/* Default to no authentication if not specified */
if (encoded_auth == MM_BEARER_HUAWEI_AUTH_UNKNOWN)
encoded_auth = MM_BEARER_HUAWEI_AUTH_NONE;
if (!user && !passwd)
command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\"",
apn == NULL ? "" : apn);
else {
if (encoded_auth == MM_BEARER_HUAWEI_AUTH_NONE) {
encoded_auth = MM_BEARER_HUAWEI_AUTH_CHAP;
mm_obj_dbg (self, "using default (CHAP) authentication method");
}
command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\",\"%s\",\"%s\",%d",
apn == NULL ? "" : apn,
user == NULL ? "" : user,
passwd == NULL ? "" : passwd,
encoded_auth);
}
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
3,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback)connect_ndisdup_ready,
g_object_ref (self));
g_free (command);
return;
}
case CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY:
/* Wait for dial up timeout, retries for 180 times
* (1s between the retries, so it means 3 minutes).
* If too many retries, failed
*/
if (ctx->check_count > MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT) {
/* Clear context */
self->priv->connect_pending = NULL;
g_task_return_new_error (task,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
"Connection attempt timed out");
g_object_unref (task);
return;
}
/* Give up if too many unexpected responses to NIDSSTATQRY are encountered. */
if (ctx->failed_ndisstatqry_count > 10) {
/* Clear context */
self->priv->connect_pending = NULL;
g_task_return_new_error (task,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED,
"Connection attempt not supported.");
g_object_unref (task);
return;
}
/* Check if connected */
ctx->check_count++;
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"^NDISSTATQRY?",
3,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback)connect_ndisstatqry_check_ready,
g_object_ref (self));
return;
case CONNECT_3GPP_CONTEXT_STEP_IP_CONFIG:
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"^DHCP?",
3,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback)connect_dhcp_check_ready,
g_object_ref (self));
return;
case CONNECT_3GPP_CONTEXT_STEP_LAST:
/* Clear context */
self->priv->connect_pending = NULL;
/* Setup result */
g_task_return_pointer (
task,
mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, NULL),
(GDestroyNotify)mm_bearer_connect_result_unref);
g_object_unref (task);
return;
default:
g_assert_not_reached ();
}
}
static void
connect_3gpp (MMBroadbandBearer *_self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (_self);
Connect3gppContext *ctx;
GTask *task;
MMPort *data;
g_assert (primary != NULL);
/* We need a net data port */
data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
if (!data) {
g_task_report_new_error (self,
callback,
user_data,
connect_3gpp,
MM_CORE_ERROR,
MM_CORE_ERROR_NOT_FOUND,
"No valid data port found to launch connection");
return;
}
/* Setup connection context */
ctx = g_slice_new0 (Connect3gppContext);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->data = g_object_ref (data);
ctx->step = CONNECT_3GPP_CONTEXT_STEP_FIRST;
g_assert (self->priv->connect_pending == NULL);
g_assert (self->priv->disconnect_pending == NULL);
/* Get correct dial port to use */
ctx->primary = get_dial_port (MM_BROADBAND_MODEM_HUAWEI (ctx->modem), ctx->data, primary);
/* Default to automatic/DHCP addressing */
ctx->ipv4_config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_DHCP);
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)connect_3gpp_context_free);
g_task_set_check_cancellable (task, FALSE);
/* Run! */
connect_3gpp_context_step (task);
}
/*****************************************************************************/
/* Disconnect 3GPP */
typedef enum {
DISCONNECT_3GPP_CONTEXT_STEP_FIRST = 0,
DISCONNECT_3GPP_CONTEXT_STEP_NDISDUP,
DISCONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY,
DISCONNECT_3GPP_CONTEXT_STEP_LAST
} Disconnect3gppContextStep;
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
Disconnect3gppContextStep step;
guint check_count;
guint failed_ndisstatqry_count;
} Disconnect3gppContext;
static void
disconnect_3gpp_context_free (Disconnect3gppContext *ctx)
{
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
g_slice_free (Disconnect3gppContext, ctx);
}
static gboolean
disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void disconnect_3gpp_context_step (GTask *task);
static gboolean
disconnect_retry_ndisstatqry_check_cb (MMBroadbandBearerHuawei *self)
{
GTask *task;
/* Recover context */
task = self->priv->disconnect_pending;
g_assert (task != NULL);
/* Balance refcount */
g_object_unref (self);
/* Retry same step */
disconnect_3gpp_context_step (task);
return G_SOURCE_REMOVE;
}
static void
disconnect_ndisstatqry_check_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerHuawei *self)
{
GTask *task;
Disconnect3gppContext *ctx;
const gchar *response;
GError *error = NULL;
gboolean ipv4_available = FALSE;
gboolean ipv4_connected = FALSE;
gboolean ipv6_available = FALSE;
gboolean ipv6_connected = FALSE;
task = self->priv->disconnect_pending;
g_assert (task != NULL);
ctx = g_task_get_task_data (task);
/* Balance refcount */
g_object_unref (self);
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!response ||
!mm_huawei_parse_ndisstatqry_response (response,
&ipv4_available,
&ipv4_connected,
&ipv6_available,
&ipv6_connected,
&error)) {
ctx->failed_ndisstatqry_count++;
mm_obj_dbg (self, "unexpected response to ^NDISSTATQRY command: %s (%u attempts so far)",
error->message, ctx->failed_ndisstatqry_count);
g_error_free (error);
}
/* Disconnected IPv4? */
if (ipv4_available && !ipv4_connected) {
/* Success! */
ctx->step++;
disconnect_3gpp_context_step (task);
return;
}
/* Setup timeout to retry the same step */
g_timeout_add_seconds (1,
(GSourceFunc)disconnect_retry_ndisstatqry_check_cb,
g_object_ref (self));
}
static void
disconnect_ndisdup_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerHuawei *self)
{
GTask *task;
Disconnect3gppContext *ctx;
task = self->priv->disconnect_pending;
g_assert (task != NULL);
ctx = g_task_get_task_data (task);
/* Balance refcount */
g_object_unref (self);
/* Running NDISDUP=1,0 on an already disconnected bearer/context will
* return ERROR! Ignore errors in the NDISDUP disconnection command,
* because we're anyway going to check the bearer/context status
* afterwards. */
mm_base_modem_at_command_full_finish (modem, res, NULL);
/* Go to next step */
ctx->step++;
disconnect_3gpp_context_step (task);
}
static void
disconnect_3gpp_context_step (GTask *task)
{
MMBroadbandBearerHuawei *self;
Disconnect3gppContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case DISCONNECT_3GPP_CONTEXT_STEP_FIRST:
/* Store the task */
self->priv->disconnect_pending = task;
ctx->step++;
/* fall through */
case DISCONNECT_3GPP_CONTEXT_STEP_NDISDUP:
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"^NDISDUP=1,0",
3,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback)disconnect_ndisdup_ready,
g_object_ref (self));
return;
case DISCONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY:
/* If too many retries (1s of wait between the retries), failed */
if (ctx->check_count > MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT) {
/* Clear task */
self->priv->disconnect_pending = NULL;
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;
}
/* Give up if too many unexpected responses to NIDSSTATQRY are encountered. */
if (ctx->failed_ndisstatqry_count > 10) {
/* Clear task */
self->priv->disconnect_pending = NULL;
g_task_return_new_error (task,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED,
"Disconnection attempt not supported.");
g_object_unref (task);
return;
}
/* Check if disconnected */
ctx->check_count++;
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"^NDISSTATQRY?",
3,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback)disconnect_ndisstatqry_check_ready,
g_object_ref (self));
return;
case DISCONNECT_3GPP_CONTEXT_STEP_LAST:
/* Clear task */
self->priv->disconnect_pending = NULL;
/* Set data port as result */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
g_assert_not_reached ();
}
}
static void
disconnect_3gpp (MMBroadbandBearer *_self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (_self);
Disconnect3gppContext *ctx;
GTask *task;
g_assert (primary != NULL);
ctx = g_slice_new0 (Disconnect3gppContext);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->step = DISCONNECT_3GPP_CONTEXT_STEP_FIRST;
g_assert (self->priv->connect_pending == NULL);
g_assert (self->priv->disconnect_pending == NULL);
/* Get correct dial port to use */
ctx->primary = get_dial_port (MM_BROADBAND_MODEM_HUAWEI (ctx->modem), data, primary);
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_3gpp_context_free);
/* Start! */
disconnect_3gpp_context_step (task);
}
/*****************************************************************************/
static void
report_connection_status (MMBaseBearer *bearer,
MMBearerConnectionStatus status,
const GError *connection_error)
{
MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (bearer);
g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
status == MM_BEARER_CONNECTION_STATUS_DISCONNECTING ||
status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
/* When a pending connection / disconnection attempt is in progress, we use
* ^NDISSTATQRY? to check the connection status and thus temporarily ignore
* ^NDISSTAT unsolicited messages */
if (self->priv->connect_pending || self->priv->disconnect_pending)
return;
mm_obj_dbg (self, "received spontaneous ^NDISSTAT (%s)", mm_bearer_connection_status_get_string (status));
/* Ignore 'CONNECTED' */
if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED)
return;
/* Report disconnected right away */
MM_BASE_BEARER_CLASS (mm_broadband_bearer_huawei_parent_class)->report_connection_status (
bearer,
MM_BEARER_CONNECTION_STATUS_DISCONNECTED,
NULL);
}
/*****************************************************************************/
MMBaseBearer *
mm_broadband_bearer_huawei_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_huawei_new (MMBroadbandModemHuawei *modem,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (
MM_TYPE_BROADBAND_BEARER_HUAWEI,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_BEARER_MODEM, modem,
MM_BASE_BEARER_CONFIG, config,
NULL);
}
static void
mm_broadband_bearer_huawei_init (MMBroadbandBearerHuawei *self)
{
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_BROADBAND_BEARER_HUAWEI,
MMBroadbandBearerHuaweiPrivate);
}
static void
mm_broadband_bearer_huawei_class_init (MMBroadbandBearerHuaweiClass *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 (MMBroadbandBearerHuaweiPrivate));
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->connect_3gpp = connect_3gpp;
broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
}
@@ -0,0 +1,59 @@
/* -*- 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) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
* Copyright (C) 2012 Huawei Technologies Co., Ltd
*
* Author: Franko Fang <huananhu@huawei.com>
*/
#ifndef MM_BROADBAND_BEARER_HUAWEI_H
#define MM_BROADBAND_BEARER_HUAWEI_H
#include <glib.h>
#include <glib-object.h>
#include "mm-broadband-bearer.h"
#include "mm-broadband-modem-huawei.h"
#define MM_TYPE_BROADBAND_BEARER_HUAWEI (mm_broadband_bearer_huawei_get_type ())
#define MM_BROADBAND_BEARER_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_HUAWEI, MMBroadbandBearerHuawei))
#define MM_BROADBAND_BEARER_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_HUAWEI, MMBroadbandBearerHuaweiClass))
#define MM_IS_BROADBAND_BEARER_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_HUAWEI))
#define MM_IS_BROADBAND_BEARER_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_HUAWEI))
#define MM_BROADBAND_BEARER_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_HUAWEI, MMBroadbandBearerHuaweiClass))
typedef struct _MMBroadbandBearerHuawei MMBroadbandBearerHuawei;
typedef struct _MMBroadbandBearerHuaweiClass MMBroadbandBearerHuaweiClass;
typedef struct _MMBroadbandBearerHuaweiPrivate MMBroadbandBearerHuaweiPrivate;
struct _MMBroadbandBearerHuawei {
MMBroadbandBearer parent;
MMBroadbandBearerHuaweiPrivate *priv;
};
struct _MMBroadbandBearerHuaweiClass {
MMBroadbandBearerClass parent;
};
GType mm_broadband_bearer_huawei_get_type (void);
void mm_broadband_bearer_huawei_new (MMBroadbandModemHuawei *modem,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MMBaseBearer *mm_broadband_bearer_huawei_new_finish (GAsyncResult *res,
GError **error);
#endif /* MM_BROADBAND_BEARER_HUAWEI_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,55 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
*/
#ifndef MM_BROADBAND_MODEM_HUAWEI_H
#define MM_BROADBAND_MODEM_HUAWEI_H
#include "mm-broadband-modem.h"
#define MM_TYPE_BROADBAND_MODEM_HUAWEI (mm_broadband_modem_huawei_get_type ())
#define MM_BROADBAND_MODEM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_HUAWEI, MMBroadbandModemHuawei))
#define MM_BROADBAND_MODEM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_HUAWEI, MMBroadbandModemHuaweiClass))
#define MM_IS_BROADBAND_MODEM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_HUAWEI))
#define MM_IS_BROADBAND_MODEM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_HUAWEI))
#define MM_BROADBAND_MODEM_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_HUAWEI, MMBroadbandModemHuaweiClass))
typedef struct _MMBroadbandModemHuawei MMBroadbandModemHuawei;
typedef struct _MMBroadbandModemHuaweiClass MMBroadbandModemHuaweiClass;
typedef struct _MMBroadbandModemHuaweiPrivate MMBroadbandModemHuaweiPrivate;
struct _MMBroadbandModemHuawei {
MMBroadbandModem parent;
MMBroadbandModemHuaweiPrivate *priv;
};
struct _MMBroadbandModemHuaweiClass{
MMBroadbandModemClass parent;
};
GType mm_broadband_modem_huawei_get_type (void);
MMBroadbandModemHuawei *mm_broadband_modem_huawei_new (const gchar *device,
const gchar **driver,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
MMPortSerialAt *mm_broadband_modem_huawei_peek_port_at_for_data (MMBroadbandModemHuawei *self,
MMPort *port);
GList *mm_broadband_modem_huawei_get_at_port_list (MMBroadbandModemHuawei *self);
#endif /* MM_BROADBAND_MODEM_HUAWEI_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,193 @@
/* -*- 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 Huawei Technologies Co., Ltd
* Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
*/
#ifndef MM_MODEM_HELPERS_HUAWEI_H
#define MM_MODEM_HELPERS_HUAWEI_H
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
/*****************************************************************************/
/* ^NDISSTAT / ^NDISSTATQRY response parser */
gboolean mm_huawei_parse_ndisstatqry_response (const gchar *response,
gboolean *ipv4_available,
gboolean *ipv4_connected,
gboolean *ipv6_available,
gboolean *ipv6_connected,
GError **error);
/*****************************************************************************/
/* ^DHCP response parser */
gboolean mm_huawei_parse_dhcp_response (const char *reply,
guint *out_address,
guint *out_prefix,
guint *out_gateway,
guint *out_dns1,
guint *out_dns2,
GError **error);
/*****************************************************************************/
/* ^SYSINFO response parser */
gboolean mm_huawei_parse_sysinfo_response (const char *reply,
guint *out_srv_status,
guint *out_srv_domain,
guint *out_roam_status,
guint *out_sys_mode,
guint *out_sim_state,
gboolean *out_sys_submode_valid,
guint *out_sys_submode,
GError **error);
/*****************************************************************************/
/* ^SYSINFOEX response parser */
gboolean mm_huawei_parse_sysinfoex_response (const char *reply,
guint *out_srv_status,
guint *out_srv_domain,
guint *out_roam_status,
guint *out_sim_state,
guint *out_sys_mode,
guint *out_sys_submode,
GError **error);
/*****************************************************************************/
/* ^PREFMODE test parser */
typedef struct {
guint prefmode;
MMModemMode allowed;
MMModemMode preferred;
} MMHuaweiPrefmodeCombination;
GArray *mm_huawei_parse_prefmode_test (const gchar *response,
gpointer log_object,
GError **error);
/*****************************************************************************/
/* ^PREFMODE response parser */
const MMHuaweiPrefmodeCombination *mm_huawei_parse_prefmode_response (const gchar *response,
const GArray *supported_mode_combinations,
GError **error);
/*****************************************************************************/
/* ^SYSCFG test parser */
/* This is the default string we use as fallback when the modem gives
* an empty response to AT^SYSCFG=? */
#define MM_HUAWEI_DEFAULT_SYSCFG_FMT "^SYSCFG:(2,13,14,16),(0-3),,,"
typedef struct {
guint mode;
guint acqorder;
MMModemMode allowed;
MMModemMode preferred;
} MMHuaweiSyscfgCombination;
GArray *mm_huawei_parse_syscfg_test (const gchar *response,
gpointer log_object,
GError **error);
/*****************************************************************************/
/* ^SYSCFG response parser */
const MMHuaweiSyscfgCombination *mm_huawei_parse_syscfg_response (const gchar *response,
const GArray *supported_mode_combinations,
GError **error);
/*****************************************************************************/
/* ^SYSCFGEX test parser */
typedef struct {
gchar *mode_str;
MMModemMode allowed;
MMModemMode preferred;
} MMHuaweiSyscfgexCombination;
GArray *mm_huawei_parse_syscfgex_test (const gchar *response,
GError **error);
/*****************************************************************************/
/* ^SYSCFGEX response parser */
const MMHuaweiSyscfgexCombination *mm_huawei_parse_syscfgex_response (const gchar *response,
const GArray *supported_mode_combinations,
GError **error);
/*****************************************************************************/
/* ^NWTIME response parser */
gboolean mm_huawei_parse_nwtime_response (const gchar *response,
gchar **iso8601p,
MMNetworkTimezone **tzp,
GError **error);
/*****************************************************************************/
/* ^TIME response parser */
gboolean mm_huawei_parse_time_response (const gchar *response,
gchar **iso8601p,
MMNetworkTimezone **tzp,
GError **error);
/*****************************************************************************/
/* ^HCSQ response parser */
gboolean mm_huawei_parse_hcsq_response (const gchar *response,
MMModemAccessTechnology *out_act,
guint *out_value1,
guint *out_value2,
guint *out_value3,
guint *out_value4,
guint *out_value5,
GError **error);
/*****************************************************************************/
/* ^CVOICE response parser */
gboolean mm_huawei_parse_cvoice_response (const gchar *response,
guint *hz,
guint *bits,
GError **error);
/*****************************************************************************/
/* ^GETPORTMODE response parser */
typedef enum { /*< underscore_name=mm_huawei_port_mode >*/
MM_HUAWEI_PORT_MODE_NONE,
MM_HUAWEI_PORT_MODE_PCUI,
MM_HUAWEI_PORT_MODE_MODEM,
MM_HUAWEI_PORT_MODE_DIAG,
MM_HUAWEI_PORT_MODE_GPS,
MM_HUAWEI_PORT_MODE_NET,
MM_HUAWEI_PORT_MODE_CDROM,
MM_HUAWEI_PORT_MODE_SD,
MM_HUAWEI_PORT_MODE_BT,
MM_HUAWEI_PORT_MODE_SHELL,
} MMHuaweiPortMode;
#define MM_HUAWEI_PORT_MODE_IS_SERIAL(mode) \
(mode == MM_HUAWEI_PORT_MODE_PCUI || \
mode == MM_HUAWEI_PORT_MODE_MODEM || \
mode == MM_HUAWEI_PORT_MODE_DIAG || \
mode == MM_HUAWEI_PORT_MODE_GPS || \
mode == MM_HUAWEI_PORT_MODE_SHELL)
GArray *mm_huawei_parse_getportmode_response (const gchar *response,
gpointer log_object,
GError **error);
#endif /* MM_MODEM_HELPERS_HUAWEI_H */
+735
View File
@@ -0,0 +1,735 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
*/
#include <gmodule.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include <ModemManager-tags.h>
#include "mm-port-enums-types.h"
#include "mm-log.h"
#include "mm-plugin-huawei.h"
#include "mm-broadband-modem-huawei.h"
#include "mm-modem-helpers-huawei.h"
#include "mm-huawei-enums-types.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
#endif
#if defined WITH_MBIM
#include "mm-broadband-modem-mbim.h"
#endif
G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, MM_TYPE_PLUGIN)
MM_PLUGIN_DEFINE_MAJOR_VERSION
MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom init */
#define TAG_FIRST_INTERFACE_CONTEXT "first-interface-context"
/* Maximum time to wait for the first interface 0 to appear and get probed.
* If it doesn't appear in this time, we'll decide which will be considered the
* first interface. */
#define MAX_WAIT_TIME 5
typedef struct {
MMPortProbe *probe;
gint first_usbif;
guint timeout_id;
gboolean custom_init_run;
} FirstInterfaceContext;
static void
first_interface_context_free (FirstInterfaceContext *ctx)
{
if (ctx->timeout_id)
g_source_remove (ctx->timeout_id);
g_object_unref (ctx->probe);
g_slice_free (FirstInterfaceContext, ctx);
}
#define TAG_GETPORTMODE_RESULT "getportmode-result"
#define TAG_AT_PORT_FLAGS "at-port-flags"
typedef struct {
MMPortSerialAt *port;
gboolean curc_done;
guint curc_retries;
gboolean getportmode_done;
guint getportmode_retries;
} HuaweiCustomInitContext;
static void
huawei_custom_init_context_free (HuaweiCustomInitContext *ctx)
{
g_object_unref (ctx->port);
g_slice_free (HuaweiCustomInitContext, ctx);
}
static gboolean
huawei_custom_init_finish (MMPortProbe *probe,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_boolean (G_TASK (result), error);
}
static void huawei_custom_init_step (GTask *task);
static void
getportmode_ready (MMPortSerialAt *port,
GAsyncResult *res,
GTask *task)
{
MMDevice *device;
MMPortProbe *probe;
HuaweiCustomInitContext *ctx;
const gchar *response;
GArray *modes;
g_autoptr(GError) error = NULL;
probe = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
device = mm_port_probe_peek_device (probe);
response = mm_port_serial_at_command_finish (port, res, &error);
if (error) {
mm_obj_dbg (probe, "couldn't get port mode: '%s'", error->message);
/* If any error occurred that was not ERROR or COMMAND NOT SUPPORT then
* retry the command.
*/
if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN))
ctx->getportmode_done = TRUE;
huawei_custom_init_step (task);
return;
}
/* Mark port as being AT already */
mm_port_probe_set_result_at (probe, TRUE);
/* Flag as GETPORTMODE already done */
ctx->getportmode_done = TRUE;
modes = mm_huawei_parse_getportmode_response (response, probe, &error);
if (!modes)
mm_obj_warn (probe, "failed to parse ^GETPORTMODE response: %s", error->message);
else
g_object_set_data_full (G_OBJECT (device), TAG_GETPORTMODE_RESULT, modes, (GDestroyNotify) g_array_unref);
huawei_custom_init_step (task);
}
static void
curc_ready (MMPortSerialAt *port,
GAsyncResult *res,
GTask *task)
{
MMPortProbe *probe;
HuaweiCustomInitContext *ctx;
g_autoptr(GError) error = NULL;
probe = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
mm_port_serial_at_command_finish (port, res, &error);
if (error) {
/* Retry if we get a timeout error */
if (g_error_matches (error,
MM_SERIAL_ERROR,
MM_SERIAL_ERROR_RESPONSE_TIMEOUT))
goto out;
mm_obj_dbg (probe, "couldn't turn off unsolicited messages in secondary ports: %s", error->message);
}
mm_obj_dbg (probe, "unsolicited messages in secondary ports turned off");
ctx->curc_done = TRUE;
out:
huawei_custom_init_step (task);
}
static void
try_next_usbif (MMPortProbe *probe,
MMDevice *device)
{
FirstInterfaceContext *fi_ctx;
GList *l;
gint closest;
fi_ctx = g_object_get_data (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT);
g_assert (fi_ctx != NULL);
/* Look for the next closest one among the list of interfaces in the device,
* and enable that one as being first */
closest = G_MAXINT;
for (l = mm_device_peek_port_probe_list (device); l; l = g_list_next (l)) {
MMPortProbe *iter = MM_PORT_PROBE (l->data);
/* Only expect ttys for next probing attempt */
if (g_str_equal (mm_port_probe_get_port_subsys (iter), "tty")) {
gint usbif;
usbif = mm_kernel_device_get_interface_number (mm_port_probe_peek_port (iter));
if (usbif == fi_ctx->first_usbif) {
/* This is the one we just probed, which wasn't yet removed, so just skip it */
} else if (usbif > fi_ctx->first_usbif &&
usbif < closest) {
closest = usbif;
}
}
}
if (closest == G_MAXINT) {
/* No more ttys to try! Just return something */
closest = 0;
mm_obj_dbg (probe, "no more ports to run initial probing");
} else
mm_obj_dbg (probe, "will try initial probing with interface '%d' instead", closest);
fi_ctx->first_usbif = closest;
}
static void
huawei_custom_init_step (GTask *task)
{
MMPortProbe *probe;
HuaweiCustomInitContext *ctx;
FirstInterfaceContext *fi_ctx;
MMKernelDevice *port;
probe = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
/* If cancelled, end */
if (g_task_return_error_if_cancelled (task)) {
mm_obj_dbg (probe, "no need to keep on running custom init");
g_object_unref (task);
return;
}
if (!ctx->curc_done) {
if (ctx->curc_retries == 0) {
/* All retries consumed, probably not an AT port */
mm_port_probe_set_result_at (probe, FALSE);
/* Try with next */
try_next_usbif (probe, mm_port_probe_peek_device (probe));
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
ctx->curc_retries--;
/* Turn off unsolicited messages on secondary ports until needed */
mm_port_serial_at_command (
ctx->port,
"AT^CURC=0",
3,
FALSE, /* raw */
FALSE, /* allow_cached */
g_task_get_cancellable (task),
(GAsyncReadyCallback)curc_ready,
task);
return;
}
/* Try to get a port map from the modem */
port = mm_port_probe_peek_port (probe);
if (!ctx->getportmode_done && !mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_HUAWEI_DISABLE_GETPORTMODE")) {
if (ctx->getportmode_retries == 0) {
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
ctx->getportmode_retries--;
mm_port_serial_at_command (
ctx->port,
"AT^GETPORTMODE",
3,
FALSE, /* raw */
FALSE, /* allow_cached */
g_task_get_cancellable (task),
(GAsyncReadyCallback)getportmode_ready,
task);
return;
}
/* All done it seems */
fi_ctx = g_object_get_data (G_OBJECT (mm_port_probe_peek_device (probe)), TAG_FIRST_INTERFACE_CONTEXT);
g_assert (fi_ctx != NULL);
fi_ctx->custom_init_run = TRUE;
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static gboolean
first_interface_missing_timeout_cb (MMDevice *device)
{
FirstInterfaceContext *fi_ctx;
fi_ctx = g_object_get_data (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT);
g_assert (fi_ctx != NULL);
try_next_usbif (fi_ctx->probe, device);
/* Reload the timeout, just in case we end up not having the next interface to probe...
* which is anyway very unlikely as we got it by looking at the real probe list, but anyway... */
return G_SOURCE_CONTINUE;
}
static void
huawei_custom_init (MMPortProbe *probe,
MMPortSerialAt *port,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMDevice *device;
FirstInterfaceContext *fi_ctx;
HuaweiCustomInitContext *ctx;
GTask *task;
device = mm_port_probe_peek_device (probe);
/* The primary port (called the "modem" port in the Windows drivers) is
* always USB interface 0, and we need to detect that interface first for
* two reasons: (1) to disable unsolicited messages on other ports that
* may fill up the buffer and crash the device, and (2) to attempt to get
* the port layout for hints about what the secondary port is (called the
* "pcui" port in Windows). Thus we probe USB interface 0 first and defer
* probing other interfaces until we've got if0, at which point we allow
* the other ports to be probed too.
*/
fi_ctx = g_object_get_data (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT);
if (!fi_ctx) {
/* This is the first time we ask for the context. Set it up. */
fi_ctx = g_slice_new0 (FirstInterfaceContext);
fi_ctx->probe = g_object_ref (probe);
g_object_set_data_full (G_OBJECT (device),
TAG_FIRST_INTERFACE_CONTEXT,
fi_ctx,
(GDestroyNotify)first_interface_context_free);
/* The timeout is controlled in the data set in 'device', and therefore
* it should be safe to assume that the timeout will not get fired after
* having disposed 'device' */
fi_ctx->timeout_id = g_timeout_add_seconds (MAX_WAIT_TIME,
(GSourceFunc)first_interface_missing_timeout_cb,
device);
/* By default, we'll ask the Huawei plugin to start probing usbif 0 */
fi_ctx->first_usbif = 0;
/* Custom init of the Huawei plugin is to be run only in the first
* interface. We'll control here whether we did run it already or not. */
fi_ctx->custom_init_run = FALSE;
}
ctx = g_slice_new (HuaweiCustomInitContext);
ctx->port = g_object_ref (port);
ctx->curc_done = FALSE;
ctx->curc_retries = 3;
ctx->getportmode_done = FALSE;
ctx->getportmode_retries = 3;
task = g_task_new (probe, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)huawei_custom_init_context_free);
/* Custom init only to be run in the first interface */
if (mm_kernel_device_get_interface_number (mm_port_probe_peek_port (probe)) != fi_ctx->first_usbif) {
if (fi_ctx->custom_init_run)
/* If custom init was run already, we can consider this as successfully run */
g_task_return_boolean (task, TRUE);
else
/* Otherwise, we'll need to defer the probing a bit more */
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_RETRY,
"Defer needed");
g_object_unref (task);
return;
}
/* We can run custom init in the first interface! clear the timeout as it is no longer needed */
if (fi_ctx->timeout_id) {
g_source_remove (fi_ctx->timeout_id);
fi_ctx->timeout_id = 0;
}
huawei_custom_init_step (task);
}
/*****************************************************************************/
static gint
probe_cmp_by_usbif (MMPortProbe *a,
MMPortProbe *b)
{
return (mm_kernel_device_get_interface_number (mm_port_probe_peek_port (a)) -
mm_kernel_device_get_interface_number (mm_port_probe_peek_port (b)));
}
static guint
propagate_getportmode_hints (MMPlugin *self,
GList *probes,
gboolean *primary_flagged)
{
MMDevice *device;
GArray *modes;
GList *l;
GList *tty_probes = NULL;
guint n_ports_with_hints = 0;
guint mode_i = 0;
g_assert (probes != NULL);
device = mm_port_probe_peek_device (MM_PORT_PROBE (probes->data));
modes = g_object_get_data (G_OBJECT (device), TAG_GETPORTMODE_RESULT);
/* Nothing to do if GETPORTMODE is flagged as not supported */
if (!modes)
return 0;
/* Build a list of TTY port probes (AT and not-AT) sorted by interface number */
for (l = probes; l; l = g_list_next (l)) {
MMPortProbe *probe;
probe = MM_PORT_PROBE (l->data);
if (g_str_equal (mm_port_probe_get_port_subsys (probe), "tty"))
tty_probes = g_list_insert_sorted (tty_probes, probe, (GCompareFunc) probe_cmp_by_usbif);
}
/* Propagate the getportmode tags to the specific port probes */
for (l = tty_probes, mode_i = 0; l; l = g_list_next (l)) {
MMPortProbe *probe;
MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE;
MMHuaweiPortMode port_mode;
probe = MM_PORT_PROBE (l->data);
/* Look for the next serial port mode applicable */
while (!MM_HUAWEI_PORT_MODE_IS_SERIAL (g_array_index (modes, MMHuaweiPortMode, mode_i)) && (mode_i < modes->len))
mode_i++;
if (mode_i == modes->len) {
mm_obj_dbg (probe, "missing port mode hint");
continue;
}
port_mode = g_array_index (modes, MMHuaweiPortMode, mode_i);
if (!mm_port_probe_is_at (probe)) {
mm_obj_dbg (probe, "port mode hint for non-AT port: %s", mm_huawei_port_mode_get_string (port_mode));
mode_i++;
continue;
}
mm_obj_dbg (probe, "port mode hint for AT port: %s", mm_huawei_port_mode_get_string (port_mode));
if (port_mode == MM_HUAWEI_PORT_MODE_PCUI)
at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
else if (port_mode == MM_HUAWEI_PORT_MODE_MODEM)
at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP;
if (at_port_flags != MM_PORT_SERIAL_AT_FLAG_NONE) {
n_ports_with_hints++;
g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags));
}
mode_i++;
}
g_list_free (tty_probes);
return n_ports_with_hints;
}
static guint
propagate_description_hints (MMPlugin *self,
GList *probes,
gboolean *primary_flagged)
{
GList *l;
guint n_ports_with_hints = 0;
for (l = probes; l; l = g_list_next (l)) {
MMPortProbe *probe;
MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE;
const gchar *description;
g_autofree gchar *lower_description = NULL;
probe = MM_PORT_PROBE (l->data);
if (!mm_port_probe_is_at (probe))
continue;
description = mm_kernel_device_get_interface_description (mm_port_probe_peek_port (probe));
if (!description)
continue;
mm_obj_dbg (probe, "%s interface description: %s", mm_port_probe_get_port_name (probe), description);
lower_description = g_ascii_strdown (description, -1);
if (strstr (lower_description, "modem"))
at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP;
else if (strstr (lower_description, "pcui")) {
at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
*primary_flagged = TRUE;
}
if (at_port_flags != MM_PORT_SERIAL_AT_FLAG_NONE) {
n_ports_with_hints++;
g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags));
}
}
return n_ports_with_hints;
}
static guint
propagate_generic_hints (MMPlugin *self,
GList *probes,
gboolean *primary_flagged)
{
GList *l;
guint n_ports_with_hints = 0;
for (l = probes; l; l = g_list_next (l)) {
MMPortProbe *probe;
MMKernelDevice *kernel_device;
MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE;
probe = MM_PORT_PROBE (l->data);
if (!mm_port_probe_is_at (probe))
continue;
kernel_device = mm_port_probe_peek_port (probe);
if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PRIMARY)) {
at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
*primary_flagged = TRUE;
}
else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_SECONDARY))
at_port_flags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PPP))
at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP;
if (at_port_flags != MM_PORT_SERIAL_AT_FLAG_NONE) {
n_ports_with_hints++;
g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags));
}
}
return n_ports_with_hints;
}
static guint
fallback_primary_cdcwdm (MMPlugin *self,
GList *probes)
{
GList *l;
for (l = probes; l; l = g_list_next (l)) {
MMPortProbe *probe;
probe = MM_PORT_PROBE (l->data);
if (!mm_port_probe_is_at (probe))
continue;
if (g_str_equal (mm_port_probe_get_port_subsys (probe), "usbmisc")) {
mm_obj_dbg (self, "fallback port type hint applied to first cdc-wmd port found");
g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (MM_PORT_SERIAL_AT_FLAG_PRIMARY));
return 1;
}
}
return 0;
}
static guint
fallback_usbif0 (MMPlugin *self,
GList *probes)
{
GList *l;
for (l = probes; l; l = g_list_next (l)) {
MMPortProbe *probe;
guint usbif;
probe = MM_PORT_PROBE (l->data);
if (!mm_port_probe_is_at (probe))
continue;
usbif = mm_kernel_device_get_property_as_int_hex (mm_port_probe_peek_port (probe), "ID_USB_INTERFACE_NUM");
if (usbif == 0) {
mm_obj_dbg (self, "fallback port type hint applied to interface 0");
g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (MM_PORT_SERIAL_AT_FLAG_PPP));
return 1;
}
}
return 0;
}
static void
propagate_port_type_hints (MMPlugin *self,
GList *probes)
{
gboolean primary_flagged = FALSE;
guint n_ports_with_hints;
g_assert (probes != NULL);
if ((n_ports_with_hints = propagate_getportmode_hints (self, probes, &primary_flagged)) > 0)
mm_obj_dbg (self, "port type hints set by GETPORTMODE");
else if ((n_ports_with_hints = propagate_description_hints (self, probes, &primary_flagged)) > 0)
mm_obj_dbg (self, "port type hints set by interface descriptions");
else if ((n_ports_with_hints = propagate_generic_hints (self, probes, &primary_flagged)) > 0)
mm_obj_dbg (self, "port type hints set by generic udev tags");
/* Fallback hint for the first cdc-wdm port if no other port has been flagged as primary */
if (!primary_flagged)
n_ports_with_hints += fallback_primary_cdcwdm (self, probes);
/* If not a single port type hint available (not plugin-provided and not generic)
* then we'll assume usbif 0 is the modem port */
if (!n_ports_with_hints)
n_ports_with_hints = fallback_usbif0 (self, probes);
mm_obj_dbg (self, "%u port hints have been set", n_ports_with_hints);
}
static MMBaseModem *
create_modem (MMPlugin *self,
const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
guint16 subsystem_vendor,
GList *probes,
GError **error)
{
propagate_port_type_hints (self, probes);
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
mm_obj_dbg (self, "QMI-powered Huawei modem found...");
return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
mm_obj_dbg (self, "MBIM-powered Huawei 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_huawei_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
static gboolean
grab_port (MMPlugin *self,
MMBaseModem *modem,
MMPortProbe *probe,
GError **error)
{
MMPortSerialAtFlag pflags;
MMKernelDevice *port;
MMPortType port_type;
port_type = mm_port_probe_get_port_type (probe);
port = mm_port_probe_peek_port (probe);
pflags = (MMPortSerialAtFlag) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS));
if (pflags != MM_PORT_SERIAL_AT_FLAG_NONE) {
gchar *str;
str = mm_port_serial_at_flag_build_string_from_mask (pflags);
mm_obj_dbg (self, "(%s/%s) port will have AT flags '%s'",
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe),
str);
g_free (str);
} else {
/* The huawei plugin handles the generic udev tags itself, so explicitly request
* to avoid processing them by the generic modem. */
pflags = MM_PORT_SERIAL_AT_FLAG_NONE_NO_GENERIC;
}
return mm_base_modem_grab_port (modem,
port,
port_type,
pflags,
error);
}
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const guint16 vendor_ids[] = { 0x12d1, 0 };
static const MMAsyncMethod custom_init = {
.async = G_CALLBACK (huawei_custom_init),
.finish = G_CALLBACK (huawei_custom_init_finish),
};
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_HUAWEI,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_REQUIRED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
MM_PLUGIN_CUSTOM_INIT, &custom_init,
NULL));
}
static void
mm_plugin_huawei_init (MMPluginHuawei *self)
{
}
static void
mm_plugin_huawei_class_init (MMPluginHuaweiClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
plugin_class->grab_port = grab_port;
}
+42
View File
@@ -0,0 +1,42 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org
*/
#ifndef MM_PLUGIN_HUAWEI_H
#define MM_PLUGIN_HUAWEI_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_HUAWEI (mm_plugin_huawei_get_type ())
#define MM_PLUGIN_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuawei))
#define MM_PLUGIN_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuaweiClass))
#define MM_IS_PLUGIN_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_HUAWEI))
#define MM_IS_PLUGIN_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_HUAWEI))
#define MM_PLUGIN_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuaweiClass))
typedef struct {
MMPlugin parent;
} MMPluginHuawei;
typedef struct {
MMPluginClass parent;
} MMPluginHuaweiClass;
GType mm_plugin_huawei_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_HUAWEI_H */
+167
View File
@@ -0,0 +1,167 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
*/
#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-modem-helpers.h"
#include "mm-base-modem-at.h"
#include "mm-sim-huawei.h"
G_DEFINE_TYPE (MMSimHuawei, mm_sim_huawei, MM_TYPE_BASE_SIM)
/*****************************************************************************/
/* SIM identifier loading */
static gchar *
load_sim_identifier_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_sim_identifier_ready (MMSimHuawei *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
gchar *simid;
simid = MM_BASE_SIM_CLASS (mm_sim_huawei_parent_class)->load_sim_identifier_finish (MM_BASE_SIM (self), res, &error);
if (simid)
g_task_return_pointer (task, simid, g_free);
else
g_task_return_error (task, error);
g_object_unref (task);
}
static void
iccid_read_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBaseSim *self;
const gchar *response;
const gchar *p;
char *parsed;
response = mm_base_modem_at_command_finish (modem, res, NULL);
if (!response)
goto error;
p = mm_strip_tag (response, "^ICCID:");
if (!p)
goto error;
parsed = mm_3gpp_parse_iccid (p, NULL);
if (parsed) {
g_task_return_pointer (task, parsed, g_free);
g_object_unref (task);
return;
}
error:
/* Chain up to parent method; older devices don't support ^ICCID */
self = g_task_get_source_object (task);
MM_BASE_SIM_CLASS (mm_sim_huawei_parent_class)->load_sim_identifier (self,
(GAsyncReadyCallback) parent_load_sim_identifier_ready,
task);
}
static void
load_sim_identifier (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBaseModem *modem = NULL;
g_object_get (self,
MM_BASE_SIM_MODEM, &modem,
NULL);
mm_base_modem_at_command (
modem,
"^ICCID?",
5,
FALSE,
(GAsyncReadyCallback)iccid_read_ready,
g_task_new (self, NULL, callback, user_data));
g_object_unref (modem);
}
/*****************************************************************************/
MMBaseSim *
mm_sim_huawei_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_huawei_new (MMBaseModem *modem,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (MM_TYPE_SIM_HUAWEI,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
"active", TRUE, /* by default always active */
NULL);
}
static void
mm_sim_huawei_init (MMSimHuawei *self)
{
}
static void
mm_sim_huawei_class_init (MMSimHuaweiClass *klass)
{
MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass);
base_sim_class->load_sim_identifier = load_sim_identifier;
base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish;
}
+53
View File
@@ -0,0 +1,53 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
*/
#ifndef MM_SIM_HUAWEI_H
#define MM_SIM_HUAWEI_H
#include <glib.h>
#include <glib-object.h>
#include "mm-base-sim.h"
#define MM_TYPE_SIM_HUAWEI (mm_sim_huawei_get_type ())
#define MM_SIM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_HUAWEI, MMSimHuawei))
#define MM_SIM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_HUAWEI, MMSimHuaweiClass))
#define MM_IS_SIM_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_HUAWEI))
#define MM_IS_SIM_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_HUAWEI))
#define MM_SIM_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_HUAWEI, MMSimHuaweiClass))
typedef struct _MMSimHuawei MMSimHuawei;
typedef struct _MMSimHuaweiClass MMSimHuaweiClass;
struct _MMSimHuawei {
MMBaseSim parent;
};
struct _MMSimHuaweiClass {
MMBaseSimClass parent;
};
GType mm_sim_huawei_get_type (void);
void mm_sim_huawei_new (MMBaseModem *modem,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MMBaseSim *mm_sim_huawei_new_finish (GAsyncResult *res,
GError **error);
#endif /* MM_SIM_HUAWEI_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,882 @@
/* -*- 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 Aleksander Morgado <aleksander@gnu.org>
*/
#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-broadband-bearer-icera.h"
#include "mm-base-modem-at.h"
#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-error-helpers.h"
#include "mm-daemon-enums-types.h"
#include "mm-modem-helpers-icera.h"
G_DEFINE_TYPE (MMBroadbandBearerIcera, mm_broadband_bearer_icera, MM_TYPE_BROADBAND_BEARER);
enum {
PROP_0,
PROP_DEFAULT_IP_METHOD,
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
struct _MMBroadbandBearerIceraPrivate {
MMBearerIpMethod default_ip_method;
/* Connection related */
gpointer connect_pending;
guint connect_pending_id;
gulong connect_cancellable_id;
gulong connect_port_closed_id;
/* Disconnection related */
gpointer disconnect_pending;
guint disconnect_pending_id;
};
/*****************************************************************************/
/* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
guint cid;
} 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_task_return_error (task, error);
goto out;
}
if (!mm_icera_parse_ipdpaddr_response (response,
ctx->cid,
&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)
{
MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_self);
GetIpConfig3gppContext *ctx;
GTask *task;
ctx = g_new0 (GetIpConfig3gppContext, 1);
ctx->modem = g_object_ref (MM_BASE_MODEM (modem));
ctx->primary = g_object_ref (primary);
ctx->cid = cid;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)get_ip_config_context_free);
if (self->priv->default_ip_method == MM_BEARER_IP_METHOD_STATIC) {
gchar *command;
command = g_strdup_printf ("%%IPDPADDR=%u", cid);
mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
primary,
command,
3,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)ip_config_ready,
task);
g_free (command);
return;
}
/* Otherwise, DHCP */
if (self->priv->default_ip_method == MM_BEARER_IP_METHOD_DHCP) {
MMBearerConnectResult *connect_result;
MMBearerIpConfig *ipv4_config = NULL, *ipv6_config = NULL;
if (ip_family & MM_BEARER_IP_FAMILY_IPV4 || ip_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 (ip_family & MM_BEARER_IP_FAMILY_IPV6 || ip_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);
}
g_assert (ipv4_config || ipv6_config);
connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary),
ipv4_config,
ipv6_config);
g_clear_object (&ipv4_config);
g_clear_object (&ipv6_config);
g_task_return_pointer (task,
connect_result,
(GDestroyNotify)mm_bearer_connect_result_unref);
g_object_unref (task);
return;
}
g_assert_not_reached ();
}
/*****************************************************************************/
/* 3GPP disconnection */
static gboolean
disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
disconnect_3gpp_timed_out_cb (MMBroadbandBearerIcera *self)
{
GTask *task;
/* Recover disconnection task */
task = self->priv->disconnect_pending;
self->priv->disconnect_pending = NULL;
self->priv->disconnect_pending_id = 0;
g_task_return_new_error (task,
MM_SERIAL_ERROR,
MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
"Disconnection attempt timed out");
g_object_unref (task);
return G_SOURCE_REMOVE;
}
static void
process_pending_disconnect_attempt (MMBroadbandBearerIcera *self,
MMBearerConnectionStatus status)
{
GTask *task;
/* Recover disconnection task */
task = self->priv->disconnect_pending;
self->priv->disconnect_pending = NULL;
g_assert (task != NULL);
/* Cleanup timeout, if any */
if (self->priv->disconnect_pending_id) {
g_source_remove (self->priv->disconnect_pending_id);
self->priv->disconnect_pending_id = 0;
}
/* Received 'CONNECTED' during a disconnection attempt? */
if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Disconnection failed");
g_object_unref (task);
return;
}
/* Received 'DISCONNECTED' during a disconnection attempt? */
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED ||
status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) {
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* No other status is expected by this implementation */
g_assert_not_reached ();
}
static void
disconnect_ipdpact_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerIcera *self)
{
GError *error = NULL;
GTask *task;
/* Try to recover the disconnection task. If none found, it means the
* task was already completed and we have nothing else to do. */
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
* about the result */
mm_base_modem_at_command_full_finish (modem, res, NULL);
goto out;
}
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
goto out;
}
/* Track again */
self->priv->disconnect_pending = task;
/* Set a 60-second disconnection-failure timeout */
self->priv->disconnect_pending_id = g_timeout_add_seconds (60,
(GSourceFunc)disconnect_3gpp_timed_out_cb,
self);
out:
/* Balance refcount with the extra ref we passed to command_full() */
g_object_unref (self);
}
static void
disconnect_3gpp (MMBroadbandBearer *bearer,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (bearer);
gchar *command;
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* The unsolicited response to %IPDPACT may come before the OK does.
* We will keep the disconnection task in the bearer private data so
* that it is accessible from the unsolicited message handler. Note
* also that we do NOT pass the task to the GAsyncReadyCallback, as it
* may not be valid any more when the callback is called (it may be
* already completed in the unsolicited handling) */
g_assert (self->priv->disconnect_pending == NULL);
self->priv->disconnect_pending = task;
command = g_strdup_printf ("%%IPDPACT=%d,0", cid);
mm_base_modem_at_command_full (
MM_BASE_MODEM (modem),
primary,
command,
MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_ipdpact_ready,
g_object_ref (self)); /* we pass the bearer object! */
g_free (command);
}
/*****************************************************************************/
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
typedef struct {
MMBaseModem *modem;
MMPortSerialAt *primary;
guint cid;
MMPort *data;
guint authentication_retries;
GError *saved_error;
} Dial3gppContext;
static void
dial_3gpp_context_free (Dial3gppContext *ctx)
{
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 guint
dial_3gpp_get_connecting_cid (GTask *task)
{
Dial3gppContext *ctx;
ctx = g_task_get_task_data (task);
return ctx->cid;
}
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 (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
Dial3gppContext *ctx;
ctx = g_task_get_task_data (task);
mm_base_modem_at_command_full_finish (modem, 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)
{
Dial3gppContext *ctx;
gchar *command;
ctx = g_task_get_task_data (task);
/* Need to reset the connection attempt */
command = g_strdup_printf ("%%IPDPACT=%d,0", ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)connect_reset_ready,
task);
g_free (command);
}
static gboolean
connect_timed_out_cb (MMBroadbandBearerIcera *self)
{
GTask *task;
Dial3gppContext *ctx;
/* Cleanup timeout ID */
self->priv->connect_pending_id = 0;
/* Recover task and own it */
task = self->priv->connect_pending;
self->priv->connect_pending = NULL;
g_assert (task);
ctx = g_task_get_task_data (task);
/* Remove closed port watch, if found */
if (self->priv->connect_port_closed_id) {
g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id);
self->priv->connect_port_closed_id = 0;
}
/* Setup error to return after the reset */
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");
/* It's probably pointless to try to reset this here, but anyway... */
connect_reset (task);
return G_SOURCE_REMOVE;
}
static void
ier_query_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMBroadbandBearerIcera *self;
const gchar *response;
GError *activation_error = NULL;
self = g_task_get_source_object (task);
response = mm_base_modem_at_command_full_finish (modem, res, NULL);
if (response) {
gint nw_activation_err;
response = mm_strip_tag (response, "%IER:");
if (sscanf (response, "%*d,%*d,%d", &nw_activation_err)) {
/* 3GPP TS 24.008 Annex G error codes:
* 27 - Unknown or missing access point name
* 33 - Requested service option not subscribed
*/
if (nw_activation_err == 27 || nw_activation_err == 33)
activation_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED, self);
}
}
if (activation_error)
g_task_return_error (task, activation_error);
else
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Call setup failed");
g_object_unref (task);
}
static void
process_pending_connect_attempt (MMBroadbandBearerIcera *self,
MMBearerConnectionStatus status)
{
GTask *task;
Dial3gppContext *ctx;
/* Recover task and remove both cancellation and timeout (if any)*/
g_assert (self->priv->connect_pending);
task = self->priv->connect_pending;
self->priv->connect_pending = NULL;
ctx = g_task_get_task_data (task);
if (self->priv->connect_pending_id) {
g_source_remove (self->priv->connect_pending_id);
self->priv->connect_pending_id = 0;
}
if (self->priv->connect_port_closed_id) {
g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id);
self->priv->connect_port_closed_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;
}
/* Received CONNECTION_FAILED during a connection attempt? */
if (status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) {
/* Try to gather additional info about the connection failure */
mm_base_modem_at_command_full (
ctx->modem,
ctx->primary,
"%IER?",
60,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback) ier_query_ready,
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 void
forced_close_cb (MMBroadbandBearerIcera *self)
{
/* Just treat the forced close event as any other unsolicited message */
mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),
MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED);
}
static void
activate_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerIcera *self)
{
GTask *task;
Dial3gppContext *ctx;
GError *error = NULL;
task = g_steal_pointer (&self->priv->connect_pending);
/* Try to recover the connection context. If none found, it means the
* context was already completed and we have nothing else to do. */
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;
}
/* Errors on the dial command are fatal */
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
goto out;
}
/* Track again */
self->priv->connect_pending = task;
/* We will now setup a timeout and keep the context in the bearer's private.
* Reports of modem being connected will arrive via unsolicited messages.
* This timeout should be long enough. Actually... ideally should never get
* reached. */
self->priv->connect_pending_id = g_timeout_add_seconds (MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
(GSourceFunc)connect_timed_out_cb,
self);
/* If we get the port closed, we treat as a connect error */
ctx = g_task_get_task_data (task);
self->priv->connect_port_closed_id = g_signal_connect_swapped (ctx->primary,
"forced-close",
G_CALLBACK (forced_close_cb),
self);
out:
/* Balance refcount with the extra ref we passed to command_full() */
g_object_unref (self);
}
static void
dial_3gpp (MMBroadbandBearer *_self,
MMBaseModem *modem,
MMPortSerialAt *primary,
guint cid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_self);
GTask *task;
Dial3gppContext *ctx;
g_autofree gchar *cmd = NULL;
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;
}
/* The unsolicited response to %IPDPACT 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. Note
* also that we do NOT pass the ctx to the GAsyncReadyCallback, as it
* may not be valid any more when the callback is called (it may be
* already completed in the unsolicited handling) */
g_assert (self->priv->connect_pending == NULL);
self->priv->connect_pending = task;
cmd = g_strdup_printf ("%%IPDPACT=%d,1", ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
cmd,
MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback) activate_ready,
g_object_ref (self)); /* we pass the bearer object! */
}
/*****************************************************************************/
gint
mm_broadband_bearer_icera_get_connecting_profile_id (MMBroadbandBearerIcera *self)
{
return (self->priv->connect_pending ?
(gint)dial_3gpp_get_connecting_cid (self->priv->connect_pending) :
MM_3GPP_PROFILE_ID_UNKNOWN);
}
/*****************************************************************************/
static void
report_connection_status (MMBaseBearer *_self,
MMBearerConnectionStatus status,
const GError *connection_error)
{
MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_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 %%IPDPACT (%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_icera_parent_class)->report_connection_status (
_self,
MM_BEARER_CONNECTION_STATUS_DISCONNECTED,
connection_error);
}
}
/*****************************************************************************/
MMBaseBearer *
mm_broadband_bearer_icera_new_finish (GAsyncResult *res,
GError **error)
{
GObject *source;
GObject *bearer;
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_icera_new (MMBroadbandModem *modem,
MMBearerIpMethod ip_method,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (
MM_TYPE_BROADBAND_BEARER_ICERA,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_BEARER_MODEM, modem,
MM_BASE_BEARER_CONFIG, config,
MM_BROADBAND_BEARER_ICERA_DEFAULT_IP_METHOD, ip_method,
NULL);
}
static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (object);
switch (prop_id) {
case PROP_DEFAULT_IP_METHOD:
self->priv->default_ip_method = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (object);
switch (prop_id) {
case PROP_DEFAULT_IP_METHOD:
g_value_set_enum (value, self->priv->default_ip_method);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
mm_broadband_bearer_icera_init (MMBroadbandBearerIcera *self)
{
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_BROADBAND_BEARER_ICERA,
MMBroadbandBearerIceraPrivate);
/* Defaults */
self->priv->default_ip_method = MM_BEARER_IP_METHOD_STATIC;
}
static void
mm_broadband_bearer_icera_class_init (MMBroadbandBearerIceraClass *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 (MMBroadbandBearerIceraPrivate));
object_class->get_property = get_property;
object_class->set_property = set_property;
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;
properties[PROP_DEFAULT_IP_METHOD] =
g_param_spec_enum (MM_BROADBAND_BEARER_ICERA_DEFAULT_IP_METHOD,
"Default IP method",
"Default IP Method (static or DHCP) to use.",
MM_TYPE_BEARER_IP_METHOD,
MM_BEARER_IP_METHOD_STATIC,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_DEFAULT_IP_METHOD, properties[PROP_DEFAULT_IP_METHOD]);
}
@@ -0,0 +1,66 @@
/* -*- 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:
*
* Author: Nathan Williams <njw@google.com>
*
* Copyright (C) 2012 Google, Inc.
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
*/
#ifndef MM_BROADBAND_BEARER_ICERA_H
#define MM_BROADBAND_BEARER_ICERA_H
#include <glib.h>
#include <glib-object.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-broadband-bearer.h"
#define MM_TYPE_BROADBAND_BEARER_ICERA (mm_broadband_bearer_icera_get_type ())
#define MM_BROADBAND_BEARER_ICERA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_ICERA, MMBroadbandBearerIcera))
#define MM_BROADBAND_BEARER_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_ICERA, MMBroadbandBearerIceraClass))
#define MM_IS_BROADBAND_BEARER_ICERA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_ICERA))
#define MM_IS_BROADBAND_BEARER_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_ICERA))
#define MM_BROADBAND_BEARER_ICERA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_ICERA, MMBroadbandBearerIceraClass))
#define MM_BROADBAND_BEARER_ICERA_DEFAULT_IP_METHOD "broadband-bearer-icera-default-ip-method"
typedef struct _MMBroadbandBearerIcera MMBroadbandBearerIcera;
typedef struct _MMBroadbandBearerIceraClass MMBroadbandBearerIceraClass;
typedef struct _MMBroadbandBearerIceraPrivate MMBroadbandBearerIceraPrivate;
struct _MMBroadbandBearerIcera {
MMBroadbandBearer parent;
MMBroadbandBearerIceraPrivate *priv;
};
struct _MMBroadbandBearerIceraClass {
MMBroadbandBearerClass parent;
};
GType mm_broadband_bearer_icera_get_type (void);
/* Default bearer creation implementation */
void mm_broadband_bearer_icera_new (MMBroadbandModem *modem,
MMBearerIpMethod ip_method,
MMBearerProperties *config,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MMBaseBearer *mm_broadband_bearer_icera_new_finish (GAsyncResult *res,
GError **error);
gint mm_broadband_bearer_icera_get_connecting_profile_id (MMBroadbandBearerIcera *self);
#endif /* MM_BROADBAND_BEARER_ICERA_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,55 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
*/
#ifndef MM_BROADBAND_MODEM_ICERA_H
#define MM_BROADBAND_MODEM_ICERA_H
#include "mm-broadband-modem.h"
#define MM_TYPE_BROADBAND_MODEM_ICERA (mm_broadband_modem_icera_get_type ())
#define MM_BROADBAND_MODEM_ICERA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_ICERA, MMBroadbandModemIcera))
#define MM_BROADBAND_MODEM_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_ICERA, MMBroadbandModemIceraClass))
#define MM_IS_BROADBAND_MODEM_ICERA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_ICERA))
#define MM_IS_BROADBAND_MODEM_ICERA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_ICERA))
#define MM_BROADBAND_MODEM_ICERA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_ICERA, MMBroadbandModemIceraClass))
#define MM_BROADBAND_MODEM_ICERA_DEFAULT_IP_METHOD "broadband-modem-icera-default-ip-method"
typedef struct _MMBroadbandModemIcera MMBroadbandModemIcera;
typedef struct _MMBroadbandModemIceraClass MMBroadbandModemIceraClass;
typedef struct _MMBroadbandModemIceraPrivate MMBroadbandModemIceraPrivate;
struct _MMBroadbandModemIcera {
MMBroadbandModem parent;
MMBroadbandModemIceraPrivate *priv;
};
struct _MMBroadbandModemIceraClass{
MMBroadbandModemClass parent;
};
G_MODULE_EXPORT
GType mm_broadband_modem_icera_get_type (void);
G_MODULE_EXPORT
MMBroadbandModemIcera *mm_broadband_modem_icera_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_ICERA_H */
+389
View File
@@ -0,0 +1,389 @@
/* -*- 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-icera.h"
/*****************************************************************************/
/* %IPDPADDR response parser */
static MMBearerIpConfig *
parse_ipdpaddr_v4 (const gchar **items, guint num_items, GError **error)
{
MMBearerIpConfig *config;
const gchar *dns[3] = { 0 };
guint dns_i = 0, tmp;
const gchar *netmask = NULL;
/* IP address and prefix */
tmp = 0;
if (!inet_pton (AF_INET, items[1], &tmp)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse IPv4 address '%s'", items[1]);
return NULL;
}
if (!tmp) {
/* No IPv4 config */
return NULL;
}
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
mm_bearer_ip_config_set_address (config, items[1]);
mm_bearer_ip_config_set_prefix (config, 32); /* default prefix */
/* Gateway */
tmp = 0;
if (inet_pton (AF_INET, items[2], &tmp)) {
if (tmp)
mm_bearer_ip_config_set_gateway (config, items[2]);
} else {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse gateway address '%s'", items[2]);
goto error;
}
/* DNS */
tmp = 0;
if (inet_pton (AF_INET, items[3], &tmp) && tmp)
dns[dns_i++] = items[3];
else {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse DNS address '%s'", items[3]);
goto error;
}
/* DNS2 - sometimes missing and set to 0.0.0.0 */
tmp = 0;
if (inet_pton (AF_INET, items[4], &tmp) && tmp)
dns[dns_i++] = items[4];
if (dns_i > 0)
mm_bearer_ip_config_set_dns (config, (const gchar **) dns);
/* Short form (eg, Sierra USB305) */
if (num_items < 9)
return config;
/* Devices return netmask and secondary gateway in one of two
* positions. The netmask may be either at index 7 or 8, while
* the secondary gateway may be at position 8 or 9.
*/
if (items[7] && strstr (items[7], "255.") && !strstr (items[7], "255.0.0.0"))
netmask = items[7];
if (items[8] && strstr (items[8], "255.") && !strstr (items[8], "255.0.0.0"))
netmask = items[8];
if (netmask) {
if (!inet_pton (AF_INET, netmask, &tmp)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse netmask '%s'",
netmask);
goto error;
}
mm_bearer_ip_config_set_prefix (config, mm_netmask_to_cidr (netmask));
}
/* Secondary gateway */
if (!mm_bearer_ip_config_get_gateway (config)) {
const char *gw2 = NULL;
if (num_items >= 10 && items[9] && !strstr (items[9], "255.") && !strstr (items[9], "::"))
gw2 = items[9];
/* Prefer position 8 */
if (items[8] && !strstr (items[8], "255."))
gw2 = items[8];
if (gw2 && inet_pton (AF_INET, gw2, &tmp) && tmp)
mm_bearer_ip_config_set_gateway (config, gw2);
else {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse secondary gateway address '%s'",
gw2 ? gw2 : "(unknown)");
goto error;
}
}
return config;
error:
g_object_unref (config);
return NULL;
}
static MMBearerIpConfig *
parse_ipdpaddr_v6 (const gchar **items, guint num_items, GError **error)
{
MMBearerIpConfig *config;
const gchar *dns[2] = { 0 };
struct in6_addr tmp6 = IN6ADDR_ANY_INIT;
if (num_items < 12)
return NULL;
/* No IPv6 IP and no IPv6 DNS, return NULL without error. */
if (g_strcmp0 (items[9], "::") == 0 && g_strcmp0 (items[11], "::") == 0)
return NULL;
config = mm_bearer_ip_config_new ();
/* It appears that for IPv6 %IPDPADDR returns only the expected
* link-local address and a DNS address, and that to retrieve the
* default router, extra DNS, and search domains, the host must listen
* for IPv6 Router Advertisements on the net port.
*/
if (g_strcmp0 (items[9], "::") != 0) {
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
/* IP address and prefix */
if (inet_pton (AF_INET6, items[9], &tmp6) != 1 ||
IN6_IS_ADDR_UNSPECIFIED (&tmp6)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse IPv6 address '%s'", items[9]);
goto error;
}
mm_bearer_ip_config_set_address (config, items[9]);
mm_bearer_ip_config_set_prefix (config, 64);
/* If the address is a link-local one, then SLAAC or DHCP must be used
* to get the real prefix and address. Change the method to DHCP to
* indicate this to clients.
*/
if (IN6_IS_ADDR_LINKLOCAL (&tmp6))
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
} else {
/* No IPv6 given, but DNS will be available, try with DHCP */
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
}
/* DNS server */
if (g_strcmp0 (items[11], "::") != 0) {
memset (&tmp6, 0, sizeof (tmp6));
if (inet_pton (AF_INET6, items[11], &tmp6) == 1 &&
!IN6_IS_ADDR_UNSPECIFIED (&tmp6)) {
dns[0] = items[11];
dns[1] = NULL;
mm_bearer_ip_config_set_dns (config, (const gchar **) dns);
} else {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Couldn't parse DNS address '%s'", items[11]);
goto error;
}
}
return config;
error:
g_object_unref (config);
return NULL;
}
#define IPDPADDR_TAG "%IPDPADDR: "
gboolean
mm_icera_parse_ipdpaddr_response (const gchar *response,
guint expected_cid,
MMBearerIpConfig **out_ip4_config,
MMBearerIpConfig **out_ip6_config,
GError **error)
{
MMBearerIpConfig *ip4_config = NULL;
MMBearerIpConfig *ip6_config = NULL;
GError *local = NULL;
gboolean success = FALSE;
char **items;
guint num_items, i;
guint num;
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, IPDPADDR_TAG)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing %%IPDPADDR prefix");
return FALSE;
}
/* %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>[,<nbns1>,<nbns2>[,<??>,<netmask>,<gw>]]
* %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>,<nbns1>,<nbns2>,<netmask>,<gw>
* %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>,<nbns1>,<nbns2>,<??>,<gw>,<ip6>,::,<ip6_dns1>,::,::,::,::,::
*
* Sierra USB305: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0
* K3805-Z: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,
* Nokia 21M: %IPDPADDR: 2, 33.196.7.127, 33.196.7.128, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 33.196.7.128, fe80::f:9135:5901, ::, fd00:976a::9, ::, ::, ::, ::, ::
* Nokia 21M: %IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, ::
*/
response = mm_strip_tag (response, IPDPADDR_TAG);
items = g_strsplit_set (response, ",", 0);
/* Strip any spaces on elements; inet_pton() doesn't like them */
num_items = g_strv_length (items);
for (i = 0; i < num_items; i++)
items[i] = g_strstrip (items[i]);
if (num_items < 7) {
g_set_error_literal (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Malformed IPDPADDR response (not enough items)");
goto out;
}
/* Validate context ID */
if (!mm_get_uint_from_str (items[0], &num) ||
num != expected_cid) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Unknown CID in IPDPADDR response (got %d, expected %d)",
(guint) num,
expected_cid);
goto out;
}
ip4_config = parse_ipdpaddr_v4 ((const gchar **) items, num_items, &local);
if (local) {
g_propagate_error (error, local);
goto out;
}
ip6_config = parse_ipdpaddr_v6 ((const gchar **) items, num_items, &local);
if (local) {
g_propagate_error (error, local);
goto out;
}
success = TRUE;
out:
g_strfreev (items);
*out_ip4_config = ip4_config;
*out_ip6_config = ip6_config;
return success;
}
/*****************************************************************************/
/* %IPDPCFG? response parser.
* Modifies the input list of profiles in place
*
* AT%IPDPCFG?
* %IPDPCFG: 1,0,0,,,0
* %IPDPCFG: 2,0,0,,,0
* %IPDPCFG: 3,0,2,"user","pass",0
* %IPDPCFG: 4,0,0,,,0
* OK
*/
gboolean
mm_icera_parse_ipdpcfg_query_response (const gchar *str,
GList *profiles,
gpointer log_object,
GError **error)
{
g_autoptr(GRegex) r = NULL;
g_autoptr(GError) inner_error = NULL;
g_autoptr(GMatchInfo) match_info = NULL;
guint n_updates = 0;
guint n_profiles;
n_profiles = g_list_length (profiles);
r = g_regex_new ("%IPDPCFG:\\s*(\\d+),(\\d+),(\\d+),([^,]*),([^,]*),(\\d+)",
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
0, NULL);
g_assert (r != NULL);
g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
/* Parse the results */
while (g_match_info_matches (match_info)) {
guint cid;
guint auth;
MMBearerAllowedAuth allowed_auth;
g_autofree gchar *user = NULL;
g_autofree gchar *password = NULL;
GList *l;
if (!mm_get_uint_from_match_info (match_info, 1, &cid)) {
mm_obj_warn (log_object, "couldn't parse cid from %%IPDPCFG line");
goto next;
}
if (!mm_get_uint_from_match_info (match_info, 3, &auth)) {
mm_obj_warn (log_object, "couldn't parse auth from %%IPDPCFG line");
goto next;
}
switch (auth) {
case 0:
allowed_auth = MM_BEARER_ALLOWED_AUTH_NONE;
break;
case 1:
allowed_auth = MM_BEARER_ALLOWED_AUTH_PAP;
break;
case 2:
allowed_auth = MM_BEARER_ALLOWED_AUTH_CHAP;
break;
default:
mm_obj_warn (log_object, "unexpected icera auth setting: %u", auth);
goto next;
}
user = mm_get_string_unquoted_from_match_info (match_info, 4);
password = mm_get_string_unquoted_from_match_info (match_info, 5);
mm_obj_dbg (log_object, "found icera auth settings for profile with id '%u'", cid);
/* Find profile and update in place */
for (l = profiles; l; l = g_list_next (l)) {
MM3gppProfile *iter = l->data;
if (mm_3gpp_profile_get_profile_id (iter) == (gint) cid) {
n_updates++;
mm_3gpp_profile_set_allowed_auth (iter, allowed_auth);
mm_3gpp_profile_set_user (iter, user);
mm_3gpp_profile_set_password (iter, password);
break;
}
}
if (!l)
mm_obj_warn (log_object, "couldn't update auth settings in profile with id '%d': not found", cid);
next:
g_match_info_next (match_info, NULL);
}
if (n_updates != n_profiles)
mm_obj_warn (log_object, "couldn't update auth settings in all profiles: %u/%u updated",
n_updates, n_profiles);
return TRUE;
}
@@ -0,0 +1,34 @@
/* -*- 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_ICERA_H
#define MM_MODEM_HELPERS_ICERA_H
#include "glib.h"
/* %IPDPADDR response parser */
gboolean mm_icera_parse_ipdpaddr_response (const gchar *response,
guint expected_cid,
MMBearerIpConfig **out_ip4_config,
MMBearerIpConfig **out_ip6_config,
GError **error);
/* %IPDPCFG? response parser */
gboolean mm_icera_parse_ipdpcfg_query_response (const gchar *response,
GList *profiles,
gpointer log_object,
GError **error);
#endif /* MM_MODEM_HELPERS_HUAWEI_H */
+20
View File
@@ -0,0 +1,20 @@
/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "mm-shared.h"
MM_SHARED_DEFINE_MAJOR_VERSION
MM_SHARED_DEFINE_MINOR_VERSION
MM_SHARED_DEFINE_NAME(Icera)
@@ -0,0 +1,262 @@
/* -*- 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-icera.h"
/*****************************************************************************/
/* Test %IPDPADDR responses */
typedef struct {
const gchar *str;
const guint expected_cid;
/* IPv4 */
const gchar *ipv4_addr;
const guint ipv4_prefix;
const gchar *ipv4_gw;
const gchar *ipv4_dns1;
const gchar *ipv4_dns2;
/* IPv6 */
const gchar *ipv6_addr;
const gchar *ipv6_dns1;
} IpdpaddrTest;
static const IpdpaddrTest ipdpaddr_tests[] = {
/* Sierra USB305 */
{ "%IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0\r\n",
2, "21.93.217.11", 32, "21.93.217.10", "10.177.0.34", "10.161.171.220",
NULL, NULL },
/* ZTE/Vodafone K3805-Z */
{ "%IPDPADDR: 5, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,\r\n",
5, "21.93.217.11", 24, "21.93.217.10", "10.177.0.34", "10.161.171.220",
NULL, NULL },
/* Secondary gateway check */
{ "%IPDPADDR: 5, 21.93.217.11, 0.0.0.0, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,\r\n",
5, "21.93.217.11", 24, "21.93.217.10", "10.177.0.34", "10.161.171.220",
NULL, NULL },
/* Secondary gateway check #2 */
{ "%IPDPADDR: 5, 27.107.96.189, 0.0.0.0, 121.242.190.210, 121.242.190.181, 0.0.0.0, 0.0.0.0, 255.255.255.254, 27.107.96.188\r\n",
5, "27.107.96.189", 31, "27.107.96.188", "121.242.190.210", "121.242.190.181",
NULL, NULL },
/* Nokia 21M */
{ "%IPDPADDR: 1, 33.196.7.127, 33.196.7.128, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 33.196.7.128, fe80::f:9135:5901, ::, fd00:976a::9, ::, ::, ::, ::, ::\r\n",
1, "33.196.7.127", 32, "33.196.7.128", "10.177.0.34", "10.161.171.220",
"fe80::f:9135:5901", "fd00:976a::9" },
{ "%IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, ::\r\n",
3, NULL, 0, NULL, NULL, NULL,
"fe80::2e:437b:7901", "fd00:976a::9" },
/* Some development chip (cnsbg.p1001.rev2, CL477342) */
{ "%IPDPADDR: 5, 27.107.96.189, 27.107.96.188, 121.242.190.210, 121.242.190.181, 0.0.0.0, 0.0.0.0, 255.255.255.254, 27.107.96.188\r\n",
5, "27.107.96.189", 31, "27.107.96.188", "121.242.190.210", "121.242.190.181",
NULL, NULL },
/* 21M with newer firmware */
{ "%IPDPADDR: 2, 188.150.116.13, 188.150.116.14, 188.149.250.16, 0.0.0.0, 0.0.0.0, 0.0.0.0, 255.255.0.0, 188.150.116.14, fe80::1:e414:eb01, ::, 2a00:e18:0:3::6, ::, ::, ::, ::, ::\r\n",
2, "188.150.116.13", 16, "188.150.116.14", "188.149.250.16", NULL,
"fe80::1:e414:eb01", "2a00:e18:0:3::6" },
{ "%IPDPADDR: 1, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::1f:fad1:4c01, ::, 2001:4600:4:fff::54, 2001:4600:4:1fff::54, ::, ::, ::, ::\r\n",
1, NULL, 0, NULL, NULL, NULL,
"fe80::1f:fad1:4c01", "2001:4600:4:fff::54" },
{ "%IPDPADDR: 1, 46.157.76.179, 46.157.76.180, 193.213.112.4, 130.67.15.198, 0.0.0.0, 0.0.0.0, 255.0.0.0, 46.157.76.180, ::, ::, ::, ::, ::, ::, ::, ::\r\n",
1, "46.157.76.179", 32, "46.157.76.180", "193.213.112.4", "130.67.15.198",
NULL, NULL },
{ "%IPDPADDR: 1, 0.0.0.0, 0.0.0.0, 193.213.112.4, 130.67.15.198, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, ::, ::, 2001:4600:4:fff::52, 2001:4600:4:1fff::52, ::, ::, ::, ::",
1, NULL, 0, NULL, NULL, NULL,
NULL, "2001:4600:4:fff::52" },
{ NULL }
};
static void
test_ipdpaddr (void)
{
guint i;
for (i = 0; ipdpaddr_tests[i].str; i++) {
gboolean success;
GError *error = NULL;
MMBearerIpConfig *ipv4 = NULL;
MMBearerIpConfig *ipv6 = NULL;
const gchar **dns;
guint dnslen;
success = mm_icera_parse_ipdpaddr_response (
ipdpaddr_tests[i].str,
ipdpaddr_tests[i].expected_cid,
&ipv4,
&ipv6,
&error);
g_assert_no_error (error);
g_assert (success);
/* IPv4 */
if (ipdpaddr_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), ==, ipdpaddr_tests[i].ipv4_addr);
g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv4), ==, ipdpaddr_tests[i].ipv4_prefix);
g_assert_cmpstr (mm_bearer_ip_config_get_gateway (ipv4), ==, ipdpaddr_tests[i].ipv4_gw);
dns = mm_bearer_ip_config_get_dns (ipv4);
g_assert (dns);
dnslen = g_strv_length ((gchar **) dns);
if (ipdpaddr_tests[i].ipv4_dns2 != NULL)
g_assert_cmpint (dnslen, ==, 2);
else
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv4_dns1);
g_assert_cmpstr (dns[1], ==, ipdpaddr_tests[i].ipv4_dns2);
g_object_unref (ipv4);
} else
g_assert (ipv4 == NULL);
/* IPv6 */
if (ipdpaddr_tests[i].ipv6_addr || ipdpaddr_tests[i].ipv6_dns1) {
struct in6_addr a6;
g_assert (ipv6);
if (ipdpaddr_tests[i].ipv6_addr) {
g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv6), ==, ipdpaddr_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);
} else
g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP);
dns = mm_bearer_ip_config_get_dns (ipv6);
g_assert (dns);
dnslen = g_strv_length ((gchar **) dns);
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv6_dns1);
g_object_unref (ipv6);
} else
g_assert (ipv6 == NULL);
}
}
/*****************************************************************************/
/* Test %IPDPCFG responses */
static void
test_ipdpcfg (void)
{
MM3gppProfile *profile;
GList *profiles = NULL;
GList *l;
GError *error = NULL;
gboolean result;
gboolean cid_1_validated = FALSE;
gboolean cid_2_validated = FALSE;
gboolean cid_5_validated = FALSE;
const gchar *response =
"%IPDPCFG: 1,0,0,,,0\r\n"
"%IPDPCFG: 2,0,1,\"aaaa\",\"bbbbb\",0\r\n"
"%IPDPCFG: 5,0,2,\"user\",\"pass\",0"; /* last line without CRLF */
profile = mm_3gpp_profile_new ();
mm_3gpp_profile_set_profile_id (profile, 1);
mm_3gpp_profile_set_apn (profile, "internet");
mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4);
profiles = g_list_append (profiles, profile);
profile = mm_3gpp_profile_new ();
mm_3gpp_profile_set_profile_id (profile, 2);
mm_3gpp_profile_set_apn (profile, "internet2");
mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4V6);
profiles = g_list_append (profiles, profile);
profile = mm_3gpp_profile_new ();
mm_3gpp_profile_set_profile_id (profile, 5);
mm_3gpp_profile_set_apn (profile, "internet3");
mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV6);
profiles = g_list_append (profiles, profile);
result = mm_icera_parse_ipdpcfg_query_response (response, profiles, NULL, &error);
g_assert_no_error (error);
g_assert (result);
for (l = profiles; l; l = g_list_next (l)) {
MM3gppProfile *iter = l->data;
switch (mm_3gpp_profile_get_profile_id (iter)) {
case 1:
cid_1_validated = TRUE;
g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_NONE);
g_assert (!mm_3gpp_profile_get_user (iter));
g_assert (!mm_3gpp_profile_get_password (iter));
break;
case 2:
cid_2_validated = TRUE;
g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_PAP);
g_assert_cmpstr (mm_3gpp_profile_get_user (iter), ==, "aaaa");
g_assert_cmpstr (mm_3gpp_profile_get_password (iter), ==, "bbbbb");
break;
case 5:
cid_5_validated = TRUE;
g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_CHAP);
g_assert_cmpstr (mm_3gpp_profile_get_user (iter), ==, "user");
g_assert_cmpstr (mm_3gpp_profile_get_password (iter), ==, "pass");
break;
default:
g_assert_not_reached ();
}
}
g_assert (cid_1_validated);
g_assert (cid_2_validated);
g_assert (cid_5_validated);
g_list_free_full (profiles, (GDestroyNotify)g_object_unref);
}
/*****************************************************************************/
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/icera/ipdpaddr", test_ipdpaddr);
g_test_add_func ("/MM/icera/ipdpcfg", test_ipdpcfg);
return g_test_run ();
}
@@ -0,0 +1,144 @@
/* -*- 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) 2021-2022 Intel Corporation
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-broadband-modem-mbim-intel.h"
#include "mm-iface-modem-location.h"
#include "mm-shared-xmm.h"
static void iface_modem_location_init (MMIfaceModemLocation *iface);
static void shared_xmm_init (MMSharedXmm *iface);
static MMIfaceModemLocation *iface_modem_location_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimIntel, mm_broadband_modem_mbim_intel, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_XMM, shared_xmm_init))
/*****************************************************************************/
static void
setup_ports (MMBroadbandModem *self)
{
MMPortSerialAt *ports[3];
guint i;
/* Run the shared XMM port setup logic */
mm_shared_xmm_setup_ports (self);
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* GNSS control port may or may not be a primary/secondary port */
ports[2] = mm_base_modem_peek_port_gps_control (MM_BASE_MODEM (self));
if (ports[2] && ((ports[2] == ports[0]) || (ports[2] == ports[1])))
ports[2] = NULL;
for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
g_object_set (ports[i],
MM_PORT_SERIAL_SEND_DELAY, (guint64) 0,
NULL);
}
}
/*****************************************************************************/
MMBroadbandModemMbimIntel *
mm_broadband_modem_mbim_intel_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_INTEL,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
/* MBIM bearer supports NET only */
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE,
#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, TRUE,
#endif
NULL);
}
static void
mm_broadband_modem_mbim_intel_init (MMBroadbandModemMbimIntel *self)
{
}
static void
iface_modem_location_init (MMIfaceModemLocation *iface)
{
iface_modem_location_parent = g_type_interface_peek_parent (iface);
iface->load_capabilities = mm_shared_xmm_location_load_capabilities;
iface->load_capabilities_finish = mm_shared_xmm_location_load_capabilities_finish;
iface->enable_location_gathering = mm_shared_xmm_enable_location_gathering;
iface->enable_location_gathering_finish = mm_shared_xmm_enable_location_gathering_finish;
iface->disable_location_gathering = mm_shared_xmm_disable_location_gathering;
iface->disable_location_gathering_finish = mm_shared_xmm_disable_location_gathering_finish;
iface->load_supl_server = mm_shared_xmm_location_load_supl_server;
iface->load_supl_server_finish = mm_shared_xmm_location_load_supl_server_finish;
iface->set_supl_server = mm_shared_xmm_location_set_supl_server;
iface->set_supl_server_finish = mm_shared_xmm_location_set_supl_server_finish;
}
static MMBroadbandModemClass *
peek_parent_broadband_modem_class (MMSharedXmm *self)
{
return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_intel_parent_class);
}
static MMIfaceModemLocation *
peek_parent_location_interface (MMSharedXmm *self)
{
return iface_modem_location_parent;
}
static void
shared_xmm_init (MMSharedXmm *iface)
{
iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class;
iface->peek_parent_location_interface = peek_parent_location_interface;
}
static void
mm_broadband_modem_mbim_intel_class_init (MMBroadbandModemMbimIntelClass *klass)
{
MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
broadband_modem_class->setup_ports = setup_ports;
}
@@ -0,0 +1,47 @@
/* -*- 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) 2021-2022 Intel Corporation
*/
#ifndef MM_BROADBAND_MODEM_MBIM_INTEL_H
#define MM_BROADBAND_MODEM_MBIM_INTEL_H
#include "mm-broadband-modem-mbim.h"
#define MM_TYPE_BROADBAND_MODEM_MBIM_INTEL (mm_broadband_modem_mbim_intel_get_type ())
#define MM_BROADBAND_MODEM_MBIM_INTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL, MMBroadbandModemMbimIntel))
#define MM_BROADBAND_MODEM_MBIM_INTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL, MMBroadbandModemMbimIntelClass))
#define MM_IS_BROADBAND_MODEM_MBIM_INTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL))
#define MM_IS_BROADBAND_MODEM_MBIM_INTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL))
#define MM_BROADBAND_MODEM_MBIM_INTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_INTEL, MMBroadbandModemMbimIntelClass))
typedef struct _MMBroadbandModemMbimIntel MMBroadbandModemMbimIntel;
typedef struct _MMBroadbandModemMbimIntelClass MMBroadbandModemMbimIntelClass;
struct _MMBroadbandModemMbimIntel {
MMBroadbandModemMbim parent;
};
struct _MMBroadbandModemMbimIntelClass{
MMBroadbandModemMbimClass parent;
};
GType mm_broadband_modem_mbim_intel_get_type (void);
MMBroadbandModemMbimIntel *mm_broadband_modem_mbim_intel_new (const gchar *device,
const gchar **driver,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_MBIM_INTEL_H */
+91
View File
@@ -0,0 +1,91 @@
/* -*- 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) 2021-2022 Intel Corporation
*/
#include <stdio.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-plugin-intel.h"
#include "mm-log-object.h"
#include "mm-broadband-modem.h"
#if defined WITH_MBIM
#include "mm-broadband-modem-mbim-intel.h"
#endif
G_DEFINE_TYPE (MMPluginIntel, mm_plugin_intel, 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 Intel modem found...");
return MM_BASE_MODEM (mm_broadband_modem_mbim_intel_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
#endif
mm_obj_dbg (self, "Generic Intel modem found...");
return MM_BASE_MODEM (mm_broadband_modem_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "net", "wwan", NULL };
static const guint16 vendor_ids[] = { 0x8086, 0 };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_INTEL,
MM_PLUGIN_NAME, "Intel",
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
NULL));
}
static void
mm_plugin_intel_init (MMPluginIntel *self)
{
/*nothing to be done here, but required for creating intel plugin instance*/
}
static void
mm_plugin_intel_class_init (MMPluginIntelClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+40
View File
@@ -0,0 +1,40 @@
/* -*- 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) 2021-2022 Intel Corporation
*/
#ifndef MM_PLUGIN_INTEL_H
#define MM_PLUGIN_INTEL_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_INTEL (mm_plugin_intel_get_type ())
#define MM_PLUGIN_INTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_INTEL, MMPluginIntel))
#define MM_PLUGIN_INTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_INTEL, MMPluginIntelClass))
#define MM_IS_PLUGIN_INTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_INTEL))
#define MM_IS_PLUGIN_INTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_INTEL))
#define MM_PLUGIN_INTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_INTEL, MMPluginIntelClass))
typedef struct {
MMPlugin parent;
} MMPluginIntel;
typedef struct {
MMPluginClass parent;
} MMPluginIntelClass;
GType mm_plugin_intel_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_INTEL_H */
+266
View File
@@ -0,0 +1,266 @@
/* -*- 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 Ammonit Measurement GmbH
*/
#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-bearer-iridium.h"
#include "mm-base-modem-at.h"
/* Allow up to 200s to get a proper IP connection */
#define BEARER_IRIDIUM_IP_TIMEOUT_DEFAULT 200
G_DEFINE_TYPE (MMBearerIridium, mm_bearer_iridium, MM_TYPE_BASE_BEARER)
/*****************************************************************************/
/* Connect */
typedef struct {
MMPortSerialAt *primary;
GError *saved_error;
} ConnectContext;
static void
connect_context_free (ConnectContext *ctx)
{
if (ctx->saved_error)
g_error_free (ctx->saved_error);
if (ctx->primary)
g_object_unref (ctx->primary);
g_free (ctx);
}
static MMBearerConnectResult *
connect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
connect_report_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
ConnectContext *ctx;
const gchar *result;
/* If cancelled, complete */
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
ctx = g_task_get_task_data (task);
/* If we got a proper extended reply, build the new error to be set */
result = mm_base_modem_at_command_full_finish (modem, res, NULL);
if (result && g_str_has_prefix (result, "+CEER: ") && strlen (result) > 7) {
g_task_return_new_error (task,
ctx->saved_error->domain,
ctx->saved_error->code,
"%s", &result[7]);
} else {
/* Otherwise, take the original error as it was */
g_task_return_error (task, ctx->saved_error);
ctx->saved_error = NULL;
}
g_object_unref (task);
}
static void
dial_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
ConnectContext *ctx;
MMBearerIpConfig *config;
ctx = g_task_get_task_data (task);
/* DO NOT check for cancellable here. If we got here without errors, the
* bearer is really connected and therefore we need to reflect that in
* the state machine. */
mm_base_modem_at_command_full_finish (modem, res, &(ctx->saved_error));
if (ctx->saved_error) {
/* Try to get more information why it failed */
mm_base_modem_at_command_full (
modem,
ctx->primary,
"+CEER",
3,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)connect_report_ready,
task);
return;
}
/* Port is connected; update the state */
mm_port_set_connected (MM_PORT (ctx->primary), TRUE);
/* Build IP config; always PPP based */
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_PPP);
/* Return operation result */
g_task_return_pointer (
task,
mm_bearer_connect_result_new (MM_PORT (ctx->primary), config, NULL),
(GDestroyNotify)mm_bearer_connect_result_unref);
g_object_unref (task);
g_object_unref (config);
}
static void
service_type_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
ConnectContext *ctx;
GError *error = NULL;
/* If cancelled, complete */
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
ctx = g_task_get_task_data (task);
/* Errors setting the service type will be critical */
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* We just use the default number to dial in the Iridium network. Also note
* that we won't specify a specific port to use; Iridium modems only expose
* one. */
mm_base_modem_at_command_full (
modem,
ctx->primary,
"ATDT008816000025",
MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)dial_ready,
task);
}
static void
connect (MMBaseBearer *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
ConnectContext *ctx;
GTask *task;
MMBaseModem *modem = NULL;
task = g_task_new (self, cancellable, callback, user_data);
if (mm_bearer_properties_get_multiplex (mm_base_bearer_peek_config (self)) == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Multiplex support not available");
g_object_unref (task);
return;
}
g_object_get (self,
MM_BASE_BEARER_MODEM, &modem,
NULL);
g_assert (modem);
/* Don't bother to get primary and check if connected and all that; we
* already do this check when sending the ATDT call */
/* In this context, we only keep the stuff we'll need later */
ctx = g_new0 (ConnectContext, 1);
ctx->primary = mm_base_modem_get_port_primary (modem);
g_task_set_task_data (task, ctx, (GDestroyNotify) connect_context_free);
/* Bearer service type set to 9600bps (V.110), which behaves better than the
* default 9600bps (V.32). */
mm_base_modem_at_command_full (
modem,
ctx->primary,
"+CBST=71,0,1",
3,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)service_type_ready,
task);
g_object_unref (modem);
}
/*****************************************************************************/
MMBaseBearer *
mm_bearer_iridium_new (MMBroadbandModemIridium *modem,
MMBearerProperties *config)
{
MMBaseBearer *bearer;
/* The Iridium bearer inherits from MMBaseBearer (so it's not a MMBroadbandBearer)
* and that means that the object is not async-initable, so we just use
* g_object_get() here */
bearer = g_object_new (MM_TYPE_BEARER_IRIDIUM,
MM_BASE_BEARER_MODEM, modem,
MM_BASE_BEARER_CONFIG, config,
"ip-timeout", BEARER_IRIDIUM_IP_TIMEOUT_DEFAULT,
NULL);
/* Only export valid bearers */
mm_base_bearer_export (bearer);
return bearer;
}
static void
mm_bearer_iridium_init (MMBearerIridium *self)
{
}
static void
mm_bearer_iridium_class_init (MMBearerIridiumClass *klass)
{
MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
/* Virtual methods */
base_bearer_class->connect = connect;
base_bearer_class->connect_finish = connect_finish;
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
}
+55
View File
@@ -0,0 +1,55 @@
/* -*- 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:
*
* Author: Aleksander Morgado <aleksander@lanedo.com>
*
* Copyright (C) 2012 Ammonit Measurement GmbH.
*/
#ifndef MM_BEARER_IRIDIUM_H
#define MM_BEARER_IRIDIUM_H
#include <glib.h>
#include <glib-object.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-base-bearer.h"
#include "mm-broadband-modem-iridium.h"
#define MM_TYPE_BEARER_IRIDIUM (mm_bearer_iridium_get_type ())
#define MM_BEARER_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_IRIDIUM, MMBearerIridium))
#define MM_BEARER_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_IRIDIUM, MMBearerIridiumClass))
#define MM_IS_BEARER_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_IRIDIUM))
#define MM_IS_BEARER_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_IRIDIUM))
#define MM_BEARER_IRIDIUM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_IRIDIUM, MMBearerIridiumClass))
typedef struct _MMBearerIridium MMBearerIridium;
typedef struct _MMBearerIridiumClass MMBearerIridiumClass;
struct _MMBearerIridium {
MMBaseBearer parent;
};
struct _MMBearerIridiumClass {
MMBaseBearerClass parent;
};
GType mm_bearer_iridium_get_type (void);
/* Iridium bearer creation implementation.
* NOTE it is *not* a broadband bearer, so not async-initable */
MMBaseBearer *mm_bearer_iridium_new (MMBroadbandModemIridium *modem,
MMBearerProperties *config);
#endif /* MM_BEARER_IRIDIUM_H */
@@ -0,0 +1,433 @@
/* -*- 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) 2011 - 2012 Ammonit Measurement GmbH
* Author: Aleksander Morgado <aleksander@lanedo.com>
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "ModemManager.h"
#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-base-modem-at.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem-messaging.h"
#include "mm-broadband-modem-iridium.h"
#include "mm-sim-iridium.h"
#include "mm-bearer-iridium.h"
#include "mm-modem-helpers.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemIridium, mm_broadband_modem_iridium, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init))
/*****************************************************************************/
/* Operator Code loading (3GPP interface) */
static gchar *
load_operator_code_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_operator_code (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* Only "90103" operator code is assumed */
g_task_return_pointer (task, g_strdup ("90103"), g_free);
g_object_unref (task);
}
/*****************************************************************************/
/* Operator Name loading (3GPP interface) */
static gchar *
load_operator_name_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_operator_name (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* Only "IRIDIUM" operator name is assumed */
g_task_return_pointer (task, g_strdup ("IRIDIUM"), g_free);
g_object_unref (task);
}
/*****************************************************************************/
/* Enable unsolicited events (SMS indications) (Messaging interface) */
static gboolean
messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
}
static void
messaging_enable_unsolicited_events (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
/* AT+CNMI=<mode>,[<mt>[,<bm>[,<ds>[,<bfr>]]]]
* but <bm> can only be 0,
* and <ds> can only be either 0 or 1
*
* Note: Modem may return +CMS ERROR:322, which indicates Memory Full,
* not a big deal
*/
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CNMI=2,1,0,0,1",
3,
FALSE,
callback,
user_data);
}
/*****************************************************************************/
/* Signal quality (Modem interface) */
static guint
load_signal_quality_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
gint quality = 0;
const gchar *result;
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
if (!result)
return 0;
/* Skip possible whitespaces after '+CSQF:' and before the response */
result = mm_strip_tag (result, "+CSQF:");
while (*result == ' ')
result++;
if (sscanf (result, "%d", &quality))
/* Normalize the quality. <rssi> is NOT given in dBs,
* given as a relative value between 0 and 5 */
quality = CLAMP (quality, 0, 5) * 100 / 5;
else
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Could not parse signal quality results");
return quality;
}
static void
load_signal_quality (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
/* The iridium modem may have a huge delay to get signal quality if we pass
* AT+CSQ, so we'll default to use AT+CSQF, which is a fast version that
* returns right away the last signal quality value retrieved */
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CSQF",
3,
FALSE,
callback,
user_data);
}
/*****************************************************************************/
/* Flow control (Modem interface) */
static gboolean
setup_flow_control_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_flow_control_ready (MMBroadbandModemIridium *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
/* Let the error be critical. We DO need RTS/CTS in order to have
* proper modem disabling. */
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
setup_flow_control (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
/* Enable RTS/CTS flow control.
* Other available values:
* AT&K0: Disable flow control
* AT&K3: RTS/CTS
* AT&K4: XOFF/XON
* AT&K6: Both RTS/CTS and XOFF/XON
*/
g_object_set (self, MM_BROADBAND_MODEM_FLOW_CONTROL, MM_FLOW_CONTROL_RTS_CTS, NULL);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"&K3",
3,
FALSE,
(GAsyncReadyCallback)setup_flow_control_ready,
g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Load supported modes (Modem inteface) */
static GArray *
load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_supported_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GArray *combinations;
MMModemModeCombination mode;
GTask *task;
/* Build list of combinations */
combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
/* Report CS only, Iridium connections are circuit-switched */
mode.allowed = MM_MODEM_MODE_CS;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
task = g_task_new (self, NULL, callback, user_data);
g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref);
g_object_unref (task);
}
/*****************************************************************************/
/* Create SIM (Modem inteface) */
static MMBaseSim *
create_sim_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return mm_sim_iridium_new_finish (res, error);
}
static void
create_sim (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
/* New Iridium SIM */
mm_sim_iridium_new (MM_BASE_MODEM (self),
NULL, /* cancellable */
callback,
user_data);
}
/*****************************************************************************/
/* Create Bearer (Modem interface) */
static MMBaseBearer *
create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
create_bearer (MMIfaceModem *self,
MMBearerProperties *properties,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBaseBearer *bearer;
GTask *task;
mm_obj_dbg (self, "creating Iridium bearer...");
bearer = mm_bearer_iridium_new (MM_BROADBAND_MODEM_IRIDIUM (self),
properties);
task = g_task_new (self, NULL, callback, user_data);
g_task_return_pointer (task, bearer, g_object_unref);
g_object_unref (task);
}
/*****************************************************************************/
static const gchar *primary_init_sequence[] = {
/* Disable echo */
"E0",
/* Get word responses */
"V1",
/* Extended numeric codes */
"+CMEE=1",
NULL
};
static void
setup_ports (MMBroadbandModem *self)
{
MMPortSerialAt *primary;
/* Call parent's setup ports first always */
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_iridium_parent_class)->setup_ports (self);
/* Set 9600 baudrate by default in the AT port */
mm_obj_dbg (self, "baudrate will be set to 9600 bps...");
primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
if (!primary)
return;
g_object_set (G_OBJECT (primary),
MM_PORT_SERIAL_BAUD, 9600,
MM_PORT_SERIAL_AT_INIT_SEQUENCE, primary_init_sequence,
NULL);
}
/*****************************************************************************/
MMBroadbandModemIridium *
mm_broadband_modem_iridium_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id)
{
return g_object_new (MM_TYPE_BROADBAND_MODEM_IRIDIUM,
MM_BASE_MODEM_DEVICE, device,
MM_BASE_MODEM_DRIVERS, drivers,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
/* Iridium bearer supports TTY only */
MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
/* Allow only up to 3 consecutive timeouts in the serial port */
MM_BASE_MODEM_MAX_TIMEOUTS, 3,
/* Only CS network is supported by the Iridium modem */
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, FALSE,
NULL);
}
static void
mm_broadband_modem_iridium_init (MMBroadbandModemIridium *self)
{
}
static void
iface_modem_init (MMIfaceModem *iface)
{
/* Create Iridium-specific SIM and bearer*/
iface->create_sim = create_sim;
iface->create_sim_finish = create_sim_finish;
iface->create_bearer = create_bearer;
iface->create_bearer_finish = create_bearer_finish;
/* CSQF-based signal quality */
iface->load_signal_quality = load_signal_quality;
iface->load_signal_quality_finish = load_signal_quality_finish;
/* RTS/CTS flow control */
iface->setup_flow_control = setup_flow_control;
iface->setup_flow_control_finish = setup_flow_control_finish;
/* No need to power-up/power-down the modem */
iface->load_power_state = NULL;
iface->load_power_state_finish = NULL;
iface->modem_power_up = NULL;
iface->modem_power_up_finish = NULL;
iface->modem_power_down = NULL;
iface->modem_power_down_finish = NULL;
/* Supported modes cannot be queried */
iface->load_supported_modes = load_supported_modes;
iface->load_supported_modes_finish = load_supported_modes_finish;
}
static void
iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
{
/* Fixed operator code and name to be reported */
iface->load_operator_name = load_operator_name;
iface->load_operator_name_finish = load_operator_name_finish;
iface->load_operator_code = load_operator_code;
iface->load_operator_code_finish = load_operator_code_finish;
/* Don't try to scan networks with AT+COPS=?.
* It does work, but it will only reply about the Iridium network
* being found (so not very helpful, as that is the only one expected), but
* also, it will use a non-standard reply format. Instead of supporting that
* specific format used, just fully skip it.
* For reference, the result is:
* +COPS:(002),"IRIDIUM","IRIDIUM","90103",,(000-001),(000-002)
*/
iface->scan_networks = NULL;
iface->scan_networks_finish = NULL;
}
static void
iface_modem_messaging_init (MMIfaceModemMessaging *iface)
{
iface->enable_unsolicited_events = messaging_enable_unsolicited_events;
iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish;
}
static void
mm_broadband_modem_iridium_class_init (MMBroadbandModemIridiumClass *klass)
{
MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
broadband_modem_class->setup_ports = setup_ports;
}
@@ -0,0 +1,49 @@
/* -*- 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 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2011 Red Hat, Inc.
* Copyright (C) 2011 Google Inc.
*/
#ifndef MM_BROADBAND_MODEM_IRIDIUM_H
#define MM_BROADBAND_MODEM_IRIDIUM_H
#include "mm-broadband-modem.h"
#define MM_TYPE_BROADBAND_MODEM_IRIDIUM (mm_broadband_modem_iridium_get_type ())
#define MM_BROADBAND_MODEM_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_IRIDIUM, MMBroadbandModemIridium))
#define MM_BROADBAND_MODEM_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_IRIDIUM, MMBroadbandModemIridiumClass))
#define MM_IS_BROADBAND_MODEM_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_IRIDIUM))
#define MM_IS_BROADBAND_MODEM_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_IRIDIUM))
#define MM_BROADBAND_MODEM_IRIDIUM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_IRIDIUM, MMBroadbandModemIridiumClass))
typedef struct _MMBroadbandModemIridium MMBroadbandModemIridium;
typedef struct _MMBroadbandModemIridiumClass MMBroadbandModemIridiumClass;
struct _MMBroadbandModemIridium {
MMBroadbandModem parent;
};
struct _MMBroadbandModemIridiumClass{
MMBroadbandModemClass parent;
};
GType mm_broadband_modem_iridium_get_type (void);
MMBroadbandModemIridium *mm_broadband_modem_iridium_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
guint16 vendor_id,
guint16 product_id);
#endif /* MM_BROADBAND_MODEM_IRIDIUM_H */
+89
View File
@@ -0,0 +1,89 @@
/* -*- 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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2011 - 2012 Ammonit Measurement GmbH
* Author: Aleksander Morgado <aleksander@lanedo.com>
*/
#include <string.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-plugin-iridium.h"
#include "mm-broadband-modem-iridium.h"
#include "mm-private-boxed-types.h"
G_DEFINE_TYPE (MMPluginIridium, mm_plugin_iridium, 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)
{
return MM_BASE_MODEM (mm_broadband_modem_iridium_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", NULL };
static const guint16 vendor_ids[] = { 0x1edd, 0 };
static const gchar *vendor_strings[] = { "iridium", NULL };
/* Also support motorola-branded Iridium modems */
static const mm_str_pair product_strings[] = {{(gchar *)"motorola", (gchar *)"satellite" },
{ NULL, NULL }};
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_IRIDIUM,
MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
MM_PLUGIN_ALLOWED_PRODUCT_STRINGS, product_strings,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
NULL));
}
static void
mm_plugin_iridium_init (MMPluginIridium *self)
{
}
static void
mm_plugin_iridium_class_init (MMPluginIridiumClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
}
+47
View File
@@ -0,0 +1,47 @@
/* -*- 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.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Copyright (C) 2011 - 2012 Ammonit Measurement GmbH
* Author: Aleksander Morgado <aleksander@lanedo.com>
*/
#ifndef MM_PLUGIN_IRIDIUM_H
#define MM_PLUGIN_IRIDIUM_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_IRIDIUM (mm_plugin_iridium_get_type ())
#define MM_PLUGIN_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_IRIDIUM, MMPluginIridium))
#define MM_PLUGIN_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_IRIDIUM, MMPluginIridiumClass))
#define MM_IS_PLUGIN_IRIDIUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_IRIDIUM))
#define MM_IS_PLUGIN_IRIDIUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_IRIDIUM))
#define MM_PLUGIN_IRIDIUM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_IRIDIUM, MMPluginIridiumClass))
typedef struct {
MMPlugin parent;
} MMPluginIridium;
typedef struct {
MMPluginClass parent;
} MMPluginIridiumClass;
GType mm_plugin_iridium_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_IRIDIUM_H */
+95
View File
@@ -0,0 +1,95 @@
/* -*- 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) 2011 - 2012 Ammonit Measurement GmbH.
* Author: Aleksander Morgado <aleksander@lanedo.com>
*/
#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-sim-iridium.h"
G_DEFINE_TYPE (MMSimIridium, mm_sim_iridium, MM_TYPE_BASE_SIM)
/*****************************************************************************/
MMBaseSim *
mm_sim_iridium_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_iridium_new (MMBaseModem *modem,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_async_initable_new_async (MM_TYPE_SIM_IRIDIUM,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
"active", TRUE, /* by default always active */
NULL);
}
static void
mm_sim_iridium_init (MMSimIridium *self)
{
}
static void
mm_sim_iridium_class_init (MMSimIridiumClass *klass)
{
MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass);
/* Skip querying the SIM card info, not supported by Iridium modems */
base_sim_class->load_sim_identifier = NULL;
base_sim_class->load_sim_identifier_finish = NULL;
base_sim_class->load_imsi = NULL;
base_sim_class->load_imsi_finish = NULL;
base_sim_class->load_operator_identifier = NULL;
base_sim_class->load_operator_identifier_finish = NULL;
base_sim_class->load_operator_name = NULL;
base_sim_class->load_operator_name_finish = NULL;
/* Skip managing preferred networks, not applicable to Iridium modems */
base_sim_class->load_preferred_networks = NULL;
base_sim_class->load_preferred_networks_finish = NULL;
base_sim_class->set_preferred_networks = NULL;
base_sim_class->set_preferred_networks_finish = NULL;
}

Some files were not shown because too many files have changed in this diff Show More