summaryrefslogtreecommitdiffstats
path: root/gatchat/gsm0710.c
diff options
context:
space:
mode:
Diffstat (limited to 'gatchat/gsm0710.c')
-rw-r--r--gatchat/gsm0710.c612
1 files changed, 234 insertions, 378 deletions
diff --git a/gatchat/gsm0710.c b/gatchat/gsm0710.c
index 996a887e..9a4b3d8e 100644
--- a/gatchat/gsm0710.c
+++ b/gatchat/gsm0710.c
@@ -24,63 +24,11 @@
#include <config.h>
#endif
-#include <alloca.h>
#include <string.h>
-#include <stdio.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);
-}
+#include <glib.h>
-/* 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);
-}
+#include "gsm0710.h"
static const unsigned char crc_table[256] = {
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75,
@@ -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;
- }
- while (len > 0) {
- 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;
- } 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);
+ return 0xff - gsm0710_crc(data, len);
}
-/* 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)
+static inline gboolean gsm0710_check_fcs(const guint8 *data, int len,
+ guint8 cfcs)
{
- /* Discard any data in the buffer, in case of restart */
- ctx->buffer_used = 0;
+ guint8 fcs = gsm0710_crc(data, len);
+
+ fcs = crc_table[fcs ^ cfcs];
- /* Open the control channel */
- gsm0710_write_frame(ctx, 0, GSM0710_OPEN_CHANNEL, NULL, 0);
+ if (fcs == 0xcf)
+ return TRUE;
- return 1;
+ return FALSE;
}
-/* Shut down the GSM 07.10 session, closing all channels */
-void gsm0710_shutdown(struct gsm0710_context *ctx)
+int gsm0710_advanced_extract_frame(guint8 *buf, int len,
+ guint8 *out_dlc, guint8 *out_control,
+ guint8 **out_frame, int *out_len)
{
- int channel;
+ int posn = 0;
+ int posn2;
+ int framelen;
+ guint8 dlc;
+ guint8 control;
+
+ while (posn < len) {
+ if (buf[posn] != 0x7E) {
+ posn += 1;
+ continue;
+ }
- for (channel = 1; channel <= GSM0710_MAX_CHANNELS; ++channel) {
- if (is_channel_used(ctx, channel) == 0)
+ /* 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;
+ }
- gsm0710_write_frame(ctx, channel,
- GSM0710_CLOSE_CHANNEL, NULL, 0);
- }
+ /* Undo control byte quoting in the packet */
+ posn2 = 0;
+ ++posn;
+ while (posn < framelen) {
+ if (buf[posn] == 0x7D) {
+ ++posn;
- gsm0710_write_frame(ctx, 0, GSM0710_CLOSE_CHANNEL, NULL, 0);
- memset(ctx->used_channels, 0, sizeof(ctx->used_channels));
-}
+ if (posn >= framelen)
+ break;
-/* Open a specific channel. Returns non-zero if successful */
-int gsm0710_open_channel(struct gsm0710_context *ctx, int channel)
-{
- if (channel <= 0 || channel > GSM0710_MAX_CHANNELS)
- return 0; /* Invalid channel number */
+ buf[posn2++] = buf[posn++] ^ 0x20;
+ } else {
+ buf[posn2++] = buf[posn++];
+ }
+ }
- if (is_channel_used(ctx, channel))
- return 1; /* Channel is already open */
+ /* Validate the checksum on the packet header */
+ if (!gsm0710_check_fcs(buf, 2, buf[posn2 - 1]))
+ continue;
- mark_channel_used(ctx, channel);
+ /* Decode and dispatch the packet */
+ dlc = (buf[0] >> 2) & 0x3F;
+ control = buf[1] & 0xEF; /* Strip "PF" bit */
- gsm0710_write_frame(ctx, channel, GSM0710_OPEN_CHANNEL, NULL, 0);
+ if (out_frame)
+ *out_frame = buf + 2;
- return 1;
-}
+ if (out_len)
+ *out_len = posn2 - 3;
-/* 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 (out_dlc)
+ *out_dlc = dlc;
- if (!is_channel_used(ctx, channel))
- return; /* Channel is already closed */
+ if (out_control)
+ *out_control = control;
- mark_channel_unused(ctx, channel);
+ break;
+ }
- gsm0710_write_frame(ctx, channel, GSM0710_CLOSE_CHANNEL, NULL, 0);
+ return posn;
}
-/* Determine if a specific channel is open */
-int gsm0710_is_channel_open(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 */
- return is_channel_used(ctx, channel);
-}
+ int temp, crc;
+ int size;
-/* 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;
+ frame[0] = 0x7E;
+ frame[1] = ((dlc << 2) | 0x03);
+ frame[2] = type;
+
+ crc = gsm0710_fcs(frame + 1, 2);
+
+ /* The Address field might need to be escaped if this is a response
+ * frame
+ */
+
+ /* 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 {
+ size = 3;
}
- 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);
- }
+ while (len > 0) {
+ temp = *data++ & 0xFF;
+ --len;
+
+ if (temp != 0x7E && temp != 0x7D) {
+ frame[size++] = temp;
+ } else {
+ frame[size++] = 0x7D;
+ frame[size++] = (temp ^ 0x20);
}
+ }
- /* 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);
+ if (crc != 0x7E && crc != 0x7D) {
+ frame[size++] = crc;
+ } else {
+ frame[size++] = 0x7D;
+ frame[size++] = (crc ^ 0x20);
}
- return 1;
+ frame[size++] = 0x7E;
+
+ return size;
}
-/* 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 gsm0710_basic_extract_frame(guint8 *buf, int len,
+ guint8 *out_dlc, guint8 *out_control,
+ guint8 **out_frame, int *out_len)
{
- 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;
- }
+ int posn = 0;
+ int framelen;
+ int header_size;
+ guint8 fcs;
+ guint8 dlc;
+ guint8 type;
+
+ while (posn < len) {
+ if (buf[posn] != 0xF9) {
+ posn += 1;
+ continue;
+ }
- /* We need at least 4 bytes for the header */
- if ((posn + 4) > ctx->buffer_used)
- break;
+ /* Skip additional 0xF9 bytes between frames */
+ while ((posn + 1) < len && buf[posn + 1] == 0xF9)
+ posn += 1;
- /* 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;
- }
+ /* We need at least 4 bytes for the flag + header */
+ if ((posn + 4) > len)
+ break;
- /* 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)
+ /* 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;
- /* 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;
- }
+ framelen |= buf[posn + 4] << 7;
+ header_size = 4;
+ }
- /* 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 */
+ /* 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;
+ }
- /* 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;
+ if (buf[posn + header_size + framelen + 2] != 0xF9) {
+ posn += header_size + framelen + 2;
+ continue;
+ }
- } else if (ctx->buffer[posn] == 0x7E) {
+ /* Get the channel number and packet type from the header */
+ dlc = buf[posn + 1] >> 2;
+ type = buf[posn + 2] & 0xEF; /* Strip "PF" bit */
- /* Advanced format: skip additional 0x7E bytes between frames */
- while ((posn + 1) < ctx->buffer_used &&
- ctx->buffer[posn + 1] == 0x7E) {
- ++posn;
- }
+ if (out_frame)
+ *out_frame = buf + posn + 1 + header_size;
- /* 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;
- }
+ if (out_len)
+ *out_len = framelen;
- /* 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++];
- }
- }
+ if (out_dlc)
+ *out_dlc = dlc;
- /* 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;
- }
+ if (out_control)
+ *out_control = type;
- /* 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;
- }
+ posn += header_size + framelen + 2;
- } else {
- ++posn;
- }
- }
- if (posn < ctx->buffer_used) {
- memmove(ctx->buffer, ctx->buffer + posn,
- ctx->buffer_used - posn);
- ctx->buffer_used -= posn;
- } else {
- ctx->buffer_used = 0;
+ break;
}
+
+ return posn;
}
-/* 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 gsm0710_basic_fill_frame(guint8 *frame, guint8 dlc, guint8 type,
+ const guint8 *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;
+ 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;
}
-}
-/* Set the modem status lines on a channel */
-void gsm0710_set_status(struct gsm0710_context *ctx, int channel, int status)
-{
- 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);
+ 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;
}