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