[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