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