Use a ring buffer for non-blocking HDLC output streams

This commit is contained in:
Marcel Holtmann 2010-04-11 07:01:14 +02:00
parent 6ddf874318
commit 46e77907b3
2 changed files with 94 additions and 26 deletions

View File

@ -36,7 +36,9 @@ struct _GAtHDLC {
gint ref_count; gint ref_count;
GIOChannel *channel; GIOChannel *channel;
guint read_watch; guint read_watch;
guint write_watch;
struct ring_buffer *read_buffer; struct ring_buffer *read_buffer;
struct ring_buffer *write_buffer;
guint max_read_attempts; guint max_read_attempts;
unsigned char *decode_buffer; unsigned char *decode_buffer;
guint decode_offset; guint decode_offset;
@ -47,13 +49,6 @@ struct _GAtHDLC {
gpointer debug_data; gpointer debug_data;
}; };
static void read_watch_destroy(gpointer user_data)
{
GAtHDLC *hdlc = user_data;
hdlc->read_watch = 0;
}
static void new_bytes(GAtHDLC *hdlc) static void new_bytes(GAtHDLC *hdlc)
{ {
unsigned int len = ring_buffer_len(hdlc->read_buffer); unsigned int len = ring_buffer_len(hdlc->read_buffer);
@ -144,6 +139,13 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
return TRUE; return TRUE;
} }
static void read_watch_destroy(gpointer user_data)
{
GAtHDLC *hdlc = user_data;
hdlc->read_watch = 0;
}
GAtHDLC *g_at_hdlc_new(GIOChannel *channel) GAtHDLC *g_at_hdlc_new(GIOChannel *channel)
{ {
GAtHDLC *hdlc; GAtHDLC *hdlc;
@ -164,6 +166,10 @@ GAtHDLC *g_at_hdlc_new(GIOChannel *channel)
if (!hdlc->read_buffer) if (!hdlc->read_buffer)
goto error; goto error;
hdlc->write_buffer = ring_buffer_new(BUFFER_SIZE * 2);
if (!hdlc->write_buffer)
goto error;
hdlc->decode_buffer = g_try_malloc(BUFFER_SIZE * 2); hdlc->decode_buffer = g_try_malloc(BUFFER_SIZE * 2);
if (!hdlc->decode_buffer) if (!hdlc->decode_buffer)
goto error; goto error;
@ -185,6 +191,9 @@ error:
if (hdlc->read_buffer) if (hdlc->read_buffer)
ring_buffer_free(hdlc->read_buffer); ring_buffer_free(hdlc->read_buffer);
if (hdlc->write_buffer)
ring_buffer_free(hdlc->write_buffer);
if (hdlc->decode_buffer) if (hdlc->decode_buffer)
g_free(hdlc->decode_buffer); g_free(hdlc->decode_buffer);
@ -217,6 +226,7 @@ void g_at_hdlc_unref(GAtHDLC *hdlc)
g_io_channel_unref(hdlc->channel); g_io_channel_unref(hdlc->channel);
ring_buffer_free(hdlc->read_buffer); ring_buffer_free(hdlc->read_buffer);
ring_buffer_free(hdlc->write_buffer);
g_free(hdlc->decode_buffer); g_free(hdlc->decode_buffer);
} }
@ -239,6 +249,59 @@ void g_at_hdlc_set_receive(GAtHDLC *hdlc, GAtReceiveFunc func,
hdlc->receive_data = user_data; hdlc->receive_data = user_data;
} }
static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
gpointer user_data)
{
GAtHDLC *hdlc = user_data;
GIOError err;
unsigned int len;
unsigned char *buf;
gsize bytes_written;
if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
return FALSE;
len = ring_buffer_len_no_wrap(hdlc->write_buffer);
buf = ring_buffer_read_ptr(hdlc->write_buffer, 0);
err = g_io_channel_write(hdlc->channel, (const char *) buf,
len, &bytes_written);
if (err != G_IO_ERROR_NONE) {
g_source_remove(hdlc->read_watch);
return FALSE;
}
g_at_util_debug_dump(FALSE, buf, bytes_written,
hdlc->debugf, hdlc->debug_data);
ring_buffer_drain(hdlc->write_buffer, bytes_written);
if (ring_buffer_len(hdlc->write_buffer) > 0)
return TRUE;
return FALSE;
}
static void write_watch_destroy(gpointer user_data)
{
GAtHDLC *hdlc = user_data;
hdlc->write_watch = 0;
}
static void wakeup_write(GAtHDLC *hdlc)
{
GIOChannel *channel = hdlc->channel;
if (hdlc->write_watch > 0)
return;
hdlc->write_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
can_write_data, hdlc, write_watch_destroy);
}
static inline void hdlc_put(GAtHDLC *hdlc, guint8 *buf, gsize *pos, guint8 c) static inline void hdlc_put(GAtHDLC *hdlc, guint8 *buf, gsize *pos, guint8 c)
{ {
gsize i = *pos; gsize i = *pos;
@ -252,31 +315,36 @@ static inline void hdlc_put(GAtHDLC *hdlc, guint8 *buf, gsize *pos, guint8 c)
*pos = i; *pos = i;
} }
gboolean g_at_hdlc_send(GAtHDLC *hdlc, const unsigned char *buf, gsize len) gboolean g_at_hdlc_send(GAtHDLC *hdlc, const unsigned char *data, gsize size)
{ {
unsigned char newbuf[BUFFER_SIZE * 2]; unsigned char *buf;
GIOError err; unsigned int space, i = 0;
gsize bytes_written;
gsize pos = 0, i = 0;
guint16 fcs = 0xffff; guint16 fcs = 0xffff;
gsize pos;
newbuf[pos++] = 0x7e; do {
space = ring_buffer_avail_no_wrap(hdlc->write_buffer);
if (space == 0)
break;
while (len--) { buf = ring_buffer_write_ptr(hdlc->write_buffer);
fcs = crc_ccitt_byte(fcs, buf[i]); pos = 0;
hdlc_put(hdlc, newbuf, &pos, buf[i++]);
}
fcs ^= 0xffff; while (size--) {
hdlc_put(hdlc, newbuf, &pos, fcs & 0xff); fcs = crc_ccitt_byte(fcs, data[i]);
hdlc_put(hdlc, newbuf, &pos, fcs >> 8); hdlc_put(hdlc, buf, &pos, data[i++]);
}
newbuf[pos++] = 0x7e; fcs ^= 0xffff;
hdlc_put(hdlc, buf, &pos, fcs & 0xff);
hdlc_put(hdlc, buf, &pos, fcs >> 8);
err = g_io_channel_write(hdlc->channel, (const char *) newbuf, buf[pos++] = 0x7e;
pos, &bytes_written);
g_at_util_debug_dump(FALSE, newbuf, bytes_written, ring_buffer_write_advance(hdlc->write_buffer, pos);
hdlc->debugf, hdlc->debug_data); } while (0);
wakeup_write(hdlc);
return TRUE; return TRUE;
} }

View File

@ -41,7 +41,7 @@ void g_at_hdlc_set_debug(GAtHDLC *hdlc, GAtDebugFunc func, gpointer user_data);
void g_at_hdlc_set_receive(GAtHDLC *hdlc, GAtReceiveFunc func, void g_at_hdlc_set_receive(GAtHDLC *hdlc, GAtReceiveFunc func,
gpointer user_data); gpointer user_data);
gboolean g_at_hdlc_send(GAtHDLC *hdlc, const unsigned char *buf, gsize len); gboolean g_at_hdlc_send(GAtHDLC *hdlc, const unsigned char *data, gsize size);
#ifdef __cplusplus #ifdef __cplusplus
} }