[asterisk-dev] Simple chan_jack.so where jack_read and jack_write are never called...
Fabien COMTE
fabien.comte at ercogener.com
Wed Oct 7 08:26:25 CDT 2009
Hi,
I try to write a very simple chan_jack.so (no resample, jack must run at
8000 Hz...)
The problem is that my functions jack_read and jack_write are never called
during a call. Does anyone have an idea ?
I probably get the same problem like in post "Ways of invoking channel read
function". I don't know how to set readdev fd...
Thank you,
Fabien
extensions.conf :
exten => _0.,1,Dial(console/jack)
jack.conf :
[general]
autoanswer=yes
;
; Default context (is overridden with @context syntax)
;
context=extensions-out
;
; Default extension to call
;
extension=s
input_device=default
output_device=default
client_name=asterisk
connect_input_port=from_voip:input
connect_output_port=to_voip:output
chan_jack.so :
/*
*
* By Fabien Comte based <fabien.comte at ercogener.com> on Matthew Fredrickson
<creslin at digium.com> chan_jack.c and Russell Bryant <russell at digium.com>
app_jack.c
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
* \brief JACK sound card channel driver
*
* \author Fabien Comte based <fabien.comte at ercogener.com> on Matthew
Fredrickson <creslin at digium.com> chan_jack.c and Russell Bryant
<russell at digium.com> app_jack.c
*
* \par See also
* \arg Config_jack
*
* \ingroup channel_drivers
*/
/*** MODULEINFO
<depend>jack</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 000001 $")
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "asterisk/frame.h"
#include "asterisk/channel.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/endian.h"
#include "asterisk/stringfields.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/musiconhold.h"
#include "asterisk/poll-compat.h"
/*! Global jitterbuffer configuration - by default, jb is disabled */
static struct ast_jb_conf default_jbconf = {
.flags = 0,
.max_size = -1,
.resync_threshold = -1,
.impl = ""
};
static struct ast_jb_conf global_jbconf;
#define DEBUG 0
/* Which device to use */
#define JACK_INDEV "default"
#define JACK_OUTDEV "default"
/* Lets use 160 sample frames, just like GSM. */
#define FRAME_SIZE 160
static char indevname[50] = JACK_INDEV;
static char outdevname[50] = JACK_OUTDEV;
static int silencesuppression = 0;
static int silencethreshold = 1000;
AST_MUTEX_DEFINE_STATIC(jacklock);
static const char tdesc[] = "JACK Console Channel Driver";
static const char config[] = "jack.conf";
static char context[AST_MAX_CONTEXT] = "default";
static char language[MAX_LANGUAGE] = "";
static char exten[AST_MAX_EXTENSION] = "s";
static char mohinterpret[MAX_MUSICCLASS];
static int hookstate = 0;
static struct chan_jack_pvt {
/* We only have one JACK structure -- near sighted perhaps, but it
keeps this driver as simple as possible -- as it should be. */
struct ast_channel *owner;
char exten[AST_MAX_EXTENSION];
char context[AST_MAX_CONTEXT];
} jack;
/* File descriptors for sound device */
static int readdev = -1;
static int autoanswer = 1;
static struct ast_channel *jack_request(const char *type, int format, void
*data, int *cause);
static int jack_digit(struct ast_channel *c, char digit, unsigned int
duration);
static int jack_text(struct ast_channel *c, const char *text);
static int jack_hangup(struct ast_channel *c);
static int jack_answer(struct ast_channel *c);
static struct ast_frame *jack_read(struct ast_channel *chan);
static int jack_call(struct ast_channel *c, char *dest, int timeout);
static int jack_write(struct ast_channel *chan, struct ast_frame *f);
static int jack_indicate(struct ast_channel *chan, int cond, const void
*data, size_t datalen);
static int jack_fixup(struct ast_channel *oldchan, struct ast_channel
*newchan);
static const struct ast_channel_tech jack_tech = {
.type = "Console",
.description = tdesc,
.capabilities = AST_FORMAT_SLINEAR,
.requester = jack_request,
.send_digit_end = jack_digit,
.send_text = jack_text,
.hangup = jack_hangup,
.answer = jack_answer,
.read = jack_read,
.call = jack_call,
.write = jack_write,
.indicate = jack_indicate,
.fixup = jack_fixup,
};
// jack customs start
************************************************************
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#define CHAN_JACK 1
#define RINGBUFFER_SIZE 16384
struct jack_data
{
char server_name[128];
char client_name[128];
char connect_input_port[128];
char connect_output_port[128];
jack_client_t *client;
jack_port_t *input_port;
jack_port_t *output_port;
jack_ringbuffer_t *input_rb;
jack_ringbuffer_t *output_rb;
void *output_resampler;
double output_resample_factor;
void *input_resampler;
double input_resample_factor;
unsigned int stop:1;
unsigned int no_start_server:1;
};
struct jack_data *jack_data;
static const struct {
jack_status_t status;
const char *str;
} jack_status_table[] = {
{ JackFailure, "Failure" },
{ JackInvalidOption, "Invalid Option" },
{ JackNameNotUnique, "Name Not Unique" },
{ JackServerStarted, "Server Started" },
{ JackServerFailed, "Server Failed" },
{ JackServerError, "Server Error" },
{ JackNoSuchClient, "No Such Client" },
{ JackLoadFailure, "Load Failure" },
{ JackInitFailure, "Init Failure" },
{ JackShmFailure, "Shared Memory Access Failure" },
{ JackVersionError, "Version Mismatch" },
};
static const char *jack_status_to_str(jack_status_t status)
{
int i;
for (i = 0; i < ARRAY_LEN(jack_status_table); i++) {
if (jack_status_table[i].status == status)
return jack_status_table[i].str;
}
return "Unknown Error";
}
static void log_jack_status(const char *prefix, jack_status_t status)
{
struct ast_str *str = ast_str_alloca(512);
int i, first = 0;
for (i = 0; i < (sizeof(status) * 8); i++) {
if (!(status & (1 << i)))
continue;
if (!first) {
ast_str_set(&str, 0, "%s", jack_status_to_str((1 <<
i)));
first = 1;
} else
ast_str_append(&str, 0, ", %s",
jack_status_to_str((1 << i)));
}
ast_log(LOG_NOTICE, "%s: %s\n", prefix, str->str);
}
/*!
* \brief Handle jack input port
*
* Read nframes number of samples from the input buffer, resample it
* if necessary, and write it into the appropriate ringbuffer.
*/
static void handle_input(void *buf, jack_nframes_t nframes)
{
if (hookstate == 1)
{
//ast_log(LOG_WARNING, "i\n");
short s_buf[nframes];
float *in_buf = buf;
size_t res;
int i;
size_t write_len = sizeof(s_buf);
for (i = 0; i < nframes; i++)
s_buf[i] = ((float) in_buf[i]) * ((float) SHRT_MAX);
res = jack_ringbuffer_write(jack_data->input_rb, (const char
*) s_buf, write_len);
if (res != write_len) {
ast_debug(2, "Tried to write %d bytes to the
ringbuffer, but only wrote %d\n",
(int) sizeof(s_buf), (int) res);
}
}
}
/*!
* \brief Handle jack output port
*
* Read nframes number of samples from the ringbuffer and write it out to
the
* output port buffer.
*/
static void handle_output(void *buf, jack_nframes_t nframes)
{
if (hookstate == 1)
{
//ast_log(LOG_WARNING, "o\n");
size_t res, len;
len = nframes * sizeof(float);
res = jack_ringbuffer_read(jack_data->output_rb, buf, len);
if (len != res) {
ast_debug(2, "Wanted %d bytes to send to the output
port, "
"but only got %d\n", (int) len, (int) res);
}
}
else
{
int i;
float * buf_f = (float *) buf;
for (i = 0; i < nframes; i++)
{
buf_f[i] = 0.0;
}
}
}
static int jack_process(jack_nframes_t nframes, void *arg)
{
if (jack.owner)
{
ast_log(LOG_WARNING, "p\n");
}
void *input_port_buf, *output_port_buf;
input_port_buf = jack_port_get_buffer(jack_data->input_port,
nframes);
handle_input(input_port_buf, nframes);
output_port_buf = jack_port_get_buffer(jack_data->output_port,
nframes);
handle_output(output_port_buf, nframes);
return 0;
}
static void jack_shutdown()
{
jack_data->stop = 1;
}
static struct jack_data *jack_data_alloc(void)
{
if (!(jack_data = ast_calloc(1, sizeof(struct jack_data))))
return NULL;
return jack_data;
}
static struct jack_data *destroy_jack_data()
{
if (jack_data == NULL)
{
return NULL;
}
if (jack_data->input_port) {
jack_port_unregister(jack_data->client,
jack_data->input_port);
jack_data->input_port = NULL;
}
if (jack_data->output_port) {
jack_port_unregister(jack_data->client,
jack_data->output_port);
jack_data->output_port = NULL;
}
if (jack_data->client) {
jack_client_close(jack_data->client);
jack_data->client = NULL;
}
if (jack_data->input_rb) {
jack_ringbuffer_free(jack_data->input_rb);
jack_data->input_rb = NULL;
}
if (jack_data->output_rb) {
jack_ringbuffer_free(jack_data->output_rb);
jack_data->output_rb = NULL;
}
//ast_string_field_free_memory(jack_data);
ast_free(jack_data);
return NULL;
}
// jack customs end
************************************************************
static int jack_init(void)
{
jack_status_t status = 0;
jack_options_t jack_options = JackNullOption;
if (!(jack_data->output_rb = jack_ringbuffer_create(RINGBUFFER_SIZE
* 2)))
return -1;
if (!(jack_data->input_rb =
jack_ringbuffer_create(RINGBUFFER_SIZE)))
return -1;
if (jack_data->no_start_server)
jack_options |= JackNoStartServer;
if (!ast_strlen_zero(jack_data->server_name)) {
jack_options |= JackServerName;
jack_data->client = jack_client_open(jack_data->client_name,
jack_options, &status,
jack_data->server_name);
} else {
jack_data->client = jack_client_open(jack_data->client_name,
jack_options, &status);
}
if (status)
log_jack_status("Client Open Status", status);
if (!jack_data->client)
return -1;
jack_data->input_port = jack_port_register(jack_data->client,
"input",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput |
JackPortIsTerminal, 0);
if (!jack_data->input_port) {
ast_log(LOG_ERROR, "Failed to create input port for jack
port\n");
return -1;
}
jack_data->output_port = jack_port_register(jack_data->client,
"output",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput |
JackPortIsTerminal, 0);
if (!jack_data->output_port) {
ast_log(LOG_ERROR, "Failed to create output port for jack
port\n");
return -1;
}
if (jack_set_process_callback(jack_data->client, jack_process, (void
*) NULL)) {
ast_log(LOG_ERROR, "Failed to register process callback with
jack client\n");
return -1;
}
jack_on_shutdown(jack_data->client, jack_shutdown, NULL);
if (jack_activate(jack_data->client)) {
ast_log(LOG_ERROR, "Unable to activate jack client\n");
return -1;
}
while (!ast_strlen_zero(jack_data->connect_input_port)) {
const char **ports;
int i;
ports = jack_get_ports(jack_data->client,
jack_data->connect_input_port,
NULL, JackPortIsInput);
if (!ports) {
ast_log(LOG_ERROR, "No input port matching '%s' was
found\n",
jack_data->connect_input_port);
break;
}
for (i = 0; ports[i]; i++) {
ast_debug(1, "Found port '%s' that matched specified
input port '%s'\n",
ports[i], jack_data->connect_input_port);
}
if (jack_connect(jack_data->client,
jack_port_name(jack_data->output_port), ports[0])) {
ast_log(LOG_ERROR, "Failed to connect '%s' to
'%s'\n", ports[0],
jack_port_name(jack_data->output_port));
} else {
ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
jack_port_name(jack_data->output_port));
}
free((void *) ports);
break;
}
while (!ast_strlen_zero(jack_data->connect_output_port)) {
const char **ports;
int i;
ports = jack_get_ports(jack_data->client,
jack_data->connect_output_port,
NULL, JackPortIsOutput);
if (!ports) {
ast_log(LOG_ERROR, "No output port matching '%s' was
found\n",
jack_data->connect_output_port);
break;
}
for (i = 0; ports[i]; i++) {
ast_debug(1, "Found port '%s' that matched specified
output port '%s'\n",
ports[i], jack_data->connect_output_port);
}
if (jack_connect(jack_data->client, ports[0],
jack_port_name(jack_data->input_port))) {
ast_log(LOG_ERROR, "Failed to connect '%s' to
'%s'\n", ports[0],
jack_port_name(jack_data->input_port));
} else {
ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
jack_port_name(jack_data->input_port));
}
free((void *) ports);
break;
}
return 0;
}
static int jack_digit(struct ast_channel *c, char digit, unsigned int
duration)
{
ast_mutex_lock(&jacklock);
ast_verbose(" << Console Received digit %c of duration %u ms >> \n",
digit, duration);
ast_mutex_unlock(&jacklock);
return 0;
}
static int jack_text(struct ast_channel *c, const char *text)
{
ast_mutex_lock(&jacklock);
ast_verbose(" << Console Received text %s >> \n", text);
ast_mutex_unlock(&jacklock);
return 0;
}
static void grab_owner(void)
{
while (jack.owner && ast_channel_trylock(jack.owner)) {
DEADLOCK_AVOIDANCE(&jacklock);
}
}
static int jack_call(struct ast_channel *c, char *dest, int timeout)
{
struct ast_frame f = { AST_FRAME_CONTROL };
ast_mutex_lock(&jacklock);
ast_verbose(" << Call placed to '%s' on console >> \n", dest);
if (autoanswer) {
ast_verbose(" << Auto-answered >> \n");
grab_owner();
if (jack.owner) {
f.subclass = AST_CONTROL_ANSWER;
ast_queue_frame(jack.owner, &f);
ast_channel_unlock(jack.owner);
}
} else {
ast_verbose(" << Type 'answer' to answer, or use
'autoanswer' for future calls >> \n");
grab_owner();
if (jack.owner) {
f.subclass = AST_CONTROL_RINGING;
ast_queue_frame(jack.owner, &f);
ast_channel_unlock(jack.owner);
ast_indicate(jack.owner, AST_CONTROL_RINGING);
}
}
# if CHAN_JACK
// TODO
# else
snd_pcm_prepare(jack.icard);
snd_pcm_start(jack.icard);
# endif
ast_mutex_unlock(&jacklock);
return 0;
}
static int jack_answer(struct ast_channel *c)
{
ast_mutex_lock(&jacklock);
ast_verbose(" << Console call has been answered >> \n");
ast_setstate(c, AST_STATE_UP);
# if CHAN_JACK
// TODO
# else
snd_pcm_prepare(jack.icard);
snd_pcm_start(jack.icard);
# endif
ast_mutex_unlock(&jacklock);
return 0;
}
static int jack_hangup(struct ast_channel *c)
{
ast_mutex_lock(&jacklock);
c->tech_pvt = NULL;
jack.owner = NULL;
ast_verbose(" << Hangup on console >> \n");
ast_module_unref(ast_module_info->self);
hookstate = 0;
# if CHAN_JACK
// TODO
# else
snd_pcm_drop(jack.icard);
ast_mutex_unlock(&jacklock);
# endif
return 0;
}
static int jack_write(struct ast_channel *chan, struct ast_frame *f)
{
ast_log(LOG_WARNING, "w\n");
ast_mutex_lock(&jacklock);
float f_buf[f->samples];
size_t f_buf_used = 0;
int i;
int16_t *s_buf = f->data.ptr;
size_t res;
for (i = 0; i < f->samples; i++)
{
f_buf[i] = ((float) s_buf[i]) / ((float) SHRT_MAX);
}
f_buf_used = f->samples;
res = jack_ringbuffer_write(jack_data->output_rb, (const char *)
f_buf, f_buf_used * sizeof(float));
if (res != (f_buf_used * sizeof(float)))
{
ast_debug(2, "Tried to write %d bytes to the ringbuffer, but
only wrote %d\n", (int) (f_buf_used * sizeof(float)), (int) res);
}
ast_mutex_unlock(&jacklock);
return 0;
}
static struct ast_frame *jack_read(struct ast_channel *chan)
{
ast_log(LOG_WARNING, "r\n");
ast_mutex_lock(&jacklock);
static short buf[FRAME_SIZE];
static struct ast_frame f;
f.frametype = AST_FRAME_NULL;
f.subclass = 0;
f.samples = 0;
f.datalen = 0;
f.data.ptr = NULL;
f.offset = 0;
f.src = "Console";
f.mallocd = 0;
f.delivery.tv_sec = 0;
f.delivery.tv_usec = 0;
size_t res;
res = jack_ringbuffer_read_space(jack_data->input_rb);
if (res < FRAME_SIZE) {
/* Not enough data ready for another frame, move on ... */
ast_debug(1, "Sending an empty frame for JACK\n");
memset(buf, 0, FRAME_SIZE);
}
else
{
res = jack_ringbuffer_read(jack_data->input_rb, (char *)
buf, FRAME_SIZE);
if (res < FRAME_SIZE) {
ast_log(LOG_ERROR, "Error reading from ringbuffer,
even though it said there was enough data\n");
}
}
f.frametype = AST_FRAME_VOICE;
f.subclass = AST_FORMAT_SLINEAR;
f.samples = FRAME_SIZE;
f.datalen = FRAME_SIZE * 2;
f.data.ptr = buf;
f.offset = 0;
f.src = "Console";
f.mallocd = 0;
ast_mutex_unlock(&jacklock);
return &f;
}
static int jack_fixup(struct ast_channel *oldchan, struct ast_channel
*newchan)
{
struct chan_jack_pvt *p = newchan->tech_pvt;
ast_mutex_lock(&jacklock);
p->owner = newchan; // TODO
ast_mutex_unlock(&jacklock);
return 0;
}
static int jack_indicate(struct ast_channel *chan, int cond, const void
*data, size_t datalen)
{
int res = 0;
ast_mutex_lock(&jacklock);
switch (cond) {
case AST_CONTROL_BUSY:
case AST_CONTROL_CONGESTION:
case AST_CONTROL_RINGING:
case -1:
res = -1; /* Ask for inband indications */
break;
case AST_CONTROL_PROGRESS:
case AST_CONTROL_PROCEEDING:
case AST_CONTROL_VIDUPDATE:
case AST_CONTROL_SRCUPDATE:
break;
case AST_CONTROL_HOLD:
ast_verbose(" << Console Has Been Placed on Hold >> \n");
ast_moh_start(chan, data, mohinterpret);
break;
case AST_CONTROL_UNHOLD:
ast_verbose(" << Console Has Been Retrieved from Hold >>
\n");
ast_moh_stop(chan);
break;
default:
ast_log(LOG_WARNING, "Don't know how to display condition %d
on %s\n", cond, chan->name);
res = -1;
}
ast_mutex_unlock(&jacklock);
return res;
}
static struct ast_channel *jack_new(struct chan_jack_pvt *p, int state)
{
struct ast_channel *tmp = NULL;
if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten,
p->context, 0, "JACK/%s", indevname)))
{
ast_log(LOG_ERROR, "ast_channel_alloc() failed\n");
return NULL;
}
tmp->tech = &jack_tech;
ast_channel_set_fd(tmp, 0, readdev); // TODO how to set readdev
tmp->nativeformats = AST_FORMAT_SLINEAR;
tmp->readformat = AST_FORMAT_SLINEAR;
tmp->writeformat = AST_FORMAT_SLINEAR;
tmp->tech_pvt = p;
if (!ast_strlen_zero(p->context))
ast_copy_string(tmp->context, p->context,
sizeof(tmp->context));
if (!ast_strlen_zero(p->exten))
ast_copy_string(tmp->exten, p->exten, sizeof(tmp->exten));
if (!ast_strlen_zero(language))
ast_string_field_set(tmp, language, language);
p->owner = tmp;
ast_module_ref(ast_module_info->self);
ast_jb_configure(tmp, &global_jbconf);
if (state != AST_STATE_DOWN) {
if (ast_pbx_start(tmp)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n",
tmp->name);
ast_hangup(tmp);
tmp = NULL;
}
}
return tmp;
}
static struct ast_channel *jack_request(const char *type, int fmt, void
*data, int *cause)
{
int oldformat = fmt;
struct ast_channel *tmp = NULL;
if (!(fmt &= AST_FORMAT_SLINEAR)) {
ast_log(LOG_NOTICE, "Asked to get a channel of format
'%d'\n", oldformat);
return NULL;
}
ast_mutex_lock(&jacklock);
if (jack.owner) {
ast_log(LOG_NOTICE, "Already have a call on the JACK
channel\n");
*cause = AST_CAUSE_BUSY;
} else if (!(tmp = jack_new(&jack, AST_STATE_DOWN))) {
ast_log(LOG_WARNING, "Unable to create new JACK channel\n");
}
ast_mutex_unlock(&jacklock);
ast_log(LOG_WARNING, "jack_request() OK\n");
return tmp;
}
static char *autoanswer_complete(const char *line, const char *word, int
pos, int state)
{
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
switch (state) {
case 0:
if (!ast_strlen_zero(word) && !strncasecmp(word,
"on", MIN(strlen(word), 2)))
return ast_strdup("on");
case 1:
if (!ast_strlen_zero(word) && !strncasecmp(word,
"off", MIN(strlen(word), 3)))
return ast_strdup("off");
default:
return NULL;
}
return NULL;
}
static char *console_autoanswer(struct ast_cli_entry *e, int cmd, struct
ast_cli_args *a)
{
char *res = CLI_SUCCESS;
switch (cmd) {
case CLI_INIT:
e->command = "console autoanswer";
e->usage =
"Usage: console autoanswer [on|off]\n"
" Enables or disables autoanswer feature. If
used without\n"
" argument, displays the current on/off status
of autoanswer.\n"
" The default value of autoanswer is in
'jack.conf'.\n";
return NULL;
case CLI_GENERATE:
return autoanswer_complete(a->line, a->word, a->pos, a->n);
}
if ((a->argc != 2) && (a->argc != 3))
return CLI_SHOWUSAGE;
ast_mutex_lock(&jacklock);
if (a->argc == 2) {
ast_cli(a->fd, "Auto answer is %s.\n", autoanswer ? "on" :
"off");
} else {
if (!strcasecmp(a->argv[2], "on"))
autoanswer = -1;
else if (!strcasecmp(a->argv[2], "off"))
autoanswer = 0;
else
res = CLI_SHOWUSAGE;
}
ast_mutex_unlock(&jacklock);
return res;
}
static char *console_answer(struct ast_cli_entry *e, int cmd, struct
ast_cli_args *a)
{
char *res = CLI_SUCCESS;
switch (cmd) {
case CLI_INIT:
e->command = "console answer";
e->usage =
"Usage: console answer\n"
" Answers an incoming call on the console
(JACK) channel.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 2)
return CLI_SHOWUSAGE;
ast_mutex_lock(&jacklock);
if (!jack.owner) {
ast_cli(a->fd, "No one is calling us\n");
res = CLI_FAILURE;
} else {
hookstate = 1;
grab_owner();
if (jack.owner) {
struct ast_frame f = { AST_FRAME_CONTROL,
AST_CONTROL_ANSWER };
ast_queue_frame(jack.owner, &f);
ast_channel_unlock(jack.owner);
}
}
# if CHAN_JACK
// TODO
# else
snd_pcm_prepare(jack.icard);
snd_pcm_start(jack.icard);
# endif
ast_mutex_unlock(&jacklock);
return res;
}
static char *console_sendtext(struct ast_cli_entry *e, int cmd, struct
ast_cli_args *a)
{
int tmparg = 3;
char *res = CLI_SUCCESS;
switch (cmd) {
case CLI_INIT:
e->command = "console send text";
e->usage =
"Usage: console send text <message>\n"
" Sends a text message for display on the
remote terminal.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc < 3)
return CLI_SHOWUSAGE;
ast_mutex_lock(&jacklock);
if (!jack.owner) {
ast_cli(a->fd, "No channel active\n");
res = CLI_FAILURE;
} else {
struct ast_frame f = { AST_FRAME_TEXT, 0 };
char text2send[256] = "";
while (tmparg < a->argc) {
strncat(text2send, a->argv[tmparg++],
sizeof(text2send) - strlen(text2send) - 1);
strncat(text2send, " ", sizeof(text2send) -
strlen(text2send) - 1);
}
text2send[strlen(text2send) - 1] = '\n';
f.data.ptr = text2send;
f.datalen = strlen(text2send) + 1;
grab_owner();
if (jack.owner) {
ast_queue_frame(jack.owner, &f);
f.frametype = AST_FRAME_CONTROL;
f.subclass = AST_CONTROL_ANSWER;
f.data.ptr = NULL;
f.datalen = 0;
ast_queue_frame(jack.owner, &f);
ast_channel_unlock(jack.owner);
}
}
ast_mutex_unlock(&jacklock);
return res;
}
static char *console_hangup(struct ast_cli_entry *e, int cmd, struct
ast_cli_args *a)
{
char *res = CLI_SUCCESS;
switch (cmd) {
case CLI_INIT:
e->command = "console hangup";
e->usage =
"Usage: console hangup\n"
" Hangs up any call currently placed on the
console.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 2)
return CLI_SHOWUSAGE;
ast_mutex_lock(&jacklock);
if (!jack.owner && !hookstate) {
ast_cli(a->fd, "No call to hangup\n");
res = CLI_FAILURE;
} else {
hookstate = 0;
grab_owner();
if (jack.owner) {
ast_queue_hangup_with_cause(jack.owner,
AST_CAUSE_NORMAL_CLEARING);
ast_channel_unlock(jack.owner);
}
}
ast_mutex_unlock(&jacklock);
return res;
}
static char *console_dial(struct ast_cli_entry *e, int cmd, struct
ast_cli_args *a)
{
char tmp[256], *tmp2;
char *mye, *myc;
char *d;
char *res = CLI_SUCCESS;
switch (cmd) {
case CLI_INIT:
e->command = "console dial";
e->usage =
"Usage: console dial [extension[@context]]\n"
" Dials a given extension (and context if
specified)\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if ((a->argc != 2) && (a->argc != 3))
return CLI_SHOWUSAGE;
ast_mutex_lock(&jacklock);
if (jack.owner) {
if (a->argc == 3) {
if (jack.owner) {
for (d = a->argv[2]; *d; d++) {
struct ast_frame f = { .frametype =
AST_FRAME_DTMF, .subclass = *d };
ast_queue_frame(jack.owner, &f);
}
}
} else {
ast_cli(a->fd, "You're already in a call. You can
use this only to dial digits until you hangup\n");
res = CLI_FAILURE;
}
} else {
mye = exten;
myc = context;
if (a->argc == 3) {
char *stringp = NULL;
ast_copy_string(tmp, a->argv[2], sizeof(tmp));
stringp = tmp;
strsep(&stringp, "@");
tmp2 = strsep(&stringp, "@");
if (!ast_strlen_zero(tmp))
mye = tmp;
if (!ast_strlen_zero(tmp2))
myc = tmp2;
}
if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
ast_copy_string(jack.exten, mye,
sizeof(jack.exten));
ast_copy_string(jack.context, myc,
sizeof(jack.context));
hookstate = 1;
jack_new(&jack, AST_STATE_RINGING);
} else
ast_cli(a->fd, "No such extension '%s' in context
'%s'\n", mye, myc);
}
ast_mutex_unlock(&jacklock);
return res;
}
static struct ast_cli_entry cli_jack[] = {
AST_CLI_DEFINE(console_answer, "Answer an incoming console call"),
AST_CLI_DEFINE(console_hangup, "Hangup a call on the console"),
AST_CLI_DEFINE(console_dial, "Dial an extension on the console"),
AST_CLI_DEFINE(console_sendtext, "Send text to the remote device"),
AST_CLI_DEFINE(console_autoanswer, "Sets/displays autoanswer"),
};
static int load_module(void)
{
struct ast_config *cfg;
struct ast_variable *v;
struct ast_flags config_flags = { 0 };
/* Copy the default jb config over global_jbconf */
memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
strcpy(mohinterpret, "default");
if (!(cfg = ast_config_load(config, config_flags))) {
ast_log(LOG_ERROR, "Unable to read JACK configuration file
%s. Aborting.\n", config);
return AST_MODULE_LOAD_DECLINE;
}
if (!(jack_data = jack_data_alloc()))
{
ast_verb(2, "jack_data_alloc() failed\n");
return AST_MODULE_LOAD_FAILURE;
}
if (readdev == -1)
{
// TODO
//readdev = open("/dev/null", O_RDWR); // fake fd...
}
v = ast_variable_browse(cfg, "general");
for (; v; v = v->next) {
/* handle jb conf */
if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
continue;
if (!strcasecmp(v->name, "autoanswer"))
autoanswer = ast_true(v->value);
else if (!strcasecmp(v->name, "silencesuppression"))
silencesuppression = ast_true(v->value);
else if (!strcasecmp(v->name, "silencethreshold"))
silencethreshold = atoi(v->value);
else if (!strcasecmp(v->name, "context"))
ast_copy_string(context, v->value, sizeof(context));
else if (!strcasecmp(v->name, "language"))
ast_copy_string(language, v->value,
sizeof(language));
else if (!strcasecmp(v->name, "extension"))
ast_copy_string(exten, v->value, sizeof(exten));
else if (!strcasecmp(v->name, "input_device"))
ast_copy_string(indevname, v->value,
sizeof(indevname));
else if (!strcasecmp(v->name, "output_device"))
ast_copy_string(outdevname, v->value,
sizeof(outdevname));
else if (!strcasecmp(v->name, "mohinterpret"))
ast_copy_string(mohinterpret, v->value,
sizeof(mohinterpret));
else if (!strcasecmp(v->name, "server_name"))
ast_copy_string(jack_data->server_name, v->value,
128);
else if (!strcasecmp(v->name, "client_name"))
ast_copy_string(jack_data->client_name, v->value,
128);
else if (!strcasecmp(v->name, "connect_input_port"))
ast_copy_string(jack_data->connect_input_port,
v->value, 128);
else if (!strcasecmp(v->name, "connect_output_port"))
ast_copy_string(jack_data->connect_output_port,
v->value, 128);
}
ast_config_destroy(cfg);
if (jack_init() < 0) {
ast_verb(2, "No sound card detected -- console channel will
be unavailable\n");
ast_verb(2, "Turn off JACK support by adding
'noload=chan_jack.so' in /etc/asterisk/modules.conf\n");
jack_data = destroy_jack_data();
return AST_MODULE_LOAD_DECLINE;
}
if (ast_channel_register(&jack_tech)) {
ast_log(LOG_ERROR, "Unable to register channel class
'Console'\n");
jack_data = destroy_jack_data();
return AST_MODULE_LOAD_FAILURE;
}
ast_cli_register_multiple(cli_jack, sizeof(cli_jack) / sizeof(struct
ast_cli_entry));
ast_log(LOG_ERROR, "load_module() OK\n");
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
ast_channel_unregister(&jack_tech);
ast_cli_unregister_multiple(cli_jack, sizeof(cli_jack) /
sizeof(struct ast_cli_entry));
jack_data = destroy_jack_data();
if (jack.owner)
ast_softhangup(jack.owner, AST_SOFTHANGUP_APPUNLOAD);
if (readdev != -1)
{
close(readdev);
readdev = -1;
}
if (jack.owner)
return -1;
return 0;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "JACK Console Channel Driver");
More information about the asterisk-dev
mailing list