aboutsummaryrefslogtreecommitdiff
path: root/src/mm-port-serial-qcdm.c
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2016-07-25 23:07:03 -0500
committerDan Williams <dcbw@redhat.com>2016-07-27 10:44:43 -0500
commit5350b9f568dc4375c874a3e3912458d512a3cbe4 (patch)
tree4f4929183f2ba3d1a7360ccce7149ed2d0555081 /src/mm-port-serial-qcdm.c
parent30f60de7d3f4d61a100ce1e6a01a746bd2409b81 (diff)
broadband-modem/libqcdm: add signal strength from QCDM EVDO Pilot Sets log messages
When a CDMA-only modem is registered with the EVDO network, its not possible to read signal strength in the following cases: 1) while a data connection is active on single-AT-port modems, because the AT port is used for PPP and not available for AT+CSQ, AT+CIND or vendor-specific signal strength commands 2) when the modem reports only CDMA 1x signal strength with AT+CSQ Now that we have a reasonable interpretation of RSSI from the QCDM EVDO Pilot Sets V2 log messgae, use that when other means of getting signal strength aren't available.
Diffstat (limited to 'src/mm-port-serial-qcdm.c')
-rw-r--r--src/mm-port-serial-qcdm.c160
1 files changed, 156 insertions, 4 deletions
diff --git a/src/mm-port-serial-qcdm.c b/src/mm-port-serial-qcdm.c
index 7732851f..e997bb06 100644
--- a/src/mm-port-serial-qcdm.c
+++ b/src/mm-port-serial-qcdm.c
@@ -26,10 +26,15 @@
#include "libqcdm/src/com.h"
#include "libqcdm/src/utils.h"
#include "libqcdm/src/errors.h"
+#include "libqcdm/src/dm-commands.h"
#include "mm-log.h"
G_DEFINE_TYPE (MMPortSerialQcdm, mm_port_serial_qcdm, MM_TYPE_PORT_SERIAL)
+struct _MMPortSerialQcdmPrivate {
+ GSList *unsolicited_msg_handlers;
+};
+
/*****************************************************************************/
static gboolean
@@ -60,10 +65,10 @@ find_qcdm_start (GByteArray *response, gsize *start)
}
static MMPortSerialResponseType
-parse_response (MMPortSerial *port,
- GByteArray *response,
- GByteArray **parsed_response,
- GError **error)
+parse_qcdm (GByteArray *response,
+ gboolean want_log,
+ GByteArray **parsed_response,
+ GError **error)
{
gsize start = 0;
gsize used = 0;
@@ -111,6 +116,14 @@ parse_response (MMPortSerial *port,
return MM_PORT_SERIAL_RESPONSE_NONE;
}
+ if (want_log && unescaped_buffer[0] != DIAG_CMD_LOG) {
+ /* If we only want log items and this isn't one, don't remove this
+ * DM packet from the buffer.
+ */
+ g_free (unescaped_buffer);
+ return MM_PORT_SERIAL_RESPONSE_NONE;
+ }
+
/* Successfully decapsulated the DM command. We'll build a new byte array
* with the response, and leave the input buffer cleaned up. */
g_assert (unescaped_len <= 1024);
@@ -124,6 +137,15 @@ parse_response (MMPortSerial *port,
return MM_PORT_SERIAL_RESPONSE_BUFFER;
}
+static MMPortSerialResponseType
+parse_response (MMPortSerial *port,
+ GByteArray *response,
+ GByteArray **parsed_response,
+ GError **error)
+{
+ return parse_qcdm (response, FALSE, parsed_response, error);
+}
+
/*****************************************************************************/
GByteArray *
@@ -202,6 +224,111 @@ debug_log (MMPortSerial *port, const char *prefix, const char *buf, gsize len)
/*****************************************************************************/
+typedef struct {
+ guint log_code;
+ MMPortSerialQcdmUnsolicitedMsgFn callback;
+ gboolean enable;
+ gpointer user_data;
+ GDestroyNotify notify;
+} MMQcdmUnsolicitedMsgHandler;
+
+static gint
+unsolicited_msg_handler_cmp (MMQcdmUnsolicitedMsgHandler *handler,
+ gpointer log_code)
+{
+ return handler->log_code - GPOINTER_TO_UINT (log_code);
+}
+
+void
+mm_port_serial_qcdm_add_unsolicited_msg_handler (MMPortSerialQcdm *self,
+ guint log_code,
+ MMPortSerialQcdmUnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ GSList *existing;
+ MMQcdmUnsolicitedMsgHandler *handler;
+
+ g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self));
+ g_return_if_fail (log_code > 0 && log_code <= G_MAXUINT16);
+
+ existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers,
+ GUINT_TO_POINTER (log_code),
+ (GCompareFunc)unsolicited_msg_handler_cmp);
+ if (existing) {
+ handler = existing->data;
+ /* We OVERWRITE any existing one, so if any context data existing, free it */
+ if (handler->notify)
+ handler->notify (handler->user_data);
+ } else {
+ handler = g_slice_new (MMQcdmUnsolicitedMsgHandler);
+ self->priv->unsolicited_msg_handlers = g_slist_append (self->priv->unsolicited_msg_handlers, handler);
+ handler->log_code = log_code;
+ }
+
+ handler->callback = callback;
+ handler->enable = TRUE;
+ handler->user_data = user_data;
+ handler->notify = notify;
+}
+
+void
+mm_port_serial_qcdm_enable_unsolicited_msg_handler (MMPortSerialQcdm *self,
+ guint log_code,
+ gboolean enable)
+{
+ GSList *existing;
+ MMQcdmUnsolicitedMsgHandler *handler;
+
+ g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self));
+ g_return_if_fail (log_code > 0 && log_code <= G_MAXUINT16);
+
+ existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers,
+ GUINT_TO_POINTER (log_code),
+ (GCompareFunc)unsolicited_msg_handler_cmp);
+ if (existing) {
+ handler = existing->data;
+ handler->enable = enable;
+ }
+}
+
+static void
+parse_unsolicited (MMPortSerial *port, GByteArray *response)
+{
+ MMPortSerialQcdm *self = MM_PORT_SERIAL_QCDM (port);
+ GByteArray *log_buffer = NULL;
+ GSList *iter;
+
+ if (parse_qcdm (response,
+ TRUE,
+ &log_buffer,
+ NULL) != MM_PORT_SERIAL_RESPONSE_BUFFER) {
+ return;
+ }
+
+ /* These should be guaranteed by parse_qcdm() */
+ g_return_if_fail (log_buffer);
+ g_return_if_fail (log_buffer->len > 0);
+ g_return_if_fail (log_buffer->data[0] == DIAG_CMD_LOG);
+
+ if (log_buffer->len < sizeof (DMCmdLog))
+ return;
+
+ for (iter = self->priv->unsolicited_msg_handlers; iter; iter = iter->next) {
+ MMQcdmUnsolicitedMsgHandler *handler = (MMQcdmUnsolicitedMsgHandler *) iter->data;
+ DMCmdLog *log_cmd = (DMCmdLog *) log_buffer->data;
+
+ if (!handler->enable)
+ continue;
+ if (handler->log_code != le16toh (log_cmd->log_code))
+ continue;
+ if (handler->callback)
+ handler->callback (self, log_buffer, handler->user_data);
+ }
+}
+
+/*****************************************************************************/
+
static gboolean
config_fd (MMPortSerial *port, int fd, GError **error)
{
@@ -250,14 +377,39 @@ mm_port_serial_qcdm_new_fd (int fd)
static void
mm_port_serial_qcdm_init (MMPortSerialQcdm *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_SERIAL_QCDM, MMPortSerialQcdmPrivate);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMPortSerialQcdm *self = MM_PORT_SERIAL_QCDM (object);
+
+ while (self->priv->unsolicited_msg_handlers) {
+ MMQcdmUnsolicitedMsgHandler *handler = (MMQcdmUnsolicitedMsgHandler *) self->priv->unsolicited_msg_handlers->data;
+
+ if (handler->notify)
+ handler->notify (handler->user_data);
+
+ g_slice_free (MMQcdmUnsolicitedMsgHandler, handler);
+ self->priv->unsolicited_msg_handlers = g_slist_delete_link (self->priv->unsolicited_msg_handlers,
+ self->priv->unsolicited_msg_handlers);
+ }
+
+ G_OBJECT_CLASS (mm_port_serial_qcdm_parent_class)->finalize (object);
}
static void
mm_port_serial_qcdm_class_init (MMPortSerialQcdmClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMPortSerialClass *port_class = MM_PORT_SERIAL_CLASS (klass);
+ g_type_class_add_private (object_class, sizeof (MMPortSerialQcdmPrivate));
+
/* Virtual methods */
+ object_class->finalize = finalize;
+ port_class->parse_unsolicited = parse_unsolicited;
port_class->parse_response = parse_response;
port_class->config_fd = config_fd;
port_class->debug_log = debug_log;