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
This commit is contained in:
Denis Kenzior 2009-10-14 15:50:55 -05:00
parent 90bdd961a7
commit 112d07e14e
4 changed files with 711 additions and 485 deletions

View File

@ -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);
DBG("dispatching sources for channel: %p", mux->dlcs[i-1]);
nread = mux->driver->feed_data(mux, mux->buf, mux->buf_used);
mux->buf_used -= nread;
dispatch_sources(mux->dlcs[i-1], G_IO_IN);
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]);
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;
}

View File

@ -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
@ -48,9 +49,9 @@ struct _GAtMuxDriver {
gboolean (*shutdown)(GAtMux *mux);
gboolean (*open_dlc)(GAtMux *mux, guint8 dlc);
gboolean (*close_dlc)(GAtMux *mux, guint8 dlc);
void (*ready_read)(GAtMux *mux);
void (*set_status)(GAtMux *mux, guint8 dlc, int status);
void (*set_status)(GAtMux *mux, guint8 dlc, guint8 status);
void (*write)(GAtMux *mux, guint8 dlc, const void *data, int towrite);
int (*feed_data)(GAtMux *mux, void *data, int len);
};
GAtMux *g_at_mux_new(GIOChannel *channel, const GAtMuxDriver *driver);
@ -77,7 +78,6 @@ void g_at_mux_set_dlc_status(GAtMux *mux, guint8 dlc, int status);
void g_at_mux_feed_dlc_data(GAtMux *mux, guint8 dlc,
const void *data, int tofeed);
int g_at_mux_raw_read(GAtMux *mux, void *data, int toread);
int g_at_mux_raw_write(GAtMux *mux, const void *data, int towrite);
void g_at_mux_set_data(GAtMux *mux, void *data);

View File

