summaryrefslogtreecommitdiffstats
path: root/gatchat/gatmux.c
diff options
context:
space:
mode:
authorDenis Kenzior <denkenz@gmail.com>2009-10-11 22:34:31 -0500
committerDenis Kenzior <denkenz@gmail.com>2009-10-15 16:15:16 -0500
commit5826fc9c15541000e9791a83f8932c7b4bef6d04 (patch)
tree0942cc2e1525bcb7b4880c16c2377958696258a6 /gatchat/gatmux.c
parentb1ff21f0592c10bda20fdb1c8d35a3d6bc60625c (diff)
downloadofono-5826fc9c15541000e9791a83f8932c7b4bef6d04.tar.bz2
Add ability to open/close multiple DLCs
Diffstat (limited to 'gatchat/gatmux.c')
-rw-r--r--gatchat/gatmux.c266
1 files changed, 235 insertions, 31 deletions
diff --git a/gatchat/gatmux.c b/gatchat/gatmux.c
index 2077babc..e100d30a 100644
--- a/gatchat/gatmux.c
+++ b/gatchat/gatmux.c
@@ -35,18 +35,27 @@
#include "gsm0710.h"
#include "gatmux.h"
+/* #define DBG(fmt, arg...) g_print("%s: " fmt "\n" , __func__ , ## arg) */
+#define DBG(fmt, arg...)
+
static const char *cmux_prefix[] = { "+CMUX:", NULL };
static const char *none_prefix[] = { NULL };
typedef struct _GAtMuxChannel GAtMuxChannel;
typedef struct _GAtMuxWatch GAtMuxWatch;
+#define MAX_CHANNELS 63
+#define BITMAP_SIZE 8
+
struct _GAtMuxChannel
{
GIOChannel channel;
GAtMux *mux;
GIOCondition condition;
struct ring_buffer *buffer;
+ GSList *sources;
+ gboolean throttled;
+ guint dlc;
};
struct _GAtMuxWatch
@@ -59,13 +68,14 @@ struct _GAtMuxWatch
struct _GAtMux {
gint ref_count; /* Ref count */
guint read_watch; /* GSource read id, 0 if none */
- GIOChannel *channel; /* channel */
+ guint write_watch; /* GSource write id, 0 if none */
+ GIOChannel *channel; /* main serial channel */
GAtDisconnectFunc user_disconnect; /* user disconnect func */
gpointer user_disconnect_data; /* user disconnect data */
GAtDebugFunc debugf; /* debugging output function */
gpointer debug_data; /* Data to pass to debug func */
-
- GAtMuxChannel *mux_channel;
+ GAtMuxChannel *dlcs[MAX_CHANNELS]; /* DLCs opened by the MUX */
+ guint8 newdata[BITMAP_SIZE]; /* Channels that got new data */
struct gsm0710_context ctx;
};
@@ -78,19 +88,167 @@ struct mux_setup_data {
guint frame_size;
};
+static void dispatch_sources(GAtMuxChannel *channel, GIOCondition condition)
+{
+ GAtMuxWatch *source;
+ GSList *c;
+ GSList *p;
+ GSList *t;
+
+ p = NULL;
+ c = channel->sources;
+
+ while (c) {
+ gboolean destroy = FALSE;
+
+ source = c->data;
+
+ DBG("Checking source: %p", source);
+
+ if (condition & source->condition) {
+ gpointer user_data = NULL;
+ GSourceFunc callback = NULL;
+ GSourceCallbackFuncs *cb_funcs;
+ gpointer cb_data;
+ gboolean (*dispatch) (GSource *, GSourceFunc, gpointer);
+
+ DBG("dispatching source: %p", source);
+
+ dispatch = source->source.source_funcs->dispatch;
+ cb_funcs = source->source.callback_funcs;
+ cb_data = source->source.callback_data;
+
+ if (cb_funcs)
+ cb_funcs->ref(cb_data);
+
+ if (cb_funcs)
+ cb_funcs->get(cb_data, (GSource *) source,
+ &callback, &user_data);
+
+ destroy = !dispatch((GSource *) source, callback,
+ user_data);
+
+ if (cb_funcs)
+ cb_funcs->unref(cb_data);
+ }
+
+ if (destroy) {
+ DBG("removing source: %p", source);
+
+ g_source_destroy((GSource *) source);
+
+ if (p)
+ p->next = c->next;
+ else
+ channel->sources = c->next;
+
+ t = c;
+ c = c->next;
+ g_slist_free_1(t);
+ } else {
+ p = c;
+ c = c->next;
+ }
+ }
+}
+
static gboolean received_data(GIOChannel *channel, GIOCondition cond,
gpointer data)
{
GAtMux *mux = data;
+ int i;
if (cond & G_IO_NVAL)
return FALSE;
+ DBG("received data");
+
+ memset(mux->newdata, 0, BITMAP_SIZE);
gsm0710_ready_read(&mux->ctx);
+ for (i = 1; i <= MAX_CHANNELS; i++) {
+ int offset = i / 8;
+ int bit = i % 8;
+
+ if (!(mux->newdata[offset] & (1 << bit)))
+ continue;
+
+ DBG("dispatching sources for channel: %p", mux->dlcs[i-1]);
+
+ dispatch_sources(mux->dlcs[i-1], G_IO_IN);
+ }
+
return TRUE;
}
+static void write_watcher_destroy_notify(GAtMux *mux)
+{
+ mux->write_watch = 0;
+}
+
+static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
+ gpointer data)
+{
+ GAtMux *mux = data;
+ int dlc;
+
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+ return FALSE;
+
+ DBG("Can write data");
+
+ for (dlc = 0; dlc < MAX_CHANNELS; dlc += 1) {
+ GAtMuxChannel *channel = mux->dlcs[dlc];
+
+ if (channel == NULL)
+ continue;
+
+ DBG("Checking channel for write: %p", channel);
+
+ if (channel->throttled)
+ continue;
+
+ DBG("Dispatching write sources: %p", channel);
+
+ dispatch_sources(channel, G_IO_OUT);
+ }
+
+ for (dlc = 0; dlc < MAX_CHANNELS; dlc += 1) {
+ GAtMuxChannel *channel = mux->dlcs[dlc];
+ GSList *l;
+ GAtMuxWatch *source;
+
+ if (channel == NULL)
+ continue;
+
+ if (channel->throttled)
+ continue;
+
+ for (l = channel->sources; l; l = l->next) {
+ source = l->data;
+
+ if (source->condition & G_IO_OUT)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void wakeup_writer(GAtMux *mux)
+{
+ if (mux->write_watch != 0)
+ return;
+
+ DBG("Waking up writer");
+
+ mux->write_watch = g_io_add_watch_full(mux->channel,
+ G_PRIORITY_DEFAULT,
+ G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ can_write_data, mux,
+ (GDestroyNotify)write_watcher_destroy_notify);
+}
+
static int do_read(struct gsm0710_context *ctx, void *data, int len)
{
GAtMux *mux = ctx->user_data;
@@ -118,34 +276,55 @@ static int do_write(struct gsm0710_context *ctx, const void *data, int len)
return bytes_written;
}
-static void deliver_data(struct gsm0710_context *ctx, int channel,
+static void deliver_data(struct gsm0710_context *ctx, int dlc,
const void *data, int len)
{
GAtMux *mux = ctx->user_data;
- GMainContext *context;
+ GAtMuxChannel *channel = mux->dlcs[dlc-1];
int written;
+ int offset;
+ int bit;
+
+ DBG("deliver_data: dlc: %d, channel: %p", dlc, channel);
+
+ if (channel == NULL)
+ return;
+
+ written = ring_buffer_write(channel->buffer, data, len);
- written = ring_buffer_write(mux->mux_channel->buffer, data, len);
if (written < 0)
return;
- context = g_main_context_default();
- g_main_context_wakeup(context);
+ offset = dlc / 8;
+ bit = dlc % 8;
+
+ mux->newdata[offset] |= 1 << bit;
+ channel->condition |= G_IO_IN;
}
static void deliver_status(struct gsm0710_context *ctx,
int channel, int status)
{
GAtMux *mux = ctx->user_data;
- GMainContext *context;
- if (status & GSM0710_RTS)
- mux->mux_channel->condition |= G_IO_OUT;
- else
- mux->mux_channel->condition &= ~G_IO_OUT;
+ DBG("Got status %d, for channel %d", status, channel);
- context = g_main_context_default();
- g_main_context_wakeup(context);
+ if (status & GSM0710_RTS) {
+ GSList *l;
+
+ mux->dlcs[channel-1]->throttled = FALSE;
+ DBG("setting throttled to FALSE");
+
+ for (l = mux->dlcs[channel-1]->sources; l; l = l->next) {
+ GAtMuxWatch *source = l->data;
+
+ if (source->condition & G_IO_OUT) {
+ wakeup_writer(mux);
+ break;
+ }
+ }
+ } else
+ mux->dlcs[channel-1]->throttled = TRUE;
}
static void debug_message(struct gsm0710_context *ctx, const char *msg)
@@ -273,25 +452,13 @@ gboolean g_at_mux_set_debug(GAtMux *mux, GAtDebugFunc func, gpointer user)
static gboolean watch_check(GSource *source)
{
- GAtMuxWatch *watch = (GAtMuxWatch *) source;
- GAtMuxChannel *channel = (GAtMuxChannel *) watch->channel;
-
- if (ring_buffer_len(channel->buffer) > 0)
- channel->condition |= G_IO_IN;
- else
- channel->condition &= ~G_IO_IN;
-
- if (channel->condition & watch->condition)
- return TRUE;
-
return FALSE;
}
static gboolean watch_prepare(GSource *source, gint *timeout)
{
*timeout = -1;
-
- return watch_check(source);
+ return FALSE;
}
static gboolean watch_dispatch(GSource *source, GSourceFunc callback,
@@ -342,7 +509,7 @@ static GIOStatus channel_write(GIOChannel *channel, const gchar *buf,
GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel;
GAtMux *mux = mux_channel->mux;
- gsm0710_write_data(&mux->ctx, 1, buf, count);
+ gsm0710_write_data(&mux->ctx, mux_channel->dlc, buf, count);
*bytes_written = count;
return G_IO_STATUS_NORMAL;
@@ -356,6 +523,16 @@ static GIOStatus channel_seek(GIOChannel *channel, gint64 offset,
static GIOStatus channel_close(GIOChannel *channel, GError **err)
{
+ GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel;
+ GAtMux *mux = mux_channel->mux;
+
+ DBG("closing channel: %d", mux_channel->dlc);
+
+ dispatch_sources(mux_channel, G_IO_NVAL);
+
+ gsm0710_close_channel(&mux->ctx, mux_channel->dlc);
+ mux->dlcs[mux_channel->dlc - 1] = NULL;
+
return G_IO_STATUS_NORMAL;
}
@@ -373,6 +550,8 @@ static GSource *channel_create_watch(GIOChannel *channel,
{
GSource *source;
GAtMuxWatch *watch;
+ GAtMuxChannel *dlc = (GAtMuxChannel *) channel;
+ GAtMux *mux = dlc->mux;
source = g_source_new(&watch_funcs, sizeof(GAtMuxWatch));
watch = (GAtMuxWatch *) source;
@@ -382,6 +561,16 @@ static GSource *channel_create_watch(GIOChannel *channel,
watch->condition = condition;
+ if ((watch->condition & G_IO_OUT) && dlc->throttled == FALSE)
+ wakeup_writer(mux);
+
+ DBG("Creating source: %p for channel: %p, writer: %d, reader: %d",
+ watch, channel,
+ condition & G_IO_OUT,
+ condition & G_IO_IN);
+
+ dlc->sources = g_slist_prepend(dlc->sources, watch);
+
return source;
}
@@ -413,11 +602,22 @@ GIOChannel *g_at_mux_create_channel(GAtMux *mux)
{
GAtMuxChannel *mux_channel;
GIOChannel *channel;
+ int i;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (mux->dlcs[i] == NULL)
+ break;
+ }
+
+ if (i == MAX_CHANNELS)
+ return NULL;
mux_channel = g_try_new0(GAtMuxChannel, 1);
if (mux_channel == NULL)
return NULL;
+ gsm0710_open_channel(&mux->ctx, i+1);
+
channel = (GIOChannel *) mux_channel;
g_io_channel_init(channel);
@@ -427,9 +627,13 @@ GIOChannel *g_at_mux_create_channel(GAtMux *mux)
channel->is_seekable = FALSE;
mux_channel->mux = mux;
- mux->mux_channel = mux_channel;
-
+ mux_channel->dlc = i+1;
mux_channel->buffer = ring_buffer_new(GSM0710_BUFFER_SIZE);
+ mux_channel->throttled = FALSE;
+
+ mux->dlcs[i] = mux_channel;
+
+ DBG("Created channel %p, dlc: %d", channel, i+1);
return channel;
}