From 5fc7c1e1d0b331c7198f49d1d0751a1e528aa263 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 27 Apr 2010 11:07:53 -0500 Subject: [PATCH] gatchat: Add initial GAtIO implementation --- Makefile.am | 1 + gatchat/gatio.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++ gatchat/gatio.h | 60 +++++++++++ 3 files changed, 326 insertions(+) create mode 100644 gatchat/gatio.c create mode 100644 gatchat/gatio.h diff --git a/Makefile.am b/Makefile.am index 86ccf1b5..463e52ec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,6 +55,7 @@ gatchat_sources = gatchat/gatchat.h gatchat/gatchat.c \ gatchat/gatresult.h gatchat/gatresult.c \ gatchat/gatsyntax.h gatchat/gatsyntax.c \ gatchat/ringbuffer.h gatchat/ringbuffer.c \ + gatchat/gatio.h gatchat/gatio.c \ gatchat/crc-ccitt.h gatchat/crc-ccitt.c \ gatchat/gatmux.h gatchat/gatmux.c \ gatchat/gsm0710.h gatchat/gsm0710.c \ diff --git a/gatchat/gatio.c b/gatchat/gatio.c new file mode 100644 index 00000000..964bc057 --- /dev/null +++ b/gatchat/gatio.c @@ -0,0 +1,265 @@ +/* + * + * AT chat library with GLib integration + * + * Copyright (C) 2008-2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "ringbuffer.h" +#include "gatio.h" +#include "gatutil.h" + +struct _GAtIO { + gint ref_count; /* Ref count */ + guint read_watch; /* GSource read id, 0 if no */ + GIOChannel *channel; /* comms channel */ + GAtDisconnectFunc user_disconnect; /* user disconnect func */ + gpointer user_disconnect_data; /* user disconnect data */ + struct ring_buffer *buf; /* Current read buffer */ + guint max_read_attempts; /* max reads / select */ + GAtIOReadFunc read_handler; /* Read callback */ + gpointer read_data; /* Read callback userdata */ + GAtDebugFunc debugf; /* debugging output function */ + gpointer debug_data; /* Data to pass to debug func */ + gboolean destroyed; /* Re-entrancy guard */ +}; + +static void read_watcher_destroy_notify(gpointer user_data) +{ + GAtIO *io = user_data; + + ring_buffer_free(io->buf); + io->buf = NULL; + + io->channel = NULL; + io->read_watch = 0; + + if (io->user_disconnect) + io->user_disconnect(io->user_disconnect_data); + + if (io->destroyed) + g_free(io); +} + +static gboolean received_data(GIOChannel *channel, GIOCondition cond, + gpointer data) +{ + unsigned char *buf; + GAtIO *io = data; + GIOError err; + gsize rbytes; + gsize toread; + gsize total_read = 0; + guint read_count = 0; + + if (cond & G_IO_NVAL) + return FALSE; + + /* Regardless of condition, try to read all the data available */ + do { + toread = ring_buffer_avail_no_wrap(io->buf); + + if (toread == 0) + break; + + rbytes = 0; + buf = ring_buffer_write_ptr(io->buf, 0); + + err = g_io_channel_read(channel, (char *) buf, toread, &rbytes); + g_at_util_debug_chat(TRUE, (char *)buf, rbytes, + io->debugf, io->debug_data); + + read_count++; + + total_read += rbytes; + + if (rbytes > 0) + ring_buffer_write_advance(io->buf, rbytes); + + } while (err == G_IO_ERROR_NONE && rbytes > 0 && + read_count < io->max_read_attempts); + + if (total_read > 0 && io->read_handler) + io->read_handler(io->buf, io->read_data); + + if (cond & (G_IO_HUP | G_IO_ERR)) + return FALSE; + + if (read_count > 0 && rbytes == 0 && err != G_IO_ERROR_AGAIN) + return FALSE; + + return TRUE; +} + +static GAtIO *create_io(GIOChannel *channel, GIOFlags flags) +{ + GAtIO *io; + + if (!channel) + return NULL; + + io = g_try_new0(GAtIO, 1); + if (!io) + return io; + + io->ref_count = 1; + io->debugf = NULL; + + if (flags & G_IO_FLAG_NONBLOCK) + io->max_read_attempts = 3; + else + io->max_read_attempts = 1; + + io->buf = ring_buffer_new(4096); + + if (!io->buf) + goto error; + + if (!g_at_util_setup_io(channel, flags)) + goto error; + + io->channel = channel; + io->read_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + received_data, io, + read_watcher_destroy_notify); + + return io; + +error: + if (io->buf) + ring_buffer_free(io->buf); + + g_free(io); + + return NULL; +} + +GAtIO *g_at_io_new(GIOChannel *channel) +{ + return create_io(channel, G_IO_FLAG_NONBLOCK); +} + +GAtIO *g_at_io_new_blocking(GIOChannel *channel) +{ + return create_io(channel, 0); +} + +GIOChannel *g_at_io_get_channel(GAtIO *io) +{ + if (io == NULL) + return NULL; + + return io->channel; +} + +gboolean g_at_io_set_read_handler(GAtIO *io, GAtIOReadFunc read_handler, + gpointer user_data) +{ + if (io == NULL) + return FALSE; + + io->read_handler = read_handler; + io->read_data = user_data; + + if (read_handler && ring_buffer_len(io->buf) > 0) + read_handler(io->buf, user_data); + + return TRUE; +} + +GAtIO *g_at_io_ref(GAtIO *io) +{ + if (io == NULL) + return NULL; + + g_atomic_int_inc(&io->ref_count); + + return io; +} + +void g_at_io_unref(GAtIO *io) +{ + gboolean is_zero; + + if (io == NULL) + return; + + is_zero = g_atomic_int_dec_and_test(&io->ref_count); + + if (is_zero == FALSE) + return; + + g_at_io_shutdown(io); + + /* glib delays the destruction of the watcher until it exits, this + * means we can't free the data just yet, even though we've been + * destroyed already. We have to wait until the read_watcher + * destroy function gets called + */ + if (io->read_watch != 0) + io->destroyed = TRUE; + else + g_free(io); +} + +gboolean g_at_io_shutdown(GAtIO *io) +{ + if (io->channel == NULL) + return FALSE; + + if (io->read_watch) + g_source_remove(io->read_watch); + + return TRUE; +} + +gboolean g_at_io_set_disconnect_function(GAtIO *io, + GAtDisconnectFunc disconnect, gpointer user_data) +{ + if (io == NULL) + return FALSE; + + io->user_disconnect = disconnect; + io->user_disconnect_data = user_data; + + return TRUE; +} + +gboolean g_at_io_set_debug(GAtIO *io, GAtDebugFunc func, gpointer user_data) +{ + if (io == NULL) + return FALSE; + + io->debugf = func; + io->debug_data = user_data; + + return TRUE; +} diff --git a/gatchat/gatio.h b/gatchat/gatio.h new file mode 100644 index 00000000..87eb633b --- /dev/null +++ b/gatchat/gatio.h @@ -0,0 +1,60 @@ +/* + * + * AT chat library with GLib integration + * + * Copyright (C) 2008-2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GATIO_H +#define __GATIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "gat.h" + +struct _GAtIO; + +typedef struct _GAtIO GAtIO; + +typedef void (*GAtIOReadFunc)(struct ring_buffer *buffer, gpointer user_data); +typedef void (*GAtIOWriteFunc)(gpointer user_data); + +GAtIO *g_at_io_new(GIOChannel *channel); +GAtIO *g_at_io_new_blocking(GIOChannel *channel); + +GIOChannel *g_at_io_get_channel(GAtIO *io); + +GAtIO *g_at_io_ref(GAtIO *io); +void g_at_io_unref(GAtIO *io); + +gboolean g_at_io_shutdown(GAtIO *io); + +gboolean g_at_io_set_read_handler(GAtIO *io, GAtIOReadFunc read_handler, + gpointer user_data); + +gboolean g_at_io_set_disconnect_function(GAtIO *io, + GAtDisconnectFunc disconnect, gpointer user_data); + +gboolean g_at_io_set_debug(GAtIO *io, GAtDebugFunc func, gpointer user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* __GATIO_H */