00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 176810 $")
00037
00038 #include <fcntl.h>
00039 #include <stdlib.h>
00040 #include <unistd.h>
00041 #include <netinet/in.h>
00042 #include <string.h>
00043 #include <stdio.h>
00044 #include <sys/ioctl.h>
00045 #include <errno.h>
00046 #include <sys/mman.h>
00047 #include <sys/poll.h>
00048
00049 #include "asterisk/lock.h"
00050 #include "asterisk/translate.h"
00051 #include "asterisk/config.h"
00052 #include "asterisk/options.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/cli.h"
00055 #include "asterisk/logger.h"
00056 #include "asterisk/channel.h"
00057 #include "asterisk/utils.h"
00058 #include "asterisk/linkedlists.h"
00059 #include "asterisk/dahdi_compat.h"
00060 #include "asterisk/frame.h"
00061 #include "asterisk/ulaw.h"
00062
00063 #define BUFFER_SIZE 8000
00064
00065 #define G723_SAMPLES 240
00066 #define G729_SAMPLES 160
00067
00068 static unsigned int global_useplc = 0;
00069
00070 static struct channel_usage {
00071 int total;
00072 int encoders;
00073 int decoders;
00074 } channels;
00075
00076 static char show_transcoder_usage[] =
00077 "Usage: show transcoder\n"
00078 " Displays channel utilization of DAHDI transcoder(s).\n";
00079
00080 static char transcoder_show_usage[] =
00081 "Usage: transcoder show\n"
00082 " Displays channel utilization of DAHDI transcoder(s).\n";
00083
00084 static int transcoder_show(int fd, int argc, char **argv);
00085
00086 static struct ast_cli_entry cli_deprecated[] = {
00087 { { "show", "transcoder", NULL },
00088 transcoder_show,
00089 "Display DAHDI transcoder utilization.",
00090 show_transcoder_usage}
00091 };
00092
00093 static struct ast_cli_entry cli[] = {
00094 { { "transcoder", "show", NULL },
00095 transcoder_show,
00096 "Display DAHDI transcoder utilization.",
00097 transcoder_show_usage, NULL,
00098 &cli_deprecated[0]}
00099 };
00100
00101 struct format_map {
00102 unsigned int map[32][32];
00103 };
00104
00105 static struct format_map global_format_map = { { { 0 } } };
00106
00107 struct translator {
00108 struct ast_translator t;
00109 AST_LIST_ENTRY(translator) entry;
00110 };
00111
00112 static AST_LIST_HEAD_STATIC(translators, translator);
00113
00114 struct codec_dahdi_pvt {
00115 int fd;
00116 struct dahdi_transcoder_formats fmts;
00117 unsigned int softslin:1;
00118 unsigned int fake:2;
00119 uint16_t required_samples;
00120 uint16_t samples_in_buffer;
00121 uint8_t ulaw_buffer[1024];
00122 };
00123
00124
00125 static int ulawtolin(struct ast_trans_pvt *pvt)
00126 {
00127 struct codec_dahdi_pvt *ztp = pvt->pvt;
00128 int i = ztp->required_samples;
00129 uint8_t *src = &ztp->ulaw_buffer[0];
00130 int16_t *dst = (int16_t *)pvt->outbuf + pvt->datalen;
00131
00132
00133 while (i--) {
00134 *dst++ = AST_MULAW(*src++);
00135 }
00136
00137 return 0;
00138 }
00139
00140
00141 static int lintoulaw(struct ast_trans_pvt *pvt, struct ast_frame *f)
00142 {
00143 struct codec_dahdi_pvt *ztp = pvt->pvt;
00144 int i = f->samples;
00145 uint8_t *dst = &ztp->ulaw_buffer[ztp->samples_in_buffer];
00146 int16_t *src = (int16_t*)f->data;
00147
00148 if (ztp->samples_in_buffer + i > sizeof(ztp->ulaw_buffer)) {
00149 ast_log(LOG_ERROR, "Out of buffer space!\n");
00150 return -i;
00151 }
00152
00153 while (i--) {
00154 *dst++ = AST_LIN2MU(*src++);
00155 }
00156
00157 ztp->samples_in_buffer += f->samples;
00158 return 0;
00159 }
00160
00161 static int transcoder_show(int fd, int argc, char **argv)
00162 {
00163 struct channel_usage copy;
00164
00165 copy = channels;
00166
00167 if (copy.total == 0)
00168 ast_cli(fd, "No DAHDI transcoders found.\n");
00169 else
00170 ast_cli(fd, "%d/%d encoders/decoders of %d channels are in use.\n", copy.encoders, copy.decoders, copy.total);
00171
00172 return RESULT_SUCCESS;
00173 }
00174
00175 static void dahdi_write_frame(struct codec_dahdi_pvt *ztp, const uint8_t *buffer, const ssize_t count)
00176 {
00177 int res;
00178 struct pollfd p = {0};
00179 if (!count) return;
00180 res = write(ztp->fd, buffer, count);
00181 if (option_verbose > 10) {
00182 if (-1 == res) {
00183 ast_log(LOG_ERROR, "Failed to write to transcoder: %s\n", strerror(errno));
00184 }
00185 if (count != res) {
00186 ast_log(LOG_ERROR, "Requested write of %zd bytes, but only wrote %d bytes.\n", count, res);
00187 }
00188 }
00189 p.fd = ztp->fd;
00190 p.events = POLLOUT;
00191 res = poll(&p, 1, 50);
00192 }
00193
00194 static int dahdi_encoder_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
00195 {
00196 struct codec_dahdi_pvt *ztp = pvt->pvt;
00197
00198 if (!f->subclass) {
00199
00200 ztp->fake = 2;
00201 pvt->samples = f->samples;
00202 return 0;
00203 }
00204
00205
00206
00207 if (ztp->softslin) {
00208 if (lintoulaw(pvt, f)) {
00209 return -1;
00210 }
00211 } else {
00212
00213
00214
00215
00216 if (ztp->samples_in_buffer + f->samples > sizeof(ztp->ulaw_buffer)) {
00217 ast_log(LOG_ERROR, "Out of buffer space.\n");
00218 return -1;
00219 }
00220 memcpy(&ztp->ulaw_buffer[ztp->samples_in_buffer], f->data, f->samples);
00221 ztp->samples_in_buffer += f->samples;
00222 }
00223
00224 while (ztp->samples_in_buffer > ztp->required_samples) {
00225 dahdi_write_frame(ztp, ztp->ulaw_buffer, ztp->required_samples);
00226 ztp->samples_in_buffer -= ztp->required_samples;
00227 if (ztp->samples_in_buffer) {
00228
00229 memmove(ztp->ulaw_buffer, &ztp->ulaw_buffer[ztp->required_samples],
00230 ztp->samples_in_buffer);
00231 }
00232 }
00233 pvt->samples += f->samples;
00234 pvt->datalen = 0;
00235 return -1;
00236 }
00237
00238 static struct ast_frame *dahdi_encoder_frameout(struct ast_trans_pvt *pvt)
00239 {
00240 struct codec_dahdi_pvt *ztp = pvt->pvt;
00241 int res;
00242
00243 if (2 == ztp->fake) {
00244 ztp->fake = 1;
00245 pvt->f.frametype = AST_FRAME_VOICE;
00246 pvt->f.subclass = 0;
00247 pvt->f.samples = ztp->required_samples;
00248 pvt->f.data = NULL;
00249 pvt->f.offset = 0;
00250 pvt->f.datalen = 0;
00251 pvt->f.mallocd = 0;
00252 ast_set_flag(&pvt->f, AST_FRFLAG_FROM_TRANSLATOR);
00253 pvt->samples = 0;
00254
00255 return &pvt->f;
00256
00257 } else if (1 == ztp->fake) {
00258 ztp->fake = 0;
00259 return NULL;
00260 }
00261
00262 res = read(ztp->fd, pvt->outbuf + pvt->datalen, pvt->t->buf_size - pvt->datalen);
00263 if (-1 == res) {
00264 if (EWOULDBLOCK == errno) {
00265
00266 return NULL;
00267 } else {
00268 ast_log(LOG_ERROR, "Failed to read from transcoder: %s\n", strerror(errno));
00269 return NULL;
00270 }
00271 } else {
00272 pvt->f.datalen = res;
00273 pvt->f.samples = ztp->required_samples;
00274 pvt->f.frametype = AST_FRAME_VOICE;
00275 pvt->f.subclass = 1 << (pvt->t->dstfmt);
00276 pvt->f.mallocd = 0;
00277 pvt->f.offset = AST_FRIENDLY_OFFSET;
00278 pvt->f.src = pvt->t->name;
00279 pvt->f.data = pvt->outbuf;
00280 ast_set_flag(&pvt->f, AST_FRFLAG_FROM_TRANSLATOR);
00281
00282 pvt->samples = 0;
00283 pvt->datalen = 0;
00284 return &pvt->f;
00285 }
00286
00287
00288 return NULL;
00289 }
00290
00291 static int dahdi_decoder_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
00292 {
00293 struct codec_dahdi_pvt *ztp = pvt->pvt;
00294
00295 if (!f->subclass) {
00296
00297 ztp->fake = 2;
00298 pvt->samples = f->samples;
00299 return 0;
00300 }
00301
00302 if (!f->datalen) {
00303 if (f->samples != ztp->required_samples) {
00304 ast_log(LOG_ERROR, "%d != %d %d\n", f->samples, ztp->required_samples, f->datalen);
00305 }
00306 }
00307 dahdi_write_frame(ztp, f->data, f->datalen);
00308 pvt->samples += f->samples;
00309 pvt->datalen = 0;
00310 return -1;
00311 }
00312
00313 static struct ast_frame *dahdi_decoder_frameout(struct ast_trans_pvt *pvt)
00314 {
00315 int res;
00316 struct codec_dahdi_pvt *ztp = pvt->pvt;
00317
00318 if (2 == ztp->fake) {
00319 ztp->fake = 1;
00320 pvt->f.frametype = AST_FRAME_VOICE;
00321 pvt->f.subclass = 0;
00322 pvt->f.samples = ztp->required_samples;
00323 pvt->f.data = NULL;
00324 pvt->f.offset = 0;
00325 pvt->f.datalen = 0;
00326 pvt->f.mallocd = 0;
00327 ast_set_flag(&pvt->f, AST_FRFLAG_FROM_TRANSLATOR);
00328 pvt->samples = 0;
00329 return &pvt->f;
00330 } else if (1 == ztp->fake) {
00331 pvt->samples = 0;
00332 ztp->fake = 0;
00333 return NULL;
00334 }
00335
00336
00337 if (ztp->softslin) {
00338 res = read(ztp->fd, ztp->ulaw_buffer, sizeof(ztp->ulaw_buffer));
00339 } else {
00340 res = read(ztp->fd, pvt->outbuf + pvt->datalen, pvt->t->buf_size - pvt->datalen);
00341 }
00342
00343 if (-1 == res) {
00344 if (EWOULDBLOCK == errno) {
00345
00346 return NULL;
00347 } else {
00348 ast_log(LOG_ERROR, "Failed to read from transcoder: %s\n", strerror(errno));
00349 return NULL;
00350 }
00351 } else {
00352 if (ztp->softslin) {
00353 ulawtolin(pvt);
00354 pvt->f.datalen = res * 2;
00355 } else {
00356 pvt->f.datalen = res;
00357 }
00358 pvt->datalen = 0;
00359 pvt->f.frametype = AST_FRAME_VOICE;
00360 pvt->f.subclass = 1 << (pvt->t->dstfmt);
00361 pvt->f.mallocd = 0;
00362 pvt->f.offset = AST_FRIENDLY_OFFSET;
00363 pvt->f.src = pvt->t->name;
00364 pvt->f.data = pvt->outbuf;
00365 pvt->f.samples = ztp->required_samples;
00366 ast_set_flag(&pvt->f, AST_FRFLAG_FROM_TRANSLATOR);
00367 pvt->samples = 0;
00368
00369 return &pvt->f;
00370 }
00371
00372
00373 return NULL;
00374 }
00375
00376
00377 static void dahdi_destroy(struct ast_trans_pvt *pvt)
00378 {
00379 struct codec_dahdi_pvt *ztp = pvt->pvt;
00380
00381 switch (ztp->fmts.dstfmt) {
00382 case AST_FORMAT_G729A:
00383 case AST_FORMAT_G723_1:
00384 ast_atomic_fetchadd_int(&channels.encoders, -1);
00385 break;
00386 default:
00387 ast_atomic_fetchadd_int(&channels.decoders, -1);
00388 break;
00389 }
00390
00391 close(ztp->fd);
00392 }
00393
00394 static int dahdi_translate(struct ast_trans_pvt *pvt, int dest, int source)
00395 {
00396
00397 int fd;
00398 struct codec_dahdi_pvt *ztp = pvt->pvt;
00399 int flags;
00400 int tried_once = 0;
00401 #ifdef HAVE_ZAPTEL
00402 const char *dev_filename = "/dev/zap/transcode";
00403 #else
00404 const char *dev_filename = "/dev/dahdi/transcode";
00405 #endif
00406
00407 if ((fd = open(dev_filename, O_RDWR)) < 0) {
00408 ast_log(LOG_ERROR, "Failed to open %s: %s\n", dev_filename, strerror(errno));
00409 return -1;
00410 }
00411
00412 ztp->fmts.srcfmt = (1 << source);
00413 ztp->fmts.dstfmt = (1 << dest);
00414
00415 ast_log(LOG_DEBUG, "Opening transcoder channel from %d to %d.\n", source, dest);
00416
00417 retry:
00418 if (ioctl(fd, DAHDI_TC_ALLOCATE, &ztp->fmts)) {
00419 if ((ENODEV == errno) && !tried_once) {
00420
00421
00422
00423
00424
00425
00426
00427
00428 if (AST_FORMAT_SLINEAR == ztp->fmts.srcfmt) {
00429 ast_log(LOG_DEBUG, "Using soft_slin support on source\n");
00430 ztp->softslin = 1;
00431 ztp->fmts.srcfmt = AST_FORMAT_ULAW;
00432 } else if (AST_FORMAT_SLINEAR == ztp->fmts.dstfmt) {
00433 ast_log(LOG_DEBUG, "Using soft_slin support on destination\n");
00434 ztp->softslin = 1;
00435 ztp->fmts.dstfmt = AST_FORMAT_ULAW;
00436 }
00437 tried_once = 1;
00438 goto retry;
00439 }
00440 ast_log(LOG_ERROR, "Unable to attach to transcoder: %s\n", strerror(errno));
00441 close(fd);
00442
00443 return -1;
00444 }
00445
00446 flags = fcntl(fd, F_GETFL);
00447 if (flags > - 1) {
00448 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
00449 ast_log(LOG_WARNING, "Could not set non-block mode!\n");
00450 }
00451
00452 ztp->fd = fd;
00453
00454 ztp->required_samples = ((ztp->fmts.dstfmt|ztp->fmts.srcfmt)&AST_FORMAT_G723_1) ? G723_SAMPLES : G729_SAMPLES;
00455
00456 switch (ztp->fmts.dstfmt) {
00457 case AST_FORMAT_G729A:
00458 ast_atomic_fetchadd_int(&channels.encoders, +1);
00459 break;
00460 case AST_FORMAT_G723_1:
00461 ast_atomic_fetchadd_int(&channels.encoders, +1);
00462 break;
00463 default:
00464 ast_atomic_fetchadd_int(&channels.decoders, +1);
00465 break;
00466 }
00467
00468 return 0;
00469 }
00470
00471 static int dahdi_new(struct ast_trans_pvt *pvt)
00472 {
00473 return dahdi_translate(pvt, pvt->t->dstfmt, pvt->t->srcfmt);
00474 }
00475
00476 static struct ast_frame *fakesrc_sample(void)
00477 {
00478
00479 static struct ast_frame f = {
00480 .frametype = AST_FRAME_VOICE,
00481 .samples = 160,
00482 .src = __PRETTY_FUNCTION__
00483 };
00484
00485 return &f;
00486 }
00487
00488 static int is_encoder(struct translator *zt)
00489 {
00490 if (zt->t.srcfmt&(AST_FORMAT_ULAW|AST_FORMAT_ALAW|AST_FORMAT_SLINEAR)) {
00491 return 1;
00492 } else {
00493 return 0;
00494 }
00495 }
00496
00497 static int register_translator(int dst, int src)
00498 {
00499 struct translator *zt;
00500 int res;
00501
00502 if (!(zt = ast_calloc(1, sizeof(*zt)))) {
00503 return -1;
00504 }
00505
00506 snprintf((char *) (zt->t.name), sizeof(zt->t.name), "zap%sto%s",
00507 ast_getformatname((1 << src)), ast_getformatname((1 << dst)));
00508 zt->t.srcfmt = (1 << src);
00509 zt->t.dstfmt = (1 << dst);
00510 zt->t.buf_size = BUFFER_SIZE;
00511 if (is_encoder(zt)) {
00512 zt->t.framein = dahdi_encoder_framein;
00513 zt->t.frameout = dahdi_encoder_frameout;
00514 #if 0
00515 zt->t.buffer_samples = 0;
00516 #endif
00517 } else {
00518 zt->t.framein = dahdi_decoder_framein;
00519 zt->t.frameout = dahdi_decoder_frameout;
00520 #if 0
00521 if (AST_FORMAT_G723_1 == zt->t.srcfmt) {
00522 zt->t.plc_samples = G723_SAMPLES;
00523 } else {
00524 zt->t.plc_samples = G729_SAMPLES;
00525 }
00526 zt->t.buffer_samples = zt->t.plc_samples * 8;
00527 #endif
00528 }
00529 zt->t.destroy = dahdi_destroy;
00530 zt->t.buffer_samples = 0;
00531 zt->t.newpvt = dahdi_new;
00532 zt->t.sample = fakesrc_sample;
00533 #if 0
00534 zt->t.useplc = global_useplc;
00535 #endif
00536 zt->t.useplc = 0;
00537 zt->t.native_plc = 0;
00538
00539 zt->t.desc_size = sizeof(struct codec_dahdi_pvt);
00540 if ((res = ast_register_translator(&zt->t))) {
00541 free(zt);
00542 return -1;
00543 }
00544
00545 AST_LIST_LOCK(&translators);
00546 AST_LIST_INSERT_HEAD(&translators, zt, entry);
00547 AST_LIST_UNLOCK(&translators);
00548
00549 global_format_map.map[dst][src] = 1;
00550
00551 return res;
00552 }
00553
00554 static void drop_translator(int dst, int src)
00555 {
00556 struct translator *cur;
00557
00558 AST_LIST_LOCK(&translators);
00559 AST_LIST_TRAVERSE_SAFE_BEGIN(&translators, cur, entry) {
00560 if (cur->t.srcfmt != src)
00561 continue;
00562
00563 if (cur->t.dstfmt != dst)
00564 continue;
00565
00566 AST_LIST_REMOVE_CURRENT(&translators, entry);
00567 ast_unregister_translator(&cur->t);
00568 free(cur);
00569 global_format_map.map[dst][src] = 0;
00570 break;
00571 }
00572 AST_LIST_TRAVERSE_SAFE_END;
00573 AST_LIST_UNLOCK(&translators);
00574 }
00575
00576 static void unregister_translators(void)
00577 {
00578 struct translator *cur;
00579
00580 AST_LIST_LOCK(&translators);
00581 while ((cur = AST_LIST_REMOVE_HEAD(&translators, entry))) {
00582 ast_unregister_translator(&cur->t);
00583 free(cur);
00584 }
00585 AST_LIST_UNLOCK(&translators);
00586 }
00587
00588 static void parse_config(void)
00589 {
00590 struct ast_variable *var;
00591 struct ast_config *cfg = ast_config_load("codecs.conf");
00592
00593 if (!cfg)
00594 return;
00595
00596 for (var = ast_variable_browse(cfg, "plc"); var; var = var->next) {
00597 if (!strcasecmp(var->name, "genericplc")) {
00598 global_useplc = ast_true(var->value);
00599 if (option_verbose > 2)
00600 ast_verbose(VERBOSE_PREFIX_3 "codec_zap: %susing generic PLC\n",
00601 global_useplc ? "" : "not ");
00602 }
00603 }
00604
00605 ast_config_destroy(cfg);
00606 }
00607
00608 static void build_translators(struct format_map *map, unsigned int dstfmts, unsigned int srcfmts)
00609 {
00610 unsigned int src, dst;
00611
00612 for (src = 0; src < 32; src++) {
00613 for (dst = 0; dst < 32; dst++) {
00614 if (!(srcfmts & (1 << src)))
00615 continue;
00616
00617 if (!(dstfmts & (1 << dst)))
00618 continue;
00619
00620 if (global_format_map.map[dst][src])
00621 continue;
00622
00623 if (!register_translator(dst, src))
00624 map->map[dst][src] = 1;
00625 }
00626 }
00627 }
00628
00629 static int find_transcoders(void)
00630 {
00631 struct dahdi_transcoder_info info = { 0, };
00632 struct format_map map = { { { 0 } } };
00633 int fd, res;
00634 unsigned int x, y;
00635
00636 if ((fd = open(DAHDI_FILE_TRANSCODE, O_RDWR)) < 0) {
00637 ast_log(LOG_ERROR, "Failed to open " DAHDI_FILE_TRANSCODE ": %s\n", strerror(errno));
00638 return 0;
00639 }
00640
00641 for (info.tcnum = 0; !(res = ioctl(fd, DAHDI_TC_GETINFO, &info)); info.tcnum++) {
00642 if (option_verbose > 1)
00643 ast_verbose(VERBOSE_PREFIX_2 "Found transcoder '%s'.\n", info.name);
00644
00645
00646
00647
00648
00649
00650
00651 if (info.dstfmts & (AST_FORMAT_ULAW | AST_FORMAT_ALAW)) {
00652 info.dstfmts |= AST_FORMAT_SLINEAR;
00653 info.dstfmts &= ~(AST_FORMAT_ULAW | AST_FORMAT_ALAW);
00654 }
00655 if (info.srcfmts & (AST_FORMAT_ULAW | AST_FORMAT_ALAW)) {
00656 info.srcfmts |= AST_FORMAT_SLINEAR;
00657 info.srcfmts &= ~(AST_FORMAT_ULAW | AST_FORMAT_ALAW);
00658 }
00659
00660 build_translators(&map, info.dstfmts, info.srcfmts);
00661 ast_atomic_fetchadd_int(&channels.total, info.numchannels / 2);
00662
00663 }
00664
00665 close(fd);
00666
00667 if (!info.tcnum && (option_verbose > 1))
00668 ast_verbose(VERBOSE_PREFIX_2 "No hardware transcoders found.\n");
00669
00670 for (x = 0; x < 32; x++) {
00671 for (y = 0; y < 32; y++) {
00672 if (!map.map[x][y] && global_format_map.map[x][y])
00673 drop_translator(x, y);
00674 }
00675 }
00676
00677 return 0;
00678 }
00679
00680 static int reload(void)
00681 {
00682 struct translator *cur;
00683
00684 parse_config();
00685
00686 AST_LIST_LOCK(&translators);
00687 AST_LIST_TRAVERSE(&translators, cur, entry)
00688 cur->t.useplc = global_useplc;
00689 AST_LIST_UNLOCK(&translators);
00690
00691 return 0;
00692 }
00693
00694 static int unload_module(void)
00695 {
00696 ast_cli_unregister_multiple(cli, sizeof(cli) / sizeof(cli[0]));
00697 unregister_translators();
00698
00699 return 0;
00700 }
00701
00702 static int load_module(void)
00703 {
00704 ast_ulaw_init();
00705 parse_config();
00706 find_transcoders();
00707 ast_cli_register_multiple(cli, sizeof(cli) / sizeof(cli[0]));
00708
00709 return 0;
00710 }
00711
00712 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Generic DAHDI Transcoder Codec Translator",
00713 .load = load_module,
00714 .unload = unload_module,
00715 .reload = reload,
00716 );