[asterisk-commits] jdixon: branch jdixon/chan_usbradio-1.4 r140555 - /team/jdixon/chan_usbradio-...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Sep 2 03:33:49 CDT 2008


Author: jdixon
Date: Tue Sep  2 03:33:49 2008
New Revision: 140555

URL: http://svn.digium.com/view/asterisk?view=rev&rev=140555
Log:
More better and more working chan_irlp

Modified:
    team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_irlp.c

Modified: team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_irlp.c
URL: http://svn.digium.com/view/asterisk/team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_irlp.c?view=diff&rev=140555&r1=140554&r2=140555
==============================================================================
--- team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_irlp.c (original)
+++ team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_irlp.c Tue Sep  2 03:33:49 2008
@@ -29,7 +29,7 @@
 /*** MODULEINFO
  ***/
 
-/* Version 0.2, 08/31/2008
+/* Version 0.3, 9/2/2008
 irlp channel driver for Asterisk/app_rpt.
 This is a first attempt.
 
@@ -40,28 +40,6 @@
 (included in the rtpDir bridge package)
 onto your local IRLP node, before using this driver.
 
-You will use this driver to connect to IRLP adpcm refXXXX reflectors.
-To connect to IRLP stnXXXX nodes or IRLP gsm refXXXX reflectors, 
-use the irlpgsm driver.
-
-Lets say you know the adpcm IRLP reflector that you wish to connect 
-this driver to, and lets assume it is IRLP reflector refXXX5
-and lets assume that the IRLP reflector refXXX5 which is channel 5,
-uses adpcm codec.
-
-Using the above example, the port to use for this driver would be 
-(5 * 2) + 2074 = 2084
-So in the rxchannel specification in /etc/asterisk/rpt.conf
-under a node stanza, you will have this:
-
-   rxchannel=irlp/0.0.0.0:2084
-
-and of course, this driver will open/lock UDP
-ports 2084(audio),2085(control)
-
-If the linux box is protected by a NAT router,
-leave the IP address as 0.0.0.0,
-do not use 127.0.0.1
 
 You must also make sure that the IRLP ispeaker starts with:
    
@@ -83,9 +61,6 @@
 might compete for the same port(s), so set irlpEnable=no in
 the config file for rtpDir/rtpDir_tm bridge or use different port(s)
 in this driver or rtpDir/rtpDir_tm bridge.
-
-It would be better to have written all this code 
-in C++ instead of C, but Asterisk wants C only.
 
 */
 
@@ -138,8 +113,12 @@
 
 #define	LARGEST_PACKET_SIZE 1024
 #define	IRLP_ROOT "/home/irlp/local"
-
-enum {IRLP_ISADPCM,IRLP_ISGSM} ;
+#define	IRLP_RESET "su - repeater /home/irlp/scripts/suwrap /home/irlp/scripts/irlp_reset"
+#define	IRLP_END "su - repeater /home/irlp/scripts/suwrap /home/irlp/scripts/end &"
+#define	IRLP_CALL_REFL "su - repeater /home/irlp/scripts/suwrap /home/irlp/scripts/connect_to_reflector ref%04d &"
+#define	IRLP_CALL "su - repeater /home/irlp/scripts/suwrap /home/irlp/scripts/call stn%04d &"
+
+enum {IRLP_NOPROTO,IRLP_ISADPCM,IRLP_ISGSM} ;
 
 struct irlp_audio
 {
@@ -165,21 +144,12 @@
 };
 
 struct irlp_pvt {
-        int audio_sock;
-        int ctrl_sock;
 	struct ast_channel *owner;
 	char app[16];		
-	char stream[80];
-	int proto;
 	char txkey;
 	int rxkey;
 	int keepalive;
 	struct ast_frame fr;	
-	int txindex;
-	struct irlp_rxqast rxqast;
-	char *outbuf_old;
-	int rxlen;
-	int rxidx;
 	struct ast_module_user *u;
 };
 
@@ -195,20 +165,37 @@
 */
 static char mycall[IRLP_CALL_SIZE + 1];
 static char mynode[IRLP_NODE_SIZE + 1];
+static char astnode[20];
 static short rtcptimeout = 10;
 static short localispeakerport = 2174;
-
+static int radmode = 0;
+static int nodenum = 0;
 static int audio_sock = -1;
 static int ctrl_sock = -1;
-static uint16_t audio_port = 2084;
-static uint16_t ctrl_port = 2085;
-
+static int tx_audio_port = 0;
+static int alt_audio_sock = -1;
+static int alt_ctrl_sock = -1;
+static uint16_t audio_port_cfg = 2084;
+static uint16_t audio_port;
+static uint16_t ctrl_port;
 static char *config = "irlp.conf";
-
 static const char tdesc[] = "irlp channel driver by KI4LKF";
 static int prefformat = AST_FORMAT_ADPCM;
 static char context[AST_MAX_EXTENSION] = "default";
 static char type[] = "irlp";
+static pthread_t irlp_reader_thread;
+static int run_forever = 1;
+static int proto = IRLP_NOPROTO;
+static int in_node = 0;
+static int txindex;
+static struct irlp_rxqast rxqast;
+static char *outbuf_old;
+static int rxlen;
+static int rxidx;
+static int ready = 0;
+static struct ast_channel *curcall = NULL;
+AST_MUTEX_DEFINE_STATIC(irlplock);
+static char stream[256];
 
 #ifdef OLD_ASTERISK
 #define ast_free free
@@ -224,9 +211,10 @@
 static int irlp_digit_begin(struct ast_channel *c, char digit);
 static int irlp_digit_end(struct ast_channel *c, char digit, unsigned int duratiion);
 static int irlp_text(struct ast_channel *c, const char *text);
-
+static struct irlp_pvt *irlp_alloc(void *data);
 static int is_rtcp_bye(unsigned char *p, int len);
 static int is_valid_rtcp(unsigned char *p, int len);
+static struct ast_channel *irlp_new(struct irlp_pvt *i, int state);
 
 static const struct ast_channel_tech irlp_tech = {
 	.type = type,
@@ -243,6 +231,34 @@
 	.send_digit_end = irlp_digit_end,
 };
 