@ -24,64 +24,12 @@
#include <config.h>
#endif
#include <alloca.h>
#include <string.h>
#include <stdio.h>
#include <glib.h>
#include "gsm0710.h"
/* Frame types and subtypes */
#define GSM0710_OPEN_CHANNEL 0x3F
#define GSM0710_CLOSE_CHANNEL 0x53
#define GSM0710_DATA 0xEF
#define GSM0710_DATA_ALT 0x03
#define GSM0710_STATUS_SET 0xE3
#define GSM0710_STATUS_ACK 0xE1
/* Initialize a GSM 07.10 context, in preparation for startup */
void gsm0710_initialize(struct gsm0710_context *ctx)
{
ctx->mode = GSM0710_MODE_BASIC;
ctx->frame_size = GSM0710_DEFAULT_FRAME_SIZE;
ctx->buffer_used = 0;
memset(ctx->used_channels, 0, sizeof(ctx->used_channels));
ctx->user_data = NULL;
ctx->read = NULL;
ctx->write = NULL;
ctx->deliver_data = NULL;
ctx->deliver_status = NULL;
ctx->debug_message = NULL;
ctx->packet_filter = NULL;
}
/* Determine if a channel is in use */
static int is_channel_used(struct gsm0710_context *ctx, int channel)
{
int index = channel / 32;
return ((ctx->used_channels[index] & (1L << (channel % 32))) != 0);
}
/* Mark a channel as used */
static void mark_channel_used(struct gsm0710_context *ctx, int channel)
{
int index = channel / 32;
ctx->used_channels[index] |= (1L << (channel % 32));
}
/* Mark a channel as unused */
static void mark_channel_unused(struct gsm0710_context *ctx, int channel)
{
int index = channel / 32;
ctx->used_channels[index] &= ~(1L << (channel % 32));
}
/* Write a debug message */
static void gsm0710_debug(struct gsm0710_context *ctx, const char *msg)
{
if (ctx->debug_message)
ctx->debug_message(ctx, msg);
}
static const unsigned char crc_table[256] = {
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75,
0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
@ -117,386 +65,294 @@ static const unsigned char crc_table[256] = {
0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
};
static unsigned char gsm0710_compute_crc(const unsigned char *data, int len)
static inline guint8 gsm0710_crc(const guint8 *data, int len)
{
int sum = 0xFF;
while (len > 0) {
sum = crc_table[(sum ^ *data++) & 0xFF];
--len;
}
return (~sum & 0xFF);
guint8 crc = 0xFF;
int i;
for (i = 0; i < len; i++)
crc = crc_table[crc ^ data[i]];
return crc;
}
/* Write a raw GSM 07.10 frame to the underlying device */
static void gsm0710_write_frame(struct gsm0710_context *ctx, int channel,
int type, const unsigned char *data, int len)
static inline guint8 gsm0710_fcs(const guint8 *data, int len)
{
unsigned char *frame = alloca(ctx->frame_size * 2 + 8);
int size;
if (len > ctx->frame_size)
len = ctx->frame_size;
if (ctx->mode) {
int temp, crc;
frame[0] = 0x7E;
frame[1] = ((channel << 2) | 0x03);
frame[2] = type;
crc = gsm0710_compute_crc(frame + 1, 2);
if (type == 0x7E || type == 0x7D) {
/* Need to quote the type field now that crc has been computed */
frame[2] = 0x7D;
frame[3] = (type ^ 0x20);
size = 4;
} else {
size = 3;
return 0xff - gsm0710_crc(data, len);
}
static inline gboolean gsm0710_check_fcs(const guint8 *data, int len,
guint8 cfcs)
{
guint8 fcs = gsm0710_crc(data, len);
fcs = crc_table[fcs ^ cfcs];
if (fcs == 0xcf)
return TRUE;
return FALSE;
}
int gsm0710_advanced_extract_frame(guint8 *buf, int len,
guint8 *out_dlc, guint8 *out_control,
guint8 **out_frame, int *out_len)
{
int posn = 0;
int posn2;
int framelen;
guint8 dlc;
guint8 control;
while (posn < len) {
if (buf[posn] != 0x7E) {
posn += 1;
continue;
}
while (len > 0) {
temp = *data++ & 0xFF;
--len;
if (temp != 0x7E && temp != 0x7D) {
frame[size++] = temp;
/* Skip additional 0x7E bytes between frames */
while ((posn + 1) < len && buf[posn + 1] == 0x7E)
posn += 1;
/* Search for the end of the packet (the next 0x7E byte) */
framelen = posn + 1;
while (framelen < len && buf[framelen] != 0x7E)
framelen += 1;
if (framelen >= len)
break;
if (framelen < 4) {
posn = framelen;
continue;
}
/* Undo control byte quoting in the packet */
posn2 = 0;
++posn;
while (posn < framelen) {
if (buf[posn] == 0x7D) {
++posn;
if (posn >= framelen)
break;
buf[posn2++] = buf[posn++] ^ 0x20;
} else {
frame[size++] = 0x7D;
frame[size++] = (temp ^ 0x20);
buf[posn2++] = buf[posn++];
}
}
if (crc != 0x7E && crc != 0x7D) {
frame[size++] = crc;
} else {
frame[size++] = 0x7D;
frame[size++] = (crc ^ 0x20);
}
frame[size++] = 0x7E;
} else {
int header_size;
frame[0] = 0xF9;
frame[1] = ((channel << 2) | 0x03);
frame[2] = type;
if (len <= 127) {
frame[3] = ((len << 1) | 0x01);
header_size = size = 4;
} else {
frame[3] = (len << 1);
frame[4] = (len >> 7);
header_size = size = 5;
}
if (len > 0) {
memcpy(frame + size, data, len);
size += len;
}
/* Note: GSM 07.10 says that the CRC is only computed over the header */
frame[size++] = gsm0710_compute_crc(frame + 1, header_size - 1);
frame[size++] = 0xF9;
}
if (ctx->write)
ctx->write(ctx, frame, size);
}
/* Start up the GSM 07.10 session on the underlying device.
The underlying device is assumed to already be in
multiplexing mode. Returns zero on failure */
int gsm0710_startup(struct gsm0710_context *ctx)
{
/* Discard any data in the buffer, in case of restart */
ctx->buffer_used = 0;
/* Open the control channel */
gsm0710_write_frame(ctx, 0, GSM0710_OPEN_CHANNEL, NULL, 0);
return 1;
}
/* Shut down the GSM 07.10 session, closing all channels */
void gsm0710_shutdown(struct gsm0710_context *ctx)
{
int channel;
for (channel = 1; channel <= GSM0710_MAX_CHANNELS; ++channel) {
if (is_channel_used(ctx, channel) == 0)
/* Validate the checksum on the packet header */
if (!gsm0710_check_fcs(buf, 2, buf[posn2 - 1]))
continue;
gsm0710_write_frame(ctx, channel,
GSM0710_CLOSE_CHANNEL, NULL, 0);
/* Decode and dispatch the packet */
dlc = (buf[0] >> 2) & 0x3F;
control = buf[1] & 0xEF; /* Strip "PF" bit */
if (out_frame)
*out_frame = buf + 2;
if (out_len)
*out_len = posn2 - 3;
if (out_dlc)
*out_dlc = dlc;
if (out_control)
*out_control = control;
break;
}
gsm0710_write_frame(ctx, 0, GSM0710_CLOSE_CHANNEL, NULL, 0);
memset(ctx->used_channels, 0, sizeof(ctx->used_channels));
return posn;
}
/* Open a specific channel. Returns non-zero if successful */
int gsm0710_open_channel(struct gsm0710_context *ctx, int channel)
int gsm0710_advanced_fill_frame(guint8 *frame, guint8 dlc, guint8 type,
const guint8 *data, int len)
{
if (channel <= 0 || channel > GSM0710_MAX_CHANNELS)
return 0; /* Invalid channel number */
int temp, crc;
int size;
if (is_channel_used(ctx, channel))
return 1; /* Channel is already open */
frame[0] = 0x7E;
frame[1] = ((dlc << 2) | 0x03);
frame[2] = type;
mark_channel_used(ctx, channel);
crc = gsm0710_fcs(frame + 1, 2);
gsm0710_write_frame(ctx, channel, GSM0710_OPEN_CHANNEL, NULL, 0);
/* The Address field might need to be escaped if this is a response
* frame
*/
return 1;
}
/* Close a specific channel */
void gsm0710_close_channel(struct gsm0710_context *ctx, int channel)
{
if (channel <= 0 || channel > GSM0710_MAX_CHANNELS)
return; /* Invalid channel number */
if (!is_channel_used(ctx, channel))
return; /* Channel is already closed */
mark_channel_unused(ctx, channel);
gsm0710_write_frame(ctx, channel, GSM0710_CLOSE_CHANNEL, NULL, 0);
}
/* Determine if a specific channel is open */
int gsm0710_is_channel_open(struct gsm0710_context *ctx, int channel)
{
if (channel <= 0 || channel > GSM0710_MAX_CHANNELS)
return 0; /* Invalid channel number */
return is_channel_used(ctx, channel);
}
/* Process an incoming GSM 07.10 packet */
static int gsm0710_packet(struct gsm0710_context *ctx, int channel, int type,
const unsigned char *data, int len)
{
if (ctx->packet_filter &&
ctx->packet_filter(ctx, channel, type, data, len)) {
/* The filter has extracted and processed the packet */
return 1;
}
if (type == 0xEF || type == 0x03) {
if (channel >= 1 && channel <= GSM0710_MAX_CHANNELS &&
is_channel_used(ctx, channel)) {
/* Ordinary data packet */
if (ctx->deliver_data)
ctx->deliver_data(ctx, channel, data, len);
} else if (channel == 0) {
/* An embedded command or response on channel 0 */
if (len >= 2 && data[0] == GSM0710_STATUS_SET) {
return gsm0710_packet(ctx, channel,
GSM0710_STATUS_ACK,
data + 2, len - 2);
} 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 */
gsm0710_write_frame(ctx, 0, GSM0710_DATA,
resp, len);
}
}
} else if (type == GSM0710_STATUS_ACK && channel == 0) {
unsigned char resp[33];
/* Status change message */
if (len >= 2) {
/* Handle status changes on other channels */
channel = ((data[0] & 0xFC) >> 2);
if (channel >= 1 && channel <= GSM0710_MAX_CHANNELS &&
is_channel_used(ctx, channel)) {
if (ctx->deliver_status)
ctx->deliver_status(ctx, channel,
data[1] & 0xFF);
}
}
/* Send the response to the status change request to ACK it */
gsm0710_debug(ctx,
"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);
gsm0710_write_frame(ctx, 0, GSM0710_DATA, resp, len + 2);
}
return 1;
}
/* Function that is called when the underlying device is ready to be read.
A callback will be made to ctx->read to get the data for processing */
void gsm0710_ready_read(struct gsm0710_context *ctx)
{
int len, posn, posn2, header_size, channel, type;
/* Read more data from the underlying serial device */
if (!ctx->read)
return;
len = ctx->read(ctx, ctx->buffer + ctx->buffer_used,
sizeof(ctx->buffer) - ctx->buffer_used);
if (len <= 0)
return;
/* Update the buffer size */
ctx->buffer_used += len;
/* Break the incoming data up into packets */
posn = 0;
while (posn < ctx->buffer_used) {
if (ctx->buffer[posn] == 0xF9) {
/* Basic format: skip additional 0xF9 bytes between frames */
while ((posn + 1) < ctx->buffer_used &&
ctx->buffer[posn + 1] == 0xF9) {
++posn;
}
/* We need at least 4 bytes for the header */
if ((posn + 4) > ctx->buffer_used)
break;
/* The low bit of the second byte should be 1,
which indicates a short channel number */
if ((ctx->buffer[posn + 1] & 0x01) == 0) {
++posn;
continue;
}
/* Get the packet length and validate it */
len = (ctx->buffer[posn + 3] >> 1) & 0x7F;
if ((ctx->buffer[posn + 3] & 0x01) != 0) {
/* Single-byte length indication */
header_size = 3;
} else {
/* Double-byte length indication */
if ((posn + 5) > ctx->buffer_used)
break;
len |= ((int)(ctx->buffer[posn + 4])) << 7;
header_size = 4;
}
if ((posn + header_size + 2 + len) > ctx->buffer_used)
break;
/* Verify the packet header checksum */
if (((gsm0710_compute_crc(ctx->buffer + posn + 1,
header_size) ^ ctx->buffer[posn + len + header_size + 1]) & 0xFF) != 0) {
gsm0710_debug(ctx,
"*** GSM 07.10 checksum check failed ***");
posn += len + header_size + 2;
continue;
}
/* Get the channel number and packet type from the header */
channel = (ctx->buffer[posn + 1] >> 2) & 0x3F;
type = ctx->buffer[posn + 2] & 0xEF; /* Strip "PF" bit */
/* Dispatch data packets to the appropriate channel */
if (!gsm0710_packet(ctx, channel, type,
ctx->buffer + posn + header_size + 1, len)) {
/* Session has been terminated */
ctx->buffer_used = 0;
return;
}
posn += len + header_size + 2;
} else if (ctx->buffer[posn] == 0x7E) {
/* Advanced format: skip additional 0x7E bytes between frames */
while ((posn + 1) < ctx->buffer_used &&
ctx->buffer[posn + 1] == 0x7E) {
++posn;
}
/* Search for the end of the packet (the next 0x7E byte) */
len = posn + 1;
while (len < ctx->buffer_used &&
ctx->buffer[len] != 0x7E) {
++len;
}
if (len >= ctx->buffer_used) {
/* There are insufficient bytes for a packet at present */
if (posn == 0 && len >= (int)sizeof(ctx->buffer)) {
/* The buffer is full and we were unable to find a
legitimate packet. Discard the buffer and restart */
posn = len;
}
break;
}
/* Undo control byte quoting in the packet */
posn2 = 0;
++posn;
while (posn < len) {
if (ctx->buffer[posn] == 0x7D) {
++posn;
if (posn >= len)
break;
ctx->buffer[posn2++] = (ctx->buffer[posn++] ^ 0x20);
} else {
ctx->buffer[posn2++] = ctx->buffer[posn++];
}
}
/* Validate the checksum on the packet header */
if (posn2 >= 3) {
if (((gsm0710_compute_crc(ctx->buffer, 2) ^
ctx->buffer[posn2 - 1]) & 0xFF) != 0) {
gsm0710_debug(ctx,
"*** GSM 07.10 advanced checksum "
"check failed ***");
continue;
}
} else {
gsm0710_debug(ctx,
"*** GSM 07.10 advanced packet "
"is too small ***");
continue;
}
/* Decode and dispatch the packet */
channel = (ctx->buffer[0] >> 2) & 0x3F;
type = ctx->buffer[1] & 0xEF; /* Strip "PF" bit */
if (!gsm0710_packet(ctx, channel, type,
ctx->buffer + 2, posn2 - 3)) {
/* Session has been terminated */
ctx->buffer_used = 0;
return;
}
} else {
++posn;
}
}
if (posn < ctx->buffer_used) {
memmove(ctx->buffer, ctx->buffer + posn,
ctx->buffer_used - posn);
ctx->buffer_used -= posn;
/* Need to quote the type field now that crc has been computed */
if (type == 0x7E || type == 0x7D) {
frame[2] = 0x7D;
frame[3] = (type ^ 0x20);
size = 4;
} else {
ctx->buffer_used = 0;
size = 3;
}
}
/* Write a block of data to the the underlying device. It will be split
into several frames according to the frame size, if necessary */
void gsm0710_write_data(struct gsm0710_context *ctx, int channel,
const void *data, int len)
{
int temp;
while (len > 0) {
temp = len;
if (temp > ctx->frame_size)
temp = ctx->frame_size;
gsm0710_write_frame(ctx, channel, GSM0710_DATA, data, temp);
data = ((const unsigned char *) data) + temp;
len -= temp;
temp = *data++ & 0xFF;
--len;
if (temp != 0x7E && temp != 0x7D) {
frame[size++] = temp;
} else {
frame[size++] = 0x7D;
frame[size++] = (temp ^ 0x20);
}
}
if (crc != 0x7E && crc != 0x7D) {
frame[size++] = crc;
} else {
frame[size++] = 0x7D;
frame[size++] = (crc ^ 0x20);
}
frame[size++] = 0x7E;
return size;
}
/* Set the modem status lines on a channel */
void gsm0710_set_status(struct gsm0710_context *ctx, int channel, int status)
int gsm0710_basic_extract_frame(guint8 *buf, int len,
guint8 *out_dlc, guint8 *out_control,
guint8 **out_frame, int *out_len)
{
unsigned char data[4];
data[0] = GSM0710_STATUS_SET;
data[1] = 0x03;
data[2] = ((channel << 2) | 0x03);
data[3] = status;
gsm0710_write_frame(ctx, 0, GSM0710_DATA, data, 4);
int posn = 0;
int framelen;
int header_size;
guint8 fcs;
guint8 dlc;
guint8 type;
while (posn < len) {
if (buf[posn] != 0xF9) {
posn += 1;
continue;
}
/* Skip additional 0xF9 bytes between frames */
while ((posn + 1) < len && buf[posn + 1] == 0xF9)
posn += 1;
/* We need at least 4 bytes for the flag + header */
if ((posn + 4) > len)
break;
/* The low bit of the second byte should be 1,
which indicates a short channel number. According to
27.010 Section 5.2.3, if this is not true, then
the frame is invalid and should be discarded
*/
if ((buf[posn + 1] & 0x01) == 0) {
++posn;
continue;
}
/* Get the packet length and validate it */
framelen = buf[posn + 3] >> 1;
if ((buf[posn + 3] & 0x01) != 0) {
/* Single-byte length indication */
header_size = 3;
} else {
/* Double-byte length indication */
if ((posn + 5) > len)
break;
framelen |= buf[posn + 4] << 7;
header_size = 4;
}
/* Total size of the packet is the flag + 3 or 4 byte header
* Address Control Length followed by Information and FCS.
* However, we must check the presence of the end flag
* according to 27.010 Section 5.2.3
*/
if ((posn + header_size + 3 + framelen) > len)
break;
fcs = buf[posn + 1 + header_size + framelen];
/*
* The end flag is not guaranteed to be only ours
* according to 27.010 Section 5.2.6.1:
* "The closing flag may also be the opening flag of the
* following frame", thus we do not consume it in the following
* stages
*/
/*
* If FCS is invalid, discard the packet in accordance to
* Section 5.2.3 of 27.010
*/
if (!gsm0710_check_fcs(buf + posn + 1, header_size, fcs)) {
posn += header_size + framelen + 2;
continue;
}
if (buf[posn + header_size + framelen + 2] != 0xF9) {
posn += header_size + framelen + 2;
continue;
}
/* Get the channel number and packet type from the header */
dlc = buf[posn + 1] >> 2;
type = buf[posn + 2] & 0xEF; /* Strip "PF" bit */
if (out_frame)
*out_frame = buf + posn + 1 + header_size;
if (out_len)
*out_len = framelen;
if (out_dlc)
*out_dlc = dlc;
if (out_control)
*out_control = type;
posn += header_size + framelen + 2;
break;
}
return posn;
}
int gsm0710_basic_fill_frame(guint8 *frame, guint8 dlc, guint8 type,
const guint8 *data, int len)
{
int size;
int header_size;
frame[0] = 0xF9;
frame[1] = ((dlc << 2) | 0x03);
frame[2] = type;
if (len <= 127) {
frame[3] = ((len << 1) | 0x01);
header_size = 4;
} else {
frame[3] = (len << 1);
frame[4] = (len >> 7);
header_size = 5;
}
size = header_size;
if (len > 0) {
memcpy(frame + header_size, data, len);
size += len;
}
/* Note: GSM 07.10 says that the CRC is only computed over the header */
frame[size++] = gsm0710_fcs(frame + 1, header_size - 1);
frame[size++] = 0xF9;
return size;
}

View File

@ -20,63 +20,34 @@
*
*/
#ifndef GSM0710_P_H
#define GSM0710_P_H
#ifndef __GSM0710_H
#define __GSM0710_H
#ifdef __cplusplus
extern "C" {
#endif
#define GSM0710_BUFFER_SIZE 4096
#define GSM0710_DEFAULT_FRAME_SIZE 31
#define GSM0710_MAX_CHANNELS 63
/* Frame types and subtypes */
#define GSM0710_OPEN_CHANNEL 0x3F
#define GSM0710_CLOSE_CHANNEL 0x53
#define GSM0710_DATA 0xEF
#define GSM0710_DATA_ALT 0x03
#define GSM0710_STATUS_SET 0xE3
#define GSM0710_STATUS_ACK 0xE1
/* Multiplexer modes */
#define GSM0710_MODE_BASIC 0
#define GSM0710_MODE_ADVANCED 1
int gsm0710_basic_extract_frame(guint8 *data, int len,
guint8 *out_dlc, guint8 *out_type,
guint8 **frame, int *out_len);
/* Status flags */
#define GSM0710_FC 0x02
#define GSM0710_DTR 0x04
#define GSM0710_DSR 0x04
#define GSM0710_RTS 0x08
#define GSM0710_CTS 0x08
#define GSM0710_DCD 0x80
int gsm0710_basic_fill_frame(guint8 *frame, guint8 dlc, guint8 type,
const guint8 *data, int len);
struct gsm0710_context
{
/* GSM 07.10 implementation details */
int mode;
int frame_size;
int port_speed;
unsigned char buffer[GSM0710_BUFFER_SIZE];
int buffer_used;
unsigned long used_channels[(GSM0710_MAX_CHANNELS + 31) / 32];
/* Hooks to other levels */
void *user_data;
int (*read)(struct gsm0710_context *ctx, void *data, int len);
int (*write)(struct gsm0710_context *ctx, const void *data, int len);
void (*deliver_data)(struct gsm0710_context *ctx, int channel,
const void *data, int len);
void (*deliver_status)(struct gsm0710_context *ctx,
int channel, int status);
void (*debug_message)(struct gsm0710_context *ctx, const char *msg);
int (*packet_filter)(struct gsm0710_context *ctx, int channel,
int type, const unsigned char *data, int len);
};
void gsm0710_initialize(struct gsm0710_context *ctx);
int gsm0710_startup(struct gsm0710_context *ctx);
void gsm0710_shutdown(struct gsm0710_context *ctx);
int gsm0710_open_channel(struct gsm0710_context *ctx, int channel);
void gsm0710_close_channel(struct gsm0710_context *ctx, int channel);
int gsm0710_is_channel_open(struct gsm0710_context *ctx, int channel);
void gsm0710_ready_read(struct gsm0710_context *ctx);
void gsm0710_write_data(struct gsm0710_context *ctx, int channel,
const void *data, int len);
void gsm0710_set_status(struct gsm0710_context *ctx, int channel, int status);
int gsm0710_advanced_extract_frame(guint8 *data, int len,
guint8 *out_dlc, guint8 *out_type,
guint8 **frame, int *out_len);
int gsm0710_advanced_fill_frame(guint8 *frame, guint8 dlc, guint8 type,
const guint8 *data, int len);
#ifdef __cplusplus
};
#endif