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