[asterisk-commits] jdixon: branch jdixon/chan_usbradio-1.4 r128734 - /team/jdixon/chan_usbradio-...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Jul 7 15:31:01 CDT 2008
Author: jdixon
Date: Mon Jul 7 15:31:01 2008
New Revision: 128734
URL: http://svn.digium.com/view/asterisk?view=rev&rev=128734
Log:
added chan_rtpdir.c
Added:
team/jdixon/chan_usbradio-1.4/channels/chan_rtpdir.c (with props)
Added: team/jdixon/chan_usbradio-1.4/channels/chan_rtpdir.c
URL: http://svn.digium.com/view/asterisk/team/jdixon/chan_usbradio-1.4/channels/chan_rtpdir.c?view=auto&rev=128734
==============================================================================
--- team/jdixon/chan_usbradio-1.4/channels/chan_rtpdir.c (added)
+++ team/jdixon/chan_usbradio-1.4/channels/chan_rtpdir.c Mon Jul 7 15:31:01 2008
@@ -1,0 +1,525 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Copyright (C) 2008, Jim Dixon
+ * Jim Dixon <jim at lambdatel.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Ham Radio Bridging Channel support
+ *
+ * \author Jim Dixon <jim at lambdatel.com>
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ ***/
+
+/* Version 0.1, 07/07/2008
+
+Channel connection for Asterisk to KI4LKF's RtpDir Echolink/IRLP/app_rpt
+bridging program for Amateur Radio over VOIP.
+
+Its invoked as rtpdir/HISIP:HISPORT[:MYPORT]
+
+HISIP is the IP address (or FQDN) of the RtpDir program
+HISPORT is the UDP socket of the RtpDir program
+MYPORT (optional) is the UDP socket that Asterisk listens on for this channel
+
+
+This protocol sends UDP packets in both directions containing 4 frames
+of 33 byte (160 sample) GSM encoded payload every 80 ms. It indicates
+the presence of signal (PTT/COR) by sending packets, and no signal by
+not sending packets. While not sending packets, it sends a "keep alive"
+packet (a 64 byte packet containing nothing important) to indicate that
+the Asterisk system is still there. The Asterisk System may receive a
+64 byte packet, but will discard it (for testing purposes). The protocol
+is connectionless and is intended to be used to emulate "nailed-up"
+facilities.
+
+[Commentary by Jim Dixon]
+Admittedly, this is by no means a fully-robust protocol. By the nature of
+UDP and the Internet, doing it "properly" takes quite a bit more then
+this. This was not my specification, it was KI4LKF's. He wanted a simple,
+UDP based protocol that was able to be implemented simply and efficiently.
+For the most part, it should work more then sufficiently if both sides are
+in the same system, or even 2 systems on the same LAN (as long as its not
+a super-busy LAN). If all this turns out to work well, then give him all
+the credit for the protocol design. I just wrote the channel driver to his
+specifications.
+
+Also, the selection of using GSM and doing it in 4 packet-sized blocks came
+from experimentation and finding that it works the best under the condidions
+of the system. Again, it wasnt my first choice, but thats what works, and
+sometimes youve just gotta go with that.
+
+*/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <search.h>
+#include <sys/ioctl.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+#define MAX_RXKEY_TIME 4
+#define KEEPALIVE_TIME 50 * 7
+
+#define BLOCKING_FACTOR 4
+#define GSM_FRAME_SIZE 33
+
+static const char tdesc[] = "RTPDIR Ham Radio Bridging Driver";
+
+/* Only linear is allowed */
+static int prefformat = AST_FORMAT_GSM;
+
+static char context[AST_MAX_EXTENSION] = "default";
+static char type[] = "rtpdir";
+static char keepstr[] =
+ "I'm a packet that contains absoultely no useful data whatsoever";
+/* rtpdir creates private structures on demand */
+
+struct rtpdir_rxq {
+ struct rtpdir_rxq *qe_forw;
+ struct rtpdir_rxq *qe_back;
+ char buf[GSM_FRAME_SIZE];
+} ;
+
+struct rtpdir_pvt {
+ int rtpdir; /* open UDP socket */
+ struct ast_channel *owner; /* Channel we belong to, possibly NULL */
+ char app[16]; /* Our app */
+ char stream[80]; /* Our stream */
+ struct sockaddr_in si_other; /* for UDP sending */
+ char txkey;
+ int rxkey;
+ int keepalive;
+ struct ast_frame fr; /* "null" frame */
+ char txbuf[GSM_FRAME_SIZE * BLOCKING_FACTOR];
+ int txindex;
+ struct rtpdir_rxq rxq;
+ struct ast_module_user *u; /*! for holding a reference to this module */
+};
+
+static struct ast_channel *rtpdir_request(const char *type, int format, void *data, int *cause);
+static int rtpdir_call(struct ast_channel *ast, char *dest, int timeout);
+static int rtpdir_hangup(struct ast_channel *ast);
+static struct ast_frame *rtpdir_xread(struct ast_channel *ast);
+static int rtpdir_xwrite(struct ast_channel *ast, struct ast_frame *frame);
+static int rtpdir_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen);
+
+static const struct ast_channel_tech rtpdir_tech = {
+ .type = type,
+ .description = tdesc,
+ .capabilities = AST_FORMAT_GSM,
+ .requester = rtpdir_request,
+ .call = rtpdir_call,
+ .hangup = rtpdir_hangup,
+ .read = rtpdir_xread,
+ .write = rtpdir_xwrite,
+ .indicate = rtpdir_indicate,
+};
+
+static int rtpdir_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct rtpdir_pvt *p;
+
+ p = ast->tech_pvt;
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "rtpdir_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+ /* When we call, it just works, really, there's no destination... Just
+ ring the phone and wait for someone to answer */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name);
+
+ ast_setstate(ast,AST_STATE_UP);
+ return 0;
+}
+
+static void rtpdir_destroy(struct rtpdir_pvt *p)
+{
+ if (p->rtpdir)
+ close(p->rtpdir);
+ ast_module_user_remove(p->u);
+ free(p);
+}
+
+static struct rtpdir_pvt *rtpdir_alloc(void *data)
+{
+ struct rtpdir_pvt *p;
+ int flags = 0;
+ char stream[256];
+ struct sockaddr_in si_me;
+ struct hostent *host;
+ struct ast_hostent ah;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(hisip);
+ AST_APP_ARG(hisport);
+ AST_APP_ARG(myport);
+ );
+
+ if (ast_strlen_zero(data)) return NULL;
+
+ AST_NONSTANDARD_APP_ARGS(args,data,':');
+
+ if ((!args.hisip) || (!args.hisip[0])) args.hisip = "127.0.0.1";
+ if ((!args.hisport) || (!args.hisport[0])) args.hisport = "1234";
+ if ((!args.myport) || (!args.myport[0])) args.myport = args.hisport;
+
+ p = malloc(sizeof(struct rtpdir_pvt));
+ if (p) {
+ memset(p, 0, sizeof(struct rtpdir_pvt));
+
+ sprintf(stream,"%s:%d",args.hisip,atoi(args.hisport));
+ strcpy(p->stream,stream);
+ p->rxq.qe_forw = &p->rxq;
+ p->rxq.qe_back = &p->rxq;
+
+ memset(&ah,0,sizeof(ah));
+ host = ast_gethostbyname(args.hisip,&ah);
+ if (!host)
+ {
+ ast_log(LOG_WARNING, "Unable to find host %s\n", args.hisip);
+ free(p);
+ return NULL;
+ }
+ memset((char *) &p->si_other, 0, sizeof(p->si_other));
+ p->si_other.sin_addr = *(struct in_addr *)host->h_addr;
+ p->si_other.sin_family = AF_INET;
+ p->si_other.sin_port = htons(atoi(args.hisport));
+
+ if ((p->rtpdir=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
+ {
+ ast_log(LOG_WARNING, "Unable to create new socket for rtpdir connection\n");
+ free(p);
+ return(NULL);
+
+ }
+
+ memset((char *) &si_me, 0, sizeof(si_me));
+ si_me.sin_family = AF_INET;
+ si_me.sin_port = htons(atoi(args.myport));
+ si_me.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (!strncmp(ast_inet_ntoa(p->si_other.sin_addr),"127.",4))
+ si_me.sin_addr.s_addr = inet_addr("127.0.0.1");
+ if (bind(p->rtpdir, &si_me, sizeof(si_me))==-1)
+ {
+ ast_log(LOG_WARNING, "Unable to bind port for rtpdir connection\n");
+ free(p);
+ return(NULL);
+
+ }
+ if (!p->rtpdir) {
+ ast_log(LOG_WARNING, "Unable to allocate new rtpdir stream '%s' with flags %d\n", stream, flags);
+ free(p);
+ return NULL;
+ }
+ }
+ return p;
+}
+
+static int rtpdir_hangup(struct ast_channel *ast)
+{
+ struct rtpdir_pvt *p;
+ p = ast->tech_pvt;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "rtpdir_hangup(%s)\n", ast->name);
+ if (!ast->tech_pvt) {
+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+ rtpdir_destroy(p);
+ ast->tech_pvt = NULL;
+ ast_setstate(ast, AST_STATE_DOWN);
+ return 0;
+}
+
+static int rtpdir_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen)
+{
+ struct rtpdir_pvt *p = ast->tech_pvt;
+
+ switch (cond) {
+ case AST_CONTROL_RADIO_KEY:
+ p->txkey = 1;
+ break;
+ case AST_CONTROL_RADIO_UNKEY:
+ p->txkey = 0;
+ break;
+ case AST_CONTROL_HANGUP:
+ return -1;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static struct ast_frame *rtpdir_xread(struct ast_channel *ast)
+{
+ struct rtpdir_pvt *p = ast->tech_pvt;
+ char buf[512];
+ struct sockaddr_in si_them;
+ unsigned int themlen;
+ int n,i;
+ struct ast_frame fr;
+ struct rtpdir_rxq *qp;
+
+ themlen = sizeof(struct sockaddr_in);
+ if ((n = recvfrom(p->rtpdir,buf,sizeof(buf),0,&si_them,&themlen)) == -1)
+ {
+ ast_log(LOG_WARNING,"Cannot recvfrom()");
+ return NULL;
+ }
+ if (n < (GSM_FRAME_SIZE * BLOCKING_FACTOR))
+ {
+ p->fr.frametype = 0;
+ p->fr.subclass = 0;
+ p->fr.datalen = 0;
+ p->fr.samples = 0;
+ p->fr.data = NULL;
+ p->fr.src = type;
+ p->fr.offset = 0;
+ p->fr.mallocd=0;
+ p->fr.delivery.tv_sec = 0;
+ p->fr.delivery.tv_usec = 0;
+ return &p->fr;
+ }
+ if (memcmp(&si_them.sin_addr,&p->si_other.sin_addr,sizeof(si_them.sin_addr)))
+ {
+ ast_log(LOG_NOTICE,"Received packet from %s, expecting it from %s\n",
+ ast_inet_ntoa(si_them.sin_addr),ast_inet_ntoa(p->si_other.sin_addr));
+ p->fr.frametype = 0;
+ p->fr.subclass = 0;
+ p->fr.datalen = 0;
+ p->fr.samples = 0;
+ p->fr.data = NULL;
+ p->fr.src = type;
+ p->fr.offset = 0;
+ p->fr.mallocd=0;
+ p->fr.delivery.tv_sec = 0;
+ p->fr.delivery.tv_usec = 0;
+ return &p->fr;
+ }
+ for(i = 0; i < BLOCKING_FACTOR; i++)
+ {
+ qp = malloc(sizeof(struct rtpdir_rxq));
+ if (!qp)
+ {
+ ast_log(LOG_NOTICE,"Cannot malloc for qp\n");
+ break;
+ }
+ memcpy(qp->buf,buf + (GSM_FRAME_SIZE * i),GSM_FRAME_SIZE);
+ insque((struct qelem *) qp,(struct qelem *) p->rxq.qe_back);
+ }
+
+ fr.datalen = 0;
+ fr.samples = 0;
+ fr.frametype = 0;
+ fr.subclass = 0;
+ fr.data = 0;
+ fr.src = type;
+ fr.offset = 0;
+ fr.mallocd=0;
+ fr.delivery.tv_sec = 0;
+ fr.delivery.tv_usec = 0;
+
+ return &p->fr;
+
+}
+
+static int rtpdir_xwrite(struct ast_channel *ast, struct ast_frame *frame)
+{
+ struct rtpdir_pvt *p = ast->tech_pvt;
+ struct ast_frame fr;
+ struct rtpdir_rxq *qp;
+ char buf[GSM_FRAME_SIZE + AST_FRIENDLY_OFFSET];
+
+ if (ast->_state != AST_STATE_UP) {
+ /* Don't try tos end audio on-hook */
+ return 0;
+ }
+ /* Write a frame of (presumably voice) data */
+ if (frame->frametype != AST_FRAME_VOICE) return 0;
+
+ /* if something in rx queue */
+ if (p->rxq.qe_forw != &p->rxq)
+ {
+ if (!p->rxkey)
+ {
+ fr.datalen = 0;
+ fr.samples = 0;
+ fr.frametype = AST_FRAME_CONTROL;
+ fr.subclass = AST_CONTROL_RADIO_KEY;
+ fr.data = 0;
+ fr.src = type;
+ fr.offset = 0;
+ fr.mallocd=0;
+ fr.delivery.tv_sec = 0;
+ fr.delivery.tv_usec = 0;
+ ast_queue_frame(ast,&fr);
+ }
+ p->rxkey = MAX_RXKEY_TIME;
+ qp = p->rxq.qe_forw;
+ remque((struct qelem *) qp);
+ memcpy(buf + AST_FRIENDLY_OFFSET,qp->buf,GSM_FRAME_SIZE);
+ free(qp);
+
+ fr.datalen = GSM_FRAME_SIZE;
+ fr.samples = 160;
+ fr.frametype = AST_FRAME_VOICE;
+ fr.subclass = AST_FORMAT_GSM;
+ fr.data = buf + AST_FRIENDLY_OFFSET;
+ fr.src = type;
+ fr.offset = AST_FRIENDLY_OFFSET;
+ fr.mallocd=0;
+ fr.delivery.tv_sec = 0;
+ fr.delivery.tv_usec = 0;
+ ast_queue_frame(ast,&fr);
+ }
+ if (p->rxkey == 1)
+ {
+ fr.datalen = 0;
+ fr.samples = 0;
+ fr.frametype = AST_FRAME_CONTROL;
+ fr.subclass = AST_CONTROL_RADIO_UNKEY;
+ fr.data = 0;
+ fr.src = type;
+ fr.offset = 0;
+ fr.mallocd=0;
+ fr.delivery.tv_sec = 0;
+ fr.delivery.tv_usec = 0;
+ ast_queue_frame(ast,&fr);
+ }
+ if (p->rxkey) p->rxkey--;
+
+ if (!(frame->subclass & (AST_FORMAT_GSM))) {
+ ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
+ return 0;
+ }
+ if (p->txkey || p->txindex)
+ {
+ p->keepalive = KEEPALIVE_TIME;
+ memcpy(&p->txbuf[GSM_FRAME_SIZE * p->txindex++],frame->data,GSM_FRAME_SIZE);
+ }
+ if (p->txindex >= BLOCKING_FACTOR)
+ {
+ if (sendto(p->rtpdir,p->txbuf,GSM_FRAME_SIZE * BLOCKING_FACTOR,
+ 0,&p->si_other,sizeof(p->si_other)) == -1)
+ return -1;
+ p->txindex = 0;
+ }
+ if (p->txkey) return 0;
+ if (p->keepalive--) return 0;
+ p->keepalive = KEEPALIVE_TIME;
+ if (sendto(p->rtpdir,keepstr,sizeof(keepstr),0,&p->si_other,sizeof(p->si_other)) == -1)
+ return -1;
+ return 0;
+}
+
+static struct ast_channel *rtpdir_new(struct rtpdir_pvt *i, int state)
+{
+ struct ast_channel *tmp;
+ tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, 0, "rtpdir/%s", i->stream);
+ if (tmp) {
+ tmp->tech = &rtpdir_tech;
+ tmp->fds[0] = i->rtpdir;
+ tmp->nativeformats = prefformat;
+ tmp->rawreadformat = prefformat;
+ tmp->rawwriteformat = prefformat;
+ tmp->writeformat = prefformat;
+ tmp->readformat = prefformat;
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->tech_pvt = i;
+ ast_copy_string(tmp->context, context, sizeof(tmp->context));
+ ast_copy_string(tmp->exten, "s", sizeof(tmp->exten));
+ ast_string_field_set(tmp, language, "");
+ i->owner = tmp;
+ i->u = ast_module_user_add(tmp);
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ }
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ return tmp;
+}
+
+
+static struct ast_channel *rtpdir_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat;
+ struct rtpdir_pvt *p;
+ struct ast_channel *tmp = NULL;
+
+ oldformat = format;
+ format &= (AST_FORMAT_GSM);
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
+ return NULL;
+ }
+ p = rtpdir_alloc(data);
+ if (p) {
+ tmp = rtpdir_new(p, AST_STATE_DOWN);
+ if (!tmp)
+ rtpdir_destroy(p);
+ }
+ return tmp;
+}
+
+static int unload_module(void)
+{
+ /* First, take us out of the channel loop */
+ ast_channel_unregister(&rtpdir_tech);
+ return 0;
+}
+
+static int load_module(void)
+{
+ /* Make sure we can register our channel type */
+ if (ast_channel_register(&rtpdir_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+ return -1;
+ }
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "RTPDIR Ham Radio Bridging Thingy");
Propchange: team/jdixon/chan_usbradio-1.4/channels/chan_rtpdir.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/jdixon/chan_usbradio-1.4/channels/chan_rtpdir.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/jdixon/chan_usbradio-1.4/channels/chan_rtpdir.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
More information about the asterisk-commits
mailing list