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