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