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