asterisk-chan-dongle/at_read.c

237 lines
4.7 KiB
C

/*
Copyright (C) 2009 - 2010
Artem Makhutov <artem@makhutov.org>
http://www.makhutov.org
Dmitry Vagin <dmitry2004@yandex.ru>
Copyright (C) 2010 - 2011
bg <bg_one@mail.ru>
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* vasprintf() in asterisk/utils.h */
#endif /* #ifndef _GNU_SOURCE */
#include "ast_config.h"
#include <sys/types.h>
#include <errno.h>
#include <asterisk/channel.h> /* ast_waitfor_n_fd() */
#include <asterisk/logger.h> /* ast_debug() */
#include "chan_dongle.h"
#include "at_read.h"
#include "ringbuffer.h"
/*!
* \brief Wait for activity on an socket
* \param fd -- file descriptor
* \param ms -- pointer to an int containing a timeout in ms
* \return 0 on timeout and the socket fd (non-zero) otherwise
* \retval 0 timeout
*/
EXPORT_DEF int at_wait (int fd, int* ms)
{
int exception, outfd;
outfd = ast_waitfor_n_fd (&fd, 1, ms, &exception);
if (outfd < 0)
{
outfd = 0;
}
return outfd;
}
#/* return number of bytes read */
EXPORT_DEF ssize_t at_read (int fd, const char * dev, struct ringbuffer* rb)
{
struct iovec iov[2];
int iovcnt;
ssize_t n = -1;
/* TODO: read until major error */
iovcnt = rb_write_iov (rb, iov);
if (iovcnt > 0)
{
n = readv (fd, iov, iovcnt);
if (n < 0)
{
if (errno != EINTR && errno != EAGAIN)
{
ast_debug (1, "[%s] readv() error: %d\n", dev, errno);
return n;
}
return 0;
}
else if (n > 0)
{
rb_write_upd (rb, n);
ast_debug (5, "[%s] receive %zu byte, used %zu, free %zu, read %zu, write %zu\n",
dev, n, rb_used (rb), rb_free (rb), rb->read, rb->write);
iovcnt = rb_read_all_iov (rb, iov);
if (iovcnt > 0)
{
if (iovcnt == 2)
{
ast_debug (5, "[%s] [%.*s%.*s]\n", dev,
(int) iov[0].iov_len, (char*) iov[0].iov_base,
(int) iov[1].iov_len, (char*) iov[1].iov_base);
}
else
{
ast_debug (5, "[%s] [%.*s]\n", dev,
(int) iov[0].iov_len, (char*) iov[0].iov_base);
}
}
}
}
else
ast_log (LOG_ERROR, "[%s] at cmd receive buffer overflow\n", dev);
return n;
}
EXPORT_DEF int at_read_result_iov (const char * dev, int * read_result, struct ringbuffer* rb, struct iovec iov[2])
{
int iovcnt = 0;
int res;
size_t s;
s = rb_used (rb);
if (s > 0)
{
/* ast_debug (5, "[%s] d_read_result %d len %d input [%.*s]\n", dev, *read_result, s, MIN(s, rb->size - rb->read), (char*)rb->buffer + rb->read);
*/
if (*read_result == 0)
{
res = rb_memcmp (rb, "\r\n", 2);
if (res == 0)
{
rb_read_upd (rb, 2);
*read_result = 1;
return at_read_result_iov (dev, read_result, rb, iov);
}
else if (res > 0)
{
if (rb_memcmp (rb, "\n", 1) == 0)
{
ast_debug (5, "[%s] multiline response\n", dev);
rb_read_upd (rb, 1);
return at_read_result_iov (dev, read_result, rb, iov);
}
if (rb_read_until_char_iov (rb, iov, '\r') > 0)
{
s = iov[0].iov_len + iov[1].iov_len + 1;
}
rb_read_upd (rb, s);
return at_read_result_iov (dev, read_result, rb, iov);
}
return 0;
}
else
{
if (rb_memcmp (rb, "+CSSI:", 6) == 0)
{
iovcnt = rb_read_n_iov (rb, iov, 8);
if (iovcnt > 0)
{
*read_result = 0;
}
return iovcnt;
}
else if (rb_memcmp (rb, "\r\n+CSSU:", 8) == 0 || rb_memcmp (rb, "\r\n+CMS ERROR:", 13) == 0 || rb_memcmp (rb, "\r\n+CMGS:", 8) == 0)
{
rb_read_upd (rb, 2);
return at_read_result_iov (dev, read_result, rb, iov);
}
else if (rb_memcmp (rb, "> ", 2) == 0)
{
*read_result = 0;
return rb_read_n_iov (rb, iov, 2);
}
else if (rb_memcmp (rb, "+CMGR:", 6) == 0 || rb_memcmp (rb, "+CNUM:", 6) == 0 || rb_memcmp (rb, "ERROR+CNUM:", 11) == 0 || rb_memcmp (rb, "+CLCC:", 6) == 0)
{
iovcnt = rb_read_until_mem_iov (rb, iov, "\n\r\nOK\r\n", 7);
if (iovcnt > 0)
{
*read_result = 0;
}
return iovcnt;
}
else
{
iovcnt = rb_read_until_mem_iov (rb, iov, "\r\n", 2);
if (iovcnt > 0)
{
*read_result = 0;
s = iov[0].iov_len + iov[1].iov_len + 1;
return rb_read_n_iov (rb, iov, s);
}
}
}
}
return 0;
}
EXPORT_DEF at_res_t at_read_result_classification (struct ringbuffer * rb, size_t len)
{
at_res_t at_res = RES_UNKNOWN;
unsigned idx;
for(idx = at_responses.ids_first; idx < at_responses.ids; idx++)
{
if (rb_memcmp (rb, at_responses.responses[idx].id, at_responses.responses[idx].idlen) == 0)
{
at_res = at_responses.responses[idx].res;
break;
}
}
switch (at_res)
{
case RES_SMS_PROMPT:
len = 2;
break;
case RES_CMGR:
len += 7;
break;
case RES_CSSI:
len = 8;
break;
default:
len += 1;
break;
}
rb_read_upd (rb, len);
/* ast_debug (5, "receive result '%s'\n", at_res2str (at_res));
*/
return at_res;
}