summaryrefslogtreecommitdiffstats
path: root/gatchat/gatmux.c
diff options
context:
space:
mode:
authorDenis Kenzior <denkenz@gmail.com>2009-10-14 15:50:55 -0500
committerDenis Kenzior <denkenz@gmail.com>2009-10-15 16:15:17 -0500
commit112d07e14e26cf4905375547848efa7d72bee979 (patch)
tree8034f2f3251916f9ab7f4bacb900075a792b3a42 /gatchat/gatmux.c
parent90bdd961a7ee433b279653e35d187a7d1ed742e7 (diff)
downloadofono-112d07e14e26cf4905375547848efa7d72bee979.tar.bz2
Refactor: Add driver model to GAtMux
GAtMux can now be made to work with multiple multiplexing protocols. Currently on the 27.010 (07.10) Advanced and Basic modes are supported. However, further protocol support can be added by providing the necessary driver functions for GAtMux
Diffstat (limited to 'gatchat/gatmux.c')
-rw-r--r--gatchat/gatmux.c451
1 files changed, 425 insertions, 26 deletions
diff --git a/gatchat/gatmux.c b/gatchat/gatmux.c
index e2385c6e..424a5743 100644
--- a/gatchat/gatmux.c
+++ b/gatchat/gatmux.c
@@ -3,6 +3,7 @@
* AT chat library with GLib integration
*
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
+ * Copyright (C) 2009 Trolltech ASA.
*
* 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
@@ -27,13 +28,13 @@
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
-#include <termios.h>
+#include <alloca.h>
#include <glib.h>
#include "ringbuffer.h"
-#include "gsm0710.h"
#include "gatmux.h"
+#include "gsm0710.h"
/* #define DBG(fmt, arg...) g_print("%s: " fmt "\n" , __func__ , ## arg) */
#define DBG(fmt, arg...)
@@ -43,9 +44,17 @@ static const char *none_prefix[] = { NULL };
typedef struct _GAtMuxChannel GAtMuxChannel;
typedef struct _GAtMuxWatch GAtMuxWatch;
+typedef void (*GAtMuxWriteFrame)(GAtMux *mux, guint8 dlc, guint8 control,
+ const guint8 *data, int len);
-#define MAX_CHANNELS 63
+/* While 63 channels are theoretically possible, channel 62 and 63 is reserved
+ * by 27.010 for use as the beginning of frame and end of frame flags.
+ * Refer to Section 5.6 in 27.007
+ */
+#define MAX_CHANNELS 61
#define BITMAP_SIZE 8
+#define MUX_CHANNEL_BUFFER_SIZE 4096
+#define MUX_BUFFER_SIZE 4096
struct _GAtMuxChannel
{
@@ -78,6 +87,8 @@ struct _GAtMux {
guint8 newdata[BITMAP_SIZE]; /* Channels that got new data */
const GAtMuxDriver *driver; /* Driver functions */
void *driver_data; /* Driver data */
+ char buf[MUX_BUFFER_SIZE]; /* Buffer on the main mux */
+ int buf_used; /* Bytes of buf being used */
};
struct mux_setup_data {
@@ -158,29 +169,56 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
{
GAtMux *mux = data;
int i;
+ GError *error = NULL;
+ GIOStatus status;
+ gsize bytes_read;
if (cond & G_IO_NVAL)
return FALSE;
DBG("received data");
- memset(mux->newdata, 0, BITMAP_SIZE);
+ bytes_read = 0;
+ status = g_io_channel_read_chars(mux->channel, mux->buf + mux->buf_used,
+ sizeof(mux->buf) - mux->buf_used,
+ &bytes_read, &error);
- if (mux->driver->ready_read)
- mux->driver->ready_read(mux);
+ mux->buf_used += bytes_read;
- for (i = 1; i <= MAX_CHANNELS; i++) {
- int offset = i / 8;
- int bit = i % 8;
+ if (bytes_read > 0 && mux->driver->feed_data) {
+ int nread;
- if (!(mux->newdata[offset] & (1 << bit)))
- continue;
+ memset(mux->newdata, 0, BITMAP_SIZE);
+
+ nread = mux->driver->feed_data(mux, mux->buf, mux->buf_used);
+ mux->buf_used -= nread;
+
+ if (mux->buf_used > 0)
+ memmove(mux->buf, mux->buf + nread, mux->buf_used);
+
+ 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]);
+ DBG("dispatching sources for channel: %p",
+ mux->dlcs[i-1]);
- dispatch_sources(mux->dlcs[i-1], G_IO_IN);
+ dispatch_sources(mux->dlcs[i-1], G_IO_IN);
+ }
}
+ if (cond & (G_IO_HUP | G_IO_ERR))
+ return FALSE;
+
+ if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
+ return FALSE;
+
+ if (mux->buf_used == sizeof(mux->buf))
+ return FALSE;
+
return TRUE;
}
@@ -252,18 +290,6 @@ static void wakeup_writer(GAtMux *mux)
(GDestroyNotify)write_watcher_destroy_notify);
}
-int g_at_mux_raw_read(GAtMux *mux, void *data, int toread)
-{
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_read;
-
- status = g_io_channel_read_chars(mux->channel, data, toread,
- &bytes_read, &error);
-
- return bytes_read;
-}
-
int g_at_mux_raw_write(GAtMux *mux, const void *data, int towrite)
{
GError *error = NULL;
@@ -638,7 +664,7 @@ GIOChannel *g_at_mux_create_channel(GAtMux *mux)
mux_channel->mux = mux;
mux_channel->dlc = i+1;
- mux_channel->buffer = ring_buffer_new(GSM0710_BUFFER_SIZE);
+ mux_channel->buffer = ring_buffer_new(MUX_CHANNEL_BUFFER_SIZE);
mux_channel->throttled = FALSE;
mux->dlcs[i] = mux_channel;
@@ -812,3 +838,376 @@ gboolean g_at_mux_setup_gsm0710(GAtChat *chat,
return FALSE;
}
+
+#define GSM0710_BUFFER_SIZE 4096
+
+struct gsm0710_data {
+ int frame_size;
+};
+
+/* Process an incoming GSM 07.10 packet */
+static gboolean gsm0710_packet(GAtMux *mux, int dlc, guint8 control,
+ const unsigned char *data, int len,
+ GAtMuxWriteFrame write_frame)
+{
+ if (control == 0xEF || control == 0x03) {
+ if (dlc >= 1 && dlc <= 63) {
+ g_at_mux_feed_dlc_data(mux, dlc, data, len);
+ return TRUE;
+ }
+
+ if (dlc == 0) {
+ /* An embedded command or response on channel 0 */
+ if (len >= 2 && data[0] == GSM0710_STATUS_SET) {
+ return gsm0710_packet(mux, dlc,
+ GSM0710_STATUS_ACK,
+ data + 2, len - 2,
+ write_frame);
+ } else if (len >= 2 && data[0] == 0x43) {
+ /* Test command from other side - send the same bytes back */
+ unsigned char *resp = alloca(len);
+ memcpy(resp, data, len);
+ resp[0] = 0x41; /* Clear the C/R bit in the response */
+ write_frame(mux, 0, GSM0710_DATA, resp, len);
+ }
+ }
+ } else if (control == GSM0710_STATUS_ACK && dlc == 0) {
+ unsigned char resp[33];
+
+ /* Status change message */
+ if (len >= 2) {
+ /* Handle status changes on other channels */
+ dlc = ((data[0] & 0xFC) >> 2);
+
+ if (dlc >= 1 && dlc <= 63)
+ g_at_mux_set_dlc_status(mux, dlc, data[1]);
+ }
+
+ /* Send the response to the status change request to ACK it */
+ DBG("received status line signal, sending response");
+ if (len > 31)
+ len = 31;
+ resp[0] = GSM0710_STATUS_ACK;
+ resp[1] = ((len << 1) | 0x01);
+ memcpy(resp + 2, data, len);
+ write_frame(mux, 0, GSM0710_DATA, resp, len + 2);
+ }
+
+ return TRUE;
+}
+
+static void gsm0710_basic_write_frame(GAtMux *mux, guint8 dlc, guint8 control,
+ const guint8 *data, int towrite)
+{
+ struct gsm0710_data *gd = g_at_mux_get_data(mux);
+ guint8 *frame = alloca(gd->frame_size + 7);
+ int frame_size;
+
+ frame_size = gsm0710_basic_fill_frame(frame, dlc, control,
+ data, towrite);
+ g_at_mux_raw_write(mux, frame, frame_size);
+}
+
+#define COMPOSE_STATUS_FRAME(data, dlc, status) \
+ guint8 data[4]; \
+ data[0] = GSM0710_STATUS_SET; \
+ data[1] = 0x03; \
+ data[2] = ((dlc << 2) | 0x03); \
+ data[3] = status
+
+static void gsm0710_basic_remove(GAtMux *mux)
+{
+ struct gsm0710_data *gd = g_at_mux_get_data(mux);
+
+ g_free(gd);
+ g_at_mux_set_data(mux, NULL);
+}
+
+static gboolean gsm0710_basic_startup(GAtMux *mux)
+{
+ guint8 frame[6];
+ int frame_size;
+
+ frame_size = gsm0710_basic_fill_frame(frame, 0, GSM0710_OPEN_CHANNEL,
+ NULL, 0);
+ g_at_mux_raw_write(mux, frame, frame_size);
+
+ return TRUE;
+}
+
+static gboolean gsm0710_basic_shutdown(GAtMux *mux)
+{
+ guint8 frame[6];
+ int frame_size;
+
+ frame_size = gsm0710_basic_fill_frame(frame, 0, GSM0710_CLOSE_CHANNEL,
+ NULL, 0);
+ g_at_mux_raw_write(mux, frame, frame_size);
+
+ return TRUE;
+}
+
+static gboolean gsm0710_basic_open_dlc(GAtMux *mux, guint8 dlc)
+{
+ guint8 frame[6];
+ int frame_size;
+
+ frame_size = gsm0710_basic_fill_frame(frame, dlc, GSM0710_OPEN_CHANNEL,
+ NULL, 0);
+ g_at_mux_raw_write(mux, frame, frame_size);
+
+ return TRUE;
+}
+
+static gboolean gsm0710_basic_close_dlc(GAtMux *mux, guint8 dlc)
+{
+ guint8 frame[6];
+ int frame_size;
+
+ frame_size = gsm0710_basic_fill_frame(frame, dlc, GSM0710_CLOSE_CHANNEL,
+ NULL, 0);
+ g_at_mux_raw_write(mux, frame, frame_size);
+
+ return TRUE;
+}
+
+static int gsm0710_basic_feed_data(GAtMux *mux, void *data, int len)
+{
+ int total = 0;
+ int nread;
+ guint8 dlc;
+ guint8 ctrl;
+ guint8 *frame;
+ int frame_len;
+
+ do {
+ frame = NULL;
+ nread = gsm0710_basic_extract_frame(data, len, &dlc, &ctrl,
+ &frame, &frame_len);
+
+ total += nread;
+ data += nread;
+ len -= nread;
+
+ if (frame == NULL)
+ break;
+
+ gsm0710_packet(mux, dlc, ctrl, frame, frame_len,
+ gsm0710_basic_write_frame);
+ } while (nread > 0);
+
+ return total;
+}
+
+static void gsm0710_basic_set_status(GAtMux *mux, guint8 dlc, guint8 status)
+{
+ struct gsm0710_data *gd = g_at_mux_get_data(mux);
+ guint8 *frame = alloca(gd->frame_size + 7);
+ int frame_size;
+
+ COMPOSE_STATUS_FRAME(data, dlc, status);
+ frame_size = gsm0710_basic_fill_frame(frame, 0, GSM0710_DATA, data, 4);
+ g_at_mux_raw_write(mux, frame, frame_size);
+}
+
+static void gsm0710_basic_write(GAtMux *mux, guint8 dlc,
+ const void *data, int towrite)
+{
+ struct gsm0710_data *gd = g_at_mux_get_data(mux);
+ guint8 *frame = alloca(gd->frame_size + 7);
+ int max;
+ int frame_size;
+
+ while (towrite > 0) {
+ max = MIN(towrite, gd->frame_size);
+ frame_size = gsm0710_basic_fill_frame(frame, dlc,
+ GSM0710_DATA, data, max);
+ g_at_mux_raw_write(mux, frame, frame_size);
+ data = data + max;
+ towrite -= max;
+ }
+}
+
+static GAtMuxDriver gsm0710_basic_driver = {
+ .remove = gsm0710_basic_remove,
+ .startup = gsm0710_basic_startup,
+ .shutdown = gsm0710_basic_shutdown,
+ .open_dlc = gsm0710_basic_open_dlc,
+ .close_dlc = gsm0710_basic_close_dlc,
+ .feed_data = gsm0710_basic_feed_data,
+ .set_status = gsm0710_basic_set_status,
+ .write = gsm0710_basic_write,
+};
+
+GAtMux *g_at_mux_new_gsm0710_basic(GIOChannel *channel, int frame_size)
+{
+ GAtMux *mux;
+ struct gsm0710_data *gd;
+
+ mux = g_at_mux_new(channel, &gsm0710_basic_driver);
+
+ if (mux == NULL)
+ return NULL;
+
+ gd = g_new0(struct gsm0710_data, 1);
+ gd->frame_size = frame_size;
+
+ g_at_mux_set_data(mux, gd);
+
+ return mux;
+}
+
+static void gsm0710_advanced_write_frame(GAtMux *mux, guint8 dlc, guint8 control,
+ const guint8 *data, int towrite)
+{
+ struct gsm0710_data *gd = g_at_mux_get_data(mux);
+ guint8 *frame = alloca(gd->frame_size * 2 + 7);
+ int frame_size;
+
+ frame_size = gsm0710_advanced_fill_frame(frame, dlc, control,
+ data, towrite);
+ g_at_mux_raw_write(mux, frame, frame_size);
+}
+
+static void gsm0710_advanced_remove(GAtMux *mux)
+{
+ struct gsm0710_data *gd = g_at_mux_get_data(mux);
+
+ g_free(gd);
+ g_at_mux_set_data(mux, NULL);
+}
+
+static gboolean gsm0710_advanced_startup(GAtMux *mux)
+{
+ guint8 frame[8]; /* Account for escapes */
+ int frame_size;
+
+ frame_size = gsm0710_advanced_fill_frame(frame, 0,
+ GSM0710_OPEN_CHANNEL, NULL, 0);
+ g_at_mux_raw_write(mux, frame, frame_size);
+
+ return TRUE;
+}
+
+static gboolean gsm0710_advanced_shutdown(GAtMux *mux)
+{
+ guint8 frame[8]; /* Account for escapes */
+ int frame_size;
+
+ frame_size = gsm0710_advanced_fill_frame(frame, 0,
+ GSM0710_CLOSE_CHANNEL, NULL, 0);
+ g_at_mux_raw_write(mux, frame, frame_size);
+
+ return TRUE;
+}
+
+static gboolean gsm0710_advanced_open_dlc(GAtMux *mux, guint8 dlc)
+{
+ guint8 frame[8]; /* Account for escapes */
+ int frame_size;
+
+ frame_size = gsm0710_advanced_fill_frame(frame, dlc,
+ GSM0710_OPEN_CHANNEL, NULL, 0);
+ g_at_mux_raw_write(mux, frame, frame_size);
+
+ return TRUE;
+}
+
+static gboolean gsm0710_advanced_close_dlc(GAtMux *mux, guint8 dlc)
+{
+ guint8 frame[8]; /* Account for escapes */
+ int frame_size;
+
+ frame_size = gsm0710_advanced_fill_frame(frame, dlc,
+ GSM0710_CLOSE_CHANNEL, NULL, 0);
+ g_at_mux_raw_write(mux, frame, frame_size);
+
+ return TRUE;
+}
+
+static int gsm0710_advanced_feed_data(GAtMux *mux, void *data, int len)
+{
+ int total = 0;
+ int nread;
+ guint8 dlc;
+ guint8 ctrl;
+ guint8 *frame;
+ int frame_len;
+
+ do {
+ frame = NULL;
+ nread = gsm0710_advanced_extract_frame(data, len, &dlc, &ctrl,
+ &frame, &frame_len);
+
+ total += nread;
+ data += nread;
+ len -= nread;
+
+ if (frame == NULL)
+ break;
+
+ gsm0710_packet(mux, dlc, ctrl, frame, frame_len,
+ gsm0710_advanced_write_frame);
+ } while (nread > 0);
+
+ return total;
+}
+
+static void gsm0710_advanced_set_status(GAtMux *mux, guint8 dlc, guint8 status)
+{
+ struct gsm0710_data *gd = g_at_mux_get_data(mux);
+ guint8 *frame = alloca(gd->frame_size * 2 + 7);
+ int frame_size;
+
+ COMPOSE_STATUS_FRAME(data, dlc, status);
+ frame_size = gsm0710_advanced_fill_frame(frame, 0,
+ GSM0710_DATA, data, 4);
+ g_at_mux_raw_write(mux, frame, frame_size);
+}
+
+static void gsm0710_advanced_write(GAtMux *mux, guint8 dlc,
+ const void *data, int towrite)
+{
+ struct gsm0710_data *gd = g_at_mux_get_data(mux);
+ guint8 *frame = alloca(gd->frame_size * 2 + 7);
+ int max;
+ int frame_size;
+
+ while (towrite > 0) {
+ max = MIN(towrite, gd->frame_size);
+ frame_size = gsm0710_advanced_fill_frame(frame, dlc,
+ GSM0710_DATA, data, max);
+ g_at_mux_raw_write(mux, frame, frame_size);
+ data = data + max;
+ towrite -= max;
+ }
+}
+
+static GAtMuxDriver gsm0710_advanced_driver = {
+ .remove = gsm0710_advanced_remove,
+ .startup = gsm0710_advanced_startup,
+ .shutdown = gsm0710_advanced_shutdown,
+ .open_dlc = gsm0710_advanced_open_dlc,
+ .close_dlc = gsm0710_advanced_close_dlc,
+ .feed_data = gsm0710_advanced_feed_data,
+ .set_status = gsm0710_advanced_set_status,
+ .write = gsm0710_advanced_write,
+};
+
+GAtMux *g_at_mux_new_gsm0710_advanced(GIOChannel *channel, int frame_size)
+{
+ GAtMux *mux;
+ struct gsm0710_data *gd;
+
+ mux = g_at_mux_new(channel, &gsm0710_advanced_driver);
+
+ if (mux == NULL)
+ return NULL;
+
+ gd = g_new0(struct gsm0710_data, 1);
+ gd->frame_size = frame_size;
+
+ g_at_mux_set_data(mux, gd);
+
+ return mux;
+}