+void static reset_stuff(void)
+{
+
+	if (alt_audio_sock != -1) close(alt_audio_sock);
+	alt_audio_sock = -1;
+	if (alt_ctrl_sock != -1) close(alt_ctrl_sock);
+	alt_ctrl_sock = -1;
+	proto = IRLP_NOPROTO;
+	in_node = 0;
+	nodenum = 0;
+	txindex = 0;
+	ready = 0;
+	rxqast.qe_forw = &rxqast;
+	rxqast.qe_back = &rxqast;
+	remote_irlp_node_ip[0] = 0;
+	tx_audio_port = audio_port_cfg;
+	audio_port = audio_port_cfg;
+	ctrl_port = audio_port + 1;
+	if (curcall)
+	{
+	       curcall->nativeformats = AST_FORMAT_ADPCM;
+	       ast_set_read_format(curcall,curcall->readformat);
+	       ast_set_write_format(curcall,curcall->writeformat);
+	}
+	return;
+}
+
+
 static char *irlp_read_file(char *basename,char *fname)
 {
 char s[200],*str;
@@ -320,9 +336,30 @@
     return (p == end)?1:0;
 }
 
+static int do_new_call(void)
+{
+
+	struct irlp_pvt *p;
+
+	p = irlp_alloc((void *)"");
+	if (!p)
+	{
+		ast_log(LOG_ERROR,"Cannot alloc irlp channel\n");
+		return -1;
+	}
+	curcall = irlp_new(p,AST_STATE_RINGING);
+	if (!curcall)
+	{
+		ast_log(LOG_ERROR,"Cannot alloc irlp channel\n");
+		return -1;
+	}
+	return 0;
+}
+
 static int irlp_call(struct ast_channel *ast, char *dest, int timeout)
 {
 	struct irlp_pvt *p;
+	char *cp,str[100];
 
 	p = ast->tech_pvt;
 
@@ -330,104 +367,346 @@
 		ast_log(LOG_WARNING, "irlp_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);
+	cp = irlp_read_file(IRLP_ROOT,"active");
+	if (cp && *cp) 
+	{
+		ast_safe_system(IRLP_END);
+		usleep(10000);
+		ast_safe_system(IRLP_RESET);
+		usleep(10000);
+	}
+	if (cp) free(cp);
+	if ((!radmode) && nodenum)
+	{
+		if (nodenum >= 9000)
+			snprintf(str,sizeof(str) - 1,IRLP_CALL_REFL,nodenum);
+		else
+			snprintf(str,sizeof(str) - 1,IRLP_CALL,nodenum);
+		ast_safe_system(str);
+		usleep(10000);
+	}		
+
+	ast_setstate(ast,(radmode) ? AST_STATE_UP : AST_STATE_RINGING);
 	return 0;
 }
 
 static void irlp_destroy(struct irlp_pvt *p)
 {
-	if (p->audio_sock) {
-                close(p->audio_sock);
-                p->audio_sock = -1;
-        }
-
-        if (p->ctrl_sock) {
-                close(p->ctrl_sock);
-                p->ctrl_sock = -1;
-        }
-
+	reset_stuff();
+	curcall = NULL;
 	ast_module_user_remove(p->u);
 	ast_free(p);
 }
 
+static void process_codec_file(struct ast_channel *ast)
+{
+	char *cp;
+
+	  if (!ready) return;
+	  cp = irlp_read_file(IRLP_ROOT,"codec");
+	  if (cp) 
+	  {
+		  if (!strncasecmp(cp,"GSM",3))
+		  {
+			  if (proto == IRLP_NOPROTO)
+				  ast_log(LOG_NOTICE,"irlp channel format set to GSM\n");
+			  else if (proto != IRLP_ISGSM)
+				  ast_log(LOG_NOTICE,"irlp channel format changed to GSM\n");
+			  proto = IRLP_ISGSM;
+		          ast->nativeformats = AST_FORMAT_GSM;
+		  }
+		  else 
+		  {
+			  if (proto == IRLP_NOPROTO)
+				  ast_log(LOG_NOTICE,"irlp channel format set to ADPCM\n");
+			  else if (proto != IRLP_ISADPCM)
+				  ast_log(LOG_NOTICE,"irlp channel format changed to GSM\n");
+			  proto = IRLP_ISADPCM;
+		          ast->nativeformats = AST_FORMAT_ADPCM;
+		  }
+		  ast_set_read_format(ast,ast->readformat);
+		  ast_set_write_format(ast,ast->writeformat);
+		  free(cp);
+	  }
+	  return;
+}
+
+
+static void *irlp_reader(void *nothing)
+{
+
+	fd_set fds[2];
+	struct timeval tmout;
+	int i,myaud,myctl,x;
+
+	char buf[LARGEST_PACKET_SIZE + 1];
+	struct sockaddr_in sin;
+        struct irlp_rxqast *qpast;
+        char ip[IRLP_IP_SIZE + 1],*cp;
+        socklen_t fromlen;
+	ssize_t recvlen;
+        size_t len;
+
+	ast_log(LOG_NOTICE, "IRLP reader thread started.\n");
+	while(run_forever)
+	{
+		if ((proto == IRLP_NOPROTO) && curcall) process_codec_file(curcall);
+		myaud = (alt_audio_sock != -1) ? alt_audio_sock : audio_sock;
+		myctl = (alt_ctrl_sock != -1) ? alt_ctrl_sock : ctrl_sock;
+		FD_ZERO(fds);
+		FD_SET(myaud,fds);
+		FD_SET(myctl,fds);
+		x = myaud;
+		if (myctl > x) x = myctl;
+		tmout.tv_sec = 0;
+		tmout.tv_usec = 50000;
+		i = select(x + 1,fds,NULL,NULL,&tmout);
+		if (i == 0) 
+		{
+			continue;
+		}
+		if (i < 0)
+		{
+			ast_log(LOG_ERROR,"Error in select()\n");
+			pthread_exit(NULL);
+		}
+		if (FD_ISSET(myctl,fds)) /* if a ctrl packet */
+		{ 
+	           fromlen = sizeof(struct sockaddr_in);
+	           recvlen = recvfrom(myctl,
+                                  buf,
+                                  LARGEST_PACKET_SIZE,
+                                  0,
+                                  (struct sockaddr *)&sin,&fromlen);
+			
+	           if (recvlen > 0) 
+		   {
+	              buf[recvlen] = '\0';
+#ifdef  OLD_ASTERISK
+	              ast_inet_ntoa(ip,IRLP_IP_SIZE,sin.sin_addr);
+#else
+	              strncpy(ip,ast_inet_ntoa(sin.sin_addr),IRLP_IP_SIZE);
+#endif
+	              if (is_valid_rtcp((unsigned char *)buf,recvlen))
+		      {
+	                 if (!is_rtcp_bye((unsigned char *)buf,recvlen))
+			 {
+	                    if (strncmp(ip, "127.0.0.1",IRLP_IP_SIZE) != 0)
+			    {
+	                       if (strncmp(remote_irlp_node_ip, ip, IRLP_IP_SIZE) != 0) 
+			       {
+	                          strncpy(remote_irlp_node_ip, ip, IRLP_IP_SIZE);
+				  cp = irlp_read_file(IRLP_ROOT,"active");
+				  if (cp && (strlen(cp) > 3))
+				  {
+					in_node = atoi(cp + 3);
+				  	ast_log(LOG_NOTICE,"irlp node connected from %s node %s\n",ip,cp + 3);
+				  	if (!curcall) do_new_call();
+					if ((!ready) && curcall)
+					{
+						struct ast_frame fr;
+
+						fr.datalen = 0;
+						fr.samples = 0;
+						fr.frametype = AST_FRAME_CONTROL;
+						fr.subclass = AST_CONTROL_ANSWER;
+						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(curcall,&fr);
+					}
+					ready = 1;
+					if (curcall && (proto == IRLP_NOPROTO)) process_codec_file(curcall);
+				  } else ast_log(LOG_NOTICE,"irlp node connected from %s\n", ip);
+				  if (cp) free(cp);
+	                       }
+	                    }
+	                 } 
+	                 else 
+			 {
+	                    if (strncmp(ip, remote_irlp_node_ip, IRLP_IP_SIZE) == 0)
+			    {
+			       reset_stuff();
+			       if ((!radmode) && curcall) ast_softhangup(curcall,AST_SOFTHANGUP_DEV);
+	                       ast_log(LOG_NOTICE, "received IRLP bye from %s\n",ip);
+			    }
+	                 }
+	              }
+		   }
+		}
+		if (FD_ISSET(myaud,fds)) /* if a audio packet */
+		{
+	           fromlen = sizeof(struct sockaddr_in);
+	           recvlen = recvfrom(myaud,
+                              buf,
+                              LARGEST_PACKET_SIZE,
+                              0,
+                              (struct sockaddr *)&sin,&fromlen);
+	           if (recvlen > 0)
+		   {
+	              buf[recvlen] = '\0';
+#ifdef  OLD_ASTERISK
+	              ast_inet_ntoa(ip,IRLP_IP_SIZE,sin.sin_addr);
+#else
+	              strncpy(ip,ast_inet_ntoa(sin.sin_addr),IRLP_IP_SIZE);
+#endif
+	              len = ntohl(((struct irlp_audio *)buf)->buffer.buffer_len); 
+	              if (((strncmp(ip, remote_irlp_node_ip, IRLP_IP_SIZE) == 0) ||
+	                   (strncmp(ip, "127.0.0.1", IRLP_IP_SIZE) == 0)) &&
+	                   (len > IRLP_ADPCM_STATE_INFO_SIZE) &&
+	                   ((recvlen - IRLP_HEADER_INFO_SIZE) == len)) 
+		      {
+
+	                  if (((struct irlp_audio *)buf)->compression == htonl(0x200 | 0x40000000))
+	    	          {
+			      if (proto == IRLP_NOPROTO)
+				  ast_log(LOG_NOTICE,"irlp channel format set to ADPCM\n");
+			      else if (proto != IRLP_ISADPCM)
+				  ast_log(LOG_NOTICE,"irlp channel format changed to GSM\n");
+			      proto = IRLP_ISADPCM;
+			      if (curcall)
+			      {
+		                      curcall->nativeformats = AST_FORMAT_ADPCM;
+				      ast_set_read_format(curcall,curcall->readformat);
+				      ast_set_write_format(curcall,curcall->writeformat);
+			      }
+		          }
+	                  if (((struct irlp_audio *)buf)->compression == htonl(0x20 | 0x40000000))
+		          {
+			      if (proto == IRLP_NOPROTO)
+				  ast_log(LOG_NOTICE,"irlp channel format set to GSM\n");
+			      else if (proto != IRLP_ISGSM)
+				  ast_log(LOG_NOTICE,"irlp channel format changed to GSM\n");
+			      proto = IRLP_ISGSM;
+			      if (curcall)
+			      {
+		                      curcall->nativeformats = AST_FORMAT_GSM;
+				      ast_set_read_format(curcall,curcall->readformat);
+				      ast_set_write_format(curcall,curcall->writeformat);
+			      }
+		          }
+	                 qpast = ast_malloc(sizeof(struct irlp_rxqast) + len);
+	                 if (!qpast) 
+			 {
+	                    ast_log(LOG_NOTICE,"Cannot malloc for qpast\n");
+	                    return NULL;
+	                 }
+			 if (proto == IRLP_ISADPCM)
+			 {
+		                 qpast->len = len;
+		                 memcpy(qpast->buf,((struct irlp_audio *)buf)->buffer.buffer_val,len);
+			 }
+			 else
+			 {
+		                 qpast->len = len - 2;
+		                 memcpy(qpast->buf,((struct irlp_audio *)buf)->buffer.buffer_val + 2,len);
+			 }
+	                 insque((struct qelem *)qpast,(struct qelem *)rxqast.qe_back);
+	                 if (strncmp(ip, "127.0.0.1", IRLP_IP_SIZE) == 0)
+			 {
+	                    if (remote_irlp_node_ip[0] != '\0') 
+			    {
+	                       sin.sin_family = AF_INET;
+	                       sin.sin_addr.s_addr = inet_addr(remote_irlp_node_ip);
+	                       sin.sin_port = htons(tx_audio_port);
+	                       sendto((alt_audio_sock != -1) ? alt_audio_sock : audio_sock,buf,recvlen,
+	                               0,(struct sockaddr *)&sin,sizeof(struct sockaddr));
+	                    }
+	                 }
+			 else 
+			 {
+	                    sin.sin_family = AF_INET;
+	                    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
+	                    sin.sin_port = htons(localispeakerport); 
+	                    sendto(audio_sock,buf,recvlen,
+	                           0,(struct sockaddr *)&sin,sizeof(struct sockaddr));
+	                 }
+	              }
+		   }
+		}
+ 	} 
+	ast_mutex_unlock(&irlplock);
+	ast_log(LOG_NOTICE, "IRLP read thread exited.\n");
+	pthread_exit(NULL);
+}
+
+
 static struct irlp_pvt *irlp_alloc(void *data)
 {
 	struct irlp_pvt *p;
-	/* int flags = 0; */
-	char stream[256];
 	struct sockaddr_in si_me;
         
 	AST_DECLARE_APP_ARGS(args,
-		AST_APP_ARG(myip);
-		AST_APP_ARG(myport);
+		AST_APP_ARG(nodenum);
 	);
 
-	if (ast_strlen_zero(data)) return NULL;
-
-	AST_NONSTANDARD_APP_ARGS(args,data,':');
-
-	if ((!args.myip) || (!args.myip[0])) args.myip = "127.0.0.1";
-	if ((!args.myport) || (!args.myport[0]))  args.myport = "2074";
+	args.nodenum = NULL;
+	if (!ast_strlen_zero(data)) 
+		AST_STANDARD_APP_ARGS(args,data);
 
 	p = ast_malloc(sizeof(struct irlp_pvt));
 	if (p) {
 		memset(p, 0, sizeof(struct irlp_pvt));
-		
-		sprintf(stream,"%s:%d",args.myip,atoi(args.myport));
-		strcpy(p->stream,stream);
-		p->rxqast.qe_forw = &p->rxqast;
-		p->rxqast.qe_back = &p->rxqast;
-
-		if ((p->audio_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
-			ast_log(LOG_WARNING, 
-                                "Unable to create new socket for irlp audio connection\n");
-			ast_free(p);
-			return(NULL);
-		}
-		
-		if ((p->ctrl_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
-			ast_log(LOG_WARNING, 
-                                "Unable to create new socket for irlp control connection\n");
-                        close(p->audio_sock);
-                        p->audio_sock = -1;
-			ast_free(p);
-			return(NULL);
-		}
-		memset((char *) &si_me, 0, sizeof(si_me));
-		si_me.sin_family = AF_INET;
-                if (strcmp(args.myip,"0.0.0.0") == 0)
-                   si_me.sin_addr.s_addr = htonl(INADDR_ANY);
-                else
-		   si_me.sin_addr.s_addr = inet_addr(args.myip);
-                audio_port = atoi(args.myport);
- 		si_me.sin_port = htons(audio_port);               
-		if (bind(p->audio_sock, &si_me, sizeof(si_me))==-1) {
-			ast_log(LOG_WARNING, "Unable to bind port for irlp audio connection\n");
-                        close(p->ctrl_sock); p->ctrl_sock = -1;
-                        close(p->audio_sock); p->audio_sock = -1;
-			ast_free(p);
-			return(NULL);
-
-		}
-                ctrl_port = audio_port + 1;
-		si_me.sin_port = htons(ctrl_port);
-		if (bind(p->ctrl_sock, &si_me, sizeof(si_me))==-1) {
-			ast_log(LOG_WARNING, "Unable to bind port for irlp control connection\n");
-                        close(p->ctrl_sock); p->ctrl_sock = -1;
-                        close(p->audio_sock); p->audio_sock = -1;
-			ast_free(p);
-			return(NULL);
-
-		}
-                fcntl(p->audio_sock,F_SETFL,O_NONBLOCK);
-                fcntl(p->ctrl_sock,F_SETFL,O_NONBLOCK);
-                audio_sock = p->audio_sock;
-                ctrl_sock = p->ctrl_sock;
+		nodenum = 0;
+		rxqast.qe_forw = &rxqast;
+		rxqast.qe_back = &rxqast;
+		sprintf(stream,"%d",audio_port);
+		tx_audio_port = audio_port_cfg;
+		if ((!radmode) && args.nodenum && *args.nodenum)
+		{
+			nodenum = atoi(args.nodenum);
+			if ((nodenum < 1000) || (nodenum > 9999))
+			{
+				ast_log(LOG_ERROR,"Requested node number %s invalid\n",args.nodenum);
+				ast_free(p);
+				return NULL;
+			}
+			if (nodenum >= 9000) tx_audio_port += (2 * (nodenum % 10));
+		}
+
+		if (tx_audio_port != audio_port)
+		{
+			if ((alt_audio_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+			{
+				ast_log(LOG_WARNING, 
+	                                "Unable to create new socket for irlp audio connection\n");
+				ast_free(p);
+				return(NULL);
+			}
+			if ((alt_ctrl_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+				ast_log(LOG_WARNING, 
+	                                "Unable to create new socket for irlp control connection\n");
+	                        close(alt_audio_sock);
+	                        alt_audio_sock = -1;
+				ast_free(p);
+				return(NULL);
+			}
+			memset((char *) &si_me, 0, sizeof(si_me));
+			si_me.sin_family = AF_INET;
+	                si_me.sin_addr.s_addr = htonl(INADDR_ANY);
+	 		si_me.sin_port = htons(tx_audio_port);               
+			if (bind(alt_audio_sock, &si_me, sizeof(si_me))==-1) 
+			{
+				ast_log(LOG_WARNING, "Unable to bind port for irlp audio connection\n");
+	                        close(alt_ctrl_sock); alt_ctrl_sock = -1;
+	                        close(alt_audio_sock); alt_audio_sock = -1;
+				ast_free(p);
+				return(NULL);
+			}
+			si_me.sin_port = htons(tx_audio_port + 1);
+			if (bind(alt_ctrl_sock, &si_me, sizeof(si_me))==-1) {
+				ast_log(LOG_WARNING, "Unable to bind port for irlp control connection\n");
+	                        close(alt_ctrl_sock); alt_ctrl_sock = -1;
+	                        close(alt_audio_sock); alt_audio_sock = -1;
+				ast_free(p);
+				return(NULL);
+			}
+	                fcntl(alt_audio_sock,F_SETFL,O_NONBLOCK);
+	                fcntl(alt_ctrl_sock,F_SETFL,O_NONBLOCK);
+		}
 	}
 	return p;
 }
@@ -442,9 +721,13 @@
 		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
 		return 0;
 	}
+	reset_stuff();
+	ast_safe_system(IRLP_END);
+	ast_safe_system(IRLP_RESET);
 	irlp_destroy(p);
 	ast->tech_pvt = NULL;
 	ast_setstate(ast, AST_STATE_DOWN);
+	curcall = 0;
 	return 0;
 }
 
@@ -485,157 +768,8 @@
 
 static struct ast_frame  *irlp_xread(struct ast_channel *ast)
 {
-
 	struct irlp_pvt *p = ast->tech_pvt;
-	char buf[LARGEST_PACKET_SIZE + 1];
-	struct sockaddr_in sin;
-        struct irlp_rxqast *qpast;
-        char ip[IRLP_IP_SIZE + 1],*cp;
-	int i;
-        socklen_t fromlen;
-	ssize_t recvlen;
-        size_t len;
-
-	len = 0;
-        ioctl(p->ctrl_sock,FIONREAD,&len);
-        if (len > 0) {
-           fromlen = sizeof(struct sockaddr_in);
-           recvlen = recvfrom(p->ctrl_sock,
-                                  buf,
-                                  LARGEST_PACKET_SIZE,
-                                  0,
-                                  (struct sockaddr *)&sin,&fromlen);
-           if (recvlen > 0) {
-              buf[recvlen] = '\0';
-#ifdef  OLD_ASTERISK
-              ast_inet_ntoa(ip,IRLP_IP_SIZE,sin.sin_addr);
-#else
-              strncpy(ip,ast_inet_ntoa(sin.sin_addr),IRLP_IP_SIZE);
-#endif
-              if (is_valid_rtcp((unsigned char *)buf,recvlen)) {
-                 if (!is_rtcp_bye((unsigned char *)buf,recvlen)) {
-                    if (strncmp(ip, "127.0.0.1",IRLP_IP_SIZE) != 0) {
-                       if (strncmp(remote_irlp_node_ip, ip, IRLP_IP_SIZE) != 0) { 
-                          strncpy(remote_irlp_node_ip, ip, IRLP_IP_SIZE);
-		          p->proto = IRLP_ISADPCM;
-			  cp = irlp_read_file(IRLP_ROOT,"codec");
-			  if (!strncasecmp(cp,"GSM",3))
-			  {
-				  p->proto = IRLP_ISGSM;
-			          ast->nativeformats = AST_FORMAT_GSM;
-				  ast_log(LOG_NOTICE,"irlp channel format set to GSM\n");
-			  }
-			  else 
-			  {
-			          ast->nativeformats = AST_FORMAT_ADPCM;
-				  ast_log(LOG_NOTICE,"irlp channel format set to ADPCM\n");
-			  }
-			  free(cp);
-			  ast_set_read_format(ast,ast->readformat);
-			  ast_set_write_format(ast,ast->writeformat);
-                          ast_log(LOG_NOTICE,"irlp node connected from %s\n", ip);
-                       }
-                    }
-                 } 
-                 else {
-                    if (strncmp(ip, remote_irlp_node_ip, IRLP_IP_SIZE) == 0) {
-                       remote_irlp_node_ip[0] = '\0'; 
-		       p->proto = IRLP_ISADPCM;
-		       ast->nativeformats = AST_FORMAT_ADPCM;
-		       ast_set_read_format(ast,ast->readformat);
-		       ast_set_write_format(ast,ast->writeformat);
-                       ast_log(LOG_NOTICE, "received IRLP bye from %s\n",ip);
-                    }
-                 }
-              }
-           }
-	}
-	
-        len = 0;
-        ioctl(p->audio_sock,FIONREAD,&len);
-        if (len > 0)
-	{
-           fromlen = sizeof(struct sockaddr_in);
-           recvlen = recvfrom(p->audio_sock,
-                              buf,
-                              LARGEST_PACKET_SIZE,
-                              0,
-                              (struct sockaddr *)&sin,&fromlen);
-           if (recvlen > 0)
-	     {
-              buf[recvlen] = '\0';
-#ifdef  OLD_ASTERISK
-              ast_inet_ntoa(ip,IRLP_IP_SIZE,sin.sin_addr);
-#else
-              strncpy(ip,ast_inet_ntoa(sin.sin_addr),IRLP_IP_SIZE);
-#endif
-	      i = p->proto;
-              len = ntohl(((struct irlp_audio *)buf)->buffer.buffer_len); 
-              if (((struct irlp_audio *)buf)->compression == htonl(0x200 | 0x40000000))
-	      {
-		  i = IRLP_ISADPCM;
-                  ast->nativeformats = AST_FORMAT_ADPCM;
-		  ast_set_read_format(ast,ast->readformat);
-		  ast_set_write_format(ast,ast->writeformat);
-	      }
-              if (((struct irlp_audio *)buf)->compression == htonl(0x20 | 0x40000000))
-	      {
-		  i = IRLP_ISGSM;
-                  ast->nativeformats = AST_FORMAT_GSM;
-		  ast_set_read_format(ast,ast->readformat);
-		  ast_set_write_format(ast,ast->writeformat);
-	      }
-              if (((strncmp(ip, remote_irlp_node_ip, IRLP_IP_SIZE) == 0) ||
-                   (strncmp(ip, "127.0.0.1", IRLP_IP_SIZE) == 0)) &&
-                   (len > IRLP_ADPCM_STATE_INFO_SIZE) &&
-                   ((recvlen - IRLP_HEADER_INFO_SIZE) == len)) {
-
-		 if (p->proto != i)
-		 {
-			if (i == IRLP_ISADPCM)
-				ast_log(LOG_NOTICE,"Proto changed to <ADPCM>\n");
-			else
-				ast_log(LOG_NOTICE,"Proto changed to <GSM>\n");
-		 }
-		 p->proto = i;
-
-
-                 qpast = ast_malloc(sizeof(struct irlp_rxqast) + len);
-                 if (!qpast) {
-                    ast_log(LOG_NOTICE,"Cannot malloc for qpast\n");
-                    return NULL;
-                 }
-		 if (p->proto == IRLP_ISADPCM)
-		 {
-	                 qpast->len = len;
-	                 memcpy(qpast->buf,((struct irlp_audio *)buf)->buffer.buffer_val,len);
-		 }
-		 else
-		 {
-	                 qpast->len = len - 2;
-	                 memcpy(qpast->buf,((struct irlp_audio *)buf)->buffer.buffer_val + 2,len);
-		 }
-                 insque((struct qelem *)qpast,(struct qelem *)p->rxqast.qe_back);
-
-                 if (strncmp(ip, "127.0.0.1", IRLP_IP_SIZE) == 0) {
-                    if (remote_irlp_node_ip[0] != '\0') {
-                       sin.sin_family = AF_INET;
-                       sin.sin_addr.s_addr = inet_addr(remote_irlp_node_ip);
-                       sin.sin_port = htons(audio_port);
-                       sendto(p->audio_sock,buf,recvlen,
-                               0,(struct sockaddr *)&sin,sizeof(struct sockaddr));
-                    }
-                 } else {
-                    sin.sin_family = AF_INET;
-                    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
-                    sin.sin_port = htons(localispeakerport); 
-                    sendto(p->audio_sock,buf,recvlen,
-                           0,(struct sockaddr *)&sin,sizeof(struct sockaddr));
-                 }
-              }
-           }
-        }
-  
+
         p->fr.frametype = 0;
         p->fr.subclass = 0;
         p->fr.datalen = 0;
@@ -668,8 +802,10 @@
 	if (frame->frametype != AST_FRAME_VOICE) 
            return 0;
 
+	if (proto == IRLP_NOPROTO) return 0;
+
 	frame_samples = 160;
-	if (p->proto == IRLP_ISGSM)
+	if (proto == IRLP_ISGSM)
 	{
 		blocking_factor = GSM_BLOCKING_FACTOR;
 		frame_size = GSM_FRAME_SIZE;
@@ -680,19 +816,19 @@
 		frame_size = ADPCM_FRAME_SIZE;
 	}
         /* IRLP to Asterisk */
-	if (p->rxqast.qe_forw != &p->rxqast) {
-		for(n = 0,qpast = p->rxqast.qe_forw; qpast != &p->rxqast; qpast = qpast->qe_forw) {
+	if (rxqast.qe_forw != &rxqast) {
+		for(n = 0,qpast = rxqast.qe_forw; qpast != &rxqast; qpast = qpast->qe_forw) {
 			n++;
 		}
 		if (n > QUEUE_OVERLOAD_THRESHOLD_AST) {
-			while(p->rxqast.qe_forw != &p->rxqast) {
-				qpast = p->rxqast.qe_forw;
+			while(rxqast.qe_forw != &rxqast) {
+				qpast = rxqast.qe_forw;
 				remque((struct qelem *)qpast);
 				ast_free(qpast);
 			}
-			if (p->outbuf_old) ast_free(p->outbuf_old);
-			p->rxlen = 0;
-			p->rxidx = 0;
+			if (outbuf_old) ast_free(outbuf_old);
+			rxlen = 0;
+			rxidx = 0;
 			if (p->rxkey) p->rxkey = 1;
 		} else {		
 			if (!p->rxkey) {
@@ -714,10 +850,10 @@
 	len = 0;
 	gotone = 0;
 	dosync = 0;
-	if ((p->rxqast.qe_forw != &p->rxqast) &&
-	    ((p->rxlen - p->rxidx) < frame_size))
-	{
-		qpast = p->rxqast.qe_forw;
+	if ((rxqast.qe_forw != &rxqast) &&
+	    ((rxlen - rxidx) < frame_size))
+	{
+		qpast = rxqast.qe_forw;
 		remque((struct qelem *)qpast);
 		len = qpast->len;
 		outbuf_new = malloc(len);
@@ -728,49 +864,49 @@
 		}
 		memcpy(outbuf_new,qpast->buf,len);
 		ast_free(qpast);
-		if (p->proto == IRLP_ISADPCM) len -= 3;
-		i = (p->rxlen - p->rxidx);
-		if ((p->proto == IRLP_ISADPCM) && (!p->rxidx) &&
+		if (proto == IRLP_ISADPCM) len -= 3;
+		i = (rxlen - rxidx);
+		if ((proto == IRLP_ISADPCM) && (!rxidx) &&
 			(len >= frame_size)) dosync = len;
 		/* if something to output */
 		if ((len + i) >= frame_size)
 		{
-			if (i && p->outbuf_old)
+			if (i && outbuf_old)
 			{
 				memcpy(outbuf + AST_FRIENDLY_OFFSET,
-					p->outbuf_old + p->rxidx,i);
-				ast_free(p->outbuf_old);
+					outbuf_old + rxidx,i);
+				ast_free(outbuf_old);
 			}
 			memcpy(outbuf + i + AST_FRIENDLY_OFFSET,
 				outbuf_new,frame_size - i);
-			p->rxlen = len;
-			p->rxidx = frame_size - i;
-			p->outbuf_old = outbuf_new;
+			rxlen = len;
+			rxidx = frame_size - i;
+			outbuf_old = outbuf_new;
 			gotone = 1;
 		} 
 		else
 		{
-			cp = malloc((p->rxlen - p->rxidx) + len);
+			cp = malloc((rxlen - rxidx) + len);
 			if (!cp)
 			{
 				ast_log(LOG_ERROR,"Cannot Malloc");
 				return -1;
 			}
-			if (p->rxlen) memcpy(cp,p->outbuf_old + p->rxidx,i);
+			if (rxlen) memcpy(cp,outbuf_old + rxidx,i);
 			memcpy(cp + i,outbuf_new,len);
-			p->rxlen = len + i;
-			p->rxidx = 0;
+			rxlen = len + i;
+			rxidx = 0;
 			free(outbuf_new);
 			outbuf_new = NULL;
-			free(p->outbuf_old);
-			p->outbuf_old = cp;
-		}
-	}
-	else if ((p->rxlen - p->rxidx) >= frame_size)
+			free(outbuf_old);
+			outbuf_old = cp;
+		}
+	}
+	else if ((rxlen - rxidx) >= frame_size)
 	{
 		memcpy(outbuf + AST_FRIENDLY_OFFSET,
-			p->outbuf_old + p->rxidx,frame_size);
-		p->rxidx += frame_size;
+			outbuf_old + rxidx,frame_size);
+		rxidx += frame_size;
 		gotone = 1;
 	}
 	if (gotone)
@@ -778,14 +914,14 @@
  		p->rxkey = MAX_RXKEY_TIME;
 		fr.datalen = frame_size;
 		fr.samples = frame_samples;
-		if ((p->proto == IRLP_ISADPCM) && dosync)
+		if ((proto == IRLP_ISADPCM) && dosync)
 		{
 			fr.datalen += 3;
 			memcpy(outbuf + AST_FRIENDLY_OFFSET + frame_size,
 				outbuf_new + dosync,3);
 		}
 		fr.frametype = AST_FRAME_VOICE;
-		if (p->proto == IRLP_ISADPCM)
+		if (proto == IRLP_ISADPCM)
 		{
 			fr.subclass = AST_FORMAT_ADPCM;
 		}
@@ -814,14 +950,14 @@
 		fr.delivery.tv_usec = 0;
 		ast_queue_frame(ast,&fr);
 
-		if (p->outbuf_old) ast_free(p->outbuf_old);
-		p->rxlen = 0;
-		p->rxidx = 0;
+		if (outbuf_old) ast_free(outbuf_old);
+		rxlen = 0;
+		rxidx = 0;
 	} 
 	if (p->rxkey) p->rxkey--;
 
 	i = AST_FORMAT_ADPCM;
-	if (p->proto == IRLP_ISGSM) i = AST_FORMAT_GSM;
+	if (proto == IRLP_ISGSM) i = AST_FORMAT_GSM;
 
         /* Asterisk to IRLP */
         if (!(frame->subclass & i)) {
@@ -829,19 +965,19 @@
              return 0;
         }
 	cp = frame->data;
-        if (p->txkey || p->txindex)  {
+        if (p->txkey || txindex)  {
              p->keepalive = KEEPALIVE_TIME;
-	     if ((p->proto == IRLP_ISADPCM) && (!p->txindex))
+	     if ((proto == IRLP_ISADPCM) && (!txindex))
 		 memcpy(tx_buf + (frame_size * 
 		    blocking_factor),cp + frame_size,3);
-             memcpy(tx_buf + (frame_size * p->txindex++), 
+             memcpy(tx_buf + (frame_size * txindex++), 
                     frame->data,frame_size);
         }     
 
-        if (p->txindex >= blocking_factor) { 
+        if (txindex >= blocking_factor) { 
 
            dp = (unsigned char *)irlp_audio_packet.buffer.buffer_val;
-	   if (p->proto == IRLP_ISADPCM)
+	   if (proto == IRLP_ISADPCM)
 	   {
 	           irlp_audio_packet.compression = htonl(0x200 | 0x40000000);
 	   }
@@ -852,7 +988,7 @@
            snprintf(irlp_audio_packet.sendinghost,IRLP_IP_SIZE,
                      "%s-%s",mynode,mycall);
 
-	   if (p->proto == IRLP_ISADPCM)
+	   if (proto == IRLP_ISADPCM)
 	   {
 		memcpy((char *)dp,tx_buf,(blocking_factor * frame_size) + 3);
 	        irlp_audio_packet.buffer.buffer_len = htonl((blocking_factor * 
@@ -871,14 +1007,14 @@
               sin.sin_addr.s_addr = inet_addr(remote_irlp_node_ip);
               sin.sin_port = htons(audio_port);
 
-	      if (p->proto == IRLP_ISADPCM)
+	      if (proto == IRLP_ISADPCM)
 	      {
-                  sendto(p->audio_sock,(char *)&irlp_audio_packet,507,
+                  sendto((alt_audio_sock != -1) ? alt_audio_sock : audio_sock,(char *)&irlp_audio_packet,507,
                       0,(struct sockaddr *)&sin,sizeof(struct sockaddr));
 	      }
 	      else
 	      {
-                  sendto(p->audio_sock,(char *)&irlp_audio_packet,356,
+                  sendto((alt_audio_sock != -1) ? alt_audio_sock : audio_sock,(char *)&irlp_audio_packet,356,
                       0,(struct sockaddr *)&sin,sizeof(struct sockaddr));
 	      }
            }
@@ -886,17 +1022,17 @@
            sin.sin_addr.s_addr = inet_addr("127.0.0.1");
            sin.sin_port = htons(localispeakerport);
 
-	   if (p->proto == IRLP_ISADPCM)
+	   if (proto == IRLP_ISADPCM)
 	   {
-               sendto(p->audio_sock,(char *)&irlp_audio_packet,507,
+               sendto(audio_sock,(char *)&irlp_audio_packet,507,
                   0,(struct sockaddr *)&sin,sizeof(struct sockaddr));
 	   }
 	   else
 	   {
-               sendto(p->audio_sock,(char *)&irlp_audio_packet,356,
+               sendto(audio_sock,(char *)&irlp_audio_packet,356,
                   0,(struct sockaddr *)&sin,sizeof(struct sockaddr));
 	   }
-           p->txindex = 0;
+           txindex = 0;
         }
 
 	if (p->txkey) return 0;
@@ -916,11 +1052,11 @@
 static struct ast_channel *irlp_new(struct irlp_pvt *i, int state)
 {
 	struct ast_channel *tmp;
-	tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, 0, "irlp/%s", i->stream);
+	char tmpstr[30];
+
+	tmp = ast_channel_alloc(1, state, 0, 0, "", astnode, context, 0, "irlp/%s", stream);
 	if (tmp) {
 		tmp->tech = &irlp_tech;
-                tmp->fds[0] = i->audio_sock;
-                tmp->fds[1] = i->ctrl_sock;
 		tmp->nativeformats = prefformat;
 		tmp->rawreadformat = prefformat;
 		tmp->rawwriteformat = prefformat;
@@ -930,8 +1066,10 @@
 			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_copy_string(tmp->exten, astnode,  sizeof(tmp->exten));
 		ast_string_field_set(tmp, language, "");
+		sprintf(tmpstr,"4%04u",(in_node) ? in_node : atoi(mynode));
+		ast_set_callerid(tmp,tmpstr,NULL,NULL);
 		i->owner = tmp;
 		i->u = ast_module_user_add(tmp);
 		if (state != AST_STATE_DOWN) {
@@ -952,23 +1090,40 @@
 	struct irlp_pvt *p;
 	struct ast_channel *tmp = NULL;
 	
+	if (curcall) 
+	{
+		ast_log(LOG_NOTICE,"Channel is busy!\n");
+		return NULL;
+	}
 	oldformat = format;
 	format &= (AST_FORMAT_ADPCM | AST_FORMAT_GSM);
 	if (!format) {
 		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
 		return NULL;
 	}
+	reset_stuff();
 	p = irlp_alloc(data);
 	if (p) {
 		tmp = irlp_new(p, AST_STATE_DOWN);
 		if (!tmp)
 			irlp_destroy(p);
 	}
+	curcall = tmp;
 	return tmp;
 }
 
 static int unload_module(void)
 {
+	run_forever = 0;
+	usleep(100000);
+	if (audio_sock != -1) close(audio_sock);
+	audio_sock = -1;
+	if (ctrl_sock != -1) close(ctrl_sock);
+	ctrl_sock = -1;
+	if (alt_audio_sock != -1) close(alt_audio_sock);
+	alt_audio_sock = -1;
+	if (alt_ctrl_sock != -1) close(alt_ctrl_sock);
+	alt_ctrl_sock = -1;
 	/* First, take us out of the channel loop */
 	ast_channel_unregister(&irlp_tech);
 	return 0;
@@ -978,6 +1133,9 @@
 {
 	struct ast_config *cfg = NULL;
         char *val = NULL;
+	pthread_attr_t attr;
+	struct sockaddr_in si_me;
+
 
 #ifdef  NEW_ASTERISK
         struct ast_flags zeroflag = {0};
@@ -1007,7 +1165,7 @@
 
         val = (char *)ast_variable_retrieve(cfg,"general","node");
         if (!val)
-           strncpy(mynode,"stnXXXX",IRLP_NODE_SIZE);
+           strncpy(mynode,"XXXX",IRLP_NODE_SIZE);
         else
            strncpy(mynode,val,IRLP_NODE_SIZE);
 
@@ -1017,12 +1175,80 @@
         else
            localispeakerport = atoi(val);
 
+        val = (char *)ast_variable_retrieve(cfg,"general","radmode");
+        if (val) radmode = ast_true(val);
+
+        val = (char *)ast_variable_retrieve(cfg,"general","audioport");
+        if (!val)
+	    audio_port_cfg = 2074;
+        else
+            audio_port_cfg = atoi(val);
+
+        val = (char *)ast_variable_retrieve(cfg,"general","astnode");
+        if (!val)
+	   astnode[0] = 0;
+        else
+           strncpy(astnode,val,sizeof(astnode) - 1);
+
+        val = (char *)ast_variable_retrieve(cfg,"general","context");
+        if (!val)
+	   context[0] = 0;
+        else
+           strncpy(context,val,sizeof(context) - 1);
+
         /* initialize local and remote IRLP node */
-        remote_irlp_node_ip[0] = '\0'; 
+	reset_stuff();
+	curcall = 0;
 
         ast_config_destroy(cfg);
+
+	ast_safe_system(IRLP_RESET);
+	usleep(10000);
+
+	audio_port = audio_port_cfg;
+	if ((audio_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) 
+	{
+		ast_log(LOG_WARNING, 
+                      "Unable to create new socket for irlp audio connection\n");
+		return -1;
+	}
+		
+	if ((ctrl_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
+	{
+		ast_log(LOG_WARNING, 
+                        "Unable to create new socket for irlp control connection\n");
+                close(audio_sock);
+		return -1;
+	}
+	memset((char *) &si_me, 0, sizeof(si_me));
+	si_me.sin_family = AF_INET;
+        si_me.sin_addr.s_addr = htonl(INADDR_ANY);
+	si_me.sin_port = htons(audio_port);               
+	if (bind(audio_sock, &si_me, sizeof(si_me))==-1)
+	{
+		ast_log(LOG_WARNING, "Unable to bind port for irlp audio connection\n");
+                close(ctrl_sock); ctrl_sock = -1;
+                close(audio_sock); audio_sock = -1;
+		return -1;
+	}
+        ctrl_port = audio_port + 1;
+	si_me.sin_port = htons(ctrl_port);
+	if (bind(ctrl_sock, &si_me, sizeof(si_me))==-1)
+	{
+		ast_log(LOG_WARNING, "Unable to bind port for irlp control connection\n");
+                close(ctrl_sock); ctrl_sock = -1;
+                close(audio_sock); audio_sock = -1;
+	}
+        fcntl(audio_sock,F_SETFL,O_NONBLOCK);
+        fcntl(ctrl_sock,F_SETFL,O_NONBLOCK);
+
+	ast_safe_system(IRLP_RESET);
+	usleep(100000);
         cfg = NULL; 
 
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+        ast_pthread_create(&irlp_reader_thread,&attr,irlp_reader,NULL);
 	/* Make sure we can register our channel type */
 	if (ast_channel_register(&irlp_tech)) {
 		ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);




More information about the asterisk-commits mailing list