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 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00037
00038 #include <unistd.h>
00039 #include <fcntl.h>
00040 #include <errno.h>
00041 #include <sys/ioctl.h>
00042 #include <sys/time.h>
00043 #include <string.h>
00044 #include <stdlib.h>
00045 #include <stdio.h>
00046
00047 #define ALSA_PCM_NEW_HW_PARAMS_API
00048 #define ALSA_PCM_NEW_SW_PARAMS_API
00049 #include <alsa/asoundlib.h>
00050
00051 #include "asterisk/frame.h"
00052 #include "asterisk/logger.h"
00053 #include "asterisk/channel.h"
00054 #include "asterisk/module.h"
00055 #include "asterisk/options.h"
00056 #include "asterisk/pbx.h"
00057 #include "asterisk/config.h"
00058 #include "asterisk/cli.h"
00059 #include "asterisk/utils.h"
00060 #include "asterisk/causes.h"
00061 #include "asterisk/endian.h"
00062 #include "asterisk/stringfields.h"
00063 #include "asterisk/abstract_jb.h"
00064 #include "asterisk/musiconhold.h"
00065
00066 #include "busy.h"
00067 #include "ringtone.h"
00068 #include "ring10.h"
00069 #include "answer.h"
00070
00071 #ifdef ALSA_MONITOR
00072 #include "alsa-monitor.h"
00073 #endif
00074
00075
00076 static struct ast_jb_conf default_jbconf = {
00077 .flags = 0,
00078 .max_size = -1,
00079 .resync_threshold = -1,
00080 .impl = ""
00081 };
00082 static struct ast_jb_conf global_jbconf;
00083
00084 #define DEBUG 0
00085
00086 #define ALSA_INDEV "default"
00087 #define ALSA_OUTDEV "default"
00088 #define DESIRED_RATE 8000
00089
00090
00091 #define FRAME_SIZE 160
00092 #define PERIOD_FRAMES 80
00093
00094
00095
00096
00097 #define BUFFER_FMT ((buffersize * 10) << 16) | (0x0006);
00098
00099
00100 #define MIN_SWITCH_TIME 600
00101
00102 #if __BYTE_ORDER == __LITTLE_ENDIAN
00103 static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
00104 #else
00105 static snd_pcm_format_t format = SND_PCM_FORMAT_S16_BE;
00106 #endif
00107
00108
00109 static char indevname[50] = ALSA_INDEV;
00110 static char outdevname[50] = ALSA_OUTDEV;
00111
00112 #if 0
00113 static struct timeval lasttime;
00114 #endif
00115
00116 static int silencesuppression = 0;
00117 static int silencethreshold = 1000;
00118
00119 AST_MUTEX_DEFINE_STATIC(alsalock);
00120
00121 static const char tdesc[] = "ALSA Console Channel Driver";
00122 static const char config[] = "alsa.conf";
00123
00124 static char context[AST_MAX_CONTEXT] = "default";
00125 static char language[MAX_LANGUAGE] = "";
00126 static char exten[AST_MAX_EXTENSION] = "s";
00127 static char mohinterpret[MAX_MUSICCLASS];
00128
00129 static int hookstate = 0;
00130
00131 static short silence[FRAME_SIZE] = { 0, };
00132
00133 struct sound {
00134 int ind;
00135 short *data;
00136 int datalen;
00137 int samplen;
00138 int silencelen;
00139 int repeat;
00140 };
00141
00142 static struct sound sounds[] = {
00143 {AST_CONTROL_RINGING, ringtone, sizeof(ringtone) / 2, 16000, 32000, 1},
00144 {AST_CONTROL_BUSY, busy, sizeof(busy) / 2, 4000, 4000, 1},
00145 {AST_CONTROL_CONGESTION, busy, sizeof(busy) / 2, 2000, 2000, 1},
00146 {AST_CONTROL_RING, ring10, sizeof(ring10) / 2, 16000, 32000, 1},
00147 {AST_CONTROL_ANSWER, answer, sizeof(answer) / 2, 2200, 0, 0},
00148 };
00149
00150
00151 static int sndcmd[2];
00152
00153 static struct chan_alsa_pvt {
00154
00155
00156 struct ast_channel *owner;
00157 char exten[AST_MAX_EXTENSION];
00158 char context[AST_MAX_CONTEXT];
00159 #if 0
00160 snd_pcm_t *card;
00161 #endif
00162 snd_pcm_t *icard, *ocard;
00163
00164 } alsa;
00165
00166
00167
00168
00169
00170 pthread_t sthread;
00171
00172 #define MAX_BUFFER_SIZE 100
00173
00174
00175 static int readdev = -1;
00176 static int writedev = -1;
00177
00178 static int autoanswer = 1;
00179
00180 static int cursound = -1;
00181 static int sampsent = 0;
00182 static int silencelen = 0;
00183 static int offset = 0;
00184 static int nosound = 0;
00185
00186
00187 static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause);
00188 static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration);
00189 static int alsa_text(struct ast_channel *c, const char *text);
00190 static int alsa_hangup(struct ast_channel *c);
00191 static int alsa_answer(struct ast_channel *c);
00192 static struct ast_frame *alsa_read(struct ast_channel *chan);
00193 static int alsa_call(struct ast_channel *c, char *dest, int timeout);
00194 static int alsa_write(struct ast_channel *chan, struct ast_frame *f);
00195 static int alsa_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen);
00196 static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00197
00198 static const struct ast_channel_tech alsa_tech = {
00199 .type = "Console",
00200 .description = tdesc,
00201 .capabilities = AST_FORMAT_SLINEAR,
00202 .requester = alsa_request,
00203 .send_digit_end = alsa_digit,
00204 .send_text = alsa_text,
00205 .hangup = alsa_hangup,
00206 .answer = alsa_answer,
00207 .read = alsa_read,
00208 .call = alsa_call,
00209 .write = alsa_write,
00210 .indicate = alsa_indicate,
00211 .fixup = alsa_fixup,
00212 };
00213
00214 static int send_sound(void)
00215 {
00216 short myframe[FRAME_SIZE];
00217 int total = FRAME_SIZE;
00218 short *frame = NULL;
00219 int amt = 0, res, myoff;
00220 snd_pcm_state_t state;
00221
00222 if (cursound == -1)
00223 return 0;
00224
00225 res = total;
00226 if (sampsent < sounds[cursound].samplen) {
00227 myoff = 0;
00228 while (total) {
00229 amt = total;
00230 if (amt > (sounds[cursound].datalen - offset))
00231 amt = sounds[cursound].datalen - offset;
00232 memcpy(myframe + myoff, sounds[cursound].data + offset, amt * 2);
00233 total -= amt;
00234 offset += amt;
00235 sampsent += amt;
00236 myoff += amt;
00237 if (offset >= sounds[cursound].datalen)
00238 offset = 0;
00239 }
00240
00241 if (sampsent >= sounds[cursound].samplen)
00242 silencelen = sounds[cursound].silencelen;
00243 frame = myframe;
00244 } else {
00245 if (silencelen > 0) {
00246 frame = silence;
00247 silencelen -= res;
00248 } else {
00249 if (sounds[cursound].repeat) {
00250
00251 sampsent = 0;
00252 offset = 0;
00253 } else {
00254 cursound = -1;
00255 nosound = 0;
00256 }
00257 return 0;
00258 }
00259 }
00260
00261 if (res == 0 || !frame)
00262 return 0;
00263
00264 #ifdef ALSA_MONITOR
00265 alsa_monitor_write((char *) frame, res * 2);
00266 #endif
00267 state = snd_pcm_state(alsa.ocard);
00268 if (state == SND_PCM_STATE_XRUN)
00269 snd_pcm_prepare(alsa.ocard);
00270 res = snd_pcm_writei(alsa.ocard, frame, res);
00271 if (res > 0)
00272 return 0;
00273 return 0;
00274 }
00275
00276 static void *sound_thread(void *unused)
00277 {
00278 fd_set rfds;
00279 fd_set wfds;
00280 int max, res;
00281
00282 for (;;) {
00283 FD_ZERO(&rfds);
00284 FD_ZERO(&wfds);
00285 max = sndcmd[0];
00286 FD_SET(sndcmd[0], &rfds);
00287 if (cursound > -1) {
00288 FD_SET(writedev, &wfds);
00289 if (writedev > max)
00290 max = writedev;
00291 }
00292 #ifdef ALSA_MONITOR
00293 if (!alsa.owner) {
00294 FD_SET(readdev, &rfds);
00295 if (readdev > max)
00296 max = readdev;
00297 }
00298 #endif
00299 res = ast_select(max + 1, &rfds, &wfds, NULL, NULL);
00300 if (res < 1) {
00301 ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno));
00302 continue;
00303 }
00304 #ifdef ALSA_MONITOR
00305 if (FD_ISSET(readdev, &rfds)) {
00306
00307 snd_pcm_state_t state;
00308 short buf[FRAME_SIZE];
00309 int r;
00310
00311 state = snd_pcm_state(alsa.ocard);
00312 if (state == SND_PCM_STATE_XRUN) {
00313 snd_pcm_prepare(alsa.ocard);
00314 }
00315 r = snd_pcm_readi(alsa.icard, buf, FRAME_SIZE);
00316 if (r == -EPIPE) {
00317 #if DEBUG
00318 ast_log(LOG_ERROR, "XRUN read\n");
00319 #endif
00320 snd_pcm_prepare(alsa.icard);
00321 } else if (r == -ESTRPIPE) {
00322 ast_log(LOG_ERROR, "-ESTRPIPE\n");
00323 snd_pcm_prepare(alsa.icard);
00324 } else if (r < 0) {
00325 ast_log(LOG_ERROR, "Read error: %s\n", snd_strerror(r));
00326 } else
00327 alsa_monitor_read((char *) buf, r * 2);
00328 }
00329 #endif
00330 if (FD_ISSET(sndcmd[0], &rfds)) {
00331 read(sndcmd[0], &cursound, sizeof(cursound));
00332 silencelen = 0;
00333 offset = 0;
00334 sampsent = 0;
00335 }
00336 if (FD_ISSET(writedev, &wfds))
00337 if (send_sound())
00338 ast_log(LOG_WARNING, "Failed to write sound\n");
00339 }
00340
00341 return NULL;
00342 }
00343
00344 static snd_pcm_t *alsa_card_init(char *dev, snd_pcm_stream_t stream)
00345 {
00346 int err;
00347 int direction;
00348 snd_pcm_t *handle = NULL;
00349 snd_pcm_hw_params_t *hwparams = NULL;
00350 snd_pcm_sw_params_t *swparams = NULL;
00351 struct pollfd pfd;
00352 snd_pcm_uframes_t period_size = PERIOD_FRAMES * 4;
00353
00354 snd_pcm_uframes_t buffer_size = 0;
00355
00356 unsigned int rate = DESIRED_RATE;
00357 #if 0
00358 unsigned int per_min = 1;
00359 #endif
00360
00361 snd_pcm_uframes_t start_threshold, stop_threshold;
00362
00363 err = snd_pcm_open(&handle, dev, stream, O_NONBLOCK);
00364 if (err < 0) {
00365 ast_log(LOG_ERROR, "snd_pcm_open failed: %s\n", snd_strerror(err));
00366 return NULL;
00367 } else
00368 ast_log(LOG_DEBUG, "Opening device %s in %s mode\n", dev, (stream == SND_PCM_STREAM_CAPTURE) ? "read" : "write");
00369
00370 snd_pcm_hw_params_alloca(&hwparams);
00371 snd_pcm_hw_params_any(handle, hwparams);
00372
00373 err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
00374 if (err < 0)
00375 ast_log(LOG_ERROR, "set_access failed: %s\n", snd_strerror(err));
00376
00377 err = snd_pcm_hw_params_set_format(handle, hwparams, format);
00378 if (err < 0)
00379 ast_log(LOG_ERROR, "set_format failed: %s\n", snd_strerror(err));
00380
00381 err = snd_pcm_hw_params_set_channels(handle, hwparams, 1);
00382 if (err < 0)
00383 ast_log(LOG_ERROR, "set_channels failed: %s\n", snd_strerror(err));
00384
00385 direction = 0;
00386 err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, &direction);
00387 if (rate != DESIRED_RATE)
00388 ast_log(LOG_WARNING, "Rate not correct, requested %d, got %d\n", DESIRED_RATE, rate);
00389
00390 direction = 0;
00391 err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, &direction);
00392 if (err < 0)
00393 ast_log(LOG_ERROR, "period_size(%ld frames) is bad: %s\n", period_size, snd_strerror(err));
00394 else
00395 ast_log(LOG_DEBUG, "Period size is %d\n", err);
00396
00397 buffer_size = 4096 * 2;
00398 err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size);
00399 if (err < 0)
00400 ast_log(LOG_WARNING, "Problem setting buffer size of %ld: %s\n", buffer_size, snd_strerror(err));
00401 else
00402 ast_log(LOG_DEBUG, "Buffer size is set to %d frames\n", err);
00403
00404 #if 0
00405 direction = 0;
00406 err = snd_pcm_hw_params_set_periods_min(handle, hwparams, &per_min, &direction);
00407 if (err < 0)
00408 ast_log(LOG_ERROR, "periods_min: %s\n", snd_strerror(err));
00409
00410 err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &per_max, 0);
00411 if (err < 0)
00412 ast_log(LOG_ERROR, "periods_max: %s\n", snd_strerror(err));
00413 #endif
00414
00415 err = snd_pcm_hw_params(handle, hwparams);
00416 if (err < 0)
00417 ast_log(LOG_ERROR, "Couldn't set the new hw params: %s\n", snd_strerror(err));
00418
00419 snd_pcm_sw_params_alloca(&swparams);
00420 snd_pcm_sw_params_current(handle, swparams);
00421
00422 #if 1
00423 if (stream == SND_PCM_STREAM_PLAYBACK)
00424 start_threshold = period_size;
00425 else
00426 start_threshold = 1;
00427
00428 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
00429 if (err < 0)
00430 ast_log(LOG_ERROR, "start threshold: %s\n", snd_strerror(err));
00431 #endif
00432
00433 #if 1
00434 if (stream == SND_PCM_STREAM_PLAYBACK)
00435 stop_threshold = buffer_size;
00436 else
00437 stop_threshold = buffer_size;
00438
00439 err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
00440 if (err < 0)
00441 ast_log(LOG_ERROR, "stop threshold: %s\n", snd_strerror(err));
00442 #endif
00443 #if 0
00444 err = snd_pcm_sw_params_set_xfer_align(handle, swparams, PERIOD_FRAMES);
00445 if (err < 0)
00446 ast_log(LOG_ERROR, "Unable to set xfer alignment: %s\n", snd_strerror(err));
00447 #endif
00448
00449 #if 0
00450 err = snd_pcm_sw_params_set_silence_threshold(handle, swparams, silencethreshold);
00451 if (err < 0)
00452 ast_log(LOG_ERROR, "Unable to set silence threshold: %s\n", snd_strerror(err));
00453 #endif
00454 err = snd_pcm_sw_params(handle, swparams);
00455 if (err < 0)
00456 ast_log(LOG_ERROR, "sw_params: %s\n", snd_strerror(err));
00457
00458 err = snd_pcm_poll_descriptors_count(handle);
00459 if (err <= 0)
00460 ast_log(LOG_ERROR, "Unable to get a poll descriptors count, error is %s\n", snd_strerror(err));
00461 if (err != 1)
00462 ast_log(LOG_DEBUG, "Can't handle more than one device\n");
00463
00464 snd_pcm_poll_descriptors(handle, &pfd, err);
00465 ast_log(LOG_DEBUG, "Acquired fd %d from the poll descriptor\n", pfd.fd);
00466
00467 if (stream == SND_PCM_STREAM_CAPTURE)
00468 readdev = pfd.fd;
00469 else
00470 writedev = pfd.fd;
00471
00472 return handle;
00473 }
00474
00475 static int soundcard_init(void)
00476 {
00477 alsa.icard = alsa_card_init(indevname, SND_PCM_STREAM_CAPTURE);
00478 alsa.ocard = alsa_card_init(outdevname, SND_PCM_STREAM_PLAYBACK);
00479
00480 if (!alsa.icard || !alsa.ocard) {
00481 ast_log(LOG_ERROR, "Problem opening alsa I/O devices\n");
00482 return -1;
00483 }
00484
00485 return readdev;
00486 }
00487
00488 static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration)
00489 {
00490 ast_mutex_lock(&alsalock);
00491 ast_verbose(" << Console Received digit %c of duration %u ms >> \n",
00492 digit, duration);
00493 ast_mutex_unlock(&alsalock);
00494 return 0;
00495 }
00496
00497 static int alsa_text(struct ast_channel *c, const char *text)
00498 {
00499 ast_mutex_lock(&alsalock);
00500 ast_verbose(" << Console Received text %s >> \n", text);
00501 ast_mutex_unlock(&alsalock);
00502 return 0;
00503 }
00504
00505 static void grab_owner(void)
00506 {
00507 while (alsa.owner && ast_mutex_trylock(&alsa.owner->lock)) {
00508 ast_mutex_unlock(&alsalock);
00509 usleep(1);
00510 ast_mutex_lock(&alsalock);
00511 }
00512 }
00513
00514 static int alsa_call(struct ast_channel *c, char *dest, int timeout)
00515 {
00516 int res = 3;
00517 struct ast_frame f = { AST_FRAME_CONTROL };
00518 ast_mutex_lock(&alsalock);
00519 ast_verbose(" << Call placed to '%s' on console >> \n", dest);
00520 if (autoanswer) {
00521 ast_verbose(" << Auto-answered >> \n");
00522 grab_owner();
00523 if (alsa.owner) {
00524 f.subclass = AST_CONTROL_ANSWER;
00525 ast_queue_frame(alsa.owner, &f);
00526 ast_mutex_unlock(&alsa.owner->lock);
00527 }
00528 } else {
00529 ast_verbose(" << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n");
00530 grab_owner();
00531 if (alsa.owner) {
00532 f.subclass = AST_CONTROL_RINGING;
00533 ast_queue_frame(alsa.owner, &f);
00534 ast_mutex_unlock(&alsa.owner->lock);
00535 }
00536 write(sndcmd[1], &res, sizeof(res));
00537 }
00538 snd_pcm_prepare(alsa.icard);
00539 snd_pcm_start(alsa.icard);
00540 ast_mutex_unlock(&alsalock);
00541 return 0;
00542 }
00543
00544 static void answer_sound(void)
00545 {
00546 int res;
00547 nosound = 1;
00548 res = 4;
00549 write(sndcmd[1], &res, sizeof(res));
00550
00551 }
00552
00553 static int alsa_answer(struct ast_channel *c)
00554 {
00555 ast_mutex_lock(&alsalock);
00556 ast_verbose(" << Console call has been answered >> \n");
00557 answer_sound();
00558 ast_setstate(c, AST_STATE_UP);
00559 cursound = -1;
00560 snd_pcm_prepare(alsa.icard);
00561 snd_pcm_start(alsa.icard);
00562 ast_mutex_unlock(&alsalock);
00563 return 0;
00564 }
00565
00566 static int alsa_hangup(struct ast_channel *c)
00567 {
00568 int res;
00569 ast_mutex_lock(&alsalock);
00570 cursound = -1;
00571 c->tech_pvt = NULL;
00572 alsa.owner = NULL;
00573 ast_verbose(" << Hangup on console >> \n");
00574 ast_module_unref(ast_module_info->self);
00575 if (hookstate) {
00576 hookstate = 0;
00577 if (!autoanswer) {
00578
00579 res = 2;
00580 write(sndcmd[1], &res, sizeof(res));
00581 }
00582 }
00583 snd_pcm_drop(alsa.icard);
00584 ast_mutex_unlock(&alsalock);
00585 return 0;
00586 }
00587
00588 static int alsa_write(struct ast_channel *chan, struct ast_frame *f)
00589 {
00590 static char sizbuf[8000];
00591 static int sizpos = 0;
00592 int len = sizpos;
00593 int pos;
00594 int res = 0;
00595
00596 snd_pcm_state_t state;
00597
00598
00599 if (nosound)
00600 return 0;
00601
00602 ast_mutex_lock(&alsalock);
00603
00604 if (cursound != -1) {
00605 snd_pcm_drop(alsa.ocard);
00606 snd_pcm_prepare(alsa.ocard);
00607 cursound = -1;
00608 }
00609
00610
00611
00612 if (f->datalen > sizeof(sizbuf) - sizpos) {
00613 ast_log(LOG_WARNING, "Frame too large\n");
00614 res = -1;
00615 } else {
00616 memcpy(sizbuf + sizpos, f->data, f->datalen);
00617 len += f->datalen;
00618 pos = 0;
00619 #ifdef ALSA_MONITOR
00620 alsa_monitor_write(sizbuf, len);
00621 #endif
00622 state = snd_pcm_state(alsa.ocard);
00623 if (state == SND_PCM_STATE_XRUN)
00624 snd_pcm_prepare(alsa.ocard);
00625 res = snd_pcm_writei(alsa.ocard, sizbuf, len / 2);
00626 if (res == -EPIPE) {
00627 #if DEBUG
00628 ast_log(LOG_DEBUG, "XRUN write\n");
00629 #endif
00630 snd_pcm_prepare(alsa.ocard);
00631 res = snd_pcm_writei(alsa.ocard, sizbuf, len / 2);
00632 if (res != len / 2) {
00633 ast_log(LOG_ERROR, "Write error: %s\n", snd_strerror(res));
00634 res = -1;
00635 } else if (res < 0) {
00636 ast_log(LOG_ERROR, "Write error %s\n", snd_strerror(res));
00637 res = -1;
00638 }
00639 } else {
00640 if (res == -ESTRPIPE)
00641 ast_log(LOG_ERROR, "You've got some big problems\n");
00642 else if (res < 0)
00643 ast_log(LOG_NOTICE, "Error %d on write\n", res);
00644 }
00645 }
00646 ast_mutex_unlock(&alsalock);
00647 if (res > 0)
00648 res = 0;
00649 return res;
00650 }
00651
00652
00653 static struct ast_frame *alsa_read(struct ast_channel *chan)
00654 {
00655 static struct ast_frame f;
00656 static short __buf[FRAME_SIZE + AST_FRIENDLY_OFFSET / 2];
00657 short *buf;
00658 static int readpos = 0;
00659 static int left = FRAME_SIZE;
00660 snd_pcm_state_t state;
00661 int r = 0;
00662 int off = 0;
00663
00664 ast_mutex_lock(&alsalock);
00665
00666 f.frametype = AST_FRAME_NULL;
00667 f.subclass = 0;
00668 f.samples = 0;
00669 f.datalen = 0;
00670 f.data = NULL;
00671 f.offset = 0;
00672 f.src = "Console";
00673 f.mallocd = 0;
00674 f.delivery.tv_sec = 0;
00675 f.delivery.tv_usec = 0;
00676
00677 state = snd_pcm_state(alsa.icard);
00678 if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING)) {
00679 snd_pcm_prepare(alsa.icard);
00680 }
00681
00682 buf = __buf + AST_FRIENDLY_OFFSET / 2;
00683
00684 r = snd_pcm_readi(alsa.icard, buf + readpos, left);
00685 if (r == -EPIPE) {
00686 #if DEBUG
00687 ast_log(LOG_ERROR, "XRUN read\n");
00688 #endif
00689 snd_pcm_prepare(alsa.icard);
00690 } else if (r == -ESTRPIPE) {
00691 ast_log(LOG_ERROR, "-ESTRPIPE\n");
00692 snd_pcm_prepare(alsa.icard);
00693 } else if (r < 0) {
00694 ast_log(LOG_ERROR, "Read error: %s\n", snd_strerror(r));
00695 } else if (r >= 0) {
00696 off -= r;
00697 }
00698
00699 readpos += r;
00700 left -= r;
00701
00702 if (readpos >= FRAME_SIZE) {
00703
00704 readpos = 0;
00705 left = FRAME_SIZE;
00706 if (chan->_state != AST_STATE_UP) {
00707
00708 ast_mutex_unlock(&alsalock);
00709 return &f;
00710 }
00711 f.frametype = AST_FRAME_VOICE;
00712 f.subclass = AST_FORMAT_SLINEAR;
00713 f.samples = FRAME_SIZE;
00714 f.datalen = FRAME_SIZE * 2;
00715 f.data = buf;
00716 f.offset = AST_FRIENDLY_OFFSET;
00717 f.src = "Console";
00718 f.mallocd = 0;
00719 #ifdef ALSA_MONITOR
00720 alsa_monitor_read((char *) buf, FRAME_SIZE * 2);
00721 #endif
00722
00723 }
00724 ast_mutex_unlock(&alsalock);
00725 return &f;
00726 }
00727
00728 static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00729 {
00730 struct chan_alsa_pvt *p = newchan->tech_pvt;
00731 ast_mutex_lock(&alsalock);
00732 p->owner = newchan;
00733 ast_mutex_unlock(&alsalock);
00734 return 0;
00735 }
00736
00737 static int alsa_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
00738 {
00739 int res = 0;
00740
00741 ast_mutex_lock(&alsalock);
00742
00743 switch (cond) {
00744 case AST_CONTROL_BUSY:
00745 res = 1;
00746 break;
00747 case AST_CONTROL_CONGESTION:
00748 res = 2;
00749 break;
00750 case AST_CONTROL_RINGING:
00751 case AST_CONTROL_PROGRESS:
00752 break;
00753 case -1:
00754 res = -1;
00755 break;
00756 case AST_CONTROL_VIDUPDATE:
00757 res = -1;
00758 break;
00759 case AST_CONTROL_HOLD:
00760 ast_verbose(" << Console Has Been Placed on Hold >> \n");
00761 ast_moh_start(chan, data, mohinterpret);
00762 break;
00763 case AST_CONTROL_UNHOLD:
00764 ast_verbose(" << Console Has Been Retrieved from Hold >> \n");
00765 ast_moh_stop(chan);
00766 break;
00767 default:
00768 ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, chan->name);
00769 res = -1;
00770 }
00771
00772 if (res > -1)
00773 write(sndcmd[1], &res, sizeof(res));
00774
00775 ast_mutex_unlock(&alsalock);
00776
00777 return res;
00778 }
00779
00780 static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
00781 {
00782 struct ast_channel *tmp = NULL;
00783 if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, 0, "ALSA/%s", indevname)))
00784 return NULL;
00785
00786 tmp->tech = &alsa_tech;
00787 tmp->fds[0] = readdev;
00788 tmp->nativeformats = AST_FORMAT_SLINEAR;
00789 tmp->readformat = AST_FORMAT_SLINEAR;
00790 tmp->writeformat = AST_FORMAT_SLINEAR;
00791 tmp->tech_pvt = p;
00792 if (!ast_strlen_zero(p->context))
00793 ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00794 if (!ast_strlen_zero(p->exten))
00795 ast_copy_string(tmp->exten, p->exten, sizeof(tmp->exten));
00796 if (!ast_strlen_zero(language))
00797 ast_string_field_set(tmp, language, language);
00798 p->owner = tmp;
00799 ast_module_ref(ast_module_info->self);
00800 ast_jb_configure(tmp, &global_jbconf);
00801 if (state != AST_STATE_DOWN) {
00802 if (ast_pbx_start(tmp)) {
00803 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
00804 ast_hangup(tmp);
00805 tmp = NULL;
00806 }
00807 }
00808
00809 return tmp;
00810 }
00811
00812 static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause)
00813 {
00814 int oldformat = format;
00815 struct ast_channel *tmp = NULL;
00816
00817 format &= AST_FORMAT_SLINEAR;
00818 if (!format) {
00819 ast_log(LOG_NOTICE, "Asked to get a channel of format '%d'\n", oldformat);
00820 return NULL;
00821 }
00822
00823 ast_mutex_lock(&alsalock);
00824
00825 if (alsa.owner) {
00826 ast_log(LOG_NOTICE, "Already have a call on the ALSA channel\n");
00827 *cause = AST_CAUSE_BUSY;
00828 } else if (!(tmp = alsa_new(&alsa, AST_STATE_DOWN)))
00829 ast_log(LOG_WARNING, "Unable to create new ALSA channel\n");
00830
00831 ast_mutex_unlock(&alsalock);
00832
00833 return tmp;
00834 }
00835
00836 static int console_autoanswer_deprecated(int fd, int argc, char *argv[])
00837 {
00838 int res = RESULT_SUCCESS;
00839
00840 if ((argc != 1) && (argc != 2))
00841 return RESULT_SHOWUSAGE;
00842
00843 ast_mutex_lock(&alsalock);
00844
00845 if (argc == 1) {
00846 ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off");
00847 } else {
00848 if (!strcasecmp(argv[1], "on"))
00849 autoanswer = -1;
00850 else if (!strcasecmp(argv[1], "off"))
00851 autoanswer = 0;
00852 else
00853 res = RESULT_SHOWUSAGE;
00854 }
00855
00856 ast_mutex_unlock(&alsalock);
00857
00858 return res;
00859 }
00860
00861 static int console_autoanswer(int fd, int argc, char *argv[])
00862 {
00863 int res = RESULT_SUCCESS;;
00864 if ((argc != 2) && (argc != 3))
00865 return RESULT_SHOWUSAGE;
00866 ast_mutex_lock(&alsalock);
00867 if (argc == 2) {
00868 ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off");
00869 } else {
00870 if (!strcasecmp(argv[2], "on"))
00871 autoanswer = -1;
00872 else if (!strcasecmp(argv[2], "off"))
00873 autoanswer = 0;
00874 else
00875 res = RESULT_SHOWUSAGE;
00876 }
00877 ast_mutex_unlock(&alsalock);
00878 return res;
00879 }
00880
00881 static char *autoanswer_complete(const char *line, const char *word, int pos, int state)
00882 {
00883 #ifndef MIN
00884 #define MIN(a,b) ((a) < (b) ? (a) : (b))
00885 #endif
00886 switch (state) {
00887 case 0:
00888 if (!ast_strlen_zero(word) && !strncasecmp(word, "on", MIN(strlen(word), 2)))
00889 return ast_strdup("on");
00890 case 1:
00891 if (!ast_strlen_zero(word) && !strncasecmp(word, "off", MIN(strlen(word), 3)))
00892 return ast_strdup("off");
00893 default:
00894 return NULL;
00895 }
00896 return NULL;
00897 }
00898
00899 static const char autoanswer_usage[] =
00900 "Usage: console autoanswer [on|off]\n"
00901 " Enables or disables autoanswer feature. If used without\n"
00902 " argument, displays the current on/off status of autoanswer.\n"
00903 " The default value of autoanswer is in 'alsa.conf'.\n";
00904
00905 static int console_answer_deprecated(int fd, int argc, char *argv[])
00906 {
00907 int res = RESULT_SUCCESS;
00908
00909 if (argc != 1)
00910 return RESULT_SHOWUSAGE;
00911
00912 ast_mutex_lock(&alsalock);
00913
00914 if (!alsa.owner) {
00915 ast_cli(fd, "No one is calling us\n");
00916 res = RESULT_FAILURE;
00917 } else {
00918 hookstate = 1;
00919 cursound = -1;
00920 grab_owner();
00921 if (alsa.owner) {
00922 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00923 ast_queue_frame(alsa.owner, &f);
00924 ast_mutex_unlock(&alsa.owner->lock);
00925 }
00926 answer_sound();
00927 }
00928
00929 snd_pcm_prepare(alsa.icard);
00930 snd_pcm_start(alsa.icard);
00931
00932 ast_mutex_unlock(&alsalock);
00933
00934 return RESULT_SUCCESS;
00935 }
00936
00937 static int console_answer(int fd, int argc, char *argv[])
00938 {
00939 int res = RESULT_SUCCESS;
00940
00941 if (argc != 2)
00942 return RESULT_SHOWUSAGE;
00943
00944 ast_mutex_lock(&alsalock);
00945
00946 if (!alsa.owner) {
00947 ast_cli(fd, "No one is calling us\n");
00948 res = RESULT_FAILURE;
00949 } else {
00950 hookstate = 1;
00951 cursound = -1;
00952 grab_owner();
00953 if (alsa.owner) {
00954 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00955 ast_queue_frame(alsa.owner, &f);
00956 ast_mutex_unlock(&alsa.owner->lock);
00957 }
00958 answer_sound();
00959 }
00960
00961 snd_pcm_prepare(alsa.icard);
00962 snd_pcm_start(alsa.icard);
00963
00964 ast_mutex_unlock(&alsalock);
00965
00966 return RESULT_SUCCESS;
00967 }
00968
00969 static char sendtext_usage[] =
00970 "Usage: console send text <message>\n"
00971 " Sends a text message for display on the remote terminal.\n";
00972
00973 static int console_sendtext_deprecated(int fd, int argc, char *argv[])
00974 {
00975 int tmparg = 2;
00976 int res = RESULT_SUCCESS;
00977
00978 if (argc < 2)
00979 return RESULT_SHOWUSAGE;
00980
00981 ast_mutex_lock(&alsalock);
00982
00983 if (!alsa.owner) {
00984 ast_cli(fd, "No one is calling us\n");
00985 res = RESULT_FAILURE;
00986 } else {
00987 struct ast_frame f = { AST_FRAME_TEXT, 0 };
00988 char text2send[256] = "";
00989 text2send[0] = '\0';
00990 while (tmparg < argc) {
00991 strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1);
00992 strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1);
00993 }
00994 text2send[strlen(text2send) - 1] = '\n';
00995 f.data = text2send;
00996 f.datalen = strlen(text2send) + 1;
00997 grab_owner();
00998 if (alsa.owner) {
00999 ast_queue_frame(alsa.owner, &f);
01000 f.frametype = AST_FRAME_CONTROL;
01001 f.subclass = AST_CONTROL_ANSWER;
01002 f.data = NULL;
01003 f.datalen = 0;
01004 ast_queue_frame(alsa.owner, &f);
01005 ast_mutex_unlock(&alsa.owner->lock);
01006 }
01007 }
01008
01009 ast_mutex_unlock(&alsalock);
01010
01011 return res;
01012 }
01013
01014 static int console_sendtext(int fd, int argc, char *argv[])
01015 {
01016 int tmparg = 3;
01017 int res = RESULT_SUCCESS;
01018
01019 if (argc < 3)
01020 return RESULT_SHOWUSAGE;
01021
01022 ast_mutex_lock(&alsalock);
01023
01024 if (!alsa.owner) {
01025 ast_cli(fd, "No one is calling us\n");
01026 res = RESULT_FAILURE;
01027 } else {
01028 struct ast_frame f = { AST_FRAME_TEXT, 0 };
01029 char text2send[256] = "";
01030 text2send[0] = '\0';
01031 while (tmparg < argc) {
01032 strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1);
01033 strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1);
01034 }
01035 text2send[strlen(text2send) - 1] = '\n';
01036 f.data = text2send;
01037 f.datalen = strlen(text2send) + 1;
01038 grab_owner();
01039 if (alsa.owner) {
01040 ast_queue_frame(alsa.owner, &f);
01041 f.frametype = AST_FRAME_CONTROL;
01042 f.subclass = AST_CONTROL_ANSWER;
01043 f.data = NULL;
01044 f.datalen = 0;
01045 ast_queue_frame(alsa.owner, &f);
01046 ast_mutex_unlock(&alsa.owner->lock);
01047 }
01048 }
01049
01050 ast_mutex_unlock(&alsalock);
01051
01052 return res;
01053 }
01054
01055 static char answer_usage[] =
01056 "Usage: console answer\n"
01057 " Answers an incoming call on the console (ALSA) channel.\n";
01058
01059 static int console_hangup_deprecated(int fd, int argc, char *argv[])
01060 {
01061 int res = RESULT_SUCCESS;
01062
01063 if (argc != 1)
01064 return RESULT_SHOWUSAGE;
01065
01066 cursound = -1;
01067
01068 ast_mutex_lock(&alsalock);
01069
01070 if (!alsa.owner && !hookstate) {
01071 ast_cli(fd, "No call to hangup up\n");
01072 res = RESULT_FAILURE;
01073 } else {
01074 hookstate = 0;
01075 grab_owner();
01076 if (alsa.owner) {
01077 ast_queue_hangup(alsa.owner);
01078 ast_mutex_unlock(&alsa.owner->lock);
01079 }
01080 }
01081
01082 ast_mutex_unlock(&alsalock);
01083
01084 return res;
01085 }
01086
01087 static int console_hangup(int fd, int argc, char *argv[])
01088 {
01089 int res = RESULT_SUCCESS;
01090
01091 if (argc != 2)
01092 return RESULT_SHOWUSAGE;
01093
01094 cursound = -1;
01095
01096 ast_mutex_lock(&alsalock);
01097
01098 if (!alsa.owner && !hookstate) {
01099 ast_cli(fd, "No call to hangup up\n");
01100 res = RESULT_FAILURE;
01101 } else {
01102 hookstate = 0;
01103 grab_owner();
01104 if (alsa.owner) {
01105 ast_queue_hangup(alsa.owner);
01106 ast_mutex_unlock(&alsa.owner->lock);
01107 }
01108 }
01109
01110 ast_mutex_unlock(&alsalock);
01111
01112 return res;
01113 }
01114
01115 static char hangup_usage[] =
01116 "Usage: console hangup\n"
01117 " Hangs up any call currently placed on the console.\n";
01118
01119 static int console_dial_deprecated(int fd, int argc, char *argv[])
01120 {
01121 char tmp[256], *tmp2;
01122 char *mye, *myc;
01123 char *d;
01124 int res = RESULT_SUCCESS;
01125
01126 if ((argc != 1) && (argc != 2))
01127 return RESULT_SHOWUSAGE;
01128
01129 ast_mutex_lock(&alsalock);
01130
01131 if (alsa.owner) {
01132 if (argc == 2) {
01133 d = argv[1];
01134 grab_owner();
01135 if (alsa.owner) {
01136 struct ast_frame f = { AST_FRAME_DTMF };
01137 while (*d) {
01138 f.subclass = *d;
01139 ast_queue_frame(alsa.owner, &f);
01140 d++;
01141 }
01142 ast_mutex_unlock(&alsa.owner->lock);
01143 }
01144 } else {
01145 ast_cli(fd, "You're already in a call. You can use this only to dial digits until you hangup\n");
01146 res = RESULT_FAILURE;
01147 }
01148 } else {
01149 mye = exten;
01150 myc = context;
01151 if (argc == 2) {
01152 char *stringp = NULL;
01153 ast_copy_string(tmp, argv[1], sizeof(tmp));
01154 stringp = tmp;
01155 strsep(&stringp, "@");
01156 tmp2 = strsep(&stringp, "@");
01157 if (!ast_strlen_zero(tmp))
01158 mye = tmp;
01159 if (!ast_strlen_zero(tmp2))
01160 myc = tmp2;
01161 }
01162 if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
01163 ast_copy_string(alsa.exten, mye, sizeof(alsa.exten));
01164 ast_copy_string(alsa.context, myc, sizeof(alsa.context));
01165 hookstate = 1;
01166 alsa_new(&alsa, AST_STATE_RINGING);
01167 } else
01168 ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
01169 }
01170
01171 ast_mutex_unlock(&alsalock);
01172
01173 return res;
01174 }
01175
01176 static int console_dial(int fd, int argc, char *argv[])
01177 {
01178 char tmp[256], *tmp2;
01179 char *mye, *myc;
01180 char *d;
01181 int res = RESULT_SUCCESS;
01182
01183 if ((argc != 2) && (argc != 3))
01184 return RESULT_SHOWUSAGE;
01185
01186 ast_mutex_lock(&alsalock);
01187
01188 if (alsa.owner) {
01189 if (argc == 3) {
01190 d = argv[2];
01191 grab_owner();
01192 if (alsa.owner) {
01193 struct ast_frame f = { AST_FRAME_DTMF };
01194 while (*d) {
01195 f.subclass = *d;
01196 ast_queue_frame(alsa.owner, &f);
01197 d++;
01198 }
01199 ast_mutex_unlock(&alsa.owner->lock);
01200 }
01201 } else {
01202 ast_cli(fd, "You're already in a call. You can use this only to dial digits until you hangup\n");
01203 res = RESULT_FAILURE;
01204 }
01205 } else {
01206 mye = exten;
01207 myc = context;
01208 if (argc == 3) {
01209 char *stringp = NULL;
01210 ast_copy_string(tmp, argv[2], sizeof(tmp));
01211 stringp = tmp;
01212 strsep(&stringp, "@");
01213 tmp2 = strsep(&stringp, "@");
01214 if (!ast_strlen_zero(tmp))
01215 mye = tmp;
01216 if (!ast_strlen_zero(tmp2))
01217 myc = tmp2;
01218 }
01219 if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
01220 ast_copy_string(alsa.exten, mye, sizeof(alsa.exten));
01221 ast_copy_string(alsa.context, myc, sizeof(alsa.context));
01222 hookstate = 1;
01223 alsa_new(&alsa, AST_STATE_RINGING);
01224 } else
01225 ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
01226 }
01227
01228 ast_mutex_unlock(&alsalock);
01229
01230 return res;
01231 }
01232
01233 static char dial_usage[] =
01234 "Usage: console dial [extension[@context]]\n"
01235 " Dials a given extension (and context if specified)\n";
01236
01237 static struct ast_cli_entry cli_alsa_answer_deprecated = {
01238 { "answer", NULL },
01239 console_answer_deprecated, NULL,
01240 NULL };
01241
01242 static struct ast_cli_entry cli_alsa_hangup_deprecated = {
01243 { "hangup", NULL },
01244 console_hangup_deprecated, NULL,
01245 NULL };
01246
01247 static struct ast_cli_entry cli_alsa_dial_deprecated = {
01248 { "dial", NULL },
01249 console_dial_deprecated, NULL,
01250 NULL };
01251
01252 static struct ast_cli_entry cli_alsa_send_text_deprecated = {
01253 { "send", "text", NULL },
01254 console_sendtext_deprecated, NULL,
01255 NULL };
01256
01257 static struct ast_cli_entry cli_alsa_autoanswer_deprecated = {
01258 { "autoanswer", NULL },
01259 console_autoanswer_deprecated, NULL,
01260 NULL, autoanswer_complete };
01261
01262 static struct ast_cli_entry cli_alsa[] = {
01263 { { "console", "answer", NULL },
01264 console_answer, "Answer an incoming console call",
01265 answer_usage, NULL, &cli_alsa_answer_deprecated },
01266
01267 { { "console", "hangup", NULL },
01268 console_hangup, "Hangup a call on the console",
01269 hangup_usage, NULL, &cli_alsa_hangup_deprecated },
01270
01271 { { "console", "dial", NULL },
01272 console_dial, "Dial an extension on the console",
01273 dial_usage, NULL, &cli_alsa_dial_deprecated },
01274
01275 { { "console", "send", "text", NULL },
01276 console_sendtext, "Send text to the remote device",
01277 sendtext_usage, NULL, &cli_alsa_send_text_deprecated },
01278
01279 { { "console", "autoanswer", NULL },
01280 console_autoanswer, "Sets/displays autoanswer",
01281 autoanswer_usage, autoanswer_complete, &cli_alsa_autoanswer_deprecated },
01282 };
01283
01284 static int load_module(void)
01285 {
01286 int res;
01287 struct ast_config *cfg;
01288 struct ast_variable *v;
01289
01290
01291 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
01292
01293 strcpy(mohinterpret, "default");
01294 if (!(cfg = ast_config_load(config)))
01295 return AST_MODULE_LOAD_DECLINE;
01296
01297 v = ast_variable_browse(cfg, "general");
01298 for (; v; v = v->next) {
01299
01300 if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
01301 continue;
01302
01303 if (!strcasecmp(v->name, "autoanswer"))
01304 autoanswer = ast_true(v->value);
01305 else if (!strcasecmp(v->name, "silencesuppression"))
01306 silencesuppression = ast_true(v->value);
01307 else if (!strcasecmp(v->name, "silencethreshold"))
01308 silencethreshold = atoi(v->value);
01309 else if (!strcasecmp(v->name, "context"))
01310 ast_copy_string(context, v->value, sizeof(context));
01311 else if (!strcasecmp(v->name, "language"))
01312 ast_copy_string(language, v->value, sizeof(language));
01313 else if (!strcasecmp(v->name, "extension"))
01314 ast_copy_string(exten, v->value, sizeof(exten));
01315 else if (!strcasecmp(v->name, "input_device"))
01316 ast_copy_string(indevname, v->value, sizeof(indevname));
01317 else if (!strcasecmp(v->name, "output_device"))
01318 ast_copy_string(outdevname, v->value, sizeof(outdevname));
01319 else if (!strcasecmp(v->name, "mohinterpret"))
01320 ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret));
01321 }
01322
01323 ast_config_destroy(cfg);
01324 res = pipe(sndcmd);
01325 if (res) {
01326 ast_log(LOG_ERROR, "Unable to create pipe\n");
01327 return -1;
01328 }
01329 res = soundcard_init();
01330 if (res < 0) {
01331 if (option_verbose > 1) {
01332 ast_verbose(VERBOSE_PREFIX_2 "No sound card detected -- console channel will be unavailable\n");
01333 ast_verbose(VERBOSE_PREFIX_2 "Turn off ALSA support by adding 'noload=chan_alsa.so' in /etc/asterisk/modules.conf\n");
01334 }
01335 return AST_MODULE_LOAD_DECLINE;
01336 }
01337
01338 res = ast_channel_register(&alsa_tech);
01339 if (res < 0) {
01340 ast_log(LOG_ERROR, "Unable to register channel class 'Console'\n");
01341 return -1;
01342 }
01343 ast_cli_register_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry));
01344
01345 ast_pthread_create_background(&sthread, NULL, sound_thread, NULL);
01346 #ifdef ALSA_MONITOR
01347 if (alsa_monitor_start())
01348 ast_log(LOG_ERROR, "Problem starting Monitoring\n");
01349 #endif
01350 return 0;
01351 }
01352
01353 static int unload_module(void)
01354 {
01355 ast_channel_unregister(&alsa_tech);
01356 ast_cli_unregister_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry));
01357
01358 if (alsa.icard)
01359 snd_pcm_close(alsa.icard);
01360 if (alsa.ocard)
01361 snd_pcm_close(alsa.ocard);
01362 if (sndcmd[0] > 0) {
01363 close(sndcmd[0]);
01364 close(sndcmd[1]);
01365 }
01366 if (alsa.owner)
01367 ast_softhangup(alsa.owner, AST_SOFTHANGUP_APPUNLOAD);
01368 if (alsa.owner)
01369 return -1;
01370 return 0;
01371 }
01372
01373 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ALSA Console Channel Driver");