~binli/ubuntu/trusty/modemmanager/lp1441095

« back to all changes in this revision

Viewing changes to plugins/mm-modem-icera.c

  • Committer: Package Import Robot
  • Author(s): Mathieu Trudel-Lapierre
  • Date: 2012-10-08 11:03:19 UTC
  • mfrom: (1.1.20)
  • Revision ID: package-import@ubuntu.com-20121008110319-wxd1vokudgaw2sep
Tags: 0.6.0.0.really-0ubuntu1
Upload the right tarball for 0.6.0.0 from upstream, which I somehow
mishandled before. Thanks to Marius B. Kotsbak for pointing out the issue.

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
#include "mm-at-serial-port.h"
33
33
#include "mm-generic-gsm.h"
34
34
#include "mm-modem-helpers.h"
 
35
#include "mm-utils.h"
35
36
#include "mm-log.h"
36
37
 
37
38
struct _MMModemIceraPrivate {
38
39
    /* Pending connection attempt */
39
40
    MMCallbackInfo *connect_pending_data;
40
41
    guint connect_pending_id;
 
42
    guint configure_context_id;
 
43
    guint configure_context_tries;
41
44
 
42
45
    char *username;
43
46
    char *password;
47
50
 
48
51
#define MM_MODEM_ICERA_GET_PRIVATE(m) (MM_MODEM_ICERA_GET_INTERFACE (m)->get_private(m))
49
52
 
 
53
static void connect_pending_done (MMModemIcera *self);
 
54
static void icera_call_control (MMModemIcera *self,
 
55
                                guint32 cid,
 
56
                                gboolean activate,
 
57
                                MMAtSerialResponseFn callback,
 
58
                                gpointer user_data);
 
59
 
 
60
static void
 
61
cleanup_configure_context (MMModemIcera *self)
 
62
{
 
63
    MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self);
 
64
 
 
65
    if (priv->configure_context_id != 0) {
 
66
        g_source_remove (priv->configure_context_id);
 
67
        priv->configure_context_id = 0;
 
68
    }
 
69
}
 
70
 
50
71
static void
51
72
get_allowed_mode_done (MMAtSerialPort *port,
52
73
                       GString *response,
298
319
/****************************************************************/
299
320
 
300
321
static void
301
 
disconnect_ipdpact_done (MMAtSerialPort *port,
302
 
                         GString *response,
303
 
                         GError *error,
304
 
                         gpointer user_data)
 
322
disconnect_done (MMAtSerialPort *port,
 
323
                 GString *response,
 
324
                 GError *error,
 
325
                 gpointer user_data)
305
326
{
306
327
    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
307
328
 
320
341
                              gpointer user_data)
321
342
{
322
343
    MMCallbackInfo *info;
323
 
    MMAtSerialPort *primary;
324
 
    char *command;
 
344
 
 
345
    /* Cleanup any running connect stuff */
 
346
    cleanup_configure_context (MM_MODEM_ICERA (gsm));
 
347
    connect_pending_done (MM_MODEM_ICERA (gsm));
325
348
 
326
349
    info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
327
350
 
328
 
    primary = mm_generic_gsm_get_at_port (gsm, MM_AT_PORT_FLAG_PRIMARY);
329
 
    g_assert (primary);
330
 
 
331
 
    command = g_strdup_printf ("%%IPDPACT=%d,0", cid);
332
 
    mm_at_serial_port_queue_command (primary, command, 3, disconnect_ipdpact_done, info);
333
 
    g_free (command);
 
351
    icera_call_control (MM_MODEM_ICERA (gsm), cid, FALSE, disconnect_done, info);
334
352
}
335
353
 
336
354
/*****************************************************************************/
474
492
 
475
493
static void
476
494
icera_call_control (MMModemIcera *self,
 
495
                    guint32 cid,
477
496
                    gboolean activate,
478
497
                    MMAtSerialResponseFn callback,
479
498
                    gpointer user_data)
484
503
    primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_AT_PORT_FLAG_PRIMARY);
