huawei: plugin fully ported

This commit is contained in:
Aleksander Morgado
2012-08-06 20:07:23 +02:00
parent dd71656106
commit e303e5c90d
6 changed files with 0 additions and 1692 deletions
-304
View File
@@ -1,304 +0,0 @@
/* -*- 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 Red Hat, Inc.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
#include "mm-modem-huawei-cdma.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-serial-port.h"
#include "mm-serial-parsers.h"
#include "mm-log.h"
G_DEFINE_TYPE (MMModemHuaweiCdma, mm_modem_huawei_cdma, MM_TYPE_GENERIC_CDMA)
MMModem *
mm_modem_huawei_cdma_new (const char *device,
const char *driver,
const char *plugin,
gboolean evdo_rev0,
gboolean evdo_revA,
guint32 vendor,
guint32 product)
{
gboolean try_css = TRUE;
g_return_val_if_fail (device != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
g_return_val_if_fail (plugin != NULL, NULL);
/* Don't use AT+CSS on EVDO-capable hardware for determining registration
* status, because often the device will have only an EVDO connection and
* AT+CSS won't necessarily report EVDO registration status, only 1X.
*/
if (evdo_rev0 || evdo_revA)
try_css = FALSE;
return MM_MODEM (g_object_new (MM_TYPE_MODEM_HUAWEI_CDMA,
MM_MODEM_MASTER_DEVICE, device,
MM_MODEM_DRIVER, driver,
MM_MODEM_PLUGIN, plugin,
MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0,
MM_GENERIC_CDMA_EVDO_REVA, evdo_revA,
MM_GENERIC_CDMA_REGISTRATION_TRY_CSS, try_css,
MM_MODEM_HW_VID, vendor,
MM_MODEM_HW_PID, product,
NULL));
}
/* Unsolicited message handlers */
static gint
parse_quality (const char *str, const char *detail)
{
long int quality = 0;
errno = 0;
quality = strtol (str, NULL, 10);
if (errno == 0) {
quality = CLAMP (quality, 0, 100);
mm_dbg ("%s: %ld", detail, quality);
return (gint) quality;
}
return -1;
}
static void
handle_1x_quality_change (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data);
char *str;
gint quality;
str = g_match_info_fetch (match_info, 1);
quality = parse_quality (str, "1X signal quality");
g_free (str);
if (quality >= 0)
mm_generic_cdma_update_cdma1x_quality (MM_GENERIC_CDMA (self), (guint32) quality);
}
static void
handle_evdo_quality_change (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data);
char *str;
gint quality;
str = g_match_info_fetch (match_info, 1);
quality = parse_quality (str, "EVDO signal quality");
g_free (str);
if (quality >= 0)
mm_generic_cdma_update_evdo_quality (MM_GENERIC_CDMA (self), (guint32) quality);
}
/*****************************************************************************/
static const char *
strip_response (const char *resp, const char *cmd)
{
const char *p = resp;
if (p) {
if (!strncmp (p, cmd, strlen (cmd)))
p += strlen (cmd);
while (*p == ' ')
p++;
}
return p;
}
static gboolean
uint_from_match_item (GMatchInfo *match_info, guint32 num, guint32 *val)
{
long int tmp;
char *str;
gboolean success = FALSE;
str = g_match_info_fetch (match_info, num);
g_return_val_if_fail (str != NULL, FALSE);
errno = 0;
tmp = strtol (str, NULL, 10);
if (errno == 0 && tmp >= 0 && tmp <= G_MAXUINT) {
*val = (guint32) tmp;
success = TRUE;
}
g_free (str);
return success;
}
static void
sysinfo_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
GRegex *r;
GMatchInfo *match_info;
const char *reply;
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
return;
if (error) {
/* Leave superclass' reg state alone if AT^SYSINFO isn't supported */
goto done;
}
reply = strip_response (response->str, "^SYSINFO:");
/* Format is "<srv_status>,<srv_domain>,<roam_status>,<sys_mode>,<sim_state>" */
r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
if (!r) {
mm_warn ("Huawei: ^SYSINFO parse regex creation failed.");
goto done;
}
g_regex_match (r, reply, 0, &match_info);
if (g_match_info_get_match_count (match_info) >= 5) {
MMModemCdmaRegistrationState reg_state;
guint32 val = 0;
/* At this point the generic code already knows we've been registered */
reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
if (uint_from_match_item (match_info, 1, &val)) {
if (val == 2) {
/* Service available, check roaming state */
val = 0;
if (uint_from_match_item (match_info, 3, &val)) {
if (val == 0)
reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
else if (val == 1)
reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING;
}
}
}
/* Check service type */
val = 0;
if (uint_from_match_item (match_info, 4, &val)) {
if (val == 2)
mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state);
else if (val == 4)
mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state);
else if (val == 8) {
mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state);
mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state);
}
} else {
/* Say we're registered to something even though sysmode parsing failed */
mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state);
}
} else
mm_warn ("Huawei: failed to parse ^SYSINFO response.");
g_match_info_free (match_info);
g_regex_unref (r);
done:
mm_callback_info_schedule (info);
}
static void
query_registration_state (MMGenericCdma *cdma,
MMModemCdmaRegistrationState cur_cdma_state,
MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
MMAtSerialPort *port;
info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, cur_cdma_state, cur_evdo_state, callback, user_data);
port = mm_generic_cdma_get_best_at_port (cdma, &info->error);
if (!port) {
mm_callback_info_schedule (info);
return;
}
mm_at_serial_port_queue_command (port, "^SYSINFO", 3, sysinfo_done, info);
}
/*****************************************************************************/
static void
port_grabbed (MMGenericCdma *cdma,
MMPort *port,
MMAtPortFlags pflags,
gpointer user_data)
{
GRegex *regex;
gboolean evdo0 = FALSE, evdoA = FALSE;
if (MM_IS_AT_SERIAL_PORT (port)) {
g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL);
/* 1x signal level */
regex = g_regex_new ("\\r\\n\\^RSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_1x_quality_change, cdma, NULL);
g_regex_unref (regex);
g_object_get (G_OBJECT (cdma),
MM_GENERIC_CDMA_EVDO_REV0, &evdo0,
MM_GENERIC_CDMA_EVDO_REVA, &evdoA,
NULL);
if (evdo0 || evdoA) {
/* EVDO signal level */
regex = g_regex_new ("\\r\\n\\^HRSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_evdo_quality_change, cdma, NULL);
g_regex_unref (regex);
}
}
}
/*****************************************************************************/
static void
mm_modem_huawei_cdma_init (MMModemHuaweiCdma *self)
{
}
static void
mm_modem_huawei_cdma_class_init (MMModemHuaweiCdmaClass *klass)
{
MMGenericCdmaClass *cdma_class = MM_GENERIC_CDMA_CLASS (klass);
mm_modem_huawei_cdma_parent_class = g_type_class_peek_parent (klass);
cdma_class->port_grabbed = port_grabbed;
cdma_class->query_registration_state = query_registration_state;
}
-47
View File
@@ -1,47 +0,0 @@
/* -*- 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 Red Hat, Inc.
*/
#ifndef MM_MODEM_HUAWEI_CDMA_H
#define MM_MODEM_HUAWEI_CDMA_H
#include "mm-generic-cdma.h"
#define MM_TYPE_MODEM_HUAWEI_CDMA (mm_modem_huawei_cdma_get_type ())
#define MM_MODEM_HUAWEI_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_HUAWEI_CDMA, MMModemHuaweiCdma))
#define MM_MODEM_HUAWEI_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_HUAWEI_CDMA, MMModemHuaweiCdmaClass))
#define MM_IS_MODEM_HUAWEI_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_HUAWEI_CDMA))
#define MM_IS_MODEM_HUAWEI_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_HUAWEI_CDMA))
#define MM_MODEM_HUAWEI_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_HUAWEI_CDMA, MMModemHuaweiCdmaClass))
typedef struct {
MMGenericCdma parent;
} MMModemHuaweiCdma;
typedef struct {
MMGenericCdmaClass parent;
} MMModemHuaweiCdmaClass;
GType mm_modem_huawei_cdma_get_type (void);
MMModem *mm_modem_huawei_cdma_new (const char *device,
const char *driver,
const char *plugin,
gboolean evdo_rev0,
gboolean evdo_revA,
guint32 vendor,
guint32 product);
#endif /* MM_MODEM_HUAWEI_CDMA_H */
-922
View File
@@ -1,922 +0,0 @@
/* -*- 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 - 2010 Red Hat, Inc.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
#include "mm-modem-huawei-gsm.h"
#include "mm-modem-gsm-network.h"
#include "mm-modem-gsm-card.h"
#include "mm-modem-gsm-ussd.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-at-serial-port.h"
#include "mm-serial-parsers.h"
#include "mm-log.h"
#include "mm-utils.h"
static void modem_init (MMModem *modem_class);
static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class);
static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class);
static void modem_gsm_ussd_init (MMModemGsmUssd *ussd_class);
G_DEFINE_TYPE_EXTENDED (MMModemHuaweiGsm, mm_modem_huawei_gsm, MM_TYPE_GENERIC_GSM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_USSD, modem_gsm_ussd_init)
)
#define MM_MODEM_HUAWEI_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmPrivate))
typedef struct {
/* Cached state */
guint32 band;
} MMModemHuaweiGsmPrivate;
MMModem *
mm_modem_huawei_gsm_new (const char *device,
const char *driver,
const char *plugin,
guint32 vendor,
guint32 product)
{
g_return_val_if_fail (device != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
g_return_val_if_fail (plugin != NULL, NULL);
return MM_MODEM (g_object_new (MM_TYPE_MODEM_HUAWEI_GSM,
MM_MODEM_MASTER_DEVICE, device,
MM_MODEM_DRIVER, driver,
MM_MODEM_PLUGIN, plugin,
MM_MODEM_HW_VID, vendor,
MM_MODEM_HW_PID, product,
NULL));
}
/*****************************************************************************/
typedef struct {
MMModemGsmBand mm;
guint32 huawei;
} BandTable;
static BandTable bands[] = {
/* Sort 3G first since it's preferred */
{ MM_MODEM_GSM_BAND_U2100, 0x00400000 },
{ MM_MODEM_GSM_BAND_U1900, 0x00800000 },
{ MM_MODEM_GSM_BAND_U850, 0x04000000 },
{ MM_MODEM_GSM_BAND_U900, 0x00020000 },
{ MM_MODEM_GSM_BAND_G850, 0x00080000 },
/* 2G second */
{ MM_MODEM_GSM_BAND_DCS, 0x00000080 },
{ MM_MODEM_GSM_BAND_EGSM, 0x00000300 }, /* 0x100 = Extended GSM, 0x200 = Primary GSM */
{ MM_MODEM_GSM_BAND_PCS, 0x00200000 }
};
static gboolean
band_mm_to_huawei (MMModemGsmBand band, guint32 *out_huawei)
{
int i;
/* Treat ANY as a special case: All huawei flags enabled */
if (band == MM_MODEM_GSM_BAND_ANY) {
*out_huawei = 0x3FFFFFFF;
return TRUE;
}
*out_huawei = 0;
for (i = 0; i < G_N_ELEMENTS (bands); i++) {
if (bands[i].mm & band) {
*out_huawei |= bands[i].huawei;
}
}
return (*out_huawei > 0 ? TRUE : FALSE);
}
static gboolean
band_huawei_to_mm (guint32 huawei, MMModemGsmBand *out_mm)
{
int i;
*out_mm = 0;
for (i = 0; i < G_N_ELEMENTS (bands); i++) {
if (bands[i].huawei & huawei) {
*out_mm |= bands[i].mm;
}
}
return (*out_mm > 0 ? TRUE : FALSE);
}
/*****************************************************************************/
static gboolean
parse_syscfg (MMModemHuaweiGsm *self,
const char *reply,
int *mode_a,
int *mode_b,
guint32 *band,
int *unknown1,
int *unknown2,
MMModemGsmAllowedMode *out_mode)
{
if (reply == NULL || strncmp (reply, "^SYSCFG:", 8))
return FALSE;
if (sscanf (reply + 8, "%d,%d,%x,%d,%d", mode_a, mode_b, band, unknown1, unknown2)) {
MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self);
MMModemGsmAllowedMode new_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
/* Network mode */
if (*mode_a == 2 && *mode_b == 1)
new_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
else if (*mode_a == 2 && *mode_b == 2)
new_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
else if (*mode_a == 13 && *mode_b == 1)
new_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
else if (*mode_a == 14 && *mode_b == 2)
new_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
if (out_mode)
*out_mode = new_mode;
/* Band */
priv->band = *band;
return TRUE;
}
return FALSE;
}
static void
set_allowed_mode_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
return;
if (error)
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
}
static void
set_allowed_mode (MMGenericGsm *gsm,
MMModemGsmAllowedMode mode,
MMModemFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
MMAtSerialPort *port;
int a, b;
char *command;
info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
if (!port) {
mm_callback_info_schedule (info);
return;
}
switch (mode) {
case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
a = 13;
b = 1;
break;
case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
a = 14;
b = 2;
break;
case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
a = 2;
b = 1;
break;
case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
a = 2;
b = 2;
break;
case MM_MODEM_GSM_ALLOWED_MODE_ANY:
default:
a = 2;
b = 0;
break;
}
command = g_strdup_printf ("AT^SYSCFG=%d,%d,40000000,2,4", a, b);
mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info);
g_free (command);
}
static void
get_allowed_mode_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemHuaweiGsm *self;
int mode_a, mode_b, u1, u2;
guint32 band;
MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
return;
self = MM_MODEM_HUAWEI_GSM (info->modem);
if (error)
info->error = g_error_copy (error);
else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2, &mode))
mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
mm_callback_info_schedule (info);
}
static void
get_allowed_mode (MMGenericGsm *gsm,
MMModemUIntFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
MMAtSerialPort *port;
info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
if (!port) {
mm_callback_info_schedule (info);
return;
}
mm_at_serial_port_queue_command (port, "AT^SYSCFG?", 3, get_allowed_mode_done, info);
}
static void
set_band_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemHuaweiGsm *self;
MMModemHuaweiGsmPrivate *priv;
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
return;
self = MM_MODEM_HUAWEI_GSM (info->modem);
priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self);
if (error)
info->error = g_error_copy (error);
else {
/* Success, cache the value */
priv->band = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "band"));
}
mm_callback_info_schedule (info);
}
static void
set_band (MMModemGsmNetwork *modem,
MMModemGsmBand band,
MMModemFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
MMAtSerialPort *port;
char *command;
guint32 huawei_band = 0x3FFFFFFF;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
if (!port) {
mm_callback_info_schedule (info);
return;
}
if (!band_mm_to_huawei (band, &huawei_band)) {
info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band.");
mm_callback_info_schedule (info);
} else {
mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (huawei_band), NULL);
command = g_strdup_printf ("AT^SYSCFG=16,3,%X,2,4", huawei_band);
mm_at_serial_port_queue_command (port, command, 3, set_band_done, info);
g_free (command);
}
}
static void
get_band_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemHuaweiGsm *self;
int mode_a, mode_b, u1, u2;
guint32 band;
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
return;
self = MM_MODEM_HUAWEI_GSM (info->modem);
if (error)
info->error = g_error_copy (error);
else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2, NULL)) {
MMModemGsmBand mm_band = MM_MODEM_GSM_BAND_ANY;
band_huawei_to_mm (band, &mm_band);
mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL);
}
mm_callback_info_schedule (info);
}
static void
get_band (MMModemGsmNetwork *modem,
MMModemUIntFn callback,
gpointer user_data)
{
MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem);
MMAtSerialPort *port;
MMCallbackInfo *info;
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
/* Prefer cached band from unsolicited messages if we have it */
if (priv->band != 0) {
MMModemGsmBand mm_band = MM_MODEM_GSM_BAND_ANY;
band_huawei_to_mm (priv->band, &mm_band);
mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL);
mm_callback_info_schedule (info);
return;
}
/* Otherwise ask the modem */
port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
if (!port) {
mm_callback_info_schedule (info);
return;
}
mm_at_serial_port_queue_command (port, "AT^SYSCFG?", 3, get_band_done, info);
}
static MMModemGsmAccessTech
huawei_sysinfo_to_act (int huawei)
{
switch (huawei) {
case 1:
return MM_MODEM_GSM_ACCESS_TECH_GSM;
case 2:
return MM_MODEM_GSM_ACCESS_TECH_GPRS;
case 3:
return MM_MODEM_GSM_ACCESS_TECH_EDGE;
case 4:
return MM_MODEM_GSM_ACCESS_TECH_UMTS;
case 5:
return MM_MODEM_GSM_ACCESS_TECH_HSDPA;
case 6:
return MM_MODEM_GSM_ACCESS_TECH_HSUPA;
case 7:
return MM_MODEM_GSM_ACCESS_TECH_HSPA;
case 9:
return MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS;
case 8:
/* TD-SCDMA */
default:
break;
}
return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
}
static void
get_act_request_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = user_data;
MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
GRegex *r = NULL;
GMatchInfo *match_info = NULL;
char *str;
int srv_stat = 0;
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
return;
if (error) {
info->error = g_error_copy (error);
goto done;
}
/* Can't just use \d here since sometimes you get "^SYSINFO:2,1,0,3,1,,3" */
r = g_regex_new ("\\^SYSINFO:\\s*(\\d?),(\\d?),(\\d?),(\\d?),(\\d?),(\\d?),(\\d?)$", G_REGEX_UNGREEDY, 0, NULL);
if (!r) {
g_set_error_literal (&info->error,
MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Could not parse ^SYSINFO results.");
goto done;
}
if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) {
g_set_error_literal (&info->error,
MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Could not parse ^SYSINFO results.");
goto done;
}
str = g_match_info_fetch (match_info, 1);
if (str && strlen (str))
srv_stat = atoi (str);
g_free (str);
if (srv_stat != 0) {
/* Valid service */
str = g_match_info_fetch (match_info, 7);
if (str && strlen (str))
act = huawei_sysinfo_to_act (atoi (str));
g_free (str);
}
done:
if (match_info)
g_match_info_free (match_info);
if (r)
g_regex_unref (r);
mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL);
mm_callback_info_schedule (info);
}
static void
get_access_technology (MMGenericGsm *modem,
MMModemUIntFn callback,
gpointer user_data)
{
MMAtSerialPort *port;
MMCallbackInfo *info;
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
port = mm_generic_gsm_get_best_at_port (modem, &info->error);
if (!port) {
mm_callback_info_schedule (info);
return;
}
mm_at_serial_port_queue_command (port, "^SYSINFO", 3, get_act_request_done, info);
}
/*****************************************************************************/
static gboolean
parse_match_as_num (GMatchInfo *match_info,
guint idx,
guint32 *out_num,
guint32 min,
guint32 max)
{
unsigned long int tmp;
char *str;
str = g_match_info_fetch (match_info, idx);
if (!str || !strlen (str))
return FALSE;
errno = 0;
tmp = strtoul (str, NULL, 10);
g_free (str);
if (errno != 0 || tmp < min || tmp > max)
return FALSE;
*out_num = (guint32) tmp;
return TRUE;
}
static void
send_huawei_cpin_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
GRegex *r = NULL;
GMatchInfo *match_info = NULL;
guint32 i = 0;
gboolean success = FALSE;
GArray *retry_counts;
PinRetryCount ur[4] = {
{"sim-puk", 0}, {"sim-pin", 0}, {"sim-puk2", 0}, {"sim-pin2", 0}
};
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
return;
if (error) {
info->error = g_error_copy (error);
goto done;
}
r = g_regex_new ("\\^CPIN:\\s*([^,]+),[^,]*,(\\d+),(\\d+),(\\d+),(\\d+)", G_REGEX_UNGREEDY, 0, NULL);
if (!r) {
g_set_error_literal (&info->error,
MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Could not parse ^CPIN results (error creating regex).");
goto done;
}
if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) {
g_set_error_literal (&info->error,
MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Could not parse ^CPIN results (match failed).");
goto done;
}
for (i = 0; i <= 3; i++) {
success = parse_match_as_num (match_info, i + 2, &ur[i].count, 0, 10);
if (!success) {
info->error = g_error_new_literal (MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"Could not parse ^CPIN results (missing or invalid match info).");
break;
}
}
if (success) {
retry_counts = g_array_sized_new (FALSE, TRUE, sizeof (PinRetryCount), 4);
g_array_append_vals (retry_counts, &ur, 4);
mm_callback_info_set_result (info, retry_counts, NULL);
}
done:
if (match_info)
g_match_info_free (match_info);
if (r)
g_regex_unref (r);
mm_serial_port_close (MM_SERIAL_PORT (port));
mm_callback_info_schedule (info);
}
static void
get_unlock_retries (MMModemGsmCard *modem,
MMModemArrayFn callback,
gpointer user_data)
{
MMAtSerialPort *port;
char *command;
MMCallbackInfo *info = mm_callback_info_array_new (MM_MODEM (modem), callback, user_data);
/* Ensure we have a usable port to use for the command */
port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
if (!port) {
mm_callback_info_schedule (info);
return;
}
/* Modem may not be enabled yet, which sometimes can't be done until
* the device has been unlocked. In this case we have to open the port
* ourselves.
*/
if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
mm_callback_info_schedule (info);
return;
}
/* if the modem have not yet been enabled we need to make sure echoing is turned off */
command = g_strdup_printf ("E0");
mm_at_serial_port_queue_command (port, command, 3, NULL, NULL);
g_free (command);
command = g_strdup_printf ("^CPIN?");
mm_at_serial_port_queue_command (port, command, 3, send_huawei_cpin_done, info);
g_free (command);
}
/*****************************************************************************/
/* Unsolicited message handlers */
static void
handle_signal_quality_change (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data);
char *str;
int quality = 0;
str = g_match_info_fetch (match_info, 1);
quality = atoi (str);
g_free (str);
if (quality == 99) {
/* 99 means unknown */
quality = 0;
} else {
/* Normalize the quality */
quality = CLAMP (quality, 0, 31) * 100 / 31;
}
mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (self), (guint32) quality);
}
static void
handle_mode_change (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data);
MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
char *str;
int a;
str = g_match_info_fetch (match_info, 1);
a = atoi (str);
g_free (str);
str = g_match_info_fetch (match_info, 2);
act = huawei_sysinfo_to_act (atoi (str));
g_free (str);
if (a == 3) { /* GSM/GPRS mode */
if (act > MM_MODEM_GSM_ACCESS_TECH_EDGE)
act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
} else if (a == 5) { /* WCDMA mode */
if (act < MM_MODEM_GSM_ACCESS_TECH_UMTS)
act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
} else if (a == 0)
act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
else {
mm_warn ("Couldn't parse mode change value: '%s'", str);
return;
}
mm_dbg ("Access Technology: %d", act);
mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (self), act);
}
static void
handle_status_change (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
char *str;
int n1, n2, n3, n4, n5, n6, n7;
str = g_match_info_fetch (match_info, 1);
if (sscanf (str, "%x,%x,%x,%x,%x,%x,%x", &n1, &n2, &n3, &n4, &n5, &n6, &n7)) {
mm_dbg ("Duration: %d Up: %d Kbps Down: %d Kbps Total: %d Total: %d\n",
n1, n2 * 8 / 1000, n3 * 8 / 1000, n4 / 1024, n5 / 1024);
}
g_free (str);
}
/*****************************************************************************/
static void
do_enable_power_up_done (MMGenericGsm *gsm,
GString *response,
GError *error,
MMCallbackInfo *info)
{
if (!error) {
MMAtSerialPort *primary;
/* Enable unsolicited result codes */
primary = mm_generic_gsm_get_at_port (gsm, MM_AT_PORT_FLAG_PRIMARY);
g_assert (primary);
mm_at_serial_port_queue_command (primary, "^PORTSEL=0", 5, NULL, NULL);
mm_at_serial_port_queue_command (primary, "^CURC=1", 5, NULL, NULL);
}
/* Chain up to parent */
MM_GENERIC_GSM_CLASS (mm_modem_huawei_gsm_parent_class)->do_enable_power_up_done (gsm, NULL, error, info);
}
/*****************************************************************************/
static void
disable_unsolicited_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
return;
/* Ignore all errors */
mm_callback_info_schedule (info);
}
static void
invoke_call_parent_disable_fn (MMCallbackInfo *info)
{
/* Note: we won't call the parent disable if info->modem is no longer
* valid. The invoke is called always once the info gets scheduled, which
* may happen during removed modem detection. */
if (info->modem) {
MMModem *parent_modem_iface;
parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem));
parent_modem_iface->disable (info->modem, (MMModemFn)info->callback, info->user_data);
}
}
static void
disable (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
MMAtSerialPort *primary;
MMCallbackInfo *info;
info = mm_callback_info_new_full (modem,
invoke_call_parent_disable_fn,
(GCallback)callback,
user_data);
primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_AT_PORT_FLAG_PRIMARY);
g_assert (primary);
/* Turn off unsolicited responses */
mm_at_serial_port_queue_command (primary, "^CURC=0", 5, disable_unsolicited_done, info);
}
/*****************************************************************************/
static void
port_grabbed (MMGenericGsm *gsm,
MMPort *port,
MMAtPortFlags pflags,
gpointer user_data)
{
GRegex *regex;
if (MM_IS_AT_SERIAL_PORT (port)) {
g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL);
regex = g_regex_new ("\\r\\n\\^RSSI:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_signal_quality_change, gsm, NULL);
g_regex_unref (regex);
regex = g_regex_new ("\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_mode_change, gsm, NULL);
g_regex_unref (regex);
regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_status_change, gsm, NULL);
g_regex_unref (regex);
regex = g_regex_new ("\\r\\n\\^BOOT:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, gsm, NULL);
g_regex_unref (regex);
}
}
/* Encode to packed GSM - this is what Huawei supports on all known models */
static char*
ussd_encode (MMModemGsmUssd *self, const char* command, guint *scheme)
{
char *hex;
guint8 *gsm, *packed;
guint32 len = 0, packed_len = 0;
*scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
gsm = mm_charset_utf8_to_unpacked_gsm (command, &len);
/* If command is a multiple of 7 characters long, Huawei firmwares
* apparently want that padded. Maybe all modems?
*/
if (len % 7 == 0) {
gsm = g_realloc (gsm, len + 1);
gsm[len] = 0x0d;
len++;
}
packed = gsm_pack (gsm, len, 0, &packed_len);
hex = utils_bin2hexstr (packed, packed_len);
g_free (packed);
g_free (gsm);
return hex;
}
/* Unparse packed gsm to utf8 */
static char*
ussd_decode (MMModemGsmUssd *self, const char* reply, guint scheme)
{
char *bin, *utf8;
guint8 *unpacked;
gsize bin_len;
guint32 unpacked_len;
bin = utils_hexstr2bin (reply, &bin_len);
unpacked = gsm_unpack ((guint8*) bin, (bin_len * 8) / 7, 0, &unpacked_len);
/* if the last character in a 7-byte block is padding, then drop it */
if ((bin_len % 7 == 0) && (unpacked[unpacked_len - 1] == 0x0d))
unpacked_len--;
utf8 = (char*) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
g_free (bin);
g_free (unpacked);
return utf8;
}
/*****************************************************************************/
static void
modem_init (MMModem *modem_class)
{
modem_class->disable = disable;
}
static void
modem_gsm_network_init (MMModemGsmNetwork *class)
{
class->set_band = set_band;
class->get_band = get_band;
}
static void
modem_gsm_card_init (MMModemGsmCard *class)
{
class->get_unlock_retries = get_unlock_retries;
}
static void
mm_modem_huawei_gsm_init (MMModemHuaweiGsm *self)
{
}
static void
modem_gsm_ussd_init (MMModemGsmUssd *ussd_class)
{
ussd_class->encode = ussd_encode;
ussd_class->decode = ussd_decode;
}
static void
mm_modem_huawei_gsm_class_init (MMModemHuaweiGsmClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass);
mm_modem_huawei_gsm_parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (object_class, sizeof (MMModemHuaweiGsmPrivate));
gsm_class->port_grabbed = port_grabbed;
gsm_class->set_allowed_mode = set_allowed_mode;
gsm_class->get_allowed_mode = get_allowed_mode;
gsm_class->get_access_technology = get_access_technology;
gsm_class->do_enable_power_up_done = do_enable_power_up_done;
}
-45
View File
@@ -1,45 +0,0 @@
/* -*- 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 Red Hat, Inc.
*/
#ifndef MM_MODEM_HUAWEI_GSM_H
#define MM_MODEM_HUAWEI_GSM_H
#include "mm-generic-gsm.h"
#define MM_TYPE_MODEM_HUAWEI_GSM (mm_modem_huawei_gsm_get_type ())
#define MM_MODEM_HUAWEI_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsm))
#define MM_MODEM_HUAWEI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmClass))
#define MM_IS_MODEM_HUAWEI_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_HUAWEI_GSM))
#define MM_IS_MODEM_HUAWEI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_HUAWEI_GSM))
#define MM_MODEM_HUAWEI_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmClass))
typedef struct {
MMGenericGsm parent;
} MMModemHuaweiGsm;
typedef struct {
MMGenericGsmClass parent;
} MMModemHuaweiGsmClass;
GType mm_modem_huawei_gsm_get_type (void);
MMModem *mm_modem_huawei_gsm_new (const char *device,
const char *driver,
const char *plugin,
guint32 vendor,
guint32 product);
#endif /* MM_MODEM_HUAWEI_GSM_H */
-332
View File
@@ -1,332 +0,0 @@
/* -*- 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 Red Hat, Inc.
*/
#include <string.h>
#include <stdlib.h>
#include <gmodule.h>
#include <errno.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
#include "mm-plugin-huawei.h"
#include "mm-generic-gsm.h"
#include "mm-generic-cdma.h"
#include "mm-modem-huawei-gsm.h"
#include "mm-modem-huawei-cdma.h"
#include "mm-serial-parsers.h"
#include "mm-at-serial-port.h"
#include "mm-log.h"
#include "mm-errors.h"
G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, MM_TYPE_PLUGIN_BASE)
int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_HUAWEI,
MM_PLUGIN_BASE_NAME, "Huawei",
NULL));
}
/*****************************************************************************/
#define TAG_HUAWEI_PCUI_PORT "huawei-pcui-port"
#define TAG_HUAWEI_MODEM_PORT "huawei-modem-port"
#define TAG_HUAWEI_DIAG_PORT "huawei-diag-port"
#define TAG_GETPORTMODE_SUPPORTED "getportmode-supported"
#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \
MM_PLUGIN_BASE_PORT_CAP_IS707_P | \
MM_PLUGIN_BASE_PORT_CAP_IS856 | \
MM_PLUGIN_BASE_PORT_CAP_IS856_A)
static guint32
get_level_for_capabilities (guint32 capabilities)
{
if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM)
return 10;
if (capabilities & CAP_CDMA)
return 10;
if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM)
return 10;
return 0;
}
static void
probe_result (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
guint32 capabilities,
gpointer user_data)
{
mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities));
}
static void
cache_port_mode (MMPlugin *plugin, const char *reply, const char *type, const char *tag)
{
char *p;
long i;
/* Get the USB interface number of the PCUI port */
p = strstr (reply, type);
if (p) {
errno = 0;
/* shift by 1 so NULL return from g_object_get_data() means no tag */
i = 1 + strtol (p + strlen (type), NULL, 10);
if (i > 0 && i < 256 && errno == 0)
g_object_set_data (G_OBJECT (plugin), tag, GINT_TO_POINTER ((int) i));
}
}
static gboolean
getportmode_response_cb (MMPluginBaseSupportsTask *task,
GString *response,
GError *error,
guint32 tries,
gboolean *out_stop,
guint32 *out_level,
gpointer user_data)
{
/* If any error occurred that was not ERROR or COMMAND NOT SUPPORT then
* retry the command.
*/
if (error) {
if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_UNKNOWN) == FALSE)
return tries <= 4 ? TRUE : FALSE;
} else {
MMPlugin *plugin = mm_plugin_base_supports_task_get_plugin (task);
cache_port_mode (plugin, response->str, "PCUI:", TAG_HUAWEI_PCUI_PORT);
cache_port_mode (plugin, response->str, "MDM:", TAG_HUAWEI_MODEM_PORT);
cache_port_mode (plugin, response->str, "DIAG:", TAG_HUAWEI_DIAG_PORT);
g_object_set_data (G_OBJECT (plugin), TAG_GETPORTMODE_SUPPORTED, GUINT_TO_POINTER (1));
}
/* No error or if ^GETPORTMODE is not supported, assume success */
return FALSE;
}
static gboolean
curc_response_cb (MMPluginBaseSupportsTask *task,
GString *response,
GError *error,
guint32 tries,
gboolean *out_stop,
guint32 *out_level,
gpointer user_data)
{
if (error)
return tries <= 4 ? TRUE : FALSE;
/* No error, assume success */
return FALSE;
}
static MMPluginSupportsResult
supports_port (MMPluginBase *base,
MMModem *existing,
MMPluginBaseSupportsTask *task)
{
GUdevDevice *port;
const char *subsys, *name, *driver;
int usbif;
guint16 vendor = 0, product = 0;
/* Can't do anything with non-serial ports */
port = mm_plugin_base_supports_task_get_port (task);
if (strcmp (g_udev_device_get_subsystem (port), "tty"))
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product))
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
if (vendor != 0x12d1)
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
/* The Gobi driver should claim Huawei Gobi modems */
driver = mm_plugin_base_supports_task_get_driver (task);
if (g_strcmp0 (driver, "qcserial") == 0)
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM");
if (usbif < 0)
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
/* 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.
*/
if (!existing && usbif != 0)
return MM_PLUGIN_SUPPORTS_PORT_DEFER;
/* Check if a previous probing was already launched in this port */
if (mm_plugin_base_supports_task_propagate_cached (task)) {
guint32 level;
/* A previous probing was already done, use its results */
level = get_level_for_capabilities (mm_plugin_base_supports_task_get_probed_capabilities (task));
if (level) {
mm_plugin_base_supports_task_complete (task, level);
return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS;
}
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
}
/* Turn off unsolicited messages on secondary ports until needed,
* and try to get a port map from the modem. The response will
* get handled in custom_init_response().
*/
if (usbif == 0) {
mm_plugin_base_supports_task_add_custom_init_command (task,
"AT^CURC=0",
3, /* delay */
curc_response_cb,
NULL);
mm_plugin_base_supports_task_add_custom_init_command (task,
"AT^GETPORTMODE",
3, /* delay */
getportmode_response_cb,
NULL);
}
/* Kick off a probe */
if (mm_plugin_base_probe_port (base, task, 100000, NULL))
return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS;
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
}
static MMModem *
grab_port (MMPluginBase *base,
MMModem *existing,
MMPluginBaseSupportsTask *task,
GError **error)
{
GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *devfile, *sysfs_path;
guint32 caps;
guint16 vendor = 0, product = 0;
MMPortType ptype;
int usbif;
MMAtPortFlags pflags = MM_AT_PORT_FLAG_NONE;
port = mm_plugin_base_supports_task_get_port (task);
g_assert (port);
devfile = g_udev_device_get_device_file (port);
if (!devfile) {
g_set_error (error, 0, 0, "Could not get port's sysfs file.");
return NULL;
}
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) {
g_set_error (error, 0, 0, "Could not get modem product ID.");
return NULL;
}
usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM");
if (usbif < 0) {
g_set_error (error, 0, 0, "Could not get USB device interface number.");
return NULL;
}
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
ptype = mm_plugin_base_probed_capabilities_to_port_type (caps);
if (usbif + 1 == GPOINTER_TO_INT (g_object_get_data (G_OBJECT (base), TAG_HUAWEI_PCUI_PORT)))
pflags = MM_AT_PORT_FLAG_PRIMARY;
else if (usbif + 1 == GPOINTER_TO_INT (g_object_get_data (G_OBJECT (base), TAG_HUAWEI_MODEM_PORT)))
pflags = MM_AT_PORT_FLAG_PPP;
else if (!g_object_get_data (G_OBJECT (base), TAG_GETPORTMODE_SUPPORTED)) {
/* If GETPORTMODE is not supported, we assume usbif 0 is the modem port */
if ((usbif == 0) && (ptype == MM_PORT_TYPE_AT)) {
pflags = MM_AT_PORT_FLAG_PPP;
/* For CDMA modems we assume usbif0 is both primary and PPP, since
* they don't have problems with talking on secondary ports.
*/
if (caps & CAP_CDMA)
pflags |= MM_AT_PORT_FLAG_PRIMARY;
}
}
sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
modem = mm_modem_huawei_gsm_new (sysfs_path,
mm_plugin_base_supports_task_get_driver (task),
mm_plugin_get_name (MM_PLUGIN (base)),
vendor,
product);
} else if (caps & CAP_CDMA) {
modem = mm_modem_huawei_cdma_new (sysfs_path,
mm_plugin_base_supports_task_get_driver (task),
mm_plugin_get_name (MM_PLUGIN (base)),
!!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856),
!!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A),
vendor,
product);
}
if (modem) {
if (!mm_modem_grab_port (modem, subsys, name, ptype, pflags, NULL, error)) {
g_object_unref (modem);
return NULL;
}
}
} else {
modem = existing;
if (!mm_modem_grab_port (modem, subsys, name, ptype, pflags, NULL, error))
return NULL;
}
return modem;
}
/*****************************************************************************/
static void
mm_plugin_huawei_init (MMPluginHuawei *self)
{
g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL);
}
static void
mm_plugin_huawei_class_init (MMPluginHuaweiClass *klass)
{
MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass);
pb_class->supports_port = supports_port;
pb_class->grab_port = grab_port;
}
-42
View File
@@ -1,42 +0,0 @@
/* -*- 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 Red Hat, Inc.
*/
#ifndef MM_PLUGIN_HUAWEI_H
#define MM_PLUGIN_HUAWEI_H
#include "mm-plugin.h"
#include "mm-plugin-base.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 {
MMPluginBase parent;
} MMPluginHuawei;
typedef struct {
MMPluginBaseClass parent;
} MMPluginHuaweiClass;
GType mm_plugin_huawei_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_HUAWEI_H */