Files
ModemManager/plugins/telit/mm-common-telit.c
T
Aleksander Morgado 548b7f8847 base-modem: load AT port type hints when adding port
We keep the pflags input in mm_base_modem_grab_port() so that plugins
can use other methods to gather port type hints (e.g. querying with AT
commands as in Huawei/Telit or looking at sysfs properties as in HSO).

For standard udev tag port type hints, it will be the base modem
looking them up.

Note that there is no longer any need to ignore non-flagged ports for
those modems that require primary/secondary flags. They will be
implicitly ignored when mm_base_modem_organize_ports() decides which
ports to use, as the flagged ones are preferred over the non-flagged
ones.
2018-08-10 04:19:13 +00:00

298 lines
9.5 KiB
C

/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <string.h>
#include "mm-common-telit.h"
#include "mm-log.h"
/*****************************************************************************/
#define TAG_GETPORTCFG_SUPPORTED "getportcfg-supported"
#define TAG_TELIT_MODEM_PORT "ID_MM_TELIT_PORT_TYPE_MODEM"
#define TAG_TELIT_AUX_PORT "ID_MM_TELIT_PORT_TYPE_AUX"
#define TAG_TELIT_NMEA_PORT "ID_MM_TELIT_PORT_TYPE_NMEA"
#define TELIT_GE910_FAMILY_PID 0x0022
gboolean
telit_grab_port (MMPlugin *self,
MMBaseModem *modem,
MMPortProbe *probe,
GError **error)
{
MMKernelDevice *port;
MMDevice *device;
MMPortType ptype;
MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
const gchar *subsys;
port = mm_port_probe_peek_port (probe);
ptype = mm_port_probe_get_port_type (probe);
device = mm_port_probe_peek_device (probe);
subsys = mm_port_probe_get_port_subsys (probe);
/* Just skip custom port identification for subsys different than tty */
if (!g_str_equal (subsys, "tty"))
goto out;
/* AT#PORTCFG (if supported) can be used for identifying the port layout */
if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) != NULL) {
if (g_strcmp0 (mm_kernel_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT)) == 0) {
mm_dbg ("telit: AT 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_strcmp0 (mm_kernel_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_AUX_PORT)) == 0) {
mm_dbg ("telit: AT port '%s/%s' flagged as secondary",
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe));
pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
} else if (g_strcmp0 (mm_kernel_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT)) == 0) {
mm_dbg ("telit: port '%s/%s' flagged as NMEA",
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe));
ptype = MM_PORT_TYPE_GPS;
} else
ptype = MM_PORT_TYPE_IGNORED;
}
out:
return mm_base_modem_grab_port (modem,
port,
ptype,
pflags,
error);
}
/*****************************************************************************/
/* Custom init */
typedef struct {
MMPortSerialAt *port;
gboolean getportcfg_done;
guint getportcfg_retries;
} TelitCustomInitContext;
gboolean
telit_custom_init_finish (MMPortProbe *probe,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_boolean (G_TASK (result), error);
}
static void telit_custom_init_step (GTask *task);
static gboolean
cache_port_mode (MMDevice *device,
const gchar *reply)
{
GRegex *r = NULL;
GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW;
GMatchInfo *match_info = NULL;
GError *error = NULL;
gboolean ret = FALSE;
guint portcfg_current;
/* #PORTCFG: <requested>,<active> */
r = g_regex_new ("#PORTCFG:\\s*(\\d+),(\\d+)", flags, 0, NULL);
g_assert (r != NULL);
if (!g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &error))
goto out;
if (!mm_get_uint_from_match_info (match_info, 2, &portcfg_current)) {
mm_dbg ("telit: unrecognized #PORTCFG <active> value");
goto out;
}
/* Reference for port configurations:
* HE910/UE910/UL865 Families Ports Arrangements User Guide
* GE910 Family Ports Arrangements User Guide
*/
switch (portcfg_current) {
case 0:
case 1:
case 4:
case 5:
case 7:
case 9:
case 10:
case 11:
g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, "00");
if (mm_device_get_product (device) == TELIT_GE910_FAMILY_PID)
g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, "02");
else
g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, "06");
break;
case 2:
case 3:
case 6:
g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, "00");
break;
case 8:
case 12:
g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, "00");
if (mm_device_get_product (device) == TELIT_GE910_FAMILY_PID) {
g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, "02");
g_object_set_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT, "04");
} else {
g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, "06");
g_object_set_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT, "0a");
}
break;
default:
/* portcfg value not supported */
goto out;
}
ret = TRUE;
out:
g_match_info_free (match_info);
g_regex_unref (r);
if (error != NULL) {
mm_dbg ("telit: error while matching: %s", error->message);
g_error_free (error);
}
return ret;
}
static void
getportcfg_ready (MMPortSerialAt *port,
GAsyncResult *res,
GTask *task)
{
const gchar *response;
GError *error = NULL;
MMPortProbe *probe;
TelitCustomInitContext *ctx;
ctx = g_task_get_task_data (task);
probe = g_task_get_source_object (task);
response = mm_port_serial_at_command_finish (port, res, &error);
if (error) {
mm_dbg ("telit: couldn't get port mode: '%s'",
error->message);
/* If ERROR or COMMAND NOT SUPPORT occur then do not retry the
* command.
*/
if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN))
ctx->getportcfg_done = TRUE;
} else {
MMDevice *device;
device = mm_port_probe_peek_device (probe);
/* Results are cached in the parent device object */
if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) == NULL) {
mm_dbg ("telit: retrieving port mode layout");
if (cache_port_mode (device, response)) {
g_object_set_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED, GUINT_TO_POINTER (TRUE));
ctx->getportcfg_done = TRUE;
}
}
/* Port answered to #PORTCFG, so mark it as being AT already */
mm_port_probe_set_result_at (probe, TRUE);
}
if (error)
g_error_free (error);
telit_custom_init_step (task);
}
static void
telit_custom_init_context_free (TelitCustomInitContext *ctx)
{
g_object_unref (ctx->port);
g_slice_free (TelitCustomInitContext, ctx);
}
static void
telit_custom_init_step (GTask *task)
{
MMKernelDevice *port;
MMPortProbe *probe;
TelitCustomInitContext *ctx;
ctx = g_task_get_task_data (task);
probe = g_task_get_source_object (task);
/* If cancelled, end */
if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
mm_dbg ("telit: no need to keep on running custom init in (%s)",
mm_port_get_device (MM_PORT (ctx->port)));
goto out;
}
/* Try to get a port configuration from the modem: usb interface 00
* is always linked to an AT port
*/
port = mm_port_probe_peek_port (probe);
if (!ctx->getportcfg_done &&
g_strcmp0 (mm_kernel_device_get_property (port, "ID_USB_INTERFACE_NUM"), "00") == 0) {
if (ctx->getportcfg_retries == 0)
goto out;
ctx->getportcfg_retries--;
mm_port_serial_at_command (
ctx->port,
"AT#PORTCFG?",
2,
FALSE, /* raw */
FALSE, /* allow_cached */
g_task_get_cancellable (task),
(GAsyncReadyCallback)getportcfg_ready,
task);
return;
}
out:
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
void
telit_custom_init (MMPortProbe *probe,
MMPortSerialAt *port,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
TelitCustomInitContext *ctx;
GTask *task;
ctx = g_slice_new (TelitCustomInitContext);
ctx->port = g_object_ref (port);
ctx->getportcfg_done = FALSE;
ctx->getportcfg_retries = 3;
task = g_task_new (probe, cancellable, callback, user_data);
g_task_set_check_cancellable (task, FALSE);
g_task_set_task_data (task, ctx, (GDestroyNotify)telit_custom_init_context_free);
telit_custom_init_step (task);
}