485
504
    g_assert (primary);
486
505
 
487
 
    command = g_strdup_printf ("%%IPDPACT=%d,%d", _get_cid (self), activate ? 1 : 0);
488
 
    mm_at_serial_port_queue_command (primary, command, 3, callback, user_data);
 
506
    /* Firmware might have a custom call control command (ie, Sierra) */
 
507
    if (MM_MODEM_ICERA_GET_INTERFACE (self)->get_call_control_cmd)
 
508
        command = MM_MODEM_ICERA_GET_INTERFACE (self)->get_call_control_cmd (self, cid, activate);
 
509
    else
 
510
        command = g_strdup_printf ("%%IPDPACT=%d,%d", cid, activate ? 1 : 0);
 
511
 
 
512
    mm_at_serial_port_queue_command (primary, command, 15, callback, user_data);
489
513
    g_free (command);
490
514
}
491
515
 
513
537
                                           "Connection timed out");
514
538
    }
515
539
 
516
 
    icera_call_control (self, FALSE, timeout_done, self);
 
540
    icera_call_control (self, _get_cid (self), FALSE, timeout_done, self);
517
541
    return FALSE;
518
542
}
519
543
 
542
566
 
543
567
        priv->connect_pending_data = info;
544
568
        priv->connect_pending_id = g_timeout_add_seconds (30, icera_connect_timed_out, self);
 
569
 
 
570
        /* If the implementor has a custom call control command, then assume
 
571
         * that it returns OK only when the connection is ready, and thus all
 
572
         * we have to do is complete the connection request. Otherwise, if
 
573
         * using standard Icera commands, we wait for the connection indication
 
574
         * via %IPDPACT unsolicited messages.
 
575
         */
 
576
        if (MM_MODEM_ICERA_GET_INTERFACE (self)->get_call_control_cmd)
 
577
            connect_pending_done (self);
545
578
    }
546
579
}
547
580
 
559
592
        return;
560
593
 
561
594
    /* Activate the PDP context and start the data session */
562
 
    icera_call_control (MM_MODEM_ICERA (info->modem), TRUE, icera_connected, info);
 
595
    icera_call_control (MM_MODEM_ICERA (info->modem),
 
596
                        _get_cid (MM_MODEM_ICERA (info->modem)),
 
597
                        TRUE,
 
598
                        icera_connected,
 
599
                        info);
 
600
}
 
601
 
 
602
static void configure_context (MMAtSerialPort *port, MMCallbackInfo *info,
 
603
                               char *username, char *password, gint cid);
 
604
 
 
605
static gboolean
 
606
retry_config_context (gpointer data)
 
607
{
 
608
    MMCallbackInfo *info = (MMCallbackInfo *) data;
 
609
    MMModemIcera *self = MM_MODEM_ICERA (info->modem);
 
610
    MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self);
 
611
    MMAtSerialPort *primary;
 
612
 
 
613
    priv->configure_context_id = 0;
 
614
    primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_AT_PORT_FLAG_PRIMARY);
 
615
    g_assert (primary);
 
616
    configure_context (primary, info, priv->username, priv->password, _get_cid (self));
 
617
    return FALSE;
563
618
}
564
619
 
565
620
static void
569
624
           gpointer user_data)
570
625
{
571
626
    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
 
627
    MMModemIcera *self = MM_MODEM_ICERA (info->modem);
 
628
    MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self);
572
629
 
573
630
    /* If the modem has already been removed, return without
574
631
     * scheduling callback */
575
632
    if (mm_callback_info_check_modem_removed (info))
576
633
        return;
577
634
 
578
 
    if (error)
 
635
    if (error) {
 
636
        /* Retry configuring the context. It sometimes fails with a 583
 
637
         * error ["a profile (CID) is currently active"] if a connect
 
638
         * is attempted too soon after a disconnect. */
 
639
        if (++priv->configure_context_tries < 3) {
 
640
            priv->configure_context_id =
 
641
                    g_timeout_add_seconds (1, retry_config_context, info);
 
642
            return;
 
643
        }
579
644
        mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), error, info);
