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: 83974 $")
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 *dest, const char *text, int ispdu);
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 hwparams = alloca(snd_pcm_hw_params_sizeof());
00371 memset(hwparams, 0, snd_pcm_hw_params_sizeof());
00372 snd_pcm_hw_params_any(handle, hwparams);
00373
00374 err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
00375 if (err < 0)
00376 ast_log(LOG_ERROR, "set_access failed: %s\n", snd_strerror(err));
00377
00378 err = snd_pcm_hw_params_set_format(handle, hwparams, format);
00379 if (err < 0)
00380 ast_log(LOG_ERROR, "set_format failed: %s\n", snd_strerror(err));
00381
00382 err = snd_pcm_hw_params_set_channels(handle, hwparams, 1);
00383 if (err < 0)
00384 ast_log(LOG_ERROR, "set_channels failed: %s\n", snd_strerror(err));
00385
00386 direction = 0;
00387 err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, &direction);
00388 if (rate != DESIRED_RATE)
00389 ast_log(LOG_WARNING, "Rate not correct, requested %d, got %d\n", DESIRED_RATE, rate);
00390
00391 direction = 0;
00392 err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, &direction);
00393 if (err < 0)
00394 ast_log(LOG_ERROR, "period_size(%ld frames) is bad: %s\n", period_size, snd_strerror(err));
00395 else
00396 ast_log(LOG_DEBUG, "Period size is %d\n", err);
00397
00398 buffer_size = 4096 * 2;
00399 err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size);
00400 if (err < 0)
00401 ast_log(LOG_WARNING, "Problem setting buffer size of %ld: %s\n", buffer_size, snd_strerror(err));
00402 else
00403 ast_log(LOG_DEBUG, "Buffer size is set to %d frames\n", err);
00404
00405 #if 0
00406 direction = 0;
00407 err = snd_pcm_hw_params_set_periods_min(handle, hwparams, &per_min, &direction);
00408 if (err < 0)
00409 ast_log(LOG_ERROR, "periods_min: %s\n", snd_strerror(err));
00410
00411 err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &per_max, 0);
00412 if (err < 0)
00413 ast_log(LOG_ERROR, "periods_max: %s\n", snd_strerror(err));
00414 #endif
00415
00416 err = snd_pcm_hw_params(handle, hwparams);
00417 if (err < 0)
00418 ast_log(LOG_ERROR, "Couldn't set the new hw params: %s\n", snd_strerror(err));
00419
00420 swparams = alloca(snd_pcm_sw_params_sizeof());
00421 memset(swparams, 0, snd_pcm_sw_params_sizeof());
00422 snd_pcm_sw_params_current(handle, swparams);
00423
00424 #if 1
00425 if (stream == SND_PCM_STREAM_PLAYBACK)
00426 start_threshold = period_size;
00427 else
00428 start_threshold = 1;
00429
00430 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
00431 if (err < 0)
00432 ast_log(LOG_ERROR, "start threshold: %s\n", snd_strerror(err));
00433 #endif
00434
00435 #if 1
00436 if (stream == SND_PCM_STREAM_PLAYBACK)
00437 stop_threshold = buffer_size;
00438 else
00439 stop_threshold = buffer_size;
00440
00441 err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
00442 if (err < 0)
00443 ast_log(LOG_ERROR, "stop threshold: %s\n", snd_strerror(err));
00444 #endif
00445 #if 0
00446 err = snd_pcm_sw_params_set_xfer_align(handle, swparams, PERIOD_FRAMES);
00447 if (err < 0)
00448 ast_log(LOG_ERROR, "Unable to set xfer alignment: %s\n", snd_strerror(err));
00449 #endif
00450
00451 #if 0
00452 err = snd_pcm_sw_params_set_silence_threshold(handle, swparams, silencethreshold);
00453 if (err < 0)
00454 ast_log(LOG_ERROR, "Unable to set silence threshold: %s\n", snd_strerror(err));
00455 #endif
00456 err = snd_pcm_sw_params(handle, swparams);
00457 if (err < 0)
00458 ast_log(LOG_ERROR, "sw_params: %s\n", snd_strerror(err));
00459
00460 err = snd_pcm_poll_descriptors_count(handle);
00461 if (err <= 0)
00462 ast_log(LOG_ERROR, "Unable to get a poll descriptors count, error is %s\n", snd_strerror(err));
00463 if (err != 1)
00464 ast_log(LOG_DEBUG, "Can't handle more than one device\n");
00465
00466 snd_pcm_poll_descriptors(handle, &pfd, err);
00467 ast_log(LOG_DEBUG, "Acquired fd %d from the poll descriptor\n", pfd.fd);
00468
00469 if (stream == SND_PCM_STREAM_CAPTURE)
00470 readdev = pfd.fd;
00471 else
00472 writedev = pfd.fd;
00473
00474 return handle;
00475 }
00476
00477 static int soundcard_init(void)
00478 {
00479 alsa.icard = alsa_card_init(indevname, SND_PCM_STREAM_CAPTURE);
00480 alsa.ocard = alsa_card_init(outdevname, SND_PCM_STREAM_PLAYBACK);
00481
00482 if (!alsa.icard || !alsa.ocard) {
00483 ast_log(LOG_ERROR, "Problem opening alsa I/O devices\n");
00484 return -1;
00485 }
00486
00487 return readdev;
00488 }
00489
00490 static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration)
00491 {
00492 ast_mutex_lock(&alsalock);
00493 ast_verbose(" << Console Received digit %c of duration %u ms >> \n",
00494 digit, duration);
00495 ast_mutex_unlock(&alsalock);
00496 return 0;
00497 }
00498
00499 static int alsa_text(struct ast_channel *c, const char *dest, const char *text, int ispdu)
00500 {
00501 ast_mutex_lock(&alsalock);
00502 ast_verbose(" << Console Received text %s >> \n", text);
00503 ast_mutex_unlock(&alsalock);
00504 return 0;
00505 }
00506
00507 static void grab_owner(void)
00508 {
00509 while (alsa.owner && ast_mutex_trylock(&alsa.owner->lock)) {
00510 ast_mutex_unlock(&alsalock);
00511 usleep(1);
00512 ast_mutex_lock(&alsalock);
00513 }
00514 }
00515
00516 static int alsa_call(struct ast_channel *c, char *dest, int timeout)
00517 {
00518 int res = 3;
00519 struct ast_frame f = { AST_FRAME_CONTROL };
00520 ast_mutex_lock(&alsalock);
00521 ast_verbose(" << Call placed to '%s' on console >> \n", dest);
00522 if (autoanswer) {
00523 ast_verbose(" << Auto-answered >> \n");
00524 grab_owner();
00525 if (alsa.owner) {
00526 f.subclass = AST_CONTROL_ANSWER;
00527 ast_queue_frame(alsa.owner, &f);
00528 ast_mutex_unlock(&alsa.owner->lock);
00529 }
00530 } else {
00531 ast_verbose(" << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n");
00532 grab_owner();
00533 if (alsa.owner) {
00534 f.subclass = AST_CONTROL_RINGING;
00535 ast_queue_frame(alsa.owner, &f);
00536 ast_mutex_unlock(&alsa.owner->lock);
00537 }
00538 write(sndcmd[1], &res, sizeof(res));
00539 }
00540 snd_pcm_prepare(alsa.icard);
00541 snd_pcm_start(alsa.icard);
00542 ast_mutex_unlock(&alsalock);
00543 return 0;
00544 }
00545
00546 static void answer_sound(void)
00547 {
00548 int res;
00549 nosound = 1;
00550 res = 4;
00551 write(sndcmd[1], &res, sizeof(res));
00552
00553 }
00554
00555 static int alsa_answer(struct ast_channel *c)
00556 {
00557 ast_mutex_lock(&alsalock);
00558 ast_verbose(" << Console call has been answered >> \n");
00559 answer_sound();
00560 ast_setstate(c, AST_STATE_UP);
00561 cursound = -1;
00562 snd_pcm_prepare(alsa.icard);
00563 snd_pcm_start(alsa.icard);
00564 ast_mutex_unlock(&alsalock);
00565 return 0;
00566 }
00567
00568 static int alsa_hangup(struct ast_channel *c)
00569 {
00570 int res;
00571 ast_mutex_lock(&alsalock);
00572 cursound = -1;
00573 c->tech_pvt = NULL;
00574 alsa.owner = NULL;
00575 ast_verbose(" << Hangup on console >> \n");
00576 ast_module_unref(ast_module_info->self);
00577 if (hookstate) {
00578 hookstate = 0;
00579 if (!autoanswer) {
00580
00581 res = 2;
00582 write(sndcmd[1], &res, sizeof(res));
00583 }
00584 }
00585 snd_pcm_drop(alsa.icard);
00586 ast_mutex_unlock(&alsalock);
00587 return 0;
00588 }
00589
00590 static int alsa_write(struct ast_channel *chan, struct ast_frame *f)
00591 {
00592 static char sizbuf[8000];
00593 static int sizpos = 0;
00594 int len = sizpos;
00595 int pos;
00596 int res = 0;
00597
00598 snd_pcm_state_t state;
00599
00600
00601 if (nosound)
00602 return 0;
00603
00604 ast_mutex_lock(&alsalock);
00605
00606 if (cursound != -1) {
00607 snd_pcm_drop(alsa.ocard);
00608 snd_pcm_prepare(alsa.ocard);
00609 cursound = -1;
00610 }
00611
00612
00613
00614 if (f->datalen > sizeof(sizbuf) - sizpos) {
00615 ast_log(LOG_WARNING, "Frame too large\n");
00616 res = -1;
00617 } else {
00618 memcpy(sizbuf + sizpos, f->data, f->datalen);
00619 len += f->datalen;
00620 pos = 0;
00621 #ifdef ALSA_MONITOR
00622 alsa_monitor_write(sizbuf, len);
00623 #endif
00624 state = snd_pcm_state(alsa.ocard);
00625 if (state == SND_PCM_STATE_XRUN)
00626 snd_pcm_prepare(alsa.ocard);
00627 res = snd_pcm_writei(alsa.ocard, sizbuf, len / 2);
00628 if (res == -EPIPE) {
00629 #if DEBUG
00630 ast_log(LOG_DEBUG, "XRUN write\n");
00631 #endif
00632 snd_pcm_prepare(alsa.ocard);
00633 res = snd_pcm_writei(alsa.ocard, sizbuf, len / 2);
00634 if (res != len / 2) {
00635 ast_log(LOG_ERROR, "Write error: %s\n", snd_strerror(res));
00636 res = -1;
00637 } else if (res < 0) {
00638 ast_log(LOG_ERROR, "Write error %s\n", snd_strerror(res));
00639 res = -1;
00640 }
00641 } else {
00642 if (res == -ESTRPIPE)
00643 ast_log(LOG_ERROR, "You've got some big problems\n");
00644 else if (res < 0)
00645 ast_log(LOG_NOTICE, "Error %d on write\n", res);
00646 }
00647 }
00648 ast_mutex_unlock(&alsalock);
00649 if (res > 0)
00650 res = 0;
00651 return res;
00652 }
00653
00654
00655 static struct ast_frame *alsa_read(struct ast_channel *chan)
00656 {
00657 static struct ast_frame f;
00658 static short __buf[FRAME_SIZE + AST_FRIENDLY_OFFSET / 2];
00659 short *buf;
00660 static int readpos = 0;
00661 static int left = FRAME_SIZE;
00662 snd_pcm_state_t state;
00663 int r = 0;
00664 int off = 0;
00665
00666 ast_mutex_lock(&alsalock);
00667
00668 f.frametype = AST_FRAME_NULL;
00669 f.subclass = 0;
00670 f.samples = 0;
00671 f.datalen = 0;
00672 f.data = NULL;
00673 f.offset = 0;
00674 f.src = "Console";
00675 f.mallocd = 0;
00676 f.delivery.tv_sec = 0;
00677 f.delivery.tv_usec = 0;
00678
00679 state = snd_pcm_state(alsa.icard);
00680 if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING)) {
00681 snd_pcm_prepare(alsa.icard);
00682 }
00683
00684 buf = __buf + AST_FRIENDLY_OFFSET / 2;
00685
00686 r = snd_pcm_readi(alsa.icard, buf + readpos, left);
00687 if (r == -EPIPE) {
00688 #if DEBUG
00689 ast_log(LOG_ERROR, "XRUN read\n");
00690 #endif
00691 snd_pcm_prepare(alsa.icard);
00692 } else if (r == -ESTRPIPE) {
00693 ast_log(LOG_ERROR, "-ESTRPIPE\n");
00694 snd_pcm_prepare(alsa.icard);
00695 } else if (r < 0) {
00696 ast_log(LOG_ERROR, "Read error: %s\n", snd_strerror(r));
00697 } else if (r >= 0) {
00698 off -= r;
00699 }
00700
00701 readpos += r;
00702 left -= r;
00703
00704 if (readpos >= FRAME_SIZE) {
00705
00706 readpos = 0;
00707 left = FRAME_SIZE;
00708 if (chan->_state != AST_STATE_UP) {
00709
00710 ast_mutex_unlock(&alsalock);
00711 return &f;
00712 }
00713 f.frametype = AST_FRAME_VOICE;
00714 f.subclass = AST_FORMAT_SLINEAR;
00715 f.samples = FRAME_SIZE;
00716 f.datalen = FRAME_SIZE * 2;
00717 f.data = buf;
00718 f.offset = AST_FRIENDLY_OFFSET;
00719 f.src = "Console";
00720 f.mallocd = 0;
00721 #ifdef ALSA_MONITOR
00722 alsa_monitor_read((char *) buf, FRAME_SIZE * 2);
00723 #endif
00724
00725 }
00726 ast_mutex_unlock(&alsalock);
00727 return &f;
00728 }
00729
00730 static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00731 {
00732 struct chan_alsa_pvt *p = newchan->tech_pvt;
00733 ast_mutex_lock(&alsalock);
00734 p->owner = newchan;
00735 ast_mutex_unlock(&alsalock);
00736 return 0;
00737 }
00738
00739 static int alsa_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
00740 {
00741 int res = 0;
00742
00743 ast_mutex_lock(&alsalock);
00744
00745 switch (cond) {
00746 case AST_CONTROL_BUSY:
00747 res = 1;
00748 break;
00749 case AST_CONTROL_CONGESTION:
00750 res = 2;
00751 break;
00752 case AST_CONTROL_RINGING:
00753 case AST_CONTROL_PROGRESS:
00754 break;
00755 case -1:
00756 res = -1;
00757 break;
00758 case AST_CONTROL_VIDUPDATE:
00759 res = -1;
00760 break;
00761 case AST_CONTROL_HOLD:
00762 ast_verbose(" << Console Has Been Placed on Hold >> \n");
00763 ast_moh_start(chan, data, mohinterpret);
00764 break;
00765 case AST_CONTROL_UNHOLD:
00766 ast_verbose(" << Console Has Been Retrieved from Hold >> \n");
00767 ast_moh_stop(chan);
00768 break;
00769 default:
00770 ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, chan->name);
00771 res = -1;
00772 }
00773
00774 if (res > -1)
00775 write(sndcmd[1], &res, sizeof(res));
00776
00777 ast_mutex_unlock(&alsalock);
00778
00779 return res;
00780 }
00781
00782 static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
00783 {
00784 struct ast_channel *tmp = NULL;
00785
00786 if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, 0, "ALSA/%s", indevname)))
00787 return NULL;
00788
00789 tmp->tech = &alsa_tech;
00790 tmp->fds[0] = readdev;
00791 tmp->nativeformats = AST_FORMAT_SLINEAR;
00792 tmp->readformat = AST_FORMAT_SLINEAR;
00793 tmp->writeformat = AST_FORMAT_SLINEAR;
00794 tmp->tech_pvt = p;
00795 if (!ast_strlen_zero(p->context))
00796 ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00797 if (!ast_strlen_zero(p->exten))
00798 ast_copy_string(tmp->exten, p->exten, sizeof(tmp->exten));
00799 if (!ast_strlen_zero(language))
00800 ast_string_field_set(tmp, language, language);
00801 p->owner = tmp;
00802 ast_module_ref(ast_module_info->self);
00803 ast_jb_configure(tmp, &global_jbconf);
00804 if (state != AST_STATE_DOWN) {
00805 if (ast_pbx_start(tmp)) {
00806 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
00807 ast_hangup(tmp);
00808 tmp = NULL;
00809 }
00810 }
00811
00812 return tmp;
00813 }
00814
00815 static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause)
00816 {
00817 int oldformat = format;
00818 struct ast_channel *tmp = NULL;
00819
00820 format &= AST_FORMAT_SLINEAR;
00821 if (!format) {
00822 ast_log(LOG_NOTICE, "Asked to get a channel of format '%d'\n", oldformat);
00823 return NULL;
00824 }
00825
00826 ast_mutex_lock(&alsalock);
00827
00828 if (alsa.owner) {
00829 ast_log(LOG_NOTICE, "Already have a call on the ALSA channel\n");
00830 *cause = AST_CAUSE_BUSY;
00831 } else if (!(tmp = alsa_new(&alsa, AST_STATE_DOWN)))
00832 ast_log(LOG_WARNING, "Unable to create new ALSA channel\n");
00833
00834 ast_mutex_unlock(&alsalock);
00835
00836 return tmp;
00837 }
00838
00839 static int console_autoanswer_deprecated(int fd, int argc, char *argv[])
00840 {
00841 int res = RESULT_SUCCESS;
00842
00843 if ((argc != 1) && (argc != 2))
00844 return RESULT_SHOWUSAGE;
00845
00846 ast_mutex_lock(&alsalock);
00847
00848 if (argc == 1) {
00849 ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off");
00850 } else {
00851 if (!strcasecmp(argv[1], "on"))
00852 autoanswer = -1;
00853 else if (!strcasecmp(argv[1], "off"))
00854 autoanswer = 0;
00855 else
00856 res = RESULT_SHOWUSAGE;
00857 }
00858
00859 ast_mutex_unlock(&alsalock);
00860
00861 return res;
00862 }
00863
00864 static int console_autoanswer(int fd, int argc, char *argv[])
00865 {
00866 int res = RESULT_SUCCESS;;
00867 if ((argc != 2) && (argc != 3))
00868 return RESULT_SHOWUSAGE;
00869 ast_mutex_lock(&alsalock);
00870 if (argc == 2) {
00871 ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off");
00872 } else {
00873 if (!strcasecmp(argv[2], "on"))
00874 autoanswer = -1;
00875 else if (!strcasecmp(argv[2], "off"))
00876 autoanswer = 0;
00877 else
00878 res = RESULT_SHOWUSAGE;
00879 }
00880 ast_mutex_unlock(&alsalock);
00881 return res;
00882 }
00883
00884 static char *autoanswer_complete(const char *line, const char *word, int pos, int state)
00885 {
00886 #ifndef MIN
00887 #define MIN(a,b) ((a) < (b) ? (a) : (b))
00888 #endif
00889 switch (state) {
00890 case 0:
00891 if (!ast_strlen_zero(word) && !strncasecmp(word, "on", MIN(strlen(word), 2)))
00892 return ast_strdup("on");
00893 case 1:
00894 if (!ast_strlen_zero(word) && !strncasecmp(word, "off", MIN(strlen(word), 3)))
00895 return ast_strdup("off");
00896 default:
00897 return NULL;
00898 }
00899 return NULL;
00900 }
00901
00902 static const char autoanswer_usage[] =
00903 "Usage: console autoanswer [on|off]\n"
00904 " Enables or disables autoanswer feature. If used without\n"
00905 " argument, displays the current on/off status of autoanswer.\n"
00906 " The default value of autoanswer is in 'alsa.conf'.\n";
00907
00908 static int console_answer_deprecated(int fd, int argc, char *argv[])
00909 {
00910 int res = RESULT_SUCCESS;
00911
00912 if (argc != 1)
00913 return RESULT_SHOWUSAGE;
00914
00915 ast_mutex_lock(&alsalock);
00916
00917 if (!alsa.owner) {
00918 ast_cli(fd, "No one is calling us\n");
00919 res = RESULT_FAILURE;
00920 } else {
00921 hookstate = 1;
00922 cursound = -1;
00923 grab_owner();
00924 if (alsa.owner) {
00925 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00926 ast_queue_frame(alsa.owner, &f);
00927 ast_mutex_unlock(&alsa.owner->lock);
00928 }
00929 answer_sound();
00930 }
00931
00932 snd_pcm_prepare(alsa.icard);
00933 snd_pcm_start(alsa.icard);
00934
00935 ast_mutex_unlock(&alsalock);
00936
00937 return RESULT_SUCCESS;
00938 }
00939
00940 static int console_answer(int fd, int argc, char *argv[])
00941 {
00942 int res = RESULT_SUCCESS;
00943
00944 if (argc != 2)
00945 return RESULT_SHOWUSAGE;
00946
00947 ast_mutex_lock(&alsalock);
00948
00949 if (!alsa.owner) {
00950 ast_cli(fd, "No one is calling us\n");
00951 res = RESULT_FAILURE;
00952 } else {
00953 hookstate = 1;
00954 cursound = -1;
00955 grab_owner();
00956 if (alsa.owner) {
00957 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00958 ast_queue_frame(alsa.owner, &f);
00959 ast_mutex_unlock(&alsa.owner->lock);
00960 }
00961 answer_sound();
00962 }
00963
00964 snd_pcm_prepare(alsa.icard);
00965 snd_pcm_start(alsa.icard);
00966
00967 ast_mutex_unlock(&alsalock);
00968
00969 return RESULT_SUCCESS;
00970 }
00971
00972 static char sendtext_usage[] =
00973 "Usage: console send text <message>\n"
00974 " Sends a text message for display on the remote terminal.\n";
00975
00976 static int console_sendtext_deprecated(int fd, int argc, char *argv[])
00977 {
00978 int tmparg = 2;
00979 int res = RESULT_SUCCESS;
00980
00981 if (argc < 2)
00982 return RESULT_SHOWUSAGE;
00983
00984 ast_mutex_lock(&alsalock);
00985
00986 if (!alsa.owner) {
00987 ast_cli(fd, "No one is calling us\n");
00988 res = RESULT_FAILURE;
00989 } else {
00990 struct ast_frame f = { AST_FRAME_TEXT, 0 };
00991 char text2send[256] = "";
00992 text2send[0] = '\0';
00993 while (tmparg < argc) {
00994 strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1);
00995 strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1);
00996 }
00997 text2send[strlen(text2send) - 1] = '\n';
00998 f.data = text2send;
00999 f.datalen = strlen(text2send) + 1;
01000 grab_owner();
01001 if (alsa.owner) {
01002 ast_queue_frame(alsa.owner, &f);
01003 f.frametype = AST_FRAME_CONTROL;
01004 f.subclass = AST_CONTROL_ANSWER;
01005 f.data = NULL;
01006 f.datalen = 0;
01007 ast_queue_frame(alsa.owner, &f);
01008 ast_mutex_unlock(&alsa.owner->lock);
01009 }
01010 }
01011
01012 ast_mutex_unlock(&alsalock);
01013
01014 return res;
01015 }
01016
01017 static int console_sendtext(int fd, int argc, char *argv[])
01018 {
01019 int tmparg = 3;
01020 int res = RESULT_SUCCESS;
01021
01022 if (argc < 3)
01023 return RESULT_SHOWUSAGE;
01024
01025 ast_mutex_lock(&alsalock);
01026
01027 if (!alsa.owner) {
01028 ast_cli(fd, "No one is calling us\n");
01029 res = RESULT_FAILURE;
01030 } else {
01031 struct ast_frame f = { AST_FRAME_TEXT, 0 };
01032 char text2send[256] = "";
01033 text2send[0] = '\0';
01034 while (tmparg < argc) {
01035 strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1);
01036 strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1);
01037 }
01038 text2send[strlen(text2send) - 1] = '\n';
01039 f.data = text2send;
01040 f.datalen = strlen(text2send) + 1;
01041 grab_owner();
01042 if (alsa.owner) {
01043 ast_queue_frame(alsa.owner, &f);
01044 f.frametype = AST_FRAME_CONTROL;
01045 f.subclass = AST_CONTROL_ANSWER;
01046 f.data = NULL;
01047 f.datalen = 0;
01048 ast_queue_frame(alsa.owner, &f);
01049 ast_mutex_unlock(&alsa.owner->lock);
01050 }
01051 }
01052
01053 ast_mutex_unlock(&alsalock);
01054
01055 return res;
01056 }
01057
01058 static char answer_usage[] =
01059 "Usage: console answer\n"
01060 " Answers an incoming call on the console (ALSA) channel.\n";
01061
01062 static int console_hangup_deprecated(int fd, int argc, char *argv[])
01063 {
01064 int res = RESULT_SUCCESS;
01065
01066 if (argc != 1)
01067 return RESULT_SHOWUSAGE;
01068
01069 cursound = -1;
01070
01071 ast_mutex_lock(&alsalock);
01072
01073 if (!alsa.owner && !hookstate) {
01074 ast_cli(fd, "No call to hangup up\n");
01075 res = RESULT_FAILURE;
01076 } else {
01077 hookstate = 0;
01078 grab_owner();
01079 if (alsa.owner) {
01080 ast_queue_hangup(alsa.owner);
01081 ast_mutex_unlock(&alsa.owner->lock);
01082 }
01083 }
01084
01085 ast_mutex_unlock(&alsalock);
01086
01087 return res;
01088 }
01089
01090 static int console_hangup(int fd, int argc, char *argv[])
01091 {
01092 int res = RESULT_SUCCESS;
01093
01094 if (argc != 2)
01095 return RESULT_SHOWUSAGE;
01096
01097 cursound = -1;
01098
01099 ast_mutex_lock(&alsalock);
01100
01101 if (!alsa.owner && !hookstate) {
01102 ast_cli(fd, "No call to hangup up\n");
01103 res = RESULT_FAILURE;
01104 } else {
01105 hookstate = 0;
01106 grab_owner();
01107 if (alsa.owner) {
01108 ast_queue_hangup(alsa.owner);
01109 ast_mutex_unlock(&alsa.owner->lock);
01110 }
01111 }
01112
01113 ast_mutex_unlock(&alsalock);
01114
01115 return res;
01116 }
01117
01118 static char hangup_usage[] =
01119 "Usage: console hangup\n"
01120 " Hangs up any call currently placed on the console.\n";
01121
01122 static int console_dial_deprecated(int fd, int argc, char *argv[])
01123 {
01124 char tmp[256], *tmp2;
01125 char *mye, *myc;
01126 char *d;
01127 int res = RESULT_SUCCESS;
01128
01129 if ((argc != 1) && (argc != 2))
01130 return RESULT_SHOWUSAGE;
01131
01132 ast_mutex_lock(&alsalock);
01133
01134 if (alsa.owner) {
01135 if (argc == 2) {
01136 d = argv[1];
01137 grab_owner();
01138 if (alsa.owner) {
01139 struct ast_frame f = { AST_FRAME_DTMF };
01140 while (*d) {
01141 f.subclass = *d;
01142 ast_queue_frame(alsa.owner, &f);
01143 d++;
01144 }
01145 ast_mutex_unlock(&alsa.owner->lock);
01146 }
01147 } else {
01148 ast_cli(fd, "You're already in a call. You can use this only to dial digits until you hangup\n");
01149 res = RESULT_FAILURE;
01150 }
01151 } else {
01152 mye = exten;
01153 myc = context;
01154 if (argc == 2) {
01155 char *stringp = NULL;
01156 ast_copy_string(tmp, argv[1], sizeof(tmp));
01157 stringp = tmp;
01158 strsep(&stringp, "@");
01159 tmp2 = strsep(&stringp, "@");
01160 if (!ast_strlen_zero(tmp))
01161 mye = tmp;
01162 if (!ast_strlen_zero(tmp2))
01163 myc = tmp2;
01164 }
01165 if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
01166 ast_copy_string(alsa.exten, mye, sizeof(alsa.exten));
01167 ast_copy_string(alsa.context, myc, sizeof(alsa.context));
01168 hookstate = 1;
01169 alsa_new(&alsa, AST_STATE_RINGING);
01170 } else
01171 ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
01172 }
01173
01174 ast_mutex_unlock(&alsalock);
01175
01176 return res;
01177 }
01178
01179 static int console_dial(int fd, int argc, char *argv[])
01180 {
01181 char tmp[256], *tmp2;
01182 char *mye, *myc;
01183 char *d;
01184 int res = RESULT_SUCCESS;
01185
01186 if ((argc != 2) && (argc != 3))
01187 return RESULT_SHOWUSAGE;
01188
01189 ast_mutex_lock(&alsalock);
01190
01191 if (alsa.owner) {
01192 if (argc == 3) {
01193 d = argv[2];
01194 grab_owner();
01195 if (alsa.owner) {
01196 struct ast_frame f = { AST_FRAME_DTMF };
01197 while (*d) {
01198 f.subclass = *d;
01199 ast_queue_frame(alsa.owner, &f);
01200 d++;
01201 }
01202 ast_mutex_unlock(&alsa.owner->lock);
01203 }
01204 } else {
01205 ast_cli(fd, "You're already in a call. You can use this only to dial digits until you hangup\n");
01206 res = RESULT_FAILURE;
01207 }
01208 } else {
01209 mye = exten;
01210 myc = context;
01211 if (argc == 3) {
01212 char *stringp = NULL;
01213 ast_copy_string(tmp, argv[2], sizeof(tmp));
01214 stringp = tmp;
01215 strsep(&stringp, "@");
01216 tmp2 = strsep(&stringp, "@");
01217 if (!ast_strlen_zero(tmp))
01218 mye = tmp;
01219 if (!ast_strlen_zero(tmp2))
01220 myc = tmp2;
01221 }
01222 if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
01223 ast_copy_string(alsa.exten, mye, sizeof(alsa.exten));
01224 ast_copy_string(alsa.context, myc, sizeof(alsa.context));
01225 hookstate = 1;
01226 alsa_new(&alsa, AST_STATE_RINGING);
01227 } else
01228 ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
01229 }
01230
01231 ast_mutex_unlock(&alsalock);
01232
01233 return res;
01234 }
01235
01236 static char dial_usage[] =
01237 "Usage: console dial [extension[@context]]\n"
01238 " Dials a given extension (and context if specified)\n";
01239
01240 static struct ast_cli_entry cli_alsa_answer_deprecated = {
01241 { "answer", NULL },
01242 console_answer_deprecated, NULL,
01243 NULL };
01244
01245 static struct ast_cli_entry cli_alsa_hangup_deprecated = {
01246 { "hangup", NULL },
01247 console_hangup_deprecated, NULL,
01248 NULL };
01249
01250 static struct ast_cli_entry cli_alsa_dial_deprecated = {
01251 { "dial", NULL },
01252 console_dial_deprecated, NULL,
01253 NULL };
01254
01255 static struct ast_cli_entry cli_alsa_send_text_deprecated = {
01256 { "send", "text", NULL },
01257 console_sendtext_deprecated, NULL,
01258 NULL };
01259
01260 static struct ast_cli_entry cli_alsa_autoanswer_deprecated = {
01261 { "autoanswer", NULL },
01262 console_autoanswer_deprecated, NULL,
01263 NULL, autoanswer_complete };
01264
01265 static struct ast_cli_entry cli_alsa[] = {
01266 { { "console", "answer", NULL },
01267 console_answer, "Answer an incoming console call",
01268 answer_usage, NULL, &cli_alsa_answer_deprecated },
01269
01270 { { "console", "hangup", NULL },
01271 console_hangup, "Hangup a call on the console",
01272 hangup_usage, NULL, &cli_alsa_hangup_deprecated },
01273
01274 { { "console", "dial", NULL },
01275 console_dial, "Dial an extension on the console",
01276 dial_usage, NULL, &cli_alsa_dial_deprecated },
01277
01278 { { "console", "send", "text", NULL },
01279 console_sendtext, "Send text to the remote device",
01280 sendtext_usage, NULL, &cli_alsa_send_text_deprecated },
01281
01282 { { "console", "autoanswer", NULL },
01283 console_autoanswer, "Sets/displays autoanswer",
01284 autoanswer_usage, autoanswer_complete, &cli_alsa_autoanswer_deprecated },
01285 };
01286
01287 static int load_module(void)
01288 {
01289 int res;
01290 struct ast_config *cfg;
01291 struct ast_variable *v;
01292
01293
01294 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
01295
01296 strcpy(mohinterpret, "default");
01297
01298 if ((cfg = ast_config_load(config))) {
01299 v = ast_variable_browse(cfg, "general");
01300 for (; v; v = v->next) {
01301
01302 if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
01303 continue;
01304
01305 if (!strcasecmp(v->name, "autoanswer"))
01306 autoanswer = ast_true(v->value);
01307 else if (!strcasecmp(v->name, "silencesuppression"))
01308 silencesuppression = ast_true(v->value);
01309 else if (!strcasecmp(v->name, "silencethreshold"))
01310 silencethreshold = atoi(v->value);
01311 else if (!strcasecmp(v->name, "context"))
01312 ast_copy_string(context, v->value, sizeof(context));
01313 else if (!strcasecmp(v->name, "language"))
01314 ast_copy_string(language, v->value, sizeof(language));
01315 else if (!strcasecmp(v->name, "extension"))
01316 ast_copy_string(exten, v->value, sizeof(exten));
01317 else if (!strcasecmp(v->name, "input_device"))
01318 ast_copy_string(indevname, v->value, sizeof(indevname));
01319 else if (!strcasecmp(v->name, "output_device"))
01320 ast_copy_string(outdevname, v->value, sizeof(outdevname));
01321 else if (!strcasecmp(v->name, "mohinterpret"))
01322 ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret));
01323 }
01324 ast_config_destroy(cfg);
01325 }
01326 res = pipe(sndcmd);
01327 if (res) {
01328 ast_log(LOG_ERROR, "Unable to create pipe\n");
01329 return -1;
01330 }
01331 res = soundcard_init();
01332 if (res < 0) {
01333 if (option_verbose > 1) {
01334 ast_verbose(VERBOSE_PREFIX_2 "No sound card detected -- console channel will be unavailable\n");
01335 ast_verbose(VERBOSE_PREFIX_2 "Turn off ALSA support by adding 'noload=chan_alsa.so' in /etc/asterisk/modules.conf\n");
01336 }
01337 return 0;
01338 }
01339
01340 res = ast_channel_register(&alsa_tech);
01341 if (res < 0) {
01342 ast_log(LOG_ERROR, "Unable to register channel class 'Console'\n");
01343 return -1;
01344 }
01345 ast_cli_register_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry));
01346
01347 ast_pthread_create_background(&sthread, NULL, sound_thread, NULL);
01348 #ifdef ALSA_MONITOR
01349 if (alsa_monitor_start())
01350 ast_log(LOG_ERROR, "Problem starting Monitoring\n");
01351 #endif
01352 return 0;
01353 }
01354
01355 static int unload_module(void)
01356 {
01357 ast_channel_unregister(&alsa_tech);
01358 ast_cli_unregister_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry));
01359
01360 if (alsa.icard)
01361 snd_pcm_close(alsa.icard);
01362 if (alsa.ocard)
01363 snd_pcm_close(alsa.ocard);
01364 if (sndcmd[0] > 0) {
01365 close(sndcmd[0]);
01366 close(sndcmd[1]);
01367 }
01368 if (alsa.owner)
01369 ast_softhangup(alsa.owner, AST_SOFTHANGUP_APPUNLOAD);
01370 if (alsa.owner)
01371 return -1;
01372 return 0;
01373 }
01374
01375 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ALSA Console Channel Driver");