| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * Copyright (C) 1999 - 2005, Digium, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Mark Spencer <markster@digium.com> | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * 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. | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This program is free software, distributed under the terms of | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-24 20:12:06 +00:00
										 |  |  | /*! \file
 | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-10-24 20:12:06 +00:00
										 |  |  |  * \brief ISDN4Linux TTY Driver | 
					
						
							| 
									
										
										
										
											2005-12-30 21:18:06 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * \author Mark Spencer <markster@digium.com> | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  *  | 
					
						
							| 
									
										
										
										
											2005-11-06 15:09:47 +00:00
										 |  |  |  * \ingroup channel_drivers | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | #include <sys/ioctl.h>
 | 
					
						
							| 
									
										
										
										
											2005-06-06 21:09:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-06-06 22:12:19 +00:00
										 |  |  | ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | 
					
						
							| 
									
										
										
										
											2005-06-06 21:09:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-21 06:02:45 +00:00
										 |  |  | #include "asterisk/lock.h"
 | 
					
						
							|  |  |  | #include "asterisk/vmodem.h"
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/frame.h"
 | 
					
						
							|  |  |  | #include "asterisk/logger.h"
 | 
					
						
							|  |  |  | #include "asterisk/options.h"
 | 
					
						
							|  |  |  | #include "asterisk/dsp.h"
 | 
					
						
							|  |  |  | #include "asterisk/callerid.h"
 | 
					
						
							|  |  |  | #include "asterisk/ulaw.h"
 | 
					
						
							|  |  |  | #include "asterisk/pbx.h"
 | 
					
						
							| 
									
										
										
										
											2005-10-27 02:19:37 +00:00
										 |  |  | #include "asterisk/utils.h"
 | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define STATE_COMMAND 	0
 | 
					
						
							|  |  |  | #define STATE_VOICE 	1
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static char *breakcmd = "\0x10\0x14\0x10\0x3"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static char *desc = "ISDN4Linux Emulated Modem Driver"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-27 21:34:27 +00:00
										 |  |  | static int usecnt; | 
					
						
							| 
									
										
										
										
											2004-06-09 01:45:08 +00:00
										 |  |  | AST_MUTEX_DEFINE_STATIC(usecnt_lock); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static char *i4l_idents[] = { | 
					
						
							|  |  |  | 	/* Identify ISDN4Linux Driver */ | 
					
						
							|  |  |  | 	"Linux ISDN", | 
					
						
							|  |  |  | 	NULL | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i4l_setdev(struct ast_modem_pvt *p, int dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char cmd[80]; | 
					
						
							|  |  |  | 	if ((dev != MODEM_DEV_TELCO) && (dev != MODEM_DEV_TELCO_SPK)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "ISDN4Linux only supports telco device, not %d.\n", dev); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} else	/* Convert DEV to our understanding of it */ | 
					
						
							|  |  |  | 		dev = 2; | 
					
						
							|  |  |  | 	if (ast_modem_send(p, "AT+VLS?", 0)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to select current mode %d\n", dev); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ast_modem_read_response(p, 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to select device %d\n", dev); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_modem_trim(p->response); | 
					
						
							| 
									
										
										
										
											2001-10-30 14:44:25 +00:00
										 |  |  | 	strncpy(cmd, p->response, sizeof(cmd)-1); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	if (ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Modem did not respond properly\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (dev == atoi(cmd)) { | 
					
						
							|  |  |  | 		/* We're already in the right mode, don't bother changing for fear of
 | 
					
						
							|  |  |  | 		   hanging up */ | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	snprintf(cmd, sizeof(cmd), "AT+VLS=%d", dev); | 
					
						
							|  |  |  | 	if (ast_modem_send(p, cmd, 0))  { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to select device %d\n", dev); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ast_modem_read_response(p, 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to select device %d\n", dev); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_modem_trim(p->response); | 
					
						
							|  |  |  | 	if (strcasecmp(p->response, "VCON") && strcasecmp(p->response, "OK")) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unexpected reply: %s\n", p->response); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i4l_startrec(struct ast_modem_pvt *p) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (ast_modem_send(p, "AT+VRX+VTX", 0) || | 
					
						
							|  |  |  | 	     ast_modem_expect(p, "CONNECT", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to start recording\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	p->ministate = STATE_VOICE; | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	/*  let ast dsp detect dtmf */ | 
					
						
							|  |  |  | 	if (p->dtmfmode & MODEM_DTMF_AST) { | 
					
						
							|  |  |  | 		if (p->dsp) { | 
					
						
							|  |  |  | 			ast_log(LOG_DEBUG, "Already have a dsp on %s?\n", p->dev); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			p->dsp = ast_dsp_new(); | 
					
						
							|  |  |  | 			if (p->dsp) { | 
					
						
							|  |  |  | 				ast_log(LOG_DEBUG, "Detecting DTMF inband with sw DSP on %s\n",p->dev); | 
					
						
							| 
									
										
										
										
											2005-03-26 07:12:19 +00:00
										 |  |  | 				ast_dsp_set_features(p->dsp, DSP_FEATURE_DTMF_DETECT|DSP_FEATURE_FAX_DETECT); | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 				ast_dsp_digitmode(p->dsp, DSP_DIGITMODE_DTMF | 0); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i4l_break(struct ast_modem_pvt *p) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (ast_modem_send(p, breakcmd, 2)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Failed to break\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ast_modem_send(p, "\r\n", 2)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Failed to send enter?\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | 	/* Read any outstanding junk */ | 
					
						
							|  |  |  | 	while(!ast_modem_read_response(p, 1)); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	if (ast_modem_send(p, "AT", 0)) { | 
					
						
							|  |  |  | 		/* Modem might be stuck in some weird mode, try to get it out */ | 
					
						
							|  |  |  | 		ast_modem_send(p, "+++", 3); | 
					
						
							|  |  |  | 		if (ast_modem_expect(p, "OK", 10)) { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Modem is not responding\n"); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (ast_modem_send(p, "AT", 0)) { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Modem is not responding\n"); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Modem did not respond properly\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i4l_init(struct ast_modem_pvt *p) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char cmd[256]; | 
					
						
							|  |  |  | 	if (option_debug) | 
					
						
							|  |  |  | 		ast_log(LOG_DEBUG, "i4l_init()\n"); | 
					
						
							|  |  |  | 	if (i4l_break(p)) | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	/* Force into command mode */ | 
					
						
							|  |  |  | 	p->ministate = STATE_COMMAND; | 
					
						
							|  |  |  | 	if (ast_modem_send(p, "AT+FCLASS=8", 0) || | 
					
						
							|  |  |  | 	     ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to set to voice mode\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-10-27 02:19:37 +00:00
										 |  |  | 	if (!ast_strlen_zero(p->msn)) { | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		snprintf(cmd, sizeof(cmd), "AT&E%s", p->msn); | 
					
						
							|  |  |  | 		if (ast_modem_send(p, cmd, 0) || | 
					
						
							|  |  |  | 		    ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Unable to set MSN to %s\n", p->msn); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-10-27 02:19:37 +00:00
										 |  |  | 	if (!ast_strlen_zero(p->incomingmsn)) { | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 		char *q; | 
					
						
							|  |  |  | 		snprintf(cmd, sizeof(cmd), "AT&L%s", p->incomingmsn); | 
					
						
							| 
									
										
										
										
											2004-12-19 21:13:41 +00:00
										 |  |  | 		/* translate , into ; since that is the seperator I4L uses, but can't be directly */ | 
					
						
							|  |  |  | 		/* put in the config file because it will interpret the rest of the line as comment. */ | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 		q = cmd+4; | 
					
						
							|  |  |  | 		while (*q) { | 
					
						
							|  |  |  | 			if (*q == ',') *q = ';'; | 
					
						
							|  |  |  | 			++q; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (ast_modem_send(p, cmd, 0) || | 
					
						
							|  |  |  | 		    ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Unable to set Listen to %s\n", p->msn); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ast_modem_send(p, "AT&D2", 0) || | 
					
						
							|  |  |  | 	     ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to set to DTR disconnect mode\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	if (ast_modem_send(p, "ATS18=1", 0) || | 
					
						
							|  |  |  | 	     ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to set to audio only mode\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 	if (ast_modem_send(p, "ATS13.6=1", 0) || | 
					
						
							|  |  |  | 	     ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to set to RUNG indication\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	if (ast_modem_send(p, "ATS14=4", 0) || | 
					
						
							|  |  |  | 	     ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to set to transparent mode\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2002-02-08 12:47:18 +00:00
										 |  |  | 	if (ast_modem_send(p, "ATS23=9", 0) || | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	     ast_modem_expect(p, "OK", 5)) { | 
					
						
							| 
									
										
										
										
											2002-02-08 12:47:18 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Unable to set to transparent/ringing mode\n"); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-01-24 21:43:35 +00:00
										 |  |  | 	if (ast_modem_send(p, "AT+VSM=6", 0) || | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	     ast_modem_expect(p, "OK", 5)) { | 
					
						
							| 
									
										
										
										
											2005-01-24 21:43:35 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Unable to set to muLAW mode\n"); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ast_modem_send(p, "AT+VLS=2", 0) || | 
					
						
							|  |  |  | 	     ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to set to phone line interface\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	p->escape = 0; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_frame *i4l_handle_escape(struct ast_modem_pvt *p, char esc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Handle escaped characters -- but sometimes we call it directly as 
 | 
					
						
							|  |  |  | 	   a quick way to cause known responses */ | 
					
						
							|  |  |  | 	p->fr.frametype = AST_FRAME_NULL; | 
					
						
							|  |  |  | 	p->fr.subclass = 0; | 
					
						
							|  |  |  | 	p->fr.data = NULL; | 
					
						
							|  |  |  | 	p->fr.datalen = 0; | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 	p->fr.samples = 0; | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	p->fr.offset = 0; | 
					
						
							|  |  |  | 	p->fr.mallocd = 0; | 
					
						
							| 
									
										
										
										
											2004-06-24 13:27:44 +00:00
										 |  |  | 	p->fr.delivery.tv_sec = 0; | 
					
						
							|  |  |  | 	p->fr.delivery.tv_usec = 0; | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	if (esc && option_debug) | 
					
						
							|  |  |  | 		ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	switch(esc) { | 
					
						
							|  |  |  | 	case 'R': /* Pseudo ring */ | 
					
						
							|  |  |  | 		p->fr.frametype = AST_FRAME_CONTROL; | 
					
						
							|  |  |  | 		p->fr.subclass = AST_CONTROL_RING; | 
					
						
							|  |  |  | 		return &p->fr; | 
					
						
							| 
									
										
										
										
											2002-02-08 12:47:18 +00:00
										 |  |  | 	case 'I': /* Pseudo ringing */ | 
					
						
							|  |  |  | 		p->fr.frametype = AST_FRAME_CONTROL; | 
					
						
							|  |  |  | 		p->fr.subclass =  AST_CONTROL_RINGING; | 
					
						
							|  |  |  | 		return &p->fr; | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	case 'X': /* Pseudo connect */ | 
					
						
							|  |  |  | 		p->fr.frametype = AST_FRAME_CONTROL; | 
					
						
							|  |  |  | 		p->fr.subclass = AST_CONTROL_ANSWER; | 
					
						
							|  |  |  | 		if (p->owner) | 
					
						
							| 
									
										
										
										
											2002-07-12 09:03:50 +00:00
										 |  |  | 			ast_setstate(p->owner, AST_STATE_UP); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		if (i4l_startrec(p)) | 
					
						
							|  |  |  | 			return 	NULL; | 
					
						
							|  |  |  | 		return &p->fr; | 
					
						
							|  |  |  | 	case 'b': /* Busy signal */ | 
					
						
							|  |  |  | 		p->fr.frametype = AST_FRAME_CONTROL; | 
					
						
							|  |  |  | 		p->fr.subclass = AST_CONTROL_BUSY; | 
					
						
							|  |  |  | 		return &p->fr; | 
					
						
							|  |  |  | 	case 'o': /* Overrun */ | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n"); | 
					
						
							|  |  |  | 		if (ast_modem_send(p, "\0x10E", 2))  | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Unable to flush buffers\n"); | 
					
						
							|  |  |  | 		return &p->fr;	 | 
					
						
							|  |  |  | 	case CHAR_ETX: /* End Transmission */ | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	case 'u': /* Underrun */ | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Data underrun\n"); | 
					
						
							|  |  |  | 		/* Fall Through */ | 
					
						
							|  |  |  | 	case 'd': /* Dialtone */ | 
					
						
							|  |  |  | 	case 'c': /* Calling Tone */ | 
					
						
							|  |  |  | 	case 'e': /* European version */ | 
					
						
							|  |  |  | 	case 'a': /* Answer Tone */ | 
					
						
							|  |  |  | 	case 'f': /* Bell Answer Tone */ | 
					
						
							|  |  |  | 	case 'T': /* Timing mark */ | 
					
						
							|  |  |  | 	case 't': /* Handset off hook */ | 
					
						
							|  |  |  | 	case 'h': /* Handset hungup */ | 
					
						
							|  |  |  | 		/* Ignore */ | 
					
						
							|  |  |  | 		if (option_debug) | 
					
						
							|  |  |  | 			ast_log(LOG_DEBUG, "Ignoring Escaped character '%c' (%d)\n", esc, esc); | 
					
						
							|  |  |  | 		return &p->fr; | 
					
						
							|  |  |  | 	case '0': | 
					
						
							|  |  |  | 	case '1': | 
					
						
							|  |  |  | 	case '2': | 
					
						
							|  |  |  | 	case '3': | 
					
						
							|  |  |  | 	case '4': | 
					
						
							|  |  |  | 	case '5': | 
					
						
							|  |  |  | 	case '6': | 
					
						
							|  |  |  | 	case '7': | 
					
						
							|  |  |  | 	case '8': | 
					
						
							|  |  |  | 	case '9': | 
					
						
							|  |  |  | 	case '*': | 
					
						
							|  |  |  | 	case '#': | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 		ast_log(LOG_DEBUG, "Detected outband DTMF digit: '%c' (%d)\n", esc, esc); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		p->fr.frametype=AST_FRAME_DTMF; | 
					
						
							|  |  |  | 		p->fr.subclass=esc; | 
					
						
							|  |  |  | 		return &p->fr; | 
					
						
							|  |  |  | 	case 0: /* Pseudo signal */ | 
					
						
							|  |  |  | 		return &p->fr; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &p->fr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_frame *i4l_read(struct ast_modem_pvt *p) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-11-13 00:52:57 +00:00
										 |  |  | 	unsigned char result[256]; | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	short *b; | 
					
						
							|  |  |  | 	struct ast_frame *f=NULL; | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 	int x; | 
					
						
							|  |  |  | 	if (p->ministate == STATE_COMMAND) { | 
					
						
							|  |  |  | 		/* Read the first two bytes, first, in case it's a control message */ | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 		res = read(p->fd, result, 2); | 
					
						
							|  |  |  | 		if (res < 2) { | 
					
						
							| 
									
										
										
										
											2004-12-19 21:13:41 +00:00
										 |  |  | 			/* short read, means there was a hangup? */ | 
					
						
							|  |  |  | 			/* (or is this also possible without hangup?) */ | 
					
						
							|  |  |  | 			/* Anyway, reading from unitialized buffers is a bad idea anytime. */ | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 			if (errno == EAGAIN) | 
					
						
							|  |  |  | 				return i4l_handle_escape(p, 0); | 
					
						
							|  |  |  | 			return NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		if (result[0] == CHAR_DLE) { | 
					
						
							|  |  |  | 			return i4l_handle_escape(p, result[1]); | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if ((result[0] == '\n') || (result[0] == '\r')) | 
					
						
							|  |  |  | 				return i4l_handle_escape(p, 0); | 
					
						
							|  |  |  | 			/* Read the rest of the line */ | 
					
						
							|  |  |  | 			fgets(result + 2, sizeof(result) - 2, p->f); | 
					
						
							|  |  |  | 			ast_modem_trim(result); | 
					
						
							|  |  |  | 			if (!strcasecmp(result, "VCON")) { | 
					
						
							|  |  |  | 				/* If we're in immediate mode, reply now */ | 
					
						
							| 
									
										
										
										
											2004-12-19 21:13:41 +00:00
										 |  |  | /*				if (p->mode == MODEM_MODE_IMMEDIATE) */ | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 					return i4l_handle_escape(p, 'X'); | 
					
						
							|  |  |  | 			} else | 
					
						
							|  |  |  | 			if (!strcasecmp(result, "BUSY")) { | 
					
						
							|  |  |  | 				/* Same as a busy signal */ | 
					
						
							|  |  |  | 				return i4l_handle_escape(p, 'b'); | 
					
						
							|  |  |  | 			} else | 
					
						
							|  |  |  | 			if (!strncasecmp(result, "CALLER NUMBER: ", 15 )) { | 
					
						
							| 
									
										
										
										
											2004-10-02 00:58:31 +00:00
										 |  |  | 				strncpy(p->cid_num, result + 15, sizeof(p->cid_num)-1); | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 				return i4l_handle_escape(p, 0); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 			} else | 
					
						
							| 
									
										
										
										
											2002-02-08 12:47:18 +00:00
										 |  |  | 			if (!strcasecmp(result, "RINGING")) { | 
					
						
							|  |  |  | 				if (option_verbose > 2) | 
					
						
							|  |  |  | 					ast_verbose(VERBOSE_PREFIX_3 "%s is ringing...\n", p->dev); | 
					
						
							|  |  |  | 				return i4l_handle_escape(p, 'I'); | 
					
						
							|  |  |  | 			} else | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 			if (!strncasecmp(result, "RUNG", 4)) { | 
					
						
							|  |  |  | 				/* PM2002: the line was hung up before we picked it up, bye bye */ | 
					
						
							|  |  |  | 				if (option_verbose > 2)  | 
					
						
							|  |  |  | 					ast_verbose(VERBOSE_PREFIX_3 "%s was hung up on before we answered\n", p->dev); | 
					
						
							|  |  |  | 				return NULL; | 
					
						
							|  |  |  | 			} else | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 			if (!strncasecmp(result, "RING", 4)) { | 
					
						
							|  |  |  | 				if (result[4]=='/')  | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 					strncpy(p->dnid, result + 5, sizeof(p->dnid)-1); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 				return i4l_handle_escape(p, 'R'); | 
					
						
							|  |  |  | 			} else | 
					
						
							|  |  |  | 			if (!strcasecmp(result, "NO CARRIER")) { | 
					
						
							|  |  |  | 				if (option_verbose > 2)  | 
					
						
							|  |  |  | 					ast_verbose(VERBOSE_PREFIX_3 "%s hung up on\n", p->dev); | 
					
						
							|  |  |  | 				return NULL; | 
					
						
							|  |  |  | 			} else | 
					
						
							|  |  |  | 			if (!strcasecmp(result, "NO DIALTONE")) { | 
					
						
							|  |  |  | 				/* There's no dialtone, so the line isn't working */ | 
					
						
							|  |  |  | 				ast_log(LOG_WARNING, "Device '%s' lacking dialtone\n", p->dev); | 
					
						
							|  |  |  | 				return NULL; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (option_debug) | 
					
						
							|  |  |  | 				ast_log(LOG_DEBUG, "Modem said '%s'\n", result); | 
					
						
							|  |  |  | 			return i4l_handle_escape(p, 0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* We have to be more efficient in voice mode */ | 
					
						
							|  |  |  | 		b = (short *)(p->obuf + p->obuflen); | 
					
						
							|  |  |  | 		while (p->obuflen/2 < 240) { | 
					
						
							|  |  |  | 			/* Read ahead the full amount */ | 
					
						
							|  |  |  | 			res = read(p->fd, result, 240 - p->obuflen/2); | 
					
						
							|  |  |  | 			if (res < 1) { | 
					
						
							|  |  |  | 				/* If there's nothing there, just continue on */ | 
					
						
							|  |  |  | 				if (errno == EAGAIN) | 
					
						
							|  |  |  | 					return i4l_handle_escape(p, 0); | 
					
						
							|  |  |  | 				ast_log(LOG_WARNING, "Read failed: %s\n", strerror(errno)); | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 				return NULL; | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			for (x=0;x<res;x++) { | 
					
						
							|  |  |  | 				/* Process all the bytes that we've read */ | 
					
						
							|  |  |  | 				switch(result[x]) { | 
					
						
							|  |  |  | 				case CHAR_DLE: | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | 					ast_log(LOG_DEBUG, "Ooh, an escape at %d...\n", x); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 					if (!p->escape) { | 
					
						
							|  |  |  | 						/* Note that next value is
 | 
					
						
							|  |  |  | 						   an escape, and continue. */ | 
					
						
							|  |  |  | 						p->escape++; | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						/* Send as is -- fallthrough */ | 
					
						
							|  |  |  | 						p->escape = 0; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					if (p->escape) { | 
					
						
							|  |  |  | 						ast_log(LOG_DEBUG, "Value of escape is %c (%d)...\n", result[x] < 32 ? '^' : result[x], result[x]); | 
					
						
							|  |  |  | 						p->escape = 0; | 
					
						
							|  |  |  | 						if (f)  | 
					
						
							|  |  |  | 							ast_log(LOG_WARNING, "Warning: Dropped a signal frame\n"); | 
					
						
							|  |  |  | 						f = i4l_handle_escape(p, result[x]); | 
					
						
							|  |  |  | 						/* If i4l_handle_escape says NULL, say it now, doesn't matter
 | 
					
						
							|  |  |  | 						what else is there, the connection is dead. */ | 
					
						
							|  |  |  | 						if (!f) | 
					
						
							|  |  |  | 							return NULL; | 
					
						
							|  |  |  | 					} else { | 
					
						
							| 
									
										
										
										
											2005-08-05 16:29:30 +00:00
										 |  |  | 						*(b++) = AST_MULAW((int)result[x]); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 						p->obuflen += 2; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (f) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 		if (f) { | 
					
						
							|  |  |  | 			if( ! (!(p->dtmfmode & MODEM_DTMF_I4L) && f->frametype == AST_FRAME_DTMF)) | 
					
						
							| 
									
										
										
										
											2001-03-07 00:52:22 +00:00
										 |  |  | 			return f; | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		/* If we get here, we have a complete voice frame */ | 
					
						
							|  |  |  | 		p->fr.frametype = AST_FRAME_VOICE; | 
					
						
							|  |  |  | 		p->fr.subclass = AST_FORMAT_SLINEAR; | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 		p->fr.samples = 240; | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		p->fr.data = p->obuf; | 
					
						
							|  |  |  | 		p->fr.datalen = p->obuflen; | 
					
						
							|  |  |  | 		p->fr.mallocd = 0; | 
					
						
							| 
									
										
										
										
											2004-06-24 13:27:44 +00:00
										 |  |  | 		p->fr.delivery.tv_sec = 0; | 
					
						
							|  |  |  | 		p->fr.delivery.tv_usec = 0; | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		p->fr.offset = AST_FRIENDLY_OFFSET; | 
					
						
							|  |  |  | 		p->fr.src = __FUNCTION__; | 
					
						
							|  |  |  | 		p->obuflen = 0; | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* process with dsp */ | 
					
						
							|  |  |  | 		if (p->dsp) { | 
					
						
							|  |  |  | 			f = ast_dsp_process(p->owner, p->dsp, &p->fr); | 
					
						
							|  |  |  | 			if (f && (f->frametype == AST_FRAME_DTMF)) { | 
					
						
							|  |  |  | 				ast_log(LOG_DEBUG, "Detected inband DTMF digit: %c on %s\n", f->subclass, p->dev); | 
					
						
							| 
									
										
										
										
											2005-03-26 07:12:19 +00:00
										 |  |  | 				if (f->subclass == 'f') { | 
					
						
							|  |  |  | 					/* Fax tone -- Handle and return NULL */ | 
					
						
							|  |  |  | 					struct ast_channel *ast = p->owner; | 
					
						
							|  |  |  | 					if (!p->faxhandled) { | 
					
						
							|  |  |  | 						p->faxhandled++; | 
					
						
							|  |  |  | 						if (strcmp(ast->exten, "fax")) { | 
					
						
							| 
									
										
										
										
											2005-06-06 03:18:51 +00:00
										 |  |  | 							const char *target_context = ast_strlen_zero(ast->macrocontext) ? ast->context : ast->macrocontext; | 
					
						
							|  |  |  | 							 | 
					
						
							|  |  |  | 							if (ast_exists_extension(ast, target_context, "fax", 1, ast->cid.cid_num)) { | 
					
						
							| 
									
										
										
										
											2005-03-26 07:12:19 +00:00
										 |  |  | 								if (option_verbose > 2) | 
					
						
							|  |  |  | 									ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension\n", ast->name); | 
					
						
							|  |  |  | 								/* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ | 
					
						
							| 
									
										
										
										
											2005-06-06 03:18:51 +00:00
										 |  |  | 								pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); | 
					
						
							|  |  |  | 								if (ast_async_goto(ast, target_context, "fax", 1)) | 
					
						
							|  |  |  | 									ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context); | 
					
						
							| 
									
										
										
										
											2005-03-26 07:12:19 +00:00
										 |  |  | 							} else | 
					
						
							|  |  |  | 								ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); | 
					
						
							|  |  |  | 						} else | 
					
						
							|  |  |  | 							ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n"); | 
					
						
							|  |  |  | 					} else | 
					
						
							|  |  |  | 						ast_log(LOG_DEBUG, "Fax already handled\n"); | 
					
						
							|  |  |  | 					p->fr.frametype = AST_FRAME_NULL; | 
					
						
							|  |  |  | 					p->fr.subclass = 0; | 
					
						
							|  |  |  | 					f = &p->fr; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 				return f; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		return &p->fr; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i4l_write(struct ast_modem_pvt *p, struct ast_frame *f) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2004-05-11 15:00:28 +00:00
										 |  |  | #define MAX_WRITE_SIZE 2048
 | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	unsigned char result[MAX_WRITE_SIZE << 1]; | 
					
						
							|  |  |  | 	unsigned char b; | 
					
						
							|  |  |  | 	int bpos=0, x; | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 	if (f->datalen > MAX_WRITE_SIZE) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Discarding too big frame of size %d\n", f->datalen); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (f->frametype != AST_FRAME_VOICE) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Don't know how to handle %d type frames\n", f->frametype); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (f->subclass != AST_FORMAT_SLINEAR) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Don't know how to handle anything but signed linear frames\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for (x=0;x<f->datalen/2;x++) { | 
					
						
							| 
									
										
										
										
											2005-01-24 21:43:35 +00:00
										 |  |  | 		b = AST_LIN2MU(((short *)f->data)[x]); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		result[bpos++] = b; | 
					
						
							|  |  |  | 		if (b == CHAR_DLE) | 
					
						
							|  |  |  | 			result[bpos++]=b; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | 	res = fwrite(result, bpos, 1, p->f); | 
					
						
							|  |  |  | 	res *= bpos; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 	res = write(p->fd, result, bpos); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	if (res < 1) { | 
					
						
							| 
									
										
										
										
											2002-05-13 22:29:39 +00:00
										 |  |  | 		if (errno != EAGAIN) { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Failed to write buffer\n"); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | 	printf("Result of write is %d\n", res); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static char *i4l_identify(struct ast_modem_pvt *p) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return strdup("Linux ISDN"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-03-10 06:00:17 +00:00
										 |  |  | static void i4l_incusecnt(void) | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 	ast_mutex_lock(&usecnt_lock); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	usecnt++; | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 	ast_mutex_unlock(&usecnt_lock); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	ast_update_use_count(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-03-10 06:00:17 +00:00
										 |  |  | static void i4l_decusecnt(void) | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 	ast_mutex_lock(&usecnt_lock); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	usecnt++; | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 	ast_mutex_unlock(&usecnt_lock); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	ast_update_use_count(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i4l_answer(struct ast_modem_pvt *p) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2001-10-30 14:44:25 +00:00
										 |  |  | 	if (ast_modem_send(p, "ATA\r", 4) || | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	     ast_modem_expect(p, "VCON", 10)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to answer: %s", p->response); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #if 1
 | 
					
						
							|  |  |  | 	if (ast_modem_send(p, "AT+VDD=0,8", 0) || | 
					
						
							|  |  |  | 	     ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to set to phone line interface\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	if (ast_modem_send(p, "AT+VTX+VRX", 0) || | 
					
						
							|  |  |  | 	     ast_modem_expect(p, "CONNECT", 10)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to answer: %s", p->response); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	p->ministate = STATE_VOICE; | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*  let ast dsp detect dtmf */ | 
					
						
							|  |  |  | 	if (p->dtmfmode & MODEM_DTMF_AST) { | 
					
						
							|  |  |  | 		if (p->dsp) { | 
					
						
							|  |  |  | 			ast_log(LOG_DEBUG, "Already have a dsp on %s?\n", p->dev); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			p->dsp = ast_dsp_new(); | 
					
						
							|  |  |  | 			if (p->dsp) { | 
					
						
							|  |  |  | 				ast_log(LOG_DEBUG, "Detecting DTMF inband with sw DSP on %s\n",p->dev); | 
					
						
							| 
									
										
										
										
											2005-03-26 07:12:19 +00:00
										 |  |  | 				ast_dsp_set_features(p->dsp, DSP_FEATURE_DTMF_DETECT|DSP_FEATURE_FAX_DETECT); | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 				ast_dsp_digitmode(p->dsp, DSP_DIGITMODE_DTMF | 0); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i4l_dialdigit(struct ast_modem_pvt *p, char digit) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char c[2]; | 
					
						
							|  |  |  | 	if (p->ministate == STATE_VOICE) { | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 		if (p->dtmfmodegen & MODEM_DTMF_I4L) { | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		c[0] = CHAR_DLE; | 
					
						
							|  |  |  | 		c[1] = digit; | 
					
						
							|  |  |  | 		write(p->fd, c, 2); | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 			ast_log(LOG_DEBUG, "Send ISDN out-of-band DTMF %c\n",digit); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if(p->dtmfmodegen & MODEM_DTMF_AST) { | 
					
						
							|  |  |  | 			ast_log(LOG_DEBUG, "Generating inband DTMF\n"); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	} else | 
					
						
							|  |  |  | 		ast_log(LOG_DEBUG, "Asked to send digit but call not up on %s\n", p->dev); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i4l_dial(struct ast_modem_pvt *p, char *stuff) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char cmd[80]; | 
					
						
							| 
									
										
										
										
											2004-06-21 04:29:50 +00:00
										 |  |  | 	char tmpmsn[255]; | 
					
						
							|  |  |  | 	struct ast_channel *c = p->owner; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-12-19 21:13:41 +00:00
										 |  |  | 	/* Find callerid number first, to set the correct A number */ | 
					
						
							| 
									
										
										
										
											2004-10-02 00:58:31 +00:00
										 |  |  | 	if (c && c->cid.cid_num && !(c->cid.cid_pres & 0x20)) { | 
					
						
							|  |  |  | 	    snprintf(tmpmsn, sizeof(tmpmsn), ",%s,", c->cid.cid_num); | 
					
						
							| 
									
										
										
										
											2005-10-27 02:19:37 +00:00
										 |  |  | 	    if(!ast_strlen_zero(p->outgoingmsn) && strstr(p->outgoingmsn,tmpmsn) != NULL) { | 
					
						
							| 
									
										
										
										
											2004-12-19 21:13:41 +00:00
										 |  |  | 	      /* Tell ISDN4Linux to use this as A number */ | 
					
						
							| 
									
										
										
										
											2004-10-02 00:58:31 +00:00
										 |  |  | 	      snprintf(cmd, sizeof(cmd), "AT&E%s\n", c->cid.cid_num); | 
					
						
							| 
									
										
										
										
											2004-06-21 04:29:50 +00:00
										 |  |  | 	      if (ast_modem_send(p, cmd, strlen(cmd))) { | 
					
						
							| 
									
										
										
										
											2004-10-02 00:58:31 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Unable to set A number to %s\n", c->cid.cid_num); | 
					
						
							| 
									
										
										
										
											2004-06-21 04:29:50 +00:00
										 |  |  | 	      } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	    } else { | 
					
						
							| 
									
										
										
										
											2004-10-02 00:58:31 +00:00
										 |  |  | 	      ast_log(LOG_WARNING, "Outgoing MSN %s not allowed (see outgoingmsn=%s in modem.conf)\n",c->cid.cid_num,p->outgoingmsn); | 
					
						
							| 
									
										
										
										
											2004-06-21 04:29:50 +00:00
										 |  |  | 	    } | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-31 23:02:36 +00:00
										 |  |  | 	snprintf(cmd, sizeof(cmd), "ATD%c %s\n", p->dialtype,stuff); | 
					
						
							|  |  |  | 	if (ast_modem_send(p, cmd, strlen(cmd))) { | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Unable to dial\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i4l_hangup(struct ast_modem_pvt *p) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char dummy[50]; | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 	int dtr = TIOCM_DTR; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-06-22 14:17:07 +00:00
										 |  |  | 	/* free the memory used by the DSP */ | 
					
						
							|  |  |  | 	if (p->dsp) { | 
					
						
							|  |  |  | 		ast_dsp_free(p->dsp); | 
					
						
							|  |  |  | 		p->dsp = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 	/* down DTR to hangup modem */ | 
					
						
							|  |  |  | 	ioctl(p->fd, TIOCMBIC, &dtr); | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	/* Read anything outstanding */ | 
					
						
							|  |  |  | 	while(read(p->fd, dummy, sizeof(dummy)) > 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 	/* rise DTR to re-enable line */ | 
					
						
							|  |  |  | 	ioctl(p->fd, TIOCMBIS, &dtr); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	/* Read anything outstanding */ | 
					
						
							|  |  |  | 	while(read(p->fd, dummy, sizeof(dummy)) > 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* basically we're done, just to be sure */ | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	write(p->fd, "\n\n", 2); | 
					
						
							|  |  |  | 	read(p->fd, dummy, sizeof(dummy)); | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 	if (ast_modem_send(p, "ATH", 0)) { | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Unable to hang up\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ast_modem_expect(p, "OK", 5)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Final 'OK' not received\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2002-11-29 02:14:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_modem_driver i4l_driver = | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	"i4l", | 
					
						
							|  |  |  | 	i4l_idents, | 
					
						
							|  |  |  | 	AST_FORMAT_SLINEAR, | 
					
						
							|  |  |  | 	0,		/* Not full duplex */ | 
					
						
							|  |  |  | 	i4l_incusecnt,	/* incusecnt */ | 
					
						
							|  |  |  | 	i4l_decusecnt,	/* decusecnt */ | 
					
						
							|  |  |  | 	i4l_identify,	/* identify */ | 
					
						
							|  |  |  | 	i4l_init,	/* init */ | 
					
						
							|  |  |  | 	i4l_setdev,	/* setdev */ | 
					
						
							|  |  |  | 	i4l_read, | 
					
						
							|  |  |  | 	i4l_write, | 
					
						
							|  |  |  | 	i4l_dial,	/* dial */ | 
					
						
							|  |  |  | 	i4l_answer,	/* answer */ | 
					
						
							|  |  |  | 	i4l_hangup,	/* hangup */ | 
					
						
							|  |  |  | 	i4l_startrec,	/* start record */ | 
					
						
							|  |  |  | 	NULL,	/* stop record */ | 
					
						
							|  |  |  | 	NULL,	/* start playback */ | 
					
						
							|  |  |  | 	NULL,	/* stop playback */ | 
					
						
							|  |  |  | 	NULL,	/* set silence supression */ | 
					
						
							|  |  |  | 	i4l_dialdigit,	/* dialdigit */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int usecount(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-06-24 02:15:04 +00:00
										 |  |  | 	return usecnt; | 
					
						
							| 
									
										
										
										
											2000-12-29 15:31:50 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int load_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ast_register_modem_driver(&i4l_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int unload_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ast_unregister_modem_driver(&i4l_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char *description() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return desc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-03-07 00:52:22 +00:00
										 |  |  | char *key() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ASTERISK_GPL_KEY; | 
					
						
							|  |  |  | } |