580
 
    else {
 
645
    } else {
581
646
        /* Ensure the PDP context is deactivated */
582
 
        icera_call_control (MM_MODEM_ICERA (info->modem), FALSE, old_context_clear_done, info);
583
 
    }
 
647
        icera_call_control (self, _get_cid (self), FALSE, old_context_clear_done, info);
 
648
    }
 
649
}
 
650
 
 
651
static void
 
652
configure_context(MMAtSerialPort *port, MMCallbackInfo *info,
 
653
                  char *username, char *password, gint cid)
 
654
{
 
655
    char *command;
 
656
 
 
657
    /* Both user and password are required; otherwise firmware returns an error */
 
658
    if (!username || !password)
 
659
                command = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", cid);
 
660
    else {
 
661
        command = g_strdup_printf ("%%IPDPCFG=%d,0,1,\"%s\",\"%s\"",
 
662
                                   cid,
 
663
                                   username ? username : "",
 
664
                                   password ? password : "");
 
665
 
 
666
    }
 
667
 
 
668
    mm_at_serial_port_queue_command (port, command, 3, auth_done, info);
 
669
    g_free (command);
584
670
}
585
671
 
586
672
void
593
679
    MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self);
594
680
    MMCallbackInfo *info;
595
681
    MMAtSerialPort *primary;
596
 
    gint cid;
597
 
    char *command;
598
682
 
599
683
    mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE);
600
684
 
603
687
    primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_AT_PORT_FLAG_PRIMARY);
604
688
    g_assert (primary);
605
689
 
606
 
    cid = _get_cid (self);
607
 
 
608
 
 
609
 
    /* Both user and password are required; otherwise firmware returns an error */
610
 
    if (!priv->username || !priv->password)
611
 
                command = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", cid);
612
 
    else {
613
 
        command = g_strdup_printf ("%%IPDPCFG=%d,0,1,\"%s\",\"%s\"",
614
 
                                   cid,
615
 
                                   priv->username ? priv->username : "",
616
 
                                   priv->password ? priv->password : "");
617
 
 
618
 
    }
619
 
 
620
 
    mm_at_serial_port_queue_command (primary, command, 3, auth_done, info);
621
 
    g_free (command);
 
690
    priv->configure_context_tries = 0;
 
691
    configure_context (primary, info, priv->username, priv->password, _get_cid (self));
622
692
}
623
693
 
624
694
/****************************************************************/
637
707
 
638
708
    callback (info->modem,
639
709
              GPOINTER_TO_UINT (mm_callback_info_get_data (info, "ip4-address")),
 
710
              GPOINTER_TO_UINT (mm_callback_info_get_data (info, "ip4-netmask")),
 
711
              GPOINTER_TO_UINT (mm_callback_info_get_data (info, "ip4-gateway")),
640
712
              (GArray *) mm_callback_info_get_data (info, "ip4-dns"),
641
713
              info->error, info->user_data);
642
714
}
673
745
    cid = _get_cid (MM_MODEM_ICERA (info->modem));
674
746
    dns_array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 2);
675
747
 
676
 
    /* %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>[,<nbns1>,<nbns2>] */
 
748
    /* %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>[,<nbns1>,<nbns2>[,<??>,<netmask>,<gw>]]
 
749
     *
 
750
     * 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
 
751
     * 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,
 
752
     */
677
753
    items = g_strsplit (response->str + strlen (IPDPADDR_TAG), ", ", 0);
678
754
 
