Blame plugins/altair/mm-modem-helpers-altair-lte.c

Packit Service 8101fe
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
Packit Service 8101fe
/*
Packit Service 8101fe
 * This program is free software; you can redistribute it and/or modify
Packit Service 8101fe
 * it under the terms of the GNU General Public License as published by
Packit Service 8101fe
 * the Free Software Foundation; either version 2 of the License, or
Packit Service 8101fe
 * (at your option) any later version.
Packit Service 8101fe
 *
Packit Service 8101fe
 * This program is distributed in the hope that it will be useful,
Packit Service 8101fe
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 8101fe
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 8101fe
 * GNU General Public License for more details:
Packit Service 8101fe
 *
Packit Service 8101fe
 * Copyright (C) 2013 Google Inc.
Packit Service 8101fe
 *
Packit Service 8101fe
 */
Packit Service 8101fe
Packit Service 8101fe
#include <stdlib.h>
Packit Service 8101fe
#include <string.h>
Packit Service 8101fe
Packit Service 8101fe
#include <ModemManager.h>
Packit Service 8101fe
#define _LIBMM_INSIDE_MM
Packit Service 8101fe
#include <libmm-glib.h>
Packit Service 8101fe
Packit Service 8101fe
#include "mm-modem-helpers-altair-lte.h"
Packit Service 8101fe
Packit Service 8101fe
#define MM_ALTAIR_IMS_PDN_CID           1
Packit Service 8101fe
#define MM_ALTAIR_INTERNET_PDN_CID      3
Packit Service 8101fe
Packit Service 8101fe
/*****************************************************************************/
Packit Service 8101fe
/* Bands response parser */
Packit Service 8101fe
Packit Service 8101fe
GArray *
Packit Service 8101fe
mm_altair_parse_bands_response (const gchar *response)
Packit Service 8101fe
{
Packit Service 8101fe
    gchar **split;
Packit Service 8101fe
    GArray *bands;
Packit Service 8101fe
    guint i;
Packit Service 8101fe
Packit Service 8101fe
    /*
Packit Service 8101fe
     * Response is "<band>[,<band>...]"
Packit Service 8101fe
     */
Packit Service 8101fe
    split = g_strsplit_set (response, ",", -1);
Packit Service 8101fe
    if (!split)
Packit Service 8101fe
        return NULL;
Packit Service 8101fe
Packit Service 8101fe
    bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), g_strv_length (split));
Packit Service 8101fe
Packit Service 8101fe
    for (i = 0; split[i]; i++) {
Packit Service 8101fe
        guint32 band_value;
Packit Service 8101fe
        MMModemBand band;
Packit Service 8101fe
Packit Service 8101fe
        band_value = (guint32)strtoul (split[i], NULL, 10);
Packit Service 8101fe
        band = MM_MODEM_BAND_EUTRAN_1 - 1 + band_value;
Packit Service 8101fe
Packit Service 8101fe
        /* Due to a firmware issue, the modem may incorrectly includes 0 in the
Packit Service 8101fe
         * bands response. We thus ignore any band value outside the range of
Packit Service 8101fe
         * E-UTRAN operating bands. */
Packit Service 8101fe
        if (band >= MM_MODEM_BAND_EUTRAN_1 && band <= MM_MODEM_BAND_EUTRAN_44)
Packit Service 8101fe
            g_array_append_val (bands, band);
Packit Service 8101fe
    }
Packit Service 8101fe
Packit Service 8101fe
    g_strfreev (split);
Packit Service 8101fe
Packit Service 8101fe
    return bands;
Packit Service 8101fe
}
Packit Service 8101fe
Packit Service 8101fe
/*****************************************************************************/
Packit Service 8101fe
/* +CEER response parser */
Packit Service 8101fe
Packit Service 8101fe
gchar *
Packit Service 8101fe
mm_altair_parse_ceer_response (const gchar *response,
Packit Service 8101fe
                               GError **error)
