Files
ModemManager/src/mm-at-serial-port.c
T
Dan Williams 0108a36d27 serial: let port subclasses handle their own debug logging
We want to print out QCDM messages as hex, not ASCII.  So let
each port type print out it's own communication as it wants to.
2010-04-05 12:01:54 -07:00

359 lines
12 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 Red Hat, Inc.
*/
#define _GNU_SOURCE /* for strcasestr() */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "mm-at-serial-port.h"
#include "mm-errors.h"
#include "mm-options.h"
G_DEFINE_TYPE (MMAtSerialPort, mm_at_serial_port, MM_TYPE_SERIAL_PORT)
#define MM_AT_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AT_SERIAL_PORT, MMAtSerialPortPrivate))
typedef struct {
/* Response parser data */
MMAtSerialResponseParserFn response_parser_fn;
gpointer response_parser_user_data;
GDestroyNotify response_parser_notify;
GSList *unsolicited_msg_handlers;
} MMAtSerialPortPrivate;
/*****************************************************************************/
void
mm_at_serial_port_set_response_parser (MMAtSerialPort *self,
MMAtSerialResponseParserFn fn,
gpointer user_data,
GDestroyNotify notify)
{
MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
if (priv->response_parser_notify)
priv->response_parser_notify (priv->response_parser_user_data);
priv->response_parser_fn = fn;
priv->response_parser_user_data = user_data;
priv->response_parser_notify = notify;
}
static gboolean
parse_response (MMSerialPort *port, GByteArray *response, GError **error)
{
MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
gboolean found;
GString *string;
g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE);
/* Construct the string that AT-parsing functions expect */
string = g_string_sized_new (response->len + 1);
g_string_append_len (string, (const char *) response->data, response->len);
/* Parse it */
found = priv->response_parser_fn (priv->response_parser_user_data, string, error);
/* And copy it back into the response array after the parser has removed
* matches and cleaned it up.
*/
if (response->len)
g_byte_array_remove_range (response, 0, response->len);
g_byte_array_append (response, (const guint8 *) string->str, string->len);
g_string_free (string, TRUE);
return found;
}
static gsize
handle_response (MMSerialPort *port,
GByteArray *response,
GError *error,
GCallback callback,
gpointer callback_data)
{
MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
MMAtSerialResponseFn response_callback = (MMAtSerialResponseFn) callback;
GString *string;
/* Convert to a string and call the callback */
string = g_string_sized_new (response->len + 1);
g_string_append_len (string, (const char *) response->data, response->len);
response_callback (self, string, error, callback_data);
g_string_free (string, TRUE);
return response->len;
}
/*****************************************************************************/
typedef struct {
GRegex *regex;
MMAtSerialUnsolicitedMsgFn callback;
gpointer user_data;
GDestroyNotify notify;
} MMAtUnsolicitedMsgHandler;
void
mm_at_serial_port_add_unsolicited_msg_handler (MMAtSerialPort *self,
GRegex *regex,
MMAtSerialUnsolicitedMsgFn callback,
gpointer user_data,
GDestroyNotify notify)
{
MMAtUnsolicitedMsgHandler *handler;
MMAtSerialPortPrivate *priv;
g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
g_return_if_fail (regex != NULL);
handler = g_slice_new (MMAtUnsolicitedMsgHandler);
handler->regex = g_regex_ref (regex);
handler->callback = callback;
handler->user_data = user_data;
handler->notify = notify;
priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler);
}
static gboolean
remove_eval_cb (const GMatchInfo *match_info,
GString *result,
gpointer user_data)
{
int *result_len = (int *) user_data;
int start;
int end;
if (g_match_info_fetch_pos (match_info, 0, &start, &end))
*result_len -= (end - start);
return FALSE;
}
static void
parse_unsolicited (MMSerialPort *port, GByteArray *response)
{
MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) {
MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) iter->data;
GMatchInfo *match_info;
gboolean matches;
matches = g_regex_match_full (handler->regex,
(const char *) response->data,
response->len,
0, 0, &match_info, NULL);
if (handler->callback) {
while (g_match_info_matches (match_info)) {
handler->callback (self, match_info, handler->user_data);
g_match_info_next (match_info, NULL);
}
}
g_match_info_free (match_info);
if (matches) {
/* Remove matches */
char *str;
int result_len = response->len;
str = g_regex_replace_eval (handler->regex,
(const char *) response->data,
response->len,
0, 0,
remove_eval_cb, &result_len, NULL);
g_byte_array_remove_range (response, 0, response->len);
g_byte_array_append (response, (const guint8 *) str, result_len);
g_free (str);
}
}
}
/*****************************************************************************/
static GByteArray *
at_command_to_byte_array (const char *command)
{
GByteArray *buf;
int cmdlen;
g_return_val_if_fail (command != NULL, NULL);
cmdlen = strlen (command);
buf = g_byte_array_sized_new (cmdlen + 3);
/* Make sure there's an AT in the front */
if (!g_str_has_prefix (command, "AT"))
g_byte_array_append (buf, (const guint8 *) "AT", 2);
g_byte_array_append (buf, (const guint8 *) command, cmdlen);
/* Make sure there's a trailing carriage return */
if (command[cmdlen] != '\r')
g_byte_array_append (buf, (const guint8 *) "\r", 1);
return buf;
}
void
mm_at_serial_port_queue_command (MMAtSerialPort *self,
const char *command,
guint32 timeout_seconds,
MMAtSerialResponseFn callback,
gpointer user_data)
{
GByteArray *buf;
g_return_if_fail (self != NULL);
g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
g_return_if_fail (command != NULL);
buf = at_command_to_byte_array (command);
g_return_if_fail (buf != NULL);
mm_serial_port_queue_command (MM_SERIAL_PORT (self),
buf,
TRUE,
timeout_seconds,
(MMSerialResponseFn) callback,
user_data);
}
void
mm_at_serial_port_queue_command_cached (MMAtSerialPort *self,
const char *command,
guint32 timeout_seconds,
MMAtSerialResponseFn callback,
gpointer user_data)
{
GByteArray *buf;
g_return_if_fail (self != NULL);
g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
g_return_if_fail (command != NULL);
buf = at_command_to_byte_array (command);
g_return_if_fail (buf != NULL);
mm_serial_port_queue_command_cached (MM_SERIAL_PORT (self),
buf,
TRUE,
timeout_seconds,
(MMSerialResponseFn) callback,
user_data);
}
static void
debug_log (MMSerialPort *port, const char *prefix, const char *buf, gsize len)
{
static GString *debug = NULL;
const char *s;
if (!debug)
debug = g_string_sized_new (256);
g_string_append (debug, prefix);
g_string_append (debug, " '");
s = buf;
while (len--) {
if (g_ascii_isprint (*s))
g_string_append_c (debug, *s);
else if (*s == '\r')
g_string_append (debug, "<CR>");
else if (*s == '\n')
g_string_append (debug, "<LF>");
else
g_string_append_printf (debug, "\\%d", *s);
s++;
}
g_string_append_c (debug, '\'');
g_debug ("(%s): %s", mm_port_get_device (MM_PORT (port)), debug->str);
g_string_truncate (debug, 0);
}
/*****************************************************************************/
MMAtSerialPort *
mm_at_serial_port_new (const char *name, MMPortType ptype)
{
return MM_AT_SERIAL_PORT (g_object_new (MM_TYPE_AT_SERIAL_PORT,
MM_PORT_DEVICE, name,
MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY,
MM_PORT_TYPE, ptype,
NULL));
}
static void
mm_at_serial_port_init (MMAtSerialPort *self)
{
}
static void
finalize (GObject *object)
{
MMAtSerialPort *self = MM_AT_SERIAL_PORT (object);
MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
while (priv->unsolicited_msg_handlers) {
MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data;
if (handler->notify)
handler->notify (handler->user_data);
g_regex_unref (handler->regex);
g_slice_free (MMAtUnsolicitedMsgHandler, handler);
priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers,
priv->unsolicited_msg_handlers);
}
if (priv->response_parser_notify)
priv->response_parser_notify (priv->response_parser_user_data);
G_OBJECT_CLASS (mm_at_serial_port_parent_class)->finalize (object);
}
static void
mm_at_serial_port_class_init (MMAtSerialPortClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMSerialPortClass *port_class = MM_SERIAL_PORT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMAtSerialPortPrivate));
/* Virtual methods */
object_class->finalize = finalize;
port_class->parse_unsolicited = parse_unsolicited;
port_class->parse_response = parse_response;
port_class->handle_response = handle_response;
port_class->debug_log = debug_log;
}