679
755
    for (iter = items, i = 0; *iter; iter++, i++) {
691
767
        } else if (i == 1) { /* IP address */
692
768
            if (inet_pton (AF_INET, *iter, &tmp) > 0)
693
769
                mm_callback_info_set_data (info, "ip4-address", GUINT_TO_POINTER (tmp), NULL);
 
770
        } else if (i == 2) { /* Gateway */
 
771
            if ((inet_pton (AF_INET, *iter, &tmp) > 0) && (tmp > 0))
 
772
                mm_callback_info_set_data (info, "ip4-gateway", GUINT_TO_POINTER (tmp), NULL);
694
773
        } else if (i == 3) { /* DNS 1 */
695
774
            if (inet_pton (AF_INET, *iter, &tmp) > 0)
696
775
                g_array_append_val (dns_array, tmp);
697
776
        } else if (i == 4) { /* DNS 2 */
698
777
            if (inet_pton (AF_INET, *iter, &tmp) > 0)
699
778
                g_array_append_val (dns_array, tmp);
 
779
        } else if (i == 8) { /* Netmask */
 
780
            if (inet_pton (AF_INET, *iter, &tmp) > 0)
 
781
                mm_callback_info_set_data (info, "ip4-netmask", GUINT_TO_POINTER (tmp), NULL);
 
782
        } else if (i == 9) { /* Duplicate gateway */
 
783
            if (mm_callback_info_get_data (info, "ip4-gateway") == NULL) {
 
784
                if (inet_pton (AF_INET, *iter, &tmp) > 0)
 
785
                    mm_callback_info_set_data (info, "ip4-gateway", GUINT_TO_POINTER (tmp), NULL);
 
786
            }
700
787
        }
701
788
    }
702
789
 
822
909
 
823
910
/****************************************************************/
824
911
 
 
912
typedef struct {
 
913
    MMModemGsmBand band;
 
914
    char *name;
 
915
    gboolean enabled;
 
916
    gpointer data;
 
917
} Band;
 
918
 
 
919
static void
 
920
band_free (Band *b)
 
921
{
 
922
    g_free (b->name);
 
923
    g_free (b);
 
924
}
 
925
 
 
926
static const Band modem_bands[] = {
 
927
    /* Sort 3G first since it's preferred */
 
928
    { MM_MODEM_GSM_BAND_U2100, "FDD_BAND_I",    FALSE, NULL },
 
929
    { MM_MODEM_GSM_BAND_U1900, "FDD_BAND_II",   FALSE, NULL },
 
930
    { MM_MODEM_GSM_BAND_U1800, "FDD_BAND_III",  FALSE, NULL },
 
931
    { MM_MODEM_GSM_BAND_U17IV, "FDD_BAND_IV",   FALSE, NULL },
 
932
    { MM_MODEM_GSM_BAND_U800,  "FDD_BAND_VI",   FALSE, NULL },
 
933
    { MM_MODEM_GSM_BAND_U850,  "FDD_BAND_V",    FALSE, NULL },
 
934
    { MM_MODEM_GSM_BAND_U900,  "FDD_BAND_VIII", FALSE, NULL },
 
935
    /* 2G second */
 
936
    { MM_MODEM_GSM_BAND_G850,  "G850",          FALSE, NULL },
 
937
    { MM_MODEM_GSM_BAND_DCS,   "DCS",           FALSE, NULL },
 
938
    { MM_MODEM_GSM_BAND_EGSM,  "EGSM",          FALSE, NULL },
 
939
    { MM_MODEM_GSM_BAND_PCS,   "PCS",           FALSE, NULL },
 
940
    /* And ANY last since it's most inclusive */
 
941
    { MM_MODEM_GSM_BAND_ANY,   "ANY",           FALSE, NULL },
 
942
};
 
943
 
 
944
static MMModemGsmBand
 
945
icera_band_to_mm (const char *icera)
 
946
{
 
947
    int i;
 
948
 
 
949
    for (i = 0 ; i < G_N_ELEMENTS (modem_bands); i++) {
 
950
        if (g_strcmp0 (icera, modem_bands[i].name) == 0)
 
951
            return modem_bands[i].band;
 
952
    }
 
953
    return MM_MODEM_GSM_BAND_UNKNOWN;
 
954
}
 
