📄 chan_phone.c
字号:
/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * * Mark Spencer <markster@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. *//*! \file * * \brief Generic Linux Telephony Interface driver * * \author Mark Spencer <markster@digium.com> * * \ingroup channel_drivers *//*** MODULEINFO <depend>ixjuser</depend> ***/#include "asterisk.h"ASTERISK_FILE_VERSION(__FILE__, "$Revision: 106235 $")#include <stdio.h>#include <string.h>#include <ctype.h>#include <sys/socket.h>#include <sys/time.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#include <arpa/inet.h>#include <fcntl.h>#include <sys/ioctl.h>#include <signal.h>#ifdef HAVE_LINUX_COMPILER_H#include <linux/compiler.h>#endif#include <linux/telephony.h>/* Still use some IXJ specific stuff */#include <linux/version.h>#include <linux/ixjuser.h>#include "asterisk/lock.h"#include "asterisk/channel.h"#include "asterisk/config.h"#include "asterisk/logger.h"#include "asterisk/module.h"#include "asterisk/pbx.h"#include "asterisk/options.h"#include "asterisk/utils.h"#include "asterisk/callerid.h"#include "asterisk/causes.h"#include "asterisk/stringfields.h"#include "asterisk/musiconhold.h"#include "DialTone.h"#ifdef QTI_PHONEJACK_TJ_PCI /* check for the newer quicknet driver v.3.1.0 which has this symbol */#define QNDRV_VER 310#else#define QNDRV_VER 100#endif#if QNDRV_VER > 100#ifdef __linux__#define IXJ_PHONE_RING_START(x) ioctl(p->fd, PHONE_RING_START, &x);#else /* FreeBSD and others */#define IXJ_PHONE_RING_START(x) ioctl(p->fd, PHONE_RING_START, x);#endif /* __linux__ */#else /* older driver */#define IXJ_PHONE_RING_START(x) ioctl(p->fd, PHONE_RING_START, &x);#endif#define DEFAULT_CALLER_ID "Unknown"#define PHONE_MAX_BUF 480#define DEFAULT_GAIN 0x100static const char tdesc[] = "Standard Linux Telephony API Driver";static const char config[] = "phone.conf";/* Default context for dialtone mode */static char context[AST_MAX_EXTENSION] = "default";/* Default language */static char language[MAX_LANGUAGE] = "";static int echocancel = AEC_OFF;static int silencesupression = 0;static int prefformat = AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW;/* Protect the interface list (of phone_pvt's) */AST_MUTEX_DEFINE_STATIC(iflock);/* Protect the monitoring thread, so only one process can kill or start it, and not when it's doing something critical. */AST_MUTEX_DEFINE_STATIC(monlock);/* Boolean value whether the monitoring thread shall continue. */static unsigned int monitor; /* This is the thread for the monitor which checks for input on the channels which are not currently in use. */static pthread_t monitor_thread = AST_PTHREADT_NULL;static int restart_monitor(void);/* The private structures of the Phone Jack channels are linked for selecting outgoing channels */ #define MODE_DIALTONE 1#define MODE_IMMEDIATE 2#define MODE_FXO 3#define MODE_FXS 4#define MODE_SIGMA 5static struct phone_pvt { int fd; /* Raw file descriptor for this device */ struct ast_channel *owner; /* Channel we belong to, possibly NULL */ int mode; /* Is this in the */ int lastformat; /* Last output format */ int lastinput; /* Last input format */ int ministate; /* Miniature state, for dialtone mode */ char dev[256]; /* Device name */ struct phone_pvt *next; /* Next channel in list */ struct ast_frame fr; /* Frame */ char offset[AST_FRIENDLY_OFFSET]; char buf[PHONE_MAX_BUF]; /* Static buffer for reading frames */ int obuflen; int dialtone; int txgain, rxgain; /* gain control for playing, recording */ /* 0x100 - 1.0, 0x200 - 2.0, 0x80 - 0.5 */ int cpt; /* Call Progress Tone playing? */ int silencesupression; char context[AST_MAX_EXTENSION]; char obuf[PHONE_MAX_BUF * 2]; char ext[AST_MAX_EXTENSION]; char language[MAX_LANGUAGE]; char cid_num[AST_MAX_EXTENSION]; char cid_name[AST_MAX_EXTENSION];} *iflist = NULL;static char cid_num[AST_MAX_EXTENSION];static char cid_name[AST_MAX_EXTENSION];static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause);static int phone_digit_begin(struct ast_channel *ast, char digit);static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration);static int phone_call(struct ast_channel *ast, char *dest, int timeout);static int phone_hangup(struct ast_channel *ast);static int phone_answer(struct ast_channel *ast);static struct ast_frame *phone_read(struct ast_channel *ast);static int phone_write(struct ast_channel *ast, struct ast_frame *frame);static struct ast_frame *phone_exception(struct ast_channel *ast);static int phone_send_text(struct ast_channel *ast, const char *text);static int phone_fixup(struct ast_channel *old, struct ast_channel *new);static int phone_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen);static const struct ast_channel_tech phone_tech = { .type = "Phone", .description = tdesc, .capabilities = AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW, .requester = phone_request, .send_digit_begin = phone_digit_begin, .send_digit_end = phone_digit_end, .call = phone_call, .hangup = phone_hangup, .answer = phone_answer, .read = phone_read, .write = phone_write, .exception = phone_exception, .indicate = phone_indicate, .fixup = phone_fixup};static struct ast_channel_tech phone_tech_fxs = { .type = "Phone", .description = tdesc, .requester = phone_request, .send_digit_begin = phone_digit_begin, .send_digit_end = phone_digit_end, .call = phone_call, .hangup = phone_hangup, .answer = phone_answer, .read = phone_read, .write = phone_write, .exception = phone_exception, .write_video = phone_write, .send_text = phone_send_text, .indicate = phone_indicate, .fixup = phone_fixup};static struct ast_channel_tech *cur_tech;static int phone_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen){ struct phone_pvt *p = chan->tech_pvt; int res=-1; ast_log(LOG_DEBUG, "Requested indication %d on channel %s\n", condition, chan->name); switch(condition) { case AST_CONTROL_FLASH: ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_ON_HOOK); usleep(320000); ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_OFF_HOOK); p->lastformat = -1; res = 0; break; case AST_CONTROL_HOLD: ast_moh_start(chan, data, NULL); break; case AST_CONTROL_UNHOLD: ast_moh_stop(chan); break; case AST_CONTROL_SRCUPDATE: res = 0; break; default: ast_log(LOG_WARNING, "Condition %d is not supported on channel %s\n", condition, chan->name); } return res;}static int phone_fixup(struct ast_channel *old, struct ast_channel *new){ struct phone_pvt *pvt = old->tech_pvt; if (pvt && pvt->owner == old) pvt->owner = new; return 0;}static int phone_digit_begin(struct ast_channel *chan, char digit){ /* XXX Modify this callback to let Asterisk support controlling the length of DTMF */ return 0;}static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration){ struct phone_pvt *p; int outdigit; p = ast->tech_pvt; ast_log(LOG_DEBUG, "Dialed %c\n", digit); switch(digit) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': outdigit = digit - '0'; break; case '*': outdigit = 11; break; case '#': outdigit = 12; break; case 'f': /*flash*/ case 'F': ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_ON_HOOK); usleep(320000); ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_OFF_HOOK); p->lastformat = -1; return 0; default: ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit); return -1; } ast_log(LOG_DEBUG, "Dialed %d\n", outdigit); ioctl(p->fd, PHONE_PLAY_TONE, outdigit); p->lastformat = -1; return 0;}static int phone_call(struct ast_channel *ast, char *dest, int timeout){ struct phone_pvt *p; PHONE_CID cid; time_t UtcTime; struct tm tm; int start; time(&UtcTime); ast_localtime(&UtcTime, &tm, NULL); memset(&cid, 0, sizeof(PHONE_CID)); if(&tm != NULL) { snprintf(cid.month, sizeof(cid.month), "%02d",(tm.tm_mon + 1)); snprintf(cid.day, sizeof(cid.day), "%02d", tm.tm_mday); snprintf(cid.hour, sizeof(cid.hour), "%02d", tm.tm_hour); snprintf(cid.min, sizeof(cid.min), "%02d", tm.tm_min); } /* the standard format of ast->callerid is: "name" <number>, but not always complete */ if (ast_strlen_zero(ast->cid.cid_name)) strcpy(cid.name, DEFAULT_CALLER_ID); else ast_copy_string(cid.name, ast->cid.cid_name, sizeof(cid.name)); if (ast->cid.cid_num) ast_copy_string(cid.number, ast->cid.cid_num, sizeof(cid.number)); p = ast->tech_pvt; if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { ast_log(LOG_WARNING, "phone_call called on %s, neither down nor reserved\n", ast->name); return -1; } if (option_debug) ast_log(LOG_DEBUG, "Ringing %s on %s (%d)\n", dest, ast->name, ast->fds[0]); start = IXJ_PHONE_RING_START(cid); if (start == -1) return -1; if (p->mode == MODE_FXS) { char *digit = strchr(dest, '/'); if (digit) { digit++; while (*digit) phone_digit_end(ast, *digit++, 0); } } ast_setstate(ast, AST_STATE_RINGING); ast_queue_control(ast, AST_CONTROL_RINGING); return 0;}static int phone_hangup(struct ast_channel *ast){ struct phone_pvt *p; p = ast->tech_pvt; if (option_debug) ast_log(LOG_DEBUG, "phone_hangup(%s)\n", ast->name); if (!ast->tech_pvt) { ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); return 0; } /* XXX Is there anything we can do to really hang up except stop recording? */ ast_setstate(ast, AST_STATE_DOWN); if (ioctl(p->fd, PHONE_REC_STOP)) ast_log(LOG_WARNING, "Failed to stop recording\n"); if (ioctl(p->fd, PHONE_PLAY_STOP)) ast_log(LOG_WARNING, "Failed to stop playing\n"); if (ioctl(p->fd, PHONE_RING_STOP)) ast_log(LOG_WARNING, "Failed to stop ringing\n"); if (ioctl(p->fd, PHONE_CPT_STOP)) ast_log(LOG_WARNING, "Failed to stop sounds\n"); /* If it's an FXO, hang them up */ if (p->mode == MODE_FXO) { if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK)) ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",ast->name, strerror(errno)); } /* If they're off hook, give a busy signal */ if (ioctl(p->fd, PHONE_HOOKSTATE)) { if (option_debug) ast_log(LOG_DEBUG, "Got hunghup, giving busy signal\n"); ioctl(p->fd, PHONE_BUSY); p->cpt = 1; } p->lastformat = -1; p->lastinput = -1; p->ministate = 0; p->obuflen = 0; p->dialtone = 0; memset(p->ext, 0, sizeof(p->ext)); ((struct phone_pvt *)(ast->tech_pvt))->owner = NULL; ast_module_unref(ast_module_info->self); if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", ast->name); ast->tech_pvt = NULL; ast_setstate(ast, AST_STATE_DOWN); restart_monitor(); return 0;}static int phone_setup(struct ast_channel *ast){ struct phone_pvt *p; p = ast->tech_pvt; ioctl(p->fd, PHONE_CPT_STOP); /* Nothing to answering really, just start recording */ if (ast->rawreadformat == AST_FORMAT_G723_1) { /* Prefer g723 */ ioctl(p->fd, PHONE_REC_STOP); if (p->lastinput != AST_FORMAT_G723_1) { p->lastinput = AST_FORMAT_G723_1; if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) { ast_log(LOG_WARNING, "Failed to set codec to g723.1\n"); return -1; } } } else if (ast->rawreadformat == AST_FORMAT_SLINEAR) { ioctl(p->fd, PHONE_REC_STOP); if (p->lastinput != AST_FORMAT_SLINEAR) { p->lastinput = AST_FORMAT_SLINEAR; if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) { ast_log(LOG_WARNING, "Failed to set codec to signed linear 16\n"); return -1; } } } else if (ast->rawreadformat == AST_FORMAT_ULAW) { ioctl(p->fd, PHONE_REC_STOP); if (p->lastinput != AST_FORMAT_ULAW) { p->lastinput = AST_FORMAT_ULAW; if (ioctl(p->fd, PHONE_REC_CODEC, ULAW)) { ast_log(LOG_WARNING, "Failed to set codec to uLaw\n"); return -1; } } } else if (p->mode == MODE_FXS) { ioctl(p->fd, PHONE_REC_STOP); if (p->lastinput != ast->rawreadformat) { p->lastinput = ast->rawreadformat; if (ioctl(p->fd, PHONE_REC_CODEC, ast->rawreadformat)) { ast_log(LOG_WARNING, "Failed to set codec to %d\n", ast->rawreadformat); return -1; } } } else { ast_log(LOG_WARNING, "Can't do format %s\n", ast_getformatname(ast->rawreadformat)); return -1; } if (ioctl(p->fd, PHONE_REC_START)) { ast_log(LOG_WARNING, "Failed to start recording\n"); return -1; } /* set the DTMF times (the default is too short) */ ioctl(p->fd, PHONE_SET_TONE_ON_TIME, 300); ioctl(p->fd, PHONE_SET_TONE_OFF_TIME, 200); return 0;}static int phone_answer(struct ast_channel *ast){ struct phone_pvt *p; p = ast->tech_pvt; /* In case it's a LineJack, take it off hook */ if (p->mode == MODE_FXO) { if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_OFF_HOOK)) ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n", ast->name, strerror(errno)); else ast_log(LOG_DEBUG, "Took linejack off hook\n"); } phone_setup(ast); if (option_debug) ast_log(LOG_DEBUG, "phone_answer(%s)\n", ast->name); ast->rings = 0; ast_setstate(ast, AST_STATE_UP); return 0;}#if 0static char phone_2digit(char c)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -