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
00035
00036
00037
00038
00039
00040 #include "asterisk.h"
00041
00042 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 171190 $")
00043
00044 #include <ctype.h>
00045 #include <math.h>
00046 #include <sys/ioctl.h>
00047
00048 #ifdef __linux
00049 #include <linux/soundcard.h>
00050 #elif defined(__FreeBSD__) || defined(__CYGWIN__)
00051 #include <sys/soundcard.h>
00052 #else
00053 #include <soundcard.h>
00054 #endif
00055
00056 #include "asterisk/channel.h"
00057 #include "asterisk/file.h"
00058 #include "asterisk/callerid.h"
00059 #include "asterisk/module.h"
00060 #include "asterisk/pbx.h"
00061 #include "asterisk/cli.h"
00062 #include "asterisk/causes.h"
00063 #include "asterisk/musiconhold.h"
00064 #include "asterisk/app.h"
00065
00066 #include "console_video.h"
00067
00068
00069 static struct ast_jb_conf default_jbconf =
00070 {
00071 .flags = 0,
00072 .max_size = -1,
00073 .resync_threshold = -1,
00074 .impl = "",
00075 };
00076 static struct ast_jb_conf global_jbconf;
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206 #define FRAME_SIZE 160
00207 #define QUEUE_SIZE 10
00208
00209 #if defined(__FreeBSD__)
00210 #define FRAGS 0x8
00211 #else
00212 #define FRAGS ( ( (6 * 5) << 16 ) | 0x6 )
00213 #endif
00214
00215
00216
00217
00218
00219 #define TEXT_SIZE 256
00220
00221 #if 0
00222 #define TRYOPEN 1
00223 #endif
00224 #define O_CLOSE 0x444
00225
00226 #if defined( __OpenBSD__ ) || defined( __NetBSD__ )
00227 #define DEV_DSP "/dev/audio"
00228 #else
00229 #define DEV_DSP "/dev/dsp"
00230 #endif
00231
00232 #ifndef MIN
00233 #define MIN(a,b) ((a) < (b) ? (a) : (b))
00234 #endif
00235 #ifndef MAX
00236 #define MAX(a,b) ((a) > (b) ? (a) : (b))
00237 #endif
00238
00239 static char *config = "oss.conf";
00240
00241 static int oss_debug;
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251 struct chan_oss_pvt {
00252 struct chan_oss_pvt *next;
00253
00254 char *name;
00255 int total_blocks;
00256 int sounddev;
00257 enum { M_UNSET, M_FULL, M_READ, M_WRITE } duplex;
00258 int autoanswer;
00259 int autohangup;
00260 int hookstate;
00261 char *mixer_cmd;
00262 unsigned int queuesize;
00263 unsigned int frags;
00264
00265 int warned;
00266 #define WARN_used_blocks 1
00267 #define WARN_speed 2
00268 #define WARN_frag 4
00269 int w_errors;
00270 struct timeval lastopen;
00271
00272 int overridecontext;
00273 int mute;
00274
00275
00276
00277
00278 #define BOOST_SCALE (1<<9)
00279 #define BOOST_MAX 40
00280 int boost;
00281 char device[64];
00282
00283 pthread_t sthread;
00284
00285 struct ast_channel *owner;
00286
00287 struct video_desc *env;
00288
00289 char ext[AST_MAX_EXTENSION];
00290 char ctx[AST_MAX_CONTEXT];
00291 char language[MAX_LANGUAGE];
00292 char cid_name[256];
00293 char cid_num[256];
00294 char mohinterpret[MAX_MUSICCLASS];
00295
00296
00297 char oss_write_buf[FRAME_SIZE * 2];
00298 int oss_write_dst;
00299
00300
00301
00302 char oss_read_buf[FRAME_SIZE * 2 + AST_FRIENDLY_OFFSET];
00303 int readpos;
00304 struct ast_frame read_f;
00305 };
00306
00307
00308 static struct chan_oss_pvt *find_desc(char *dev);
00309
00310 static char *oss_active;
00311
00312
00313 struct video_desc *get_video_desc(struct ast_channel *c)
00314 {
00315 struct chan_oss_pvt *o = c ? c->tech_pvt : find_desc(oss_active);
00316 return o ? o->env : NULL;
00317 }
00318 static struct chan_oss_pvt oss_default = {
00319 .sounddev = -1,
00320 .duplex = M_UNSET,
00321 .autoanswer = 1,
00322 .autohangup = 1,
00323 .queuesize = QUEUE_SIZE,
00324 .frags = FRAGS,
00325 .ext = "s",
00326 .ctx = "default",
00327 .readpos = AST_FRIENDLY_OFFSET,
00328 .lastopen = { 0, 0 },
00329 .boost = BOOST_SCALE,
00330 };
00331
00332
00333 static int setformat(struct chan_oss_pvt *o, int mode);
00334
00335 static struct ast_channel *oss_request(const char *type, int format, void *data, int *cause);
00336 static int oss_digit_begin(struct ast_channel *c, char digit);
00337 static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration);
00338 static int oss_text(struct ast_channel *c, const char *text);
00339 static int oss_hangup(struct ast_channel *c);
00340 static int oss_answer(struct ast_channel *c);
00341 static struct ast_frame *oss_read(struct ast_channel *chan);
00342 static int oss_call(struct ast_channel *c, char *dest, int timeout);
00343 static int oss_write(struct ast_channel *chan, struct ast_frame *f);
00344 static int oss_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen);
00345 static int oss_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00346 static char tdesc[] = "OSS Console Channel Driver";
00347
00348
00349 static struct ast_channel_tech oss_tech = {
00350 .type = "Console",
00351 .description = tdesc,
00352 .capabilities = AST_FORMAT_SLINEAR,
00353 .requester = oss_request,
00354 .send_digit_begin = oss_digit_begin,
00355 .send_digit_end = oss_digit_end,
00356 .send_text = oss_text,
00357 .hangup = oss_hangup,
00358 .answer = oss_answer,
00359 .read = oss_read,
00360 .call = oss_call,
00361 .write = oss_write,
00362 .write_video = console_write_video,
00363 .indicate = oss_indicate,
00364 .fixup = oss_fixup,
00365 };
00366
00367
00368
00369
00370 static struct chan_oss_pvt *find_desc(char *dev)
00371 {
00372 struct chan_oss_pvt *o = NULL;
00373
00374 if (!dev)
00375 ast_log(LOG_WARNING, "null dev\n");
00376
00377 for (o = oss_default.next; o && o->name && dev && strcmp(o->name, dev) != 0; o = o->next);
00378
00379 if (!o)
00380 ast_log(LOG_WARNING, "could not find <%s>\n", dev ? dev : "--no-device--");
00381
00382 return o;
00383 }
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396 static char *ast_ext_ctx(const char *src, char **ext, char **ctx)
00397 {
00398 struct chan_oss_pvt *o = find_desc(oss_active);
00399
00400 if (ext == NULL || ctx == NULL)
00401 return NULL;
00402
00403 *ext = *ctx = NULL;
00404
00405 if (src && *src != '\0')
00406 *ext = ast_strdup(src);
00407
00408 if (*ext == NULL)
00409 return NULL;
00410
00411 if (!o->overridecontext) {
00412
00413 *ctx = strrchr(*ext, '@');
00414 if (*ctx)
00415 *(*ctx)++ = '\0';
00416 }
00417
00418 return *ext;
00419 }
00420
00421
00422
00423
00424 static int used_blocks(struct chan_oss_pvt *o)
00425 {
00426 struct audio_buf_info info;
00427
00428 if (ioctl(o->sounddev, SNDCTL_DSP_GETOSPACE, &info)) {
00429 if (!(o->warned & WARN_used_blocks)) {
00430 ast_log(LOG_WARNING, "Error reading output space\n");
00431 o->warned |= WARN_used_blocks;
00432 }
00433 return 1;
00434 }
00435
00436 if (o->total_blocks == 0) {
00437 if (0)
00438 ast_log(LOG_WARNING, "fragtotal %d size %d avail %d\n", info.fragstotal, info.fragsize, info.fragments);
00439 o->total_blocks = info.fragments;
00440 }
00441
00442 return o->total_blocks - info.fragments;
00443 }
00444
00445
00446 static int soundcard_writeframe(struct chan_oss_pvt *o, short *data)
00447 {
00448 int res;
00449
00450 if (o->sounddev < 0)
00451 setformat(o, O_RDWR);
00452 if (o->sounddev < 0)
00453 return 0;
00454
00455
00456
00457
00458
00459
00460 res = used_blocks(o);
00461 if (res > o->queuesize) {
00462 if (o->w_errors++ == 0 && (oss_debug & 0x4))
00463 ast_log(LOG_WARNING, "write: used %d blocks (%d)\n", res, o->w_errors);
00464 return 0;
00465 }
00466 o->w_errors = 0;
00467 return write(o->sounddev, (void *)data, FRAME_SIZE * 2);
00468 }
00469
00470
00471
00472
00473
00474
00475 static int setformat(struct chan_oss_pvt *o, int mode)
00476 {
00477 int fmt, desired, res, fd;
00478
00479 if (o->sounddev >= 0) {
00480 ioctl(o->sounddev, SNDCTL_DSP_RESET, 0);
00481 close(o->sounddev);
00482 o->duplex = M_UNSET;
00483 o->sounddev = -1;
00484 }
00485 if (mode == O_CLOSE)
00486 return 0;
00487 if (ast_tvdiff_ms(ast_tvnow(), o->lastopen) < 1000)
00488 return -1;
00489 o->lastopen = ast_tvnow();
00490 fd = o->sounddev = open(o->device, mode | O_NONBLOCK);
00491 if (fd < 0) {
00492 ast_log(LOG_WARNING, "Unable to re-open DSP device %s: %s\n", o->device, strerror(errno));
00493 return -1;
00494 }
00495 if (o->owner)
00496 ast_channel_set_fd(o->owner, 0, fd);
00497
00498 #if __BYTE_ORDER == __LITTLE_ENDIAN
00499 fmt = AFMT_S16_LE;
00500 #else
00501 fmt = AFMT_S16_BE;
00502 #endif
00503 res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
00504 if (res < 0) {
00505 ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n");
00506 return -1;
00507 }
00508 switch (mode) {
00509 case O_RDWR:
00510 res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
00511
00512 res = ioctl(fd, SNDCTL_DSP_GETCAPS, &fmt);
00513 if (res == 0 && (fmt & DSP_CAP_DUPLEX)) {
00514 ast_verb(2, "Console is full duplex\n");
00515 o->duplex = M_FULL;
00516 };
00517 break;
00518
00519 case O_WRONLY:
00520 o->duplex = M_WRITE;
00521 break;
00522
00523 case O_RDONLY:
00524 o->duplex = M_READ;
00525 break;
00526 }
00527
00528 fmt = 0;
00529 res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
00530 if (res < 0) {
00531 ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
00532 return -1;
00533 }
00534 fmt = desired = DEFAULT_SAMPLE_RATE;
00535 res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt);
00536
00537 if (res < 0) {
00538 ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
00539 return -1;
00540 }
00541 if (fmt != desired) {
00542 if (!(o->warned & WARN_speed)) {
00543 ast_log(LOG_WARNING,
00544 "Requested %d Hz, got %d Hz -- sound may be choppy\n",
00545 desired, fmt);
00546 o->warned |= WARN_speed;
00547 }
00548 }
00549
00550
00551
00552
00553 if (o->frags) {
00554 fmt = o->frags;
00555 res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt);
00556 if (res < 0) {
00557 if (!(o->warned & WARN_frag)) {
00558 ast_log(LOG_WARNING,
00559 "Unable to set fragment size -- sound may be choppy\n");
00560 o->warned |= WARN_frag;
00561 }
00562 }
00563 }
00564
00565 res = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
00566 res = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &res);
00567
00568 return 0;
00569 }
00570
00571
00572
00573
00574 static int oss_digit_begin(struct ast_channel *c, char digit)
00575 {
00576 return 0;
00577 }
00578
00579 static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration)
00580 {
00581
00582 ast_verbose(" << Console Received digit %c of duration %u ms >> \n",
00583 digit, duration);
00584 return 0;
00585 }
00586
00587 static int oss_text(struct ast_channel *c, const char *text)
00588 {
00589
00590 ast_verbose(" << Console Received text %s >> \n", text);
00591 return 0;
00592 }
00593
00594
00595
00596
00597 static int oss_call(struct ast_channel *c, char *dest, int timeout)
00598 {
00599 struct chan_oss_pvt *o = c->tech_pvt;
00600 struct ast_frame f = { 0, };
00601 AST_DECLARE_APP_ARGS(args,
00602 AST_APP_ARG(name);
00603 AST_APP_ARG(flags);
00604 );
00605 char *parse = ast_strdupa(dest);
00606
00607 AST_NONSTANDARD_APP_ARGS(args, parse, '/');
00608
00609 ast_verbose(" << Call to device '%s' dnid '%s' rdnis '%s' on console from '%s' <%s> >>\n", dest, c->cid.cid_dnid, c->cid.cid_rdnis, c->cid.cid_name, c->cid.cid_num);
00610 if (!ast_strlen_zero(args.flags) && strcasecmp(args.flags, "answer") == 0) {
00611 f.frametype = AST_FRAME_CONTROL;
00612 f.subclass = AST_CONTROL_ANSWER;
00613 ast_queue_frame(c, &f);
00614 } else if (!ast_strlen_zero(args.flags) && strcasecmp(args.flags, "noanswer") == 0) {
00615 f.frametype = AST_FRAME_CONTROL;
00616 f.subclass = AST_CONTROL_RINGING;
00617 ast_queue_frame(c, &f);
00618 ast_indicate(c, AST_CONTROL_RINGING);
00619 } else if (o->autoanswer) {
00620 ast_verbose(" << Auto-answered >> \n");
00621 f.frametype = AST_FRAME_CONTROL;
00622 f.subclass = AST_CONTROL_ANSWER;
00623 ast_queue_frame(c, &f);
00624 o->hookstate = 1;
00625 } else {
00626 ast_verbose("<< Type 'answer' to answer, or use 'autoanswer' for future calls >> \n");
00627 f.frametype = AST_FRAME_CONTROL;
00628 f.subclass = AST_CONTROL_RINGING;
00629 ast_queue_frame(c, &f);
00630 ast_indicate(c, AST_CONTROL_RINGING);
00631 }
00632 return 0;
00633 }
00634
00635
00636
00637
00638 static int oss_answer(struct ast_channel *c)
00639 {
00640 struct chan_oss_pvt *o = c->tech_pvt;
00641 ast_verbose(" << Console call has been answered >> \n");
00642 ast_setstate(c, AST_STATE_UP);
00643 o->hookstate = 1;
00644 return 0;
00645 }
00646
00647 static int oss_hangup(struct ast_channel *c)
00648 {
00649 struct chan_oss_pvt *o = c->tech_pvt;
00650
00651 c->tech_pvt = NULL;
00652 o->owner = NULL;
00653 ast_verbose(" << Hangup on console >> \n");
00654 console_video_uninit(o->env);
00655 ast_module_unref(ast_module_info->self);
00656 if (o->hookstate) {
00657 if (o->autoanswer || o->autohangup) {
00658
00659 o->hookstate = 0;
00660 setformat(o, O_CLOSE);
00661 }
00662 }
00663 return 0;
00664 }
00665
00666
00667 static int oss_write(struct ast_channel *c, struct ast_frame *f)
00668 {
00669 int src;
00670 struct chan_oss_pvt *o = c->tech_pvt;
00671
00672
00673
00674
00675
00676
00677
00678 src = 0;
00679 while (src < f->datalen) {
00680
00681 int l = sizeof(o->oss_write_buf) - o->oss_write_dst;
00682
00683 if (f->datalen - src >= l) {
00684 memcpy(o->oss_write_buf + o->oss_write_dst, f->data.ptr + src, l);
00685 soundcard_writeframe(o, (short *) o->oss_write_buf);
00686 src += l;
00687 o->oss_write_dst = 0;
00688 } else {
00689 l = f->datalen - src;
00690 memcpy(o->oss_write_buf + o->oss_write_dst, f->data.ptr + src, l);
00691 src += l;
00692 o->oss_write_dst += l;
00693 }
00694 }
00695 return 0;
00696 }
00697
00698 static struct ast_frame *oss_read(struct ast_channel *c)
00699 {
00700 int res;
00701 struct chan_oss_pvt *o = c->tech_pvt;
00702 struct ast_frame *f = &o->read_f;
00703
00704
00705
00706 memset(f, '\0', sizeof(struct ast_frame));
00707 f->frametype = AST_FRAME_NULL;
00708 f->src = oss_tech.type;
00709
00710 res = read(o->sounddev, o->oss_read_buf + o->readpos, sizeof(o->oss_read_buf) - o->readpos);
00711 if (res < 0)
00712 return f;
00713
00714 o->readpos += res;
00715 if (o->readpos < sizeof(o->oss_read_buf))
00716 return f;
00717
00718 if (o->mute)
00719 return f;
00720
00721 o->readpos = AST_FRIENDLY_OFFSET;
00722 if (c->_state != AST_STATE_UP)
00723 return f;
00724
00725 f->frametype = AST_FRAME_VOICE;
00726 f->subclass = AST_FORMAT_SLINEAR;
00727 f->samples = FRAME_SIZE;
00728 f->datalen = FRAME_SIZE * 2;
00729 f->data.ptr = o->oss_read_buf + AST_FRIENDLY_OFFSET;
00730 if (o->boost != BOOST_SCALE) {
00731 int i, x;
00732 int16_t *p = (int16_t *) f->data.ptr;
00733 for (i = 0; i < f->samples; i++) {
00734 x = (p[i] * o->boost) / BOOST_SCALE;
00735 if (x > 32767)
00736 x = 32767;
00737 else if (x < -32768)
00738 x = -32768;
00739 p[i] = x;
00740 }
00741 }
00742
00743 f->offset = AST_FRIENDLY_OFFSET;
00744 return f;
00745 }
00746
00747 static int oss_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00748 {
00749 struct chan_oss_pvt *o = newchan->tech_pvt;
00750 o->owner = newchan;
00751 return 0;
00752 }
00753
00754 static int oss_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen)
00755 {
00756 struct chan_oss_pvt *o = c->tech_pvt;
00757 int res = 0;
00758
00759 switch (cond) {
00760 case AST_CONTROL_BUSY:
00761 case AST_CONTROL_CONGESTION:
00762 case AST_CONTROL_RINGING:
00763 case -1:
00764 res = -1;
00765 break;
00766 case AST_CONTROL_PROGRESS:
00767 case AST_CONTROL_PROCEEDING:
00768 case AST_CONTROL_VIDUPDATE:
00769 case AST_CONTROL_SRCUPDATE:
00770 break;
00771 case AST_CONTROL_HOLD:
00772 ast_verbose(" << Console Has Been Placed on Hold >> \n");
00773 ast_moh_start(c, data, o->mohinterpret);
00774 break;
00775 case AST_CONTROL_UNHOLD:
00776 ast_verbose(" << Console Has Been Retrieved from Hold >> \n");
00777 ast_moh_stop(c);
00778 break;
00779 default:
00780 ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, c->name);
00781 return -1;
00782 }
00783
00784 return res;
00785 }
00786
00787
00788
00789
00790 static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx, int state)
00791 {
00792 struct ast_channel *c;
00793
00794 c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, 0, "Console/%s", o->device + 5);
00795 if (c == NULL)
00796 return NULL;
00797 c->tech = &oss_tech;
00798 if (o->sounddev < 0)
00799 setformat(o, O_RDWR);
00800 ast_channel_set_fd(c, 0, o->sounddev);
00801 c->nativeformats = AST_FORMAT_SLINEAR;
00802
00803 if (state == AST_STATE_RINGING)
00804 c->nativeformats |= console_video_formats;
00805
00806 c->readformat = AST_FORMAT_SLINEAR;
00807 c->writeformat = AST_FORMAT_SLINEAR;
00808 c->tech_pvt = o;
00809
00810 if (!ast_strlen_zero(o->language))
00811 ast_string_field_set(c, language, o->language);
00812
00813
00814 c->cid.cid_ani = ast_strdup(o->cid_num);
00815 if (!ast_strlen_zero(ext))
00816 c->cid.cid_dnid = ast_strdup(ext);
00817
00818 o->owner = c;
00819 ast_module_ref(ast_module_info->self);
00820 ast_jb_configure(c, &global_jbconf);
00821 if (state != AST_STATE_DOWN) {
00822 if (ast_pbx_start(c)) {
00823 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", c->name);
00824 ast_hangup(c);
00825 o->owner = c = NULL;
00826 }
00827 }
00828 console_video_start(get_video_desc(c), c);
00829
00830 return c;
00831 }
00832
00833 static struct ast_channel *oss_request(const char *type, int format, void *data, int *cause)
00834 {
00835 struct ast_channel *c;
00836 struct chan_oss_pvt *o;
00837 AST_DECLARE_APP_ARGS(args,
00838 AST_APP_ARG(name);
00839 AST_APP_ARG(flags);
00840 );
00841 char *parse = ast_strdupa(data);
00842
00843 AST_NONSTANDARD_APP_ARGS(args, parse, '/');
00844 o = find_desc(args.name);
00845
00846 ast_log(LOG_WARNING, "oss_request ty <%s> data 0x%p <%s>\n", type, data, (char *) data);
00847 if (o == NULL) {
00848 ast_log(LOG_NOTICE, "Device %s not found\n", args.name);
00849
00850 return NULL;
00851 }
00852 if ((format & AST_FORMAT_SLINEAR) == 0) {
00853 ast_log(LOG_NOTICE, "Format 0x%x unsupported\n", format);
00854 return NULL;
00855 }
00856 if (o->owner) {
00857 ast_log(LOG_NOTICE, "Already have a call (chan %p) on the OSS channel\n", o->owner);
00858 *cause = AST_CAUSE_BUSY;
00859 return NULL;
00860 }
00861 c = oss_new(o, NULL, NULL, AST_STATE_DOWN);
00862 if (c == NULL) {
00863 ast_log(LOG_WARNING, "Unable to create new OSS channel\n");
00864 return NULL;
00865 }
00866 return c;
00867 }
00868
00869 static void store_config_core(struct chan_oss_pvt *o, const char *var, const char *value);
00870
00871
00872
00873
00874 static char *console_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00875 {
00876 struct chan_oss_pvt *o = find_desc(oss_active);
00877 const char *var, *value;
00878 switch (cmd) {
00879 case CLI_INIT:
00880 e->command = CONSOLE_VIDEO_CMDS;
00881 e->usage =
00882 "Usage: " CONSOLE_VIDEO_CMDS "...\n"
00883 " Generic handler for console commands.\n";
00884 return NULL;
00885
00886 case CLI_GENERATE:
00887 return NULL;
00888 }
00889
00890 if (a->argc < e->args)
00891 return CLI_SHOWUSAGE;
00892 if (o == NULL) {
00893 ast_log(LOG_WARNING, "Cannot find device %s (should not happen!)\n",
00894 oss_active);
00895 return CLI_FAILURE;
00896 }
00897 var = a->argv[e->args-1];
00898 value = a->argc > e->args ? a->argv[e->args] : NULL;
00899 if (value)
00900 store_config_core(o, var, value);
00901 if (!console_video_cli(o->env, var, a->fd))
00902 return CLI_SUCCESS;
00903
00904 if (!strcasecmp(var, "device")) {
00905 ast_cli(a->fd, "device is [%s]\n", o->device);
00906 }
00907 return CLI_SUCCESS;
00908 }
00909
00910 static char *console_autoanswer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00911 {
00912 struct chan_oss_pvt *o = find_desc(oss_active);
00913
00914 switch (cmd) {
00915 case CLI_INIT:
00916 e->command = "console autoanswer [on|off]";
00917 e->usage =
00918 "Usage: console autoanswer [on|off]\n"
00919 " Enables or disables autoanswer feature. If used without\n"
00920 " argument, displays the current on/off status of autoanswer.\n"
00921 " The default value of autoanswer is in 'oss.conf'.\n";
00922 return NULL;
00923
00924 case CLI_GENERATE:
00925 return NULL;
00926 }
00927
00928 if (a->argc == e->args - 1) {
00929 ast_cli(a->fd, "Auto answer is %s.\n", o->autoanswer ? "on" : "off");
00930 return CLI_SUCCESS;
00931 }
00932 if (a->argc != e->args)
00933 return CLI_SHOWUSAGE;
00934 if (o == NULL) {
00935 ast_log(LOG_WARNING, "Cannot find device %s (should not happen!)\n",
00936 oss_active);
00937 return CLI_FAILURE;
00938 }
00939 if (!strcasecmp(a->argv[e->args-1], "on"))
00940 o->autoanswer = 1;
00941 else if (!strcasecmp(a->argv[e->args - 1], "off"))
00942 o->autoanswer = 0;
00943 else
00944 return CLI_SHOWUSAGE;
00945 return CLI_SUCCESS;
00946 }
00947
00948
00949 static char *console_do_answer(int fd)
00950 {
00951 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00952 struct chan_oss_pvt *o = find_desc(oss_active);
00953 if (!o->owner) {
00954 if (fd > -1)
00955 ast_cli(fd, "No one is calling us\n");
00956 return CLI_FAILURE;
00957 }
00958 o->hookstate = 1;
00959 ast_queue_frame(o->owner, &f);
00960 return CLI_SUCCESS;
00961 }
00962
00963
00964
00965
00966 static char *console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00967 {
00968 switch (cmd) {
00969 case CLI_INIT:
00970 e->command = "console answer";
00971 e->usage =
00972 "Usage: console answer\n"
00973 " Answers an incoming call on the console (OSS) channel.\n";
00974 return NULL;
00975
00976 case CLI_GENERATE:
00977 return NULL;
00978 }
00979 if (a->argc != e->args)
00980 return CLI_SHOWUSAGE;
00981 return console_do_answer(a->fd);
00982 }
00983
00984
00985
00986
00987
00988
00989
00990 static char *console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00991 {
00992 struct chan_oss_pvt *o = find_desc(oss_active);
00993 char buf[TEXT_SIZE];
00994
00995 if (cmd == CLI_INIT) {
00996 e->command = "console send text";
00997 e->usage =
00998 "Usage: console send text <message>\n"
00999 " Sends a text message for display on the remote terminal.\n";
01000 return NULL;
01001 } else if (cmd == CLI_GENERATE)
01002 return NULL;
01003
01004 if (a->argc < e->args + 1)
01005 return CLI_SHOWUSAGE;
01006 if (!o->owner) {
01007 ast_cli(a->fd, "Not in a call\n");
01008 return CLI_FAILURE;
01009 }
01010 ast_join(buf, sizeof(buf) - 1, a->argv + e->args);
01011 if (!ast_strlen_zero(buf)) {
01012 struct ast_frame f = { 0, };
01013 int i = strlen(buf);
01014 buf[i] = '\n';
01015 f.frametype = AST_FRAME_TEXT;
01016 f.subclass = 0;
01017 f.data.ptr = buf;
01018 f.datalen = i + 1;
01019 ast_queue_frame(o->owner, &f);
01020 }
01021 return CLI_SUCCESS;
01022 }
01023
01024 static char *console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01025 {
01026 struct chan_oss_pvt *o = find_desc(oss_active);
01027
01028 if (cmd == CLI_INIT) {
01029 e->command = "console hangup";
01030 e->usage =
01031 "Usage: console hangup\n"
01032 " Hangs up any call currently placed on the console.\n";
01033 return NULL;
01034 } else if (cmd == CLI_GENERATE)
01035 return NULL;
01036
01037 if (a->argc != e->args)
01038 return CLI_SHOWUSAGE;
01039 if (!o->owner && !o->hookstate) {
01040 ast_cli(a->fd, "No call to hang up\n");
01041 return CLI_FAILURE;
01042 }
01043 o->hookstate = 0;
01044 if (o->owner)
01045 ast_queue_hangup_with_cause(o->owner, AST_CAUSE_NORMAL_CLEARING);
01046 setformat(o, O_CLOSE);
01047 return CLI_SUCCESS;
01048 }
01049
01050 static char *console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01051 {
01052 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH };
01053 struct chan_oss_pvt *o = find_desc(oss_active);
01054
01055 if (cmd == CLI_INIT) {
01056 e->command = "console flash";
01057 e->usage =
01058 "Usage: console flash\n"
01059 " Flashes the call currently placed on the console.\n";
01060 return NULL;
01061 } else if (cmd == CLI_GENERATE)
01062 return NULL;
01063
01064 if (a->argc != e->args)
01065 return CLI_SHOWUSAGE;
01066 if (!o->owner) {
01067 ast_cli(a->fd, "No call to flash\n");
01068 return CLI_FAILURE;
01069 }
01070 o->hookstate = 0;
01071 if (o->owner)
01072 ast_queue_frame(o->owner, &f);
01073 return CLI_SUCCESS;
01074 }
01075
01076 static char *console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01077 {
01078 char *s = NULL, *mye = NULL, *myc = NULL;
01079 struct chan_oss_pvt *o = find_desc(oss_active);
01080
01081 if (cmd == CLI_INIT) {
01082 e->command = "console dial";
01083 e->usage =
01084 "Usage: console dial [extension[@context]]\n"
01085 " Dials a given extension (and context if specified)\n";
01086 return NULL;
01087 } else if (cmd == CLI_GENERATE)
01088 return NULL;
01089
01090 if (a->argc > e->args + 1)
01091 return CLI_SHOWUSAGE;
01092 if (o->owner) {
01093 int i;
01094 struct ast_frame f = { AST_FRAME_DTMF, 0 };
01095
01096 if (a->argc == e->args) {
01097 ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n");
01098 return CLI_FAILURE;
01099 }
01100 s = a->argv[e->args];
01101
01102 for (i = 0; i < strlen(s); i++) {
01103 f.subclass = s[i];
01104 ast_queue_frame(o->owner, &f);
01105 }
01106 return CLI_SUCCESS;
01107 }
01108
01109 if (a->argc == e->args + 1)
01110 s = ast_ext_ctx(a->argv[e->args], &mye, &myc);
01111
01112 if (mye == NULL)
01113 mye = o->ext;
01114 if (myc == NULL)
01115 myc = o->ctx;
01116 if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
01117 o->hookstate = 1;
01118 oss_new(o, mye, myc, AST_STATE_RINGING);
01119 } else
01120 ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
01121 if (s)
01122 ast_free(s);
01123 return CLI_SUCCESS;
01124 }
01125
01126 static char *console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01127 {
01128 struct chan_oss_pvt *o = find_desc(oss_active);
01129 char *s;
01130 int toggle = 0;
01131
01132 if (cmd == CLI_INIT) {
01133 e->command = "console {mute|unmute} [toggle]";
01134 e->usage =
01135 "Usage: console {mute|unmute} [toggle]\n"
01136 " Mute/unmute the microphone.\n";
01137 return NULL;
01138 } else if (cmd == CLI_GENERATE)
01139 return NULL;
01140
01141 if (a->argc > e->args)
01142 return CLI_SHOWUSAGE;
01143 if (a->argc == e->args) {
01144 if (strcasecmp(a->argv[e->args-1], "toggle"))
01145 return CLI_SHOWUSAGE;
01146 toggle = 1;
01147 }
01148 s = a->argv[e->args-2];
01149 if (!strcasecmp(s, "mute"))
01150 o->mute = toggle ? ~o->mute : 1;
01151 else if (!strcasecmp(s, "unmute"))
01152 o->mute = toggle ? ~o->mute : 0;
01153 else
01154 return CLI_SHOWUSAGE;
01155 ast_cli(a->fd, "Console mic is %s\n", o->mute ? "off" : "on");
01156 return CLI_SUCCESS;
01157 }
01158
01159 static char *console_transfer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01160 {
01161 struct chan_oss_pvt *o = find_desc(oss_active);
01162 struct ast_channel *b = NULL;
01163 char *tmp, *ext, *ctx;
01164
01165 switch (cmd) {
01166 case CLI_INIT:
01167 e->command = "console transfer";
01168 e->usage =
01169 "Usage: console transfer <extension>[@context]\n"
01170 " Transfers the currently connected call to the given extension (and\n"
01171 " context if specified)\n";
01172 return NULL;
01173 case CLI_GENERATE:
01174 return NULL;
01175 }
01176
01177 if (a->argc != 3)
01178 return CLI_SHOWUSAGE;
01179 if (o == NULL)
01180 return CLI_FAILURE;
01181 if (o->owner == NULL || (b = ast_bridged_channel(o->owner)) == NULL) {
01182 ast_cli(a->fd, "There is no call to transfer\n");
01183 return CLI_SUCCESS;
01184 }
01185
01186 tmp = ast_ext_ctx(a->argv[2], &ext, &ctx);
01187 if (ctx == NULL)
01188 ctx = o->owner->context;
01189 if (!ast_exists_extension(b, ctx, ext, 1, b->cid.cid_num))
01190 ast_cli(a->fd, "No such extension exists\n");
01191 else {
01192 ast_cli(a->fd, "Whee, transferring %s to %s@%s.\n", b->name, ext, ctx);
01193 if (ast_async_goto(b, ctx, ext, 1))
01194 ast_cli(a->fd, "Failed to transfer :(\n");
01195 }
01196 if (tmp)
01197 ast_free(tmp);
01198 return CLI_SUCCESS;
01199 }
01200
01201 static char *console_active(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01202 {
01203 switch (cmd) {
01204 case CLI_INIT:
01205 e->command = "console active";
01206 e->usage =
01207 "Usage: console active [device]\n"
01208 " If used without a parameter, displays which device is the current\n"
01209 " console. If a device is specified, the console sound device is changed to\n"
01210 " the device specified.\n";
01211 return NULL;
01212 case CLI_GENERATE:
01213 return NULL;
01214 }
01215
01216 if (a->argc == 2)
01217 ast_cli(a->fd, "active console is [%s]\n", oss_active);
01218 else if (a->argc != 3)
01219 return CLI_SHOWUSAGE;
01220 else {
01221 struct chan_oss_pvt *o;
01222 if (strcmp(a->argv[2], "show") == 0) {
01223 for (o = oss_default.next; o; o = o->next)
01224 ast_cli(a->fd, "device [%s] exists\n", o->name);
01225 return CLI_SUCCESS;
01226 }
01227 o = find_desc(a->argv[2]);
01228 if (o == NULL)
01229 ast_cli(a->fd, "No device [%s] exists\n", a->argv[2]);
01230 else
01231 oss_active = o->name;
01232 }
01233 return CLI_SUCCESS;
01234 }
01235
01236
01237
01238
01239 static void store_boost(struct chan_oss_pvt *o, const char *s)
01240 {
01241 double boost = 0;
01242 if (sscanf(s, "%lf", &boost) != 1) {
01243 ast_log(LOG_WARNING, "invalid boost <%s>\n", s);
01244 return;
01245 }
01246 if (boost < -BOOST_MAX) {
01247 ast_log(LOG_WARNING, "boost %s too small, using %d\n", s, -BOOST_MAX);
01248 boost = -BOOST_MAX;
01249 } else if (boost > BOOST_MAX) {
01250 ast_log(LOG_WARNING, "boost %s too large, using %d\n", s, BOOST_MAX);
01251 boost = BOOST_MAX;
01252 }
01253 boost = exp(log(10) * boost / 20) * BOOST_SCALE;
01254 o->boost = boost;
01255 ast_log(LOG_WARNING, "setting boost %s to %d\n", s, o->boost);
01256 }
01257
01258 static char *console_boost(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01259 {
01260 struct chan_oss_pvt *o = find_desc(oss_active);
01261
01262 switch (cmd) {
01263 case CLI_INIT:
01264 e->command = "console boost";
01265 e->usage =
01266 "Usage: console boost [boost in dB]\n"
01267 " Sets or display mic boost in dB\n";
01268 return NULL;
01269 case CLI_GENERATE:
01270 return NULL;
01271 }
01272
01273 if (a->argc == 2)
01274 ast_cli(a->fd, "boost currently %5.1f\n", 20 * log10(((double) o->boost / (double) BOOST_SCALE)));
01275 else if (a->argc == 3)
01276 store_boost(o, a->argv[2]);
01277 return CLI_SUCCESS;
01278 }
01279
01280 static struct ast_cli_entry cli_oss[] = {
01281 AST_CLI_DEFINE(console_answer, "Answer an incoming console call"),
01282 AST_CLI_DEFINE(console_hangup, "Hangup a call on the console"),
01283 AST_CLI_DEFINE(console_flash, "Flash a call on the console"),
01284 AST_CLI_DEFINE(console_dial, "Dial an extension on the console"),
01285 AST_CLI_DEFINE(console_mute, "Disable/Enable mic input"),
01286 AST_CLI_DEFINE(console_transfer, "Transfer a call to a different extension"),
01287 AST_CLI_DEFINE(console_cmd, "Generic console command"),
01288 AST_CLI_DEFINE(console_sendtext, "Send text to the remote device"),
01289 AST_CLI_DEFINE(console_autoanswer, "Sets/displays autoanswer"),
01290 AST_CLI_DEFINE(console_boost, "Sets/displays mic boost in dB"),
01291 AST_CLI_DEFINE(console_active, "Sets/displays active console"),
01292 };
01293
01294
01295
01296
01297
01298
01299 static void store_mixer(struct chan_oss_pvt *o, const char *s)
01300 {
01301 int i;
01302
01303 for (i = 0; i < strlen(s); i++) {
01304 if (!isalnum(s[i]) && strchr(" \t-/", s[i]) == NULL) {
01305 ast_log(LOG_WARNING, "Suspect char %c in mixer cmd, ignoring:\n\t%s\n", s[i], s);
01306 return;
01307 }
01308 }
01309 if (o->mixer_cmd)
01310 ast_free(o->mixer_cmd);
01311 o->mixer_cmd = ast_strdup(s);
01312 ast_log(LOG_WARNING, "setting mixer %s\n", s);
01313 }
01314
01315
01316
01317
01318 static void store_callerid(struct chan_oss_pvt *o, const char *s)
01319 {
01320 ast_callerid_split(s, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num));
01321 }
01322
01323 static void store_config_core(struct chan_oss_pvt *o, const char *var, const char *value)
01324 {
01325 CV_START(var, value);
01326
01327
01328 if (!ast_jb_read_conf(&global_jbconf, var, value))
01329 return;
01330
01331 if (!console_video_config(&o->env, var, value))
01332 return;
01333 CV_BOOL("autoanswer", o->autoanswer);
01334 CV_BOOL("autohangup", o->autohangup);
01335 CV_BOOL("overridecontext", o->overridecontext);
01336 CV_STR("device", o->device);
01337 CV_UINT("frags", o->frags);
01338 CV_UINT("debug", oss_debug);
01339 CV_UINT("queuesize", o->queuesize);
01340 CV_STR("context", o->ctx);
01341 CV_STR("language", o->language);
01342 CV_STR("mohinterpret", o->mohinterpret);
01343 CV_STR("extension", o->ext);
01344 CV_F("mixer", store_mixer(o, value));
01345 CV_F("callerid", store_callerid(o, value)) ;
01346 CV_F("boost", store_boost(o, value));
01347
01348 CV_END;
01349 }
01350
01351
01352
01353
01354 static struct chan_oss_pvt *store_config(struct ast_config *cfg, char *ctg)
01355 {
01356 struct ast_variable *v;
01357 struct chan_oss_pvt *o;
01358
01359 if (ctg == NULL) {
01360 o = &oss_default;
01361 ctg = "general";
01362 } else {
01363 if (!(o = ast_calloc(1, sizeof(*o))))
01364 return NULL;
01365 *o = oss_default;
01366
01367 if (strcmp(ctg, "general") == 0) {
01368 o->name = ast_strdup("dsp");
01369 oss_active = o->name;
01370 goto openit;
01371 }
01372 o->name = ast_strdup(ctg);
01373 }
01374
01375 strcpy(o->mohinterpret, "default");
01376
01377 o->lastopen = ast_tvnow();
01378
01379 for (v = ast_variable_browse(cfg, ctg); v; v = v->next) {
01380 store_config_core(o, v->name, v->value);
01381 }
01382 if (ast_strlen_zero(o->device))
01383 ast_copy_string(o->device, DEV_DSP, sizeof(o->device));
01384 if (o->mixer_cmd) {
01385 char *cmd;
01386
01387 if (asprintf(&cmd, "mixer %s", o->mixer_cmd) < 0) {
01388 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
01389 } else {
01390 ast_log(LOG_WARNING, "running [%s]\n", cmd);
01391 if (system(cmd) < 0) {
01392 ast_log(LOG_WARNING, "system() failed: %s\n", strerror(errno));
01393 }
01394 ast_free(cmd);
01395 }
01396 }
01397
01398
01399 if (get_gui_startup(o->env))
01400 console_video_start(o->env, NULL);
01401
01402 if (o == &oss_default)
01403 return NULL;
01404
01405 openit:
01406 #ifdef TRYOPEN
01407 if (setformat(o, O_RDWR) < 0) {
01408 ast_verb(1, "Device %s not detected\n", ctg);
01409 ast_verb(1, "Turn off OSS support by adding " "'noload=chan_oss.so' in /etc/asterisk/modules.conf\n");
01410 goto error;
01411 }
01412 if (o->duplex != M_FULL)
01413 ast_log(LOG_WARNING, "XXX I don't work right with non " "full-duplex sound cards XXX\n");
01414 #endif
01415
01416
01417 if (o != &oss_default) {
01418 o->next = oss_default.next;
01419 oss_default.next = o;
01420 }
01421 return o;
01422
01423 #ifdef TRYOPEN
01424 error:
01425 if (o != &oss_default)
01426 ast_free(o);
01427 return NULL;
01428 #endif
01429 }
01430
01431 static int load_module(void)
01432 {
01433 struct ast_config *cfg = NULL;
01434 char *ctg = NULL;
01435 struct ast_flags config_flags = { 0 };
01436
01437
01438 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
01439
01440
01441 if (!(cfg = ast_config_load(config, config_flags))) {
01442 ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
01443 return AST_MODULE_LOAD_DECLINE;
01444 }
01445
01446 do {
01447 store_config(cfg, ctg);
01448 } while ( (ctg = ast_category_browse(cfg, ctg)) != NULL);
01449
01450 ast_config_destroy(cfg);
01451
01452 if (find_desc(oss_active) == NULL) {
01453 ast_log(LOG_NOTICE, "Device %s not found\n", oss_active);
01454
01455
01456 return AST_MODULE_LOAD_FAILURE;
01457 }
01458
01459 oss_tech.capabilities |= console_video_formats;
01460
01461 if (ast_channel_register(&oss_tech)) {
01462 ast_log(LOG_ERROR, "Unable to register channel type 'OSS'\n");
01463 return AST_MODULE_LOAD_FAILURE;
01464 }
01465
01466 ast_cli_register_multiple(cli_oss, sizeof(cli_oss) / sizeof(struct ast_cli_entry));
01467
01468 return AST_MODULE_LOAD_SUCCESS;
01469 }
01470
01471
01472 static int unload_module(void)
01473 {
01474 struct chan_oss_pvt *o, *next;
01475
01476 ast_channel_unregister(&oss_tech);
01477 ast_cli_unregister_multiple(cli_oss, sizeof(cli_oss) / sizeof(struct ast_cli_entry));
01478
01479 o = oss_default.next;
01480 while (o) {
01481 close(o->sounddev);
01482 if (o->owner)
01483 ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD);
01484 if (o->owner)
01485 return -1;
01486 next = o->next;
01487 ast_free(o->name);
01488 ast_free(o);
01489 o = next;
01490 }
01491 return 0;
01492 }
01493
01494 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "OSS Console Channel Driver");