955
 
 
956
static const char *
 
957
mm_band_to_icera (MMModemGsmBand band)
 
958
{
 
959
    int i;
 
960
 
 
961
    for (i = 0; i < G_N_ELEMENTS (modem_bands); i++) {
 
962
        if (modem_bands[i].band == band)
 
963
            return modem_bands[i].name;
 
964
    }
 
965
    return NULL;
 
966
}
 
967
 
 
968
/* returns a list of 'Band' structs */
 
969
static GSList *
 
970
build_bands_info (const gchar *response, gboolean build_command)
 
971
{
 
972
    GSList *bands = NULL;
 
973
    GRegex *r;
 
974
    GMatchInfo *match;
 
975
 
 
976
    /*
 
977
     * Response is a number of lines of the form:
 
978
     *   "EGSM": 0
 
979
     *   "FDD_BAND_I": 1
 
980
     *   ...
 
981
     * with 1 and 0 indicating whether the particular band is enabled or not.
 
982
     */
 
983
    r = g_regex_new ("^\"(\\w+)\": (\\d)",
 
984
                     G_REGEX_MULTILINE, G_REGEX_MATCH_NEWLINE_ANY,
 
985
                     NULL);
 
986
    g_assert (r != NULL);
 
987
 
 
988
    g_regex_match (r, response, 0, &match);
 
989
    while (g_match_info_matches (match)) {
 
990
        gchar *name, *enabled;
 
991
        Band *b;
 
992
        MMModemGsmBand band;
 
993
 
 
994
        name = g_match_info_fetch (match, 1);
 
995
        enabled = g_match_info_fetch (match, 2);
 
996
        band = icera_band_to_mm (name);
 
997
        if (band != MM_MODEM_GSM_BAND_UNKNOWN && band != MM_MODEM_GSM_BAND_ANY) {
 
998
            b = g_malloc0 (sizeof (Band));
 
999
            b->band = band;
 
1000
            b->enabled = (enabled[0] == '1' ? TRUE : FALSE);
 
1001
            if (build_command) {
 
1002
                /* abuse 'name' for the AT command to check band support */
 
1003
                b->name = g_strdup_printf ("%%IPBM=\"%s\",%c",
 
1004
                                           name,
 
1005
                                           b->enabled ? '1' : '0');
 
1006
            }
 
1007
            bands = g_slist_append (bands, b);
 
1008
        }
 
1009
        g_free (name);
 
1010
        g_free (enabled);
 
1011
        g_match_info_next (match, NULL);
 
1012
    }
 
1013
    g_match_info_free (match);
 
1014
    g_regex_unref (r);
 
1015
 
 
1016
    return bands;
 
1017
}
 
1018
 
 
1019
static void
 
1020
get_current_bands_done (MMAtSerialPort *port,
 
1021
                        GString *response,
 
1022
                        GError *error,
 
1023
                        gpointer user_data)
 
1024
{
 
1025
    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
 
1026
    MMModemGsmBand mm_band = MM_MODEM_GSM_BAND_ANY;
 
1027
    GSList *bands;
 
1028
    Band *b;
 
1029
    GSList *iter;
 
1030
 
 
1031
    /* If the modem has already been removed, return without
 
1032
     * scheduling callback */
 
1033
    if (mm_callback_info_check_modem_removed (info))
 
1034
        return;
 
1035
 
 
1036
    if (error) {
 
1037
        info->error = g_error_copy (error);
 
1038
        mm_callback_info_schedule (info);
 
1039
        return;
 
1040
    }
 
1041
 
 
1042
    bands = build_bands_info (response->str, FALSE);
 
1043
    if (!bands) {
 
1044
        mm_callback_info_set_result (info, GUINT_TO_POINTER (MM_MODEM_GSM_BAND_UNKNOWN), NULL);
 
1045
        mm_callback_info_schedule (info);
 
1046
        return;
 
1047
    }
 
1048
 
 
1049
    for (iter = bands; iter; iter = g_slist_next (iter)) {
 
1050
        b = iter->data;
 
1051
        if (b->enabled)
 
1052
            mm_band |= b->band;
 
1053
    }
 
1054
    g_slist_foreach (bands, (GFunc) band_free, NULL);
 
1055
    g_slist_free (bands);
 
1056
 
 
1057
    mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL);
 
