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 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 *text)
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 case AST_CONTROL_SRCUPDATE:
00770 break;
00771 default:
00772 ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, chan->name);
00773 res = -1;
00774 }
00775
00776 if (res > -1)
00777 write(sndcmd[1], &res, sizeof(res));
00778
00779 ast_mutex_unlock(&alsalock);
00780
00781 return res;
00782 }
00783
00784 static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
00785 {
00786 struct ast_channel *tmp = NULL;
00787 if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, 0, "ALSA/%s", indevname)))
00788 return NULL;
00789
00790 tmp->tech = &alsa_tech;
00791 tmp->fds[0] = readdev;
00792 tmp->nativeformats = AST_FORMAT_SLINEAR;
00793 tmp->readformat = AST_FORMAT_SLINEAR;
00794 tmp->writeformat = AST_FORMAT_SLINEAR;
00795 tmp->tech_pvt = p;
00796 if (!ast_strlen_zero(p->context))
00797 ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00798 if (!ast_strlen_zero(p->exten))
00799 ast_copy_string(tmp->exten, p->exten, sizeof(tmp->exten));
00800 if (!ast_strlen_zero(language))
00801 ast_string_field_set(tmp, language, language);
00802 p->owner = tmp;
00803 ast_module_ref(ast_module_info->self);
00804 ast_jb_configure(tmp, &global_jbconf);
00805 if (state != AST_STATE_DOWN) {
00806 if (ast_pbx_start(tmp)) {
00807 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
00808 ast_hangup(tmp);
00809 tmp = NULL;
00810 }
00811 }
00812
00813 return tmp;
00814 }
00815
00816 static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause)
00817 {
00818 int oldformat = format;
00819 struct ast_channel *tmp = NULL;
00820
00821 format &= AST_FORMAT_SLINEAR;
00822 if (!format) {
00823 ast_log(LOG_NOTICE, "Asked to get a channel of format '%d'\n", oldformat);
00824 return NULL;
00825 }
00826
00827 ast_mutex_lock(&alsalock);
00828
00829 if (alsa.owner) {
00830 ast_log(LOG_NOTICE, "Already have a call on the ALSA channel\n");
00831 *cause = AST_CAUSE_BUSY;
00832 } else if (!(tmp = alsa_new(&alsa, AST_STATE_DOWN)))
00833 ast_log(LOG_WARNING, "Unable to create new ALSA channel\n");
00834
00835 ast_mutex_unlock(&alsalock);
00836
00837 return tmp;
00838 }
00839
00840 static int console_autoanswer_deprecated(int fd, int argc, char *argv[])
00841 {
00842 int res = RESULT_SUCCESS;
00843
00844 if ((argc != 1) && (argc != 2))
00845 return RESULT_SHOWUSAGE;
00846
00847 ast_mutex_lock(&alsalock);
00848
00849 if (argc == 1) {
00850 ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off");
00851 } else {
00852 if (!strcasecmp(argv[1], "on"))
00853 autoanswer = -1;
00854 else if (!strcasecmp(argv[1], "off"))
00855 autoanswer = 0;
00856 else
00857 res = RESULT_SHOWUSAGE;
00858 }
00859
00860 ast_mutex_unlock(&alsalock);
00861
00862 return res;
00863 }
00864
00865 static int console_autoanswer(int fd, int argc, char *argv[])
00866 {
00867 int res = RESULT_SUCCESS;;
00868 if ((argc != 2) && (argc != 3))
00869 return RESULT_SHOWUSAGE;
00870 ast_mutex_lock(&alsalock);
00871 if (argc == 2) {
00872 ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off");
00873 } else {
00874 if (!strcasecmp(argv[2], "on"))
00875 autoanswer = -1;
00876 else if (!strcasecmp(argv[2], "off"))
00877 autoanswer = 0;
00878 else
00879 res = RESULT_SHOWUSAGE;
00880 }
00881 ast_mutex_unlock(&alsalock);
00882 return res;
00883 }
00884
00885 static char *autoanswer_complete(const char *line, const char *word, int pos, int state)
00886 {
00887 #ifndef MIN
00888 #define MIN(a,b) ((a) < (b) ? (a) : (b))
00889 #endif
00890 switch (state) {
00891 case 0:
00892 if (!ast_strlen_zero(word) && !strncasecmp(word, "on", MIN(strlen(word), 2)))
00893 return ast_strdup("on");
00894 case 1:
00895 if (!ast_strlen_zero(word) && !strncasecmp(word, "off", MIN(strlen(word), 3)))
00896 return ast_strdup("off");
00897 default:
00898 return NULL;
00899 }
00900 return NULL;
00901 }
00902
00903 static const char autoanswer_usage[] =
00904 "Usage: console autoanswer [on|off]\n"
00905 " Enables or disables autoanswer feature. If used without\n"
00906 " argument, displays the current on/off status of autoanswer.\n"
00907 " The default value of autoanswer is in 'alsa.conf'.\n";
00908
00909 static int console_answer_deprecated(int fd, int argc, char *argv[])
00910 {
00911 int res = RESULT_SUCCESS;
00912
00913 if (argc != 1)
00914 return RESULT_SHOWUSAGE;
00915
00916 ast_mutex_lock(&alsalock);
00917
00918 if (!alsa.owner) {
00919 ast_cli(fd, "No one is calling us\n");
00920 res = RESULT_FAILURE;
00921 } else {
00922 hookstate = 1;
00923 cursound = -1;
00924 grab_owner();
00925 if (alsa.owner) {
00926 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00927 ast_queue_frame(alsa.owner, &f);
00928 ast_mutex_unlock(&alsa.owner->lock);
00929 }
00930 answer_sound();
00931 }
00932
00933 snd_pcm_prepare(alsa.icard);
00934 snd_pcm_start(alsa.icard);
00935
00936 ast_mutex_unlock(&alsalock);
00937
00938 return RESULT_SUCCESS;
00939 }
00940
00941 static int console_answer(int fd, int argc, char *argv[])
00942 {
00943 int res = RESULT_SUCCESS;
00944
00945 if (argc != 2)
00946 return RESULT_SHOWUSAGE;
00947
00948 ast_mutex_lock(&alsalock);
00949
00950 if (!alsa.owner) {
00951 ast_cli(fd, "No one is calling us\n");
00952 res = RESULT_FAILURE;
00953 } else {
00954 hookstate = 1;
00955 cursound = -1;
00956 grab_owner();
00957 if (alsa.owner) {
00958 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00959 ast_queue_frame(alsa.owner, &f);
00960 ast_mutex_unlock(&alsa.owner->lock);
00961 }
00962 answer_sound();
00963 }
00964
00965 snd_pcm_prepare(alsa.icard);
00966 snd_pcm_start(alsa.icard);
00967
00968 ast_mutex_unlock(&alsalock);
00969
00970 return RESULT_SUCCESS;
00971 }
00972
00973 static char sendtext_usage[] =
00974 "Usage: console send text <message>\n"
00975 " Sends a text message for display on the remote terminal.\n";
00976
00977 static int console_sendtext_deprecated(int fd, int argc, char *argv[])
00978 {
00979 int tmparg = 2;
00980 int res = RESULT_SUCCESS;
00981
00982 if (argc < 2)
00983 return RESULT_SHOWUSAGE;
00984
00985 ast_mutex_lock(&alsalock);
00986
00987 if (!alsa.owner) {
00988 ast_cli(fd, "No one is calling us\n");
00989 res = RESULT_FAILURE;
00990 } else {
00991 struct ast_frame f = { AST_FRAME_TEXT, 0 };
00992 char text2send[256] = "";
00993 text2send[0] = '\0';
00994 while (tmparg < argc) {
00995 strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1);
00996 strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1);
00997 }
00998 text2send[strlen(text2send) - 1] = '\n';
00999 f.data = text2send;
01000 f.datalen = strlen(text2send) + 1;
01001 grab_owner();
01002 if (alsa.owner) {
01003 ast_queue_frame(alsa.owner, &f);
01004 f.frametype = AST_FRAME_CONTROL;
01005 f.subclass = AST_CONTROL_ANSWER;
01006 f.data = NULL;
01007 f.datalen = 0;
01008 ast_queue_frame(alsa.owner, &f);
01009 ast_mutex_unlock(&alsa.owner->lock);
01010 }
01011 }
01012
01013 ast_mutex_unlock(&alsalock);
01014
01015 return res;
01016 }
01017
01018 static int console_sendtext(int fd, int argc, char *argv[])
01019 {
01020 int tmparg = 3;
01021 int res = RESULT_SUCCESS;
01022
01023 if (argc < 3)
01024 return RESULT_SHOWUSAGE;
01025
01026 ast_mutex_lock(&alsalock);
01027
01028 if (!alsa.owner) {
01029 ast_cli(fd, "No one is calling us\n");
01030 res = RESULT_FAILURE;
01031 } else {
01032 struct ast_frame f = { AST_FRAME_TEXT, 0 };
01033 char text2send[256] = "";
01034 text2send[0] = '\0';
01035 while (tmparg < argc) {
01036 strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1);
01037 strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1);
01038 }
01039 text2send[strlen(text2send) - 1] = '\n';
01040 f.data = text2send;
01041 f.datalen = strlen(text2send) + 1;
01042 grab_owner();
01043 if (alsa.owner) {
01044 ast_queue_frame(alsa.owner, &f);
01045 f.frametype = AST_FRAME_CONTROL;
01046 f.subclass = AST_CONTROL_ANSWER;
01047 f.data = NULL;
01048 f.datalen = 0;
01049 ast_queue_frame(alsa.owner, &f);
01050 ast_mutex_unlock(&alsa.owner->lock);
01051 }
01052 }
01053
01054 ast_mutex_unlock(&alsalock);
01055
01056 return res;
01057 }
01058
01059 static char answer_usage[] =
01060 "Usage: console answer\n"
01061 " Answers an incoming call on the console (ALSA) channel.\n";
01062
01063 static int console_hangup_deprecated(int fd, int argc, char *argv[])
01064 {
01065 int res = RESULT_SUCCESS;
01066
01067 if (argc != 1)
01068 return RESULT_SHOWUSAGE;
01069
01070 cursound = -1;
01071
01072 ast_mutex_lock(&alsalock);
01073
01074 if (!alsa.owner && !hookstate) {
01075 ast_cli(fd, "No call to hangup up\n");
01076 res = RESULT_FAILURE;
01077 } else {
01078 hookstate = 0;
01079 grab_owner();
01080 if (alsa.owner) {
01081 ast_queue_hangup(alsa.owner);
01082 ast_mutex_unlock(&alsa.owner->lock);
01083 }
01084 }
01085
01086 ast_mutex_unlock(&alsalock);
01087
01088 return res;
01089 }
01090
01091 static int console_hangup(int fd, int argc, char *argv[])
01092 {
01093 int res = RESULT_SUCCESS;
01094
01095 if (argc != 2)
01096 return RESULT_SHOWUSAGE;
01097
01098 cursound = -1;
01099
01100 ast_mutex_lock(&alsalock);
01101
01102 if (!alsa.owner && !hookstate) {
01103 ast_cli(fd, "No call to hangup up\n");
01104 res = RESULT_FAILURE;
01105 } else {
01106 hookstate = 0;
01107 grab_owner();
01108 if (alsa.owner) {
01109 ast_queue_hangup(alsa.owner);
01110 ast_mutex_unlock(&alsa.owner->lock);
01111 }
01112 }
01113
01114 ast_mutex_unlock(&alsalock);
01115
01116 return res;
01117 }
01118
01119 static char hangup_usage[] =
01120 "Usage: console hangup\n"
01121 " Hangs up any call currently placed on the console.\n";
01122
01123 static int console_dial_deprecated(int fd, int argc, char *argv[])
01124 {
01125 char tmp[256], *tmp2;
01126 char *mye, *myc;
01127 char *d;
01128 int res = RESULT_SUCCESS;
01129
01130 if ((argc != 1) && (argc != 2))
01131 return RESULT_SHOWUSAGE;
01132
01133 ast_mutex_lock(&alsalock);
01134
01135 if (alsa.owner) {
01136 if (argc == 2) {
01137 d = argv[1];
01138 grab_owner();
01139 if (alsa.owner) {
01140 struct ast_frame f = { AST_FRAME_DTMF };
01141 while (*d) {
01142 f.subclass = *d;
01143 ast_queue_frame(alsa.owner, &f);
01144 d++;
01145 }
01146 ast_mutex_unlock(&alsa.owner->lock);
01147 }
01148 } else {
01149 ast_cli(fd, "You're already in a call. You can use this only to dial digits until you hangup\n");
01150 res = RESULT_FAILURE;
01151 }
01152 } else {
01153 mye = exten;
01154 myc = context;
01155 if (argc == 2) {
01156 char *stringp = NULL;
01157 ast_copy_string(tmp, argv[1], sizeof(tmp));
01158 stringp = tmp;
01159 strsep(&stringp, "@");
01160 tmp2 = strsep(&stringp, "@");
01161 if (!ast_strlen_zero(tmp))
01162 mye = tmp;
01163 if (!ast_strlen_zero(tmp2))
01164 myc = tmp2;
01165 }
01166 if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
01167 ast_copy_string(alsa.exten, mye, sizeof(alsa.exten));
01168 ast_copy_string(alsa.context, myc, sizeof(alsa.context));
01169 hookstate = 1;
01170 alsa_new(&alsa, AST_STATE_RINGING);
01171 } else
01172 ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
01173 }
01174
01175 ast_mutex_unlock(&alsalock);
01176
01177 return res;
01178 }
01179
01180 static int console_dial(int fd, int argc, char *argv[])
01181 {
01182 char tmp[256], *tmp2;
01183 char *mye, *myc;
01184 char *d;
01185 int res = RESULT_SUCCESS;
01186
01187 if ((argc != 2) && (argc != 3))
01188 return RESULT_SHOWUSAGE;
01189
01190 ast_mutex_lock(&alsalock);
01191
01192 if (alsa.owner) {
01193 if (argc == 3) {
01194 d = argv[2];
01195 grab_owner();
01196 if (alsa.owner) {
01197 struct ast_frame f = { AST_FRAME_DTMF };
01198 while (*d) {
01199 f.subclass = *d;
01200 ast_queue_frame(alsa.owner, &f);
01201 d++;
01202 }
01203 ast_mutex_unlock(&alsa.owner->lock);
01204 }
01205 } else {
01206 ast_cli(fd, "You're already in a call. You can use this only to dial digits until you hangup\n");
01207 res = RESULT_FAILURE;
01208 }
01209 } else {
01210 mye = exten;
01211 myc = context;
01212 if (argc == 3) {
01213 char *stringp = NULL;
01214 ast_copy_string(tmp, argv[2], sizeof(tmp));
01215 stringp = tmp;
01216 strsep(&stringp, "@");
01217 tmp2 = strsep(&stringp, "@");
01218 if (!ast_strlen_zero(tmp))
01219 mye = tmp;
01220 if (!ast_strlen_zero(tmp2))
01221 myc = tmp2;
01222 }
01223 if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
01224 ast_copy_string(alsa.exten, mye, sizeof(alsa.exten));
01225 ast_copy_string(alsa.context, myc, sizeof(alsa.context));
01226 hookstate = 1;
01227 alsa_new(&alsa, AST_STATE_RINGING);
01228 } else
01229 ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
01230 }
01231
01232 ast_mutex_unlock(&alsalock);
01233
01234 return res;
01235 }
01236
01237 static char dial_usage[] =
01238 "Usage: console dial [extension[@context]]\n"
01239 " Dials a given extension (and context if specified)\n";
01240
01241 static struct ast_cli_entry cli_alsa_answer_deprecated = {
01242 { "answer", NULL },
01243 console_answer_deprecated, NULL,
01244 NULL };
01245
01246 static struct ast_cli_entry cli_alsa_hangup_deprecated = {
01247 { "hangup", NULL },
01248 console_hangup_deprecated, NULL,
01249 NULL };
01250
01251 static struct ast_cli_entry cli_alsa_dial_deprecated = {
01252 { "dial", NULL },
01253 console_dial_deprecated, NULL,
01254 NULL };
01255
01256 static struct ast_cli_entry cli_alsa_send_text_deprecated = {
01257 { "send", "text", NULL },
01258 console_sendtext_deprecated, NULL,
01259 NULL };
01260
01261 static struct ast_cli_entry cli_alsa_autoanswer_deprecated = {
01262 { "autoanswer", NULL },
01263 console_autoanswer_deprecated, NULL,
01264 NULL, autoanswer_complete };
01265
01266 static struct ast_cli_entry cli_alsa[] = {
01267 { { "console", "answer", NULL },
01268 console_answer, "Answer an incoming console call",
01269 answer_usage, NULL, &cli_alsa_answer_deprecated },
01270
01271 { { "console", "hangup", NULL },
01272 console_hangup, "Hangup a call on the console",
01273 hangup_usage, NULL, &cli_alsa_hangup_deprecated },
01274
01275 { { "console", "dial", NULL },
01276 console_dial, "Dial an extension on the console",
01277 dial_usage, NULL, &cli_alsa_dial_deprecated },
01278
01279 { { "console", "send", "text", NULL },
01280 console_sendtext, "Send text to the remote device",
01281 sendtext_usage, NULL, &cli_alsa_send_text_deprecated },
01282
01283 { { "console", "autoanswer", NULL },
01284 console_autoanswer, "Sets/displays autoanswer",
01285 autoanswer_usage, autoanswer_complete, &cli_alsa_autoanswer_deprecated },
01286 };
01287
01288 static int load_module(void)
01289 {
01290 int res;
01291 struct ast_config *cfg;
01292 struct ast_variable *v;
01293
01294
01295 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
01296
01297 strcpy(mohinterpret, "default");
01298 if (!(cfg = ast_config_load(config)))
01299 return AST_MODULE_LOAD_DECLINE;
01300
01301 v = ast_variable_browse(cfg, "general");
01302 for (; v; v = v->next) {
01303
01304 if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
01305 continue;
01306
01307 if (!strcasecmp(v->name, "autoanswer"))
01308 autoanswer = ast_true(v->value);
01309 else if (!strcasecmp(v->name, "silencesuppression"))
01310 silencesuppression = ast_true(v->value);
01311 else if (!strcasecmp(v->name, "silencethreshold"))
01312 silencethreshold = atoi(v->value);
01313 else if (!strcasecmp(v->name, "context"))
01314 ast_copy_string(context, v->value, sizeof(context));
01315 else if (!strcasecmp(v->name, "language"))
01316 ast_copy_string(language, v->value, sizeof(language));
01317 else if (!strcasecmp(v->name, "extension"))
01318 ast_copy_string(exten, v->value, sizeof(exten));
01319 else if (!strcasecmp(v->name, "input_device"))
01320 ast_copy_string(indevname, v->value, sizeof(indevname));
01321 else if (!strcasecmp(v->name, "output_device"))
01322 ast_copy_string(outdevname, v->value, sizeof(outdevname));
01323 else if (!strcasecmp(v->name, "mohinterpret"))
01324 ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret));
01325 }
01326
01327 ast_config_destroy(cfg);
01328 res = pipe(sndcmd);
01329 if (res) {
01330 ast_log(LOG_ERROR, "Unable to create pipe\n");
01331 return -1;
01332 }
01333 res = soundcard_init();
01334 if (res < 0) {
01335 if (option_verbose > 1) {
01336 ast_verbose(VERBOSE_PREFIX_2 "No sound card detected -- console channel will be unavailable\n");
01337 ast_verbose(VERBOSE_PREFIX_2 "Turn off ALSA support by adding 'noload=chan_alsa.so' in /etc/asterisk/modules.conf\n");
01338 }
01339 return AST_MODULE_LOAD_DECLINE;
01340 }
01341
01342 res = ast_channel_register(&alsa_tech);
01343 if (res < 0) {
01344 ast_log(LOG_ERROR, "Unable to register channel class 'Console'\n");
01345 return -1;
01346 }
01347 ast_cli_register_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry));
01348
01349 ast_pthread_create_background(&sthread, NULL, sound_thread, NULL);
01350 #ifdef ALSA_MONITOR
01351 if (alsa_monitor_start())
01352 ast_log(LOG_ERROR, "Problem starting Monitoring\n");
01353 #endif
01354 return 0;
01355 }
01356
01357 static int unload_module(void)
01358 {
01359 ast_channel_unregister(&alsa_tech);
01360 ast_cli_unregister_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry));
01361
01362 if (alsa.icard)
01363 snd_pcm_close(alsa.icard);
01364 if (alsa.ocard)
01365 snd_pcm_close(alsa.ocard);
01366 if (sndcmd[0] > 0) {
01367 close(sndcmd[0]);
01368 close(sndcmd[1]);
01369 }
01370 if (alsa.owner)
01371 ast_softhangup(alsa.owner, AST_SOFTHANGUP_APPUNLOAD);
01372 if (alsa.owner)
01373 return -1;
01374 return 0;
01375 }
01376
01377 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ALSA Console Channel Driver");