diff options
Diffstat (limited to 'gatchat/gatmux.c')
-rw-r--r-- | gatchat/gatmux.c | 451 |
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; +} |