1058
    mm_callback_info_schedule (info);
 
1059
}
 
1060
 
 
1061
void
 
1062
mm_modem_icera_get_current_bands (MMModemIcera *self,
 
1063
                                  MMModemUIntFn callback,
 
1064
                                  gpointer user_data)
 
1065
{
 
1066
    MMAtSerialPort *port;
 
1067
    MMCallbackInfo *info;
 
1068
 
 
1069
    info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
 
1070
 
 
1071
    /* Otherwise ask the modem */
 
1072
    port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error);
 
1073
    if (!port) {
 
1074
        mm_callback_info_schedule (info);
 
1075
        return;
 
1076
    }
 
1077
 
 
1078
    mm_at_serial_port_queue_command (port, "AT%IPBM?", 3, get_current_bands_done, info);
 
1079
}
 
1080
 
 
1081
#define NUM_BANDS_TAG "num-bands"
 
1082
#define BAND_RESULT_TAG "band-result"
 
1083
 
 
1084
static void
 
1085
get_one_supported_band_done (MMAtSerialPort *port,
 
1086
                             GString *response,
 
1087
                             GError *error,
 
1088
                             gpointer user_data)
 
1089
{
 
1090
    Band *b = user_data;
 
1091
    MMCallbackInfo *info = b->data;
 
1092
    MMModemGsmBand mm_bands = MM_MODEM_GSM_BAND_UNKNOWN;
 
1093
    guint num;
 
1094
 
 
1095
    if (mm_callback_info_check_modem_removed (info) == FALSE) {
 
1096
        /* Update supported bands list */
 
1097
        mm_bands = GPOINTER_TO_UINT (mm_callback_info_get_data (info, BAND_RESULT_TAG));
 
1098
        if (error == NULL) {
 
1099
            mm_bands |= b->band;
 
1100
            mm_callback_info_set_data (info, BAND_RESULT_TAG, GUINT_TO_POINTER (mm_bands), NULL);
 
1101
        }
 
1102
 
 
1103
        num = GPOINTER_TO_UINT (mm_callback_info_get_data (info, NUM_BANDS_TAG)) - 1;
 
1104
        mm_callback_info_set_data (info, NUM_BANDS_TAG, GUINT_TO_POINTER (num), NULL);
 
1105
        if (num == 0) {
 
1106
            /* All done */
 
1107
            mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_bands), NULL);
 
1108
            mm_callback_info_schedule (info);
 
1109
        }
 
1110
    }
 
1111
    band_free (b);
 
1112
}
 
1113
 
 
1114
static void
 
1115
get_supported_bands_done (MMAtSerialPort *port,
 
1116
                          GString *response,
 
1117
                          GError *error,
 
1118
                          gpointer user_data)
 
