summaryrefslogtreecommitdiffstats
path: root/gatchat/gatmux.c
diff options
context:
space:
mode:
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;
+}