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