1119
{
 
1120
    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
 
1121
    GSList *bands, *iter;
 
1122
    Band *b;
 
1123
    guint i;
 
1124
 
 
1125
    /* If the modem has already been removed, return without
 
1126
     * scheduling callback */
 
1127
    if (mm_callback_info_check_modem_removed (info))
 
1128
        return;
 
1129
 
 
1130
    if (error) {
 
1131
        info->error = g_error_copy (error);
 
1132
        mm_callback_info_schedule (info);
 
1133
        return;
 
1134
    }
 
1135
 
 
1136
    bands = build_bands_info (response->str, TRUE);
 
1137
    if (!bands) {
 
1138
        mm_callback_info_set_result (info, GUINT_TO_POINTER (MM_MODEM_GSM_BAND_UNKNOWN), NULL);
 
1139
        mm_callback_info_schedule (info);
 
1140
        return;
 
1141
    }
 
1142
 
 
1143
    for (iter = bands, i = 0; iter; iter = g_slist_next (iter), i++) {
 
1144
        b = iter->data;
 
1145
        b->data = info;
 
1146
        mm_at_serial_port_queue_command (port, b->name, 10, get_one_supported_band_done, b);
 
1147
    }
 
1148
    /* Free list, but not items; they are freed when the AT response comes back */
 
1149
    g_slist_free (bands);
 
1150
 
 
1151
    mm_callback_info_set_data (info, NUM_BANDS_TAG, GUINT_TO_POINTER (i), NULL);
 
1152
}
 
1153
 
 
1154
void
 
1155
mm_modem_icera_get_supported_bands (MMModemIcera *self,
 
1156
                                    MMModemUIntFn callback,
 
1157
                                    gpointer user_data)
 
1158
{
 
1159
    MMAtSerialPort *port;
 
1160
    MMCallbackInfo *info;
 
1161
 
 
1162
    info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
 
1163
 
 
1164
    /* Otherwise ask the modem */
 
1165
    port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error);
 
1166
    if (!port) {
 
1167
        mm_callback_info_schedule (info);
 
1168
        return;
 
1169
    }
 
1170
 
 
1171
    /* The modems report some bands as disabled that they don't actually
 
1172
     * support enabling. Thanks Icera! So we have to try setting each
 
1173
     * band to it's current enabled/disabled value, and the modem will
 
1174
     * return an error if it doesn't support that band at all.
 
1175
     */
 
1176
    mm_at_serial_port_queue_command (port, "AT%IPBM?", 3, get_supported_bands_done, info);
 
1177
}
 
1178
 
 
1179
static void
 
1180
set_band_done (MMAtSerialPort *port,
 
1181
               GString *response,
 
1182
               GError *error,
 
1183
               gpointer user_data)
 
1184
{
 
1185
    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
 
1186
 
 
1187
    /* If the modem has already been removed, return without
 
1188
     * scheduling callback */
 
1189
    if (mm_callback_info_check_modem_removed (info))
 
1190
        return;
 
1191
 
 
1192
    if (error)
 
1193
        info->error = g_error_copy (error);
 
1194
 
 
1195
    mm_callback_info_schedule (info);
 
1196
}
 
1197
 
 
1198
void
 
1199
mm_modem_icera_set_band (MMModemIcera *self,
 
1200
                         MMModemGsmBand band,
 
1201
                         MMModemFn callback,
 
1202
                         gpointer user_data)
 
1203
{
 
1204
    MMCallbackInfo *info;
 
1205
    MMAtSerialPort *port;
 
1206
    char *command;
 
1207
    const char *icera_band;
 
1208
 
 
1209
    info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
 
1210
 
 
1211
    port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error);
 
1212
    if (!port) {
 
1213
        mm_callback_info_schedule (info);
 
1214
        return;
 
1215
    }
 
1216
 
 
1217
    /* TODO: Check how to pass more than one band in the same AT%%IPBM command */
 
1218
    if (!utils_check_for_single_value (band)) {
 
1219
        info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Cannot set more than one band.");
 
1220
        mm_callback_info_schedule (info);
 
1221
        return;
 
1222
    }
 
1223
 
 
1224
    icera_band = mm_band_to_icera (band);
 
1225
    if (!icera_band) {
 
1226
        info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band.");
 
1227
        mm_callback_info_schedule (info);
 
1228
        return;
 
1229
    }
 
1230
 
 
1231
    command = g_strdup_printf ("AT%%IPBM=\"%s\",1", icera_band);
 
1232
    mm_at_serial_port_queue_command (port, command, 10, set_band_done, info);
 
1233
    g_free (command);
 
1234
}
 
1235
 
 
1236
/****************************************************************/
 
