📄 chan_phone.c
字号:
{ if (c == 12) return '#'; else if (c == 11) return '*'; else if ((c < 10) && (c >= 0)) return '0' + c - 1; else return '?';}#endifstatic struct ast_frame *phone_exception(struct ast_channel *ast){ int res; union telephony_exception phonee; struct phone_pvt *p = ast->tech_pvt; char digit; /* Some nice norms */ p->fr.datalen = 0; p->fr.samples = 0; p->fr.data = NULL; p->fr.src = "Phone"; p->fr.offset = 0; p->fr.mallocd=0; p->fr.delivery = ast_tv(0,0); phonee.bytes = ioctl(p->fd, PHONE_EXCEPTION); if (phonee.bits.dtmf_ready) { if (option_debug) ast_log(LOG_DEBUG, "phone_exception(): DTMF\n"); /* We've got a digit -- Just handle this nicely and easily */ digit = ioctl(p->fd, PHONE_GET_DTMF_ASCII); p->fr.subclass = digit; p->fr.frametype = AST_FRAME_DTMF; return &p->fr; } if (phonee.bits.hookstate) { if (option_debug) ast_log(LOG_DEBUG, "Hookstate changed\n"); res = ioctl(p->fd, PHONE_HOOKSTATE); /* See if we've gone on hook, if so, notify by returning NULL */ if (option_debug) ast_log(LOG_DEBUG, "New hookstate: %d\n", res); if (!res && (p->mode != MODE_FXO)) return NULL; else { if (ast->_state == AST_STATE_RINGING) { /* They've picked up the phone */ p->fr.frametype = AST_FRAME_CONTROL; p->fr.subclass = AST_CONTROL_ANSWER; phone_setup(ast); ast_setstate(ast, AST_STATE_UP); return &p->fr; } else ast_log(LOG_WARNING, "Got off hook in weird state %d\n", ast->_state); } }#if 1 if (phonee.bits.pstn_ring) ast_verbose("Unit is ringing\n"); if (phonee.bits.caller_id) { ast_verbose("We have caller ID\n"); } if (phonee.bits.pstn_wink) ast_verbose("Detected Wink\n");#endif /* Strange -- nothing there.. */ p->fr.frametype = AST_FRAME_NULL; p->fr.subclass = 0; return &p->fr;}static struct ast_frame *phone_read(struct ast_channel *ast){ int res; struct phone_pvt *p = ast->tech_pvt; /* Some nice norms */ p->fr.datalen = 0; p->fr.samples = 0; p->fr.data = NULL; p->fr.src = "Phone"; p->fr.offset = 0; p->fr.mallocd=0; p->fr.delivery = ast_tv(0,0); /* Try to read some data... */ CHECK_BLOCKING(ast); res = read(p->fd, p->buf, PHONE_MAX_BUF); ast_clear_flag(ast, AST_FLAG_BLOCKING); if (res < 0) {#if 0 if (errno == EAGAIN) { ast_log(LOG_WARNING, "Null frame received\n"); p->fr.frametype = AST_FRAME_NULL; p->fr.subclass = 0; return &p->fr; }#endif ast_log(LOG_WARNING, "Error reading: %s\n", strerror(errno)); return NULL; } p->fr.data = p->buf; if (p->mode != MODE_FXS) switch(p->buf[0] & 0x3) { case '0': case '1': /* Normal */ break; case '2': case '3': /* VAD/CNG, only send two words */ res = 4; break; } p->fr.samples = 240; p->fr.datalen = res; p->fr.frametype = p->lastinput <= AST_FORMAT_MAX_AUDIO ? AST_FRAME_VOICE : p->lastinput <= AST_FORMAT_PNG ? AST_FRAME_IMAGE : AST_FRAME_VIDEO; p->fr.subclass = p->lastinput; p->fr.offset = AST_FRIENDLY_OFFSET; /* Byteswap from little-endian to native-endian */ if (p->fr.subclass == AST_FORMAT_SLINEAR) ast_frame_byteswap_le(&p->fr); return &p->fr;}static int phone_write_buf(struct phone_pvt *p, const char *buf, int len, int frlen, int swap){ int res; /* Store as much of the buffer as we can, then write fixed frames */ int space = sizeof(p->obuf) - p->obuflen; /* Make sure we have enough buffer space to store the frame */ if (space < len) len = space; if (swap) ast_swapcopy_samples(p->obuf+p->obuflen, buf, len/2); else memcpy(p->obuf + p->obuflen, buf, len); p->obuflen += len; while(p->obuflen > frlen) { res = write(p->fd, p->obuf, frlen); if (res != frlen) { if (res < 1) {/* * Card is in non-blocking mode now and it works well now, but there are * lot of messages like this. So, this message is temporarily disabled. */ return 0; } else { ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frlen); } } p->obuflen -= frlen; /* Move memory if necessary */ if (p->obuflen) memmove(p->obuf, p->obuf + frlen, p->obuflen); } return len;}static int phone_send_text(struct ast_channel *ast, const char *text){ int length = strlen(text); return phone_write_buf(ast->tech_pvt, text, length, length, 0) == length ? 0 : -1;}static int phone_write(struct ast_channel *ast, struct ast_frame *frame){ struct phone_pvt *p = ast->tech_pvt; int res; int maxfr=0; char *pos; int sofar; int expected; int codecset = 0; char tmpbuf[4]; /* Write a frame of (presumably voice) data */ if (frame->frametype != AST_FRAME_VOICE && p->mode != MODE_FXS) { if (frame->frametype != AST_FRAME_IMAGE) ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype); return 0; } if (!(frame->subclass & (AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW)) && p->mode != MODE_FXS) { ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass); return -1; }#if 0 /* If we're not in up mode, go into up mode now */ if (ast->_state != AST_STATE_UP) { ast_setstate(ast, AST_STATE_UP); phone_setup(ast); }#else if (ast->_state != AST_STATE_UP) { /* Don't try tos end audio on-hook */ return 0; }#endif if (frame->subclass == AST_FORMAT_G723_1) { if (p->lastformat != AST_FORMAT_G723_1) { ioctl(p->fd, PHONE_PLAY_STOP); ioctl(p->fd, PHONE_REC_STOP); if (ioctl(p->fd, PHONE_PLAY_CODEC, G723_63)) { ast_log(LOG_WARNING, "Unable to set G723.1 mode\n"); return -1; } if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) { ast_log(LOG_WARNING, "Unable to set G723.1 mode\n"); return -1; } p->lastformat = AST_FORMAT_G723_1; p->lastinput = AST_FORMAT_G723_1; /* Reset output buffer */ p->obuflen = 0; codecset = 1; } if (frame->datalen > 24) { ast_log(LOG_WARNING, "Frame size too large for G.723.1 (%d bytes)\n", frame->datalen); return -1; } maxfr = 24; } else if (frame->subclass == AST_FORMAT_SLINEAR) { if (p->lastformat != AST_FORMAT_SLINEAR) { ioctl(p->fd, PHONE_PLAY_STOP); ioctl(p->fd, PHONE_REC_STOP); if (ioctl(p->fd, PHONE_PLAY_CODEC, LINEAR16)) { ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n"); return -1; } if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) { ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n"); return -1; } p->lastformat = AST_FORMAT_SLINEAR; p->lastinput = AST_FORMAT_SLINEAR; codecset = 1; /* Reset output buffer */ p->obuflen = 0; } maxfr = 480; } else if (frame->subclass == AST_FORMAT_ULAW) { if (p->lastformat != AST_FORMAT_ULAW) { ioctl(p->fd, PHONE_PLAY_STOP); ioctl(p->fd, PHONE_REC_STOP); if (ioctl(p->fd, PHONE_PLAY_CODEC, ULAW)) { ast_log(LOG_WARNING, "Unable to set uLaw mode\n"); return -1; } if (ioctl(p->fd, PHONE_REC_CODEC, ULAW)) { ast_log(LOG_WARNING, "Unable to set uLaw mode\n"); return -1; } p->lastformat = AST_FORMAT_ULAW; p->lastinput = AST_FORMAT_ULAW; codecset = 1; /* Reset output buffer */ p->obuflen = 0; } maxfr = 240; } else { if (p->lastformat != frame->subclass) { ioctl(p->fd, PHONE_PLAY_STOP); ioctl(p->fd, PHONE_REC_STOP); if (ioctl(p->fd, PHONE_PLAY_CODEC, frame->subclass)) { ast_log(LOG_WARNING, "Unable to set %d mode\n", frame->subclass); return -1; } if (ioctl(p->fd, PHONE_REC_CODEC, frame->subclass)) { ast_log(LOG_WARNING, "Unable to set %d mode\n", frame->subclass); return -1; } p->lastformat = frame->subclass; p->lastinput = frame->subclass; codecset = 1; /* Reset output buffer */ p->obuflen = 0; } maxfr = 480; } if (codecset) { ioctl(p->fd, PHONE_REC_DEPTH, 3); ioctl(p->fd, PHONE_PLAY_DEPTH, 3); if (ioctl(p->fd, PHONE_PLAY_START)) { ast_log(LOG_WARNING, "Failed to start playback\n"); return -1; } if (ioctl(p->fd, PHONE_REC_START)) { ast_log(LOG_WARNING, "Failed to start recording\n"); return -1; } } /* If we get here, we have a frame of Appropriate data */ sofar = 0; pos = frame->data; while(sofar < frame->datalen) { /* Write in no more than maxfr sized frames */ expected = frame->datalen - sofar; if (maxfr < expected) expected = maxfr; /* XXX Internet Phone Jack does not handle the 4-byte VAD frame properly! XXX we have to pad it to 24 bytes still. */ if (frame->datalen == 4) { if (p->silencesupression) { memset(tmpbuf + 4, 0, sizeof(tmpbuf) - 4); memcpy(tmpbuf, frame->data, 4); expected = 24; res = phone_write_buf(p, tmpbuf, expected, maxfr, 0); } res = 4; expected=4; } else { int swap = 0;#if __BYTE_ORDER == __BIG_ENDIAN if (frame->subclass == AST_FORMAT_SLINEAR) swap = 1; /* Swap big-endian samples to little-endian as we copy */#endif res = phone_write_buf(p, pos, expected, maxfr, swap); } if (res != expected) { if ((errno != EAGAIN) && (errno != EINTR)) { if (res < 0) ast_log(LOG_WARNING, "Write returned error (%s)\n", strerror(errno)); /* * Card is in non-blocking mode now and it works well now, but there are * lot of messages like this. So, this message is temporarily disabled. */#if 0 else ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frame->datalen);#endif return -1; } else /* Pretend it worked */ res = expected; } sofar += res; pos += res; } return 0;}static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *context){ struct ast_channel *tmp; struct phone_codec_data codec; tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->ext, i->context, 0, "Phone/%s", i->dev + 5); if (tmp) { tmp->tech = cur_tech; tmp->fds[0] = i->fd; /* XXX Switching formats silently causes kernel panics XXX */ if (i->mode == MODE_FXS && ioctl(i->fd, PHONE_QUERY_CODEC, &codec) == 0) { if (codec.type == LINEAR16) tmp->nativeformats = tmp->rawreadformat = tmp->rawwriteformat = AST_FORMAT_SLINEAR; else { tmp->nativeformats = tmp->rawreadformat = tmp->rawwriteformat = prefformat & ~AST_FORMAT_SLINEAR; } } else { tmp->nativeformats = prefformat; tmp->rawreadformat = prefformat; tmp->rawwriteformat = prefformat; } /* no need to call ast_setstate: the channel_alloc already did its job */ if (state == AST_STATE_RING) tmp->rings = 1; tmp->tech_pvt = i; ast_copy_string(tmp->context, context, sizeof(tmp->context)); if (!ast_strlen_zero(i->ext)) ast_copy_string(tmp->exten, i->ext, sizeof(tmp->exten)); else strcpy(tmp->exten, "s"); if (!ast_strlen_zero(i->language)) ast_string_field_set(tmp, language, i->language); /* Don't use ast_set_callerid() here because it will * generate a NewCallerID event before the NewChannel event */ tmp->cid.cid_ani = ast_strdup(i->cid_num); i->owner = tmp; ast_module_ref(ast_module_info->self); if (state != AST_STATE_DOWN) { if (state == AST_STATE_RING) { ioctl(tmp->fds[0], PHONE_RINGBACK); i->cpt = 1; } if (ast_pbx_start(tmp)) { ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); ast_hangup(tmp); } } } else ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); return tmp;}static void phone_mini_packet(struct phone_pvt *i){ int res; char buf[1024]; /* Ignore stuff we read... */ res = read(i->fd, buf, sizeof(buf)); if (res < 1) { ast_log(LOG_WARNING, "Read returned %d: %s\n", res, strerror(errno)); return; }}static void phone_check_exception(struct phone_pvt *i){ int offhook=0; char digit[2] = {0 , 0}; union telephony_exception phonee; /* XXX Do something XXX */#if 0 ast_log(LOG_DEBUG, "Exception!\n");#endif phonee.bytes = ioctl(i->fd, PHONE_EXCEPTION); if (phonee.bits.dtmf_ready) { digit[0] = ioctl(i->fd, PHONE_GET_DTMF_ASCII); if (i->mode == MODE_DIALTONE || i->mode == MODE_FXS || i->mode == MODE_SIGMA) { ioctl(i->fd, PHONE_PLAY_STOP); ioctl(i->fd, PHONE_REC_STOP); ioctl(i->fd, PHONE_CPT_STOP); i->dialtone = 0; if (strlen(i->ext) < AST_MAX_EXTENSION - 1) strncat(i->ext, digit, sizeof(i->ext) - strlen(i->ext) - 1); if ((i->mode != MODE_FXS || !(phonee.bytes = ioctl(i->fd, PHONE_EXCEPTION)) || !phonee.bits.dtmf_ready) && ast_exists_extension(NULL, i->context, i->ext, 1, i->cid_num)) { /* It's a valid extension in its context, get moving! */ phone_new(i, AST_STATE_RING, i->context); /* No need to restart monitor, we are the monitor */ } else if (!ast_canmatch_extension(NULL, i->context, i->ext, 1, i->cid_num)) { /* There is nothing in the specified extension that can match anymore. Try the default */ if (ast_exists_extension(NULL, "default", i->ext, 1, i->cid_num)) { /* Check the default, too... */ phone_new(i, AST_STATE_RING, "default"); /* XXX This should probably be justified better XXX */ } else if (!ast_canmatch_extension(NULL, "default", i->ext, 1, i->cid_num)) { /* It's not a valid extension, give a busy signal */ if (option_debug) ast_log(LOG_DEBUG, "%s can't match anything in %s or default\n", i->ext, i->context); ioctl(i->fd, PHONE_BUSY); i->cpt = 1; } }#if 0 ast_verbose("Extension is %s\n", i->ext);#endif } } if (phonee.bits.hookstate) { offhook = ioctl(i->fd, PHONE_HOOKSTATE); if (offhook) { if (i->mode == MODE_IMMEDIATE) { phone_new(i, AST_STATE_RING, i->context); } else if (i->mode == MODE_DIALTONE) { ast_module_ref(ast_module_info->self);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -