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