1237
 
 
1238
static void
 
1239
get_unlock_retries_done (MMAtSerialPort *port,
 
1240
                         GString *response,
 
1241
                         GError *error,
 
1242
                         gpointer user_data)
 
1243
{
 
1244
    MMCallbackInfo *info = (MMCallbackInfo *) user_data;
 
1245
    int matched;
 
1246
    GArray *retry_counts;
 
1247
    PinRetryCount ur[4] = {
 
1248
        {"sim-pin", 0}, {"sim-puk", 0}, {"sim-pin2", 0}, {"sim-puk2", 0}
 
1249
    };
 
1250
 
 
1251
    /* If the modem has already been removed, return without
 
1252
     * scheduling callback */
 
1253
    if (mm_callback_info_check_modem_removed (info))
 
1254
        return;
 
1255
 
 
1256
    if (error) {
 
1257
        info->error = g_error_copy (error);
 
1258
        goto done;
 
1259
    }
 
1260
 
 
1261
    matched = sscanf (response->str, "%%PINNUM: %d, %d, %d, %d",
 
1262
                      &ur[0].count, &ur[1].count, &ur[2].count, &ur[3].count);
 
1263
    if (matched == 4) {
 
1264
        if (ur[0].count > 998) {
 
1265
            info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
 
1266
                                       "Invalid PIN attempts left %d", ur[0].count);
 
1267
            ur[0].count = 0;
 
1268
        }
 
1269
 
 
1270
        retry_counts = g_array_sized_new (FALSE, TRUE, sizeof (PinRetryCount), 4);
 
1271
        g_array_append_vals (retry_counts, &ur, 4);
 
1272
        mm_callback_info_set_result (info, retry_counts, NULL);
 
1273
    } else {
 
1274
        info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
 
1275
                                           "Could not parse PIN retries results");
 
1276
    }
 
1277
 
 
1278
done:
 
1279
    mm_serial_port_close (MM_SERIAL_PORT (port));
 
1280
    mm_callback_info_schedule (info);
 
1281
}
 
1282
 
 
1283
void
 
1284
mm_modem_icera_get_unlock_retries (MMModemIcera *self,
 
1285
                                   MMModemArrayFn callback,
 
1286
                                   gpointer user_data)
 
1287
{
 
1288
    MMAtSerialPort *port;
 
1289
    MMCallbackInfo *info;
 
1290
 
 
1291
    mm_dbg ("get_unlock_retries");
 
1292
 
 
1293
    info = mm_callback_info_array_new (MM_MODEM (self), callback, user_data);
 
1294
 
 
1295
    /* Ensure we have a usable port to use for the command */
 
1296
    port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error);
 
1297
    if (!port) {
 
1298
        mm_callback_info_schedule (info);
 
1299
        return;
 
1300
    }
 
1301
 
 
1302
    /* Modem may not be enabled yet, which sometimes can't be done until
 
1303
     * the device has been unlocked.  In this case we have to open the port
 
1304
     * ourselves.
 
1305
     */
 
1306
    if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
 
1307
        mm_callback_info_schedule (info);
 
1308
        return;
 
1309
    }
 
1310
 
 
1311
    /* if the modem have not yet been enabled we need to make sure echoing is turned off */
 
1312
    mm_at_serial_port_queue_command (port, "E0", 3, NULL, NULL);
 
1313
    mm_at_serial_port_queue_command (port, "%PINNUM?", 3, get_unlock_retries_done, info);
 
1314
 
 
1315
}
 
1316
 
 
1317
/****************************************************************/
 
1318
 
825
1319
static const char *
826
1320
get_string_property (GHashTable *properties, const char *name)
827
1321
{
930
1424
 
931
1425
    /* Clear the pending connection if necessary */
932
1426
    connect_pending_done (self);
 
1427
    cleanup_configure_context (self);
933
1428
 
934
1429
    g_free (priv->username);
935
1430
    priv->username = NULL;