Packit Service 8101fe
{
Packit Service 8101fe
    mm_autoptr(GRegex) r = NULL;
Packit Service 8101fe
    mm_autoptr(GMatchInfo) match_info = NULL;
Packit Service 8101fe
    gchar *ceer_response = NULL;
Packit Service 8101fe
Packit Service 8101fe
Packit Service 8101fe
    /* First accept an empty response as the no error case. Sometimes, the only
Packit Service 8101fe
     * response to the AT+CEER query is an OK.
Packit Service 8101fe
     */
Packit Service 8101fe
    if (g_strcmp0 ("", response) == 0) {
Packit Service 8101fe
        return g_strdup ("");
Packit Service 8101fe
    }
Packit Service 8101fe
Packit Service 8101fe
    /* The response we are interested in looks so:
Packit Service 8101fe
     * +CEER: EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED
Packit Service 8101fe
     */
Packit Service 8101fe
    r = g_regex_new ("\\+CEER:\\s*(\\w*)?",
Packit Service 8101fe
                     G_REGEX_RAW,
Packit Service 8101fe
                     0, NULL);
Packit Service 8101fe
    g_assert (r != NULL);
Packit Service 8101fe
Packit Service 8101fe
    if (!g_regex_match (r, response, 0, &match_info)) {
Packit Service 8101fe
        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse +CEER response");
Packit Service 8101fe
        return NULL;
Packit Service 8101fe
    }
Packit Service 8101fe
Packit Service 8101fe
    if (g_match_info_matches (match_info)) {
Packit Service 8101fe
        ceer_response = mm_get_string_unquoted_from_match_info (match_info, 1);
Packit Service 8101fe
        if (!ceer_response)
Packit Service 8101fe
            ceer_response = g_strdup ("");
Packit Service 8101fe
    }
Packit Service 8101fe
Packit Service 8101fe
    return ceer_response;
Packit Service 8101fe
}
Packit Service 8101fe
Packit Service 8101fe
/*****************************************************************************/
Packit Service 8101fe
/* %CGINFO="cid",1 response parser */
Packit Service 8101fe
Packit Service 8101fe
guint
Packit Service 8101fe
mm_altair_parse_cid (const gchar *response, GError **error)
Packit Service 8101fe
{
Packit Service 8101fe
    mm_autoptr(GRegex) regex = NULL;
Packit Service 8101fe
    mm_autoptr(GMatchInfo) match_info = NULL;
Packit Service 8101fe
    guint cid = -1;
Packit Service 8101fe
Packit Service 8101fe
    regex = g_regex_new ("\\%CGINFO:\\s*(\\d+)", G_REGEX_RAW, 0, NULL);
Packit Service 8101fe
    g_assert (regex);
Packit Service 8101fe
    if (!g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, error)) {
Packit Service 8101fe
        return -1;
Packit Service 8101fe
    }
Packit Service 8101fe
Packit Service 8101fe
    if (!mm_get_uint_from_match_info (match_info, 1, &cid))
Packit Service 8101fe
        g_set_error (error,
Packit Service 8101fe
                     MM_CORE_ERROR,
Packit Service 8101fe
                     MM_CORE_ERROR_FAILED,
Packit Service 8101fe
                     "Failed to parse %%CGINFO=\"cid\",1 response");
Packit Service 8101fe
Packit Service 8101fe
    return cid;
Packit Service 8101fe
}
Packit Service 8101fe
Packit Service 8101fe
/*****************************************************************************/
Packit Service 8101fe
/* %PCOINFO response parser */
Packit Service 8101fe
Packit Service 8101fe
MMPco *
Packit Service 8101fe
mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error)
Packit Service 8101fe
{
Packit Service 8101fe
    mm_autoptr(GRegex) regex = NULL;
Packit Service 8101fe
    mm_autoptr(GMatchInfo) match_info = NULL;
Packit Service 8101fe
    MMPco *pco = NULL;
Packit Service 8101fe
    gint num_matches;
Packit Service 8101fe
Packit Service 8101fe
    if (!pco_info[0])
Packit Service 8101fe
        /* No APNs configured, all done */
Packit Service 8101fe
        return NULL;
Packit Service 8101fe
Packit Service 8101fe
    /* Expected %PCOINFO response:
Packit Service 8101fe
     *
Packit Service 8101fe
     *     Solicited response: %PCOINFO:<mode>,<cid>[,<pcoid>[,<payload>]]
Packit Service 8101fe
     *     Unsolicited response: %PCOINFO:<cid>,<pcoid>[,<payload>]
Packit Service 8101fe
     */
Packit Service 8101fe
    regex = g_regex_new ("\\%PCOINFO:(?:\\s*\\d+\\s*,)?(\\d+)\\s*(,([^,\\)]*),([0-9A-Fa-f]*))?",
Packit Service 8101fe
                         G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
Packit Service 8101fe
                         0, NULL);
Packit Service 8101fe
    g_assert (regex);
Packit Service 8101fe
    if (!g_regex_match_full (regex, pco_info, strlen (pco_info), 0, 0, &match_info, error)) {
Packit Service 8101fe
        return NULL;
Packit Service 8101fe
    }
Packit Service 8101fe
Packit Service 8101fe
    num_matches = g_match_info_get_match_count (match_info);
Packit Service 8101fe
    if (num_matches != 5) {
Packit Service 8101fe
        g_set_error (error,
Packit Service 8101fe
                     MM_CORE_ERROR,
Packit Service 8101fe
                     MM_CORE_ERROR_FAILED,
Packit Service 8101fe
                     "Failed to parse substrings, number of matches: %d",
Packit Service 8101fe
                     num_matches);
Packit Service 8101fe
        return NULL;
Packit Service 8101fe
    }
Packit Service 8101fe
Packit Service 8101fe
    while (g_match_info_matches (match_info)) {
Packit Service 8101fe
        guint pco_cid;
Packit Service 8101fe
        gchar *pco_id;
Packit Service 8101fe
        gchar *pco_payload;
Packit Service 8101fe
        gsize pco_payload_len;
Packit Service 8101fe
        gchar *pco_payload_bytes = NULL;
Packit Service 8101fe
        gsize pco_payload_bytes_len;
Packit Service 8101fe
        guint8 pco_prefix[6];
Packit Service 8101fe
        GByteArray *pco_raw;
Packit Service 8101fe
        gsize pco_raw_len;
Packit Service 8101fe
Packit Service 8101fe
        if (!mm_get_uint_from_match_info (match_info, 1, &pco_cid)) {
Packit Service 8101fe
            g_set_error (error,
Packit Service 8101fe
                         MM_CORE_ERROR,
Packit Service 8101fe
                         MM_CORE_ERROR_FAILED,
Packit Service 8101fe
                         "Couldn't parse CID from PCO info: '%s'",
Packit Service 8101fe
                         pco_info);
Packit Service 8101fe
            break;
Packit Service 8101fe
        }
Packit Service 8101fe
Packit Service 8101fe
        /* We are only interested in IMS and Internet PDN PCO. */
Packit Service 8101fe
        if (pco_cid != MM_ALTAIR_IMS_PDN_CID && pco_cid != MM_ALTAIR_INTERNET_PDN_CID) {
Packit Service 8101fe
            g_match_info_next (match_info, error);
Packit Service 8101fe
            continue;
Packit Service 8101fe
        }
Packit Service 8101fe
Packit Service 8101fe
        pco_id = mm_get_string_unquoted_from_match_info (match_info, 3);
Packit Service 8101fe
        if (!pco_id) {
Packit Service 8101fe
            g_set_error (error,
Packit Service 8101fe
                         MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
Packit Service 8101fe
                         "Couldn't parse PCO ID from PCO info: '%s'",
Packit Service 8101fe
                         pco_info);
Packit Service 8101fe
            break;
Packit Service 8101fe
        }
Packit Service 8101fe
Packit Service 8101fe
        if (g_strcmp0 (pco_id, "FF00")) {
Packit Service 8101fe
            g_free (pco_id);
Packit Service 8101fe
            g_match_info_next (match_info, error);
Packit Service 8101fe
            continue;
Packit Service 8101fe
        }
Packit Service 8101fe
        g_free (pco_id);
Packit Service 8101fe
Packit Service 8101fe
        pco_payload = mm_get_string_unquoted_from_match_info (match_info, 4);
Packit Service 8101fe
        if (!pco_payload) {
Packit Service 8101fe
            g_set_error (error,
Packit Service 8101fe
                         MM_CORE_ERROR,
Packit Service 8101fe
                         MM_CORE_ERROR_FAILED,
Packit Service 8101fe
                         "Couldn't parse PCO payload from PCO info: '%s'",
Packit Service 8101fe
                         pco_info);
Packit Service 8101fe
            break;
Packit Service 8101fe
        }
Packit Service 8101fe
Packit Service 8101fe
        pco_payload_len = strlen (pco_payload);
Packit Service 8101fe
        if (pco_payload_len % 2 == 0)
Packit Service 8101fe
            pco_payload_bytes = mm_utils_hexstr2bin (pco_payload, &pco_payload_bytes_len);
Packit Service 8101fe
Packit Service 8101fe
        g_free (pco_payload);
Packit Service 8101fe
Packit Service 8101fe
        if (!pco_payload_bytes) {
Packit Service 8101fe
            g_set_error (error,
Packit Service 8101fe
                         MM_CORE_ERROR,
Packit Service 8101fe
                         MM_CORE_ERROR_FAILED,
Packit Service 8101fe
                         "Invalid PCO payload from PCO info: '%s'",
Packit Service 8101fe
                         pco_info);
Packit Service 8101fe
            break;
Packit Service 8101fe
        }
Packit Service 8101fe
Packit Service 8101fe
        /* Protocol Configuration Options (PCO) is an information element with an
Packit Service 8101fe
         * identifier (IEI) 0x27 and contains between 3 and 253 octets. See 3GPP TS
Packit Service 8101fe
         * 24.008 for more details on PCO.
Packit Service 8101fe
         *
Packit Service 8101fe
         * NOTE: The standard uses one-based indexing, but to better correlate to the
Packit Service 8101fe
         *       code, zero-based indexing is used in the description hereinafter.
Packit Service 8101fe
         *
Packit Service 8101fe
         *   Octet  | Value
Packit Service 8101fe
         *  --------+--------------------------------------------
Packit Service 8101fe
         *     0    | PCO IEI (= 0x27)
Packit Service 8101fe
         *     1    | Length of PCO contents (= total length - 2)
Packit Service 8101fe
         *     2    | bit 7      : ext
Packit Service 8101fe
         *          | bit 6 to 3 : spare (= 0b0000)
Packit Service 8101fe
         *          | bit 2 to 0 : Configuration protocol
Packit Service 8101fe
         *   3 to 4 | Element 1 ID
Packit Service 8101fe
         *     5    | Length of element 1 contents
Packit Service 8101fe
         *   6 to m | Element 1 contents
Packit Service 8101fe
         *    ...   |
Packit Service 8101fe
         */
Packit Service 8101fe
        pco_raw_len = sizeof (pco_prefix) + pco_payload_bytes_len;
Packit Service 8101fe
        pco_prefix[0] = 0x27;
Packit Service 8101fe
        pco_prefix[1] = pco_raw_len - 2;
Packit Service 8101fe
        pco_prefix[2] = 0x80;
Packit Service 8101fe
        /* Verizon uses element ID 0xFF00 for carrier-specific PCO content. */
Packit Service 8101fe
        pco_prefix[3] = 0xFF;
Packit Service 8101fe
        pco_prefix[4] = 0x00;
Packit Service 8101fe
        pco_prefix[5] = pco_payload_bytes_len;
Packit Service 8101fe
Packit Service 8101fe
        pco_raw = g_byte_array_sized_new (pco_raw_len);
Packit Service 8101fe
        g_byte_array_append (pco_raw, pco_prefix, sizeof (pco_prefix));
Packit Service 8101fe
        g_byte_array_append (pco_raw, (guint8 *)pco_payload_bytes, pco_payload_bytes_len);
Packit Service 8101fe
        g_free (pco_payload_bytes);
Packit Service 8101fe
Packit Service 8101fe
        pco = mm_pco_new ();
Packit Service 8101fe
        mm_pco_set_session_id (pco, pco_cid);
Packit Service 8101fe
        mm_pco_set_complete (pco, TRUE);
Packit Service 8101fe
        mm_pco_set_data (pco, pco_raw->data, pco_raw->len);
Packit Service 8101fe
        g_byte_array_unref (pco_raw);
Packit Service 8101fe
        break;
Packit Service 8101fe
    }
Packit Service 8101fe
Packit Service 8101fe
    return pco;
Packit Service 8101fe
}