Newer
Older
if (cmd == 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;
} else if (cmd == CLI_GENERATE)
return NULL;
if (a->argc < e->args + 1)
return CLI_SHOWUSAGE;
ast_cli(a->fd, "Not in a call\n");
return CLI_FAILURE;
ast_join(buf, sizeof(buf) - 1, a->argv + e->args);
if (!ast_strlen_zero(buf)) {
struct ast_frame f = { 0, };
int i = strlen(buf);
buf[i] = '\n';
f.frametype = AST_FRAME_TEXT;
f.subclass.integer = 0;
Michiel van Baak
committed
f.data.ptr = buf;
f.datalen = i + 1;
ast_queue_frame(o->owner, &f);
}
static char *console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct chan_oss_pvt *o = find_desc(oss_active);
Mark Spencer
committed
if (cmd == CLI_INIT) {
e->command = "console hangup";
e->usage =
"Usage: console hangup\n"
" Hangs up any call currently placed on the console.\n";
return NULL;
} else if (cmd == CLI_GENERATE)
return NULL;
if (a->argc != e->args)
return CLI_SHOWUSAGE;
if (!o->owner && !o->hookstate) { /* XXX maybe only one ? */
ast_cli(a->fd, "No call to hang up\n");
return CLI_FAILURE;
}
o->hookstate = 0;
if (o->owner)
Michiel van Baak
committed
ast_queue_hangup_with_cause(o->owner, AST_CAUSE_NORMAL_CLEARING);
Mark Spencer
committed
static char *console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
struct ast_frame f = { AST_FRAME_CONTROL, { AST_CONTROL_FLASH } };
struct chan_oss_pvt *o = find_desc(oss_active);
if (cmd == CLI_INIT) {
e->command = "console flash";
e->usage =
"Usage: console flash\n"
" Flashes the call currently placed on the console.\n";
return NULL;
} else if (cmd == CLI_GENERATE)
return NULL;
if (a->argc != e->args)
return CLI_SHOWUSAGE;
if (!o->owner) { /* XXX maybe !o->hookstate too ? */
ast_cli(a->fd, "No call to flash\n");
return CLI_FAILURE;
Mark Spencer
committed
o->hookstate = 0;
Mark Spencer
committed
ast_queue_frame(o->owner, &f);
static char *console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
char *s = NULL;
char *mye = NULL, *myc = NULL;
struct chan_oss_pvt *o = find_desc(oss_active);
if (cmd == 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;
} else if (cmd == CLI_GENERATE)
return NULL;
if (a->argc > e->args + 1)
return CLI_SHOWUSAGE;
if (o->owner) { /* already in a call */
int i;
struct ast_frame f = { AST_FRAME_DTMF, { 0 } };
Mark Spencer
committed
if (a->argc == e->args) { /* argument is mandatory here */
ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n");
return CLI_FAILURE;
/* send the string one char at a time */
for (i = 0; i < strlen(digits); i++) {
f.subclass.integer = digits[i];
ast_queue_frame(o->owner, &f);
}
}
/* if we have an argument split it into extension and context */
if (a->argc == e->args + 1)
s = ast_ext_ctx(a->argv[e->args], &mye, &myc);
/* supply default values if needed */
if (mye == NULL)
mye = o->ext;
if (myc == NULL)
myc = o->ctx;
if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
o->hookstate = 1;
oss_new(o, mye, myc, AST_STATE_RINGING, NULL);
ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
Tilghman Lesher
committed
ast_free(s);
Mark Spencer
committed
static char *console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Mark Spencer
committed
{
struct chan_oss_pvt *o = find_desc(oss_active);
int toggle = 0;
e->command = "console {mute|unmute} [toggle]";
"Usage: console {mute|unmute} [toggle]\n"
" Mute/unmute the microphone.\n";
return NULL;
} else if (cmd == CLI_GENERATE)
if (a->argc > e->args)
if (a->argc == e->args) {
if (strcasecmp(a->argv[e->args-1], "toggle"))
return CLI_SHOWUSAGE;
toggle = 1;
}
s = a->argv[e->args-2];
ast_cli(a->fd, "Console mic is %s\n", o->mute ? "off" : "on");
static char *console_transfer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Mark Spencer
committed
struct chan_oss_pvt *o = find_desc(oss_active);
struct ast_channel *b = NULL;
char *tmp, *ext, *ctx;
switch (cmd) {
case CLI_INIT:
e->command = "console transfer";
e->usage =
"Usage: console transfer <extension>[@context]\n"
" Transfers the currently connected call to the given extension (and\n"
" context if specified)\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 3)
return CLI_SHOWUSAGE;
Mark Spencer
committed
if (o == NULL)
return CLI_FAILURE;
if (o->owner == NULL || (b = ast_bridged_channel(o->owner)) == NULL) {
ast_cli(a->fd, "There is no call to transfer\n");
return CLI_SUCCESS;
Mark Spencer
committed
}
tmp = ast_ext_ctx(a->argv[2], &ext, &ctx);
if (ctx == NULL) /* supply default context if needed */
ctx = ast_strdupa(ast_channel_context(o->owner));
S_COR(ast_channel_caller(b)->id.number.valid, ast_channel_caller(b)->id.number.str, NULL))) {
ast_cli(a->fd, "No such extension exists\n");
ast_cli(a->fd, "Whee, transferring %s to %s@%s.\n", ast_channel_name(b), ext, ctx);
Mark Spencer
committed
if (ast_async_goto(b, ctx, ext, 1))
ast_cli(a->fd, "Failed to transfer :(\n");
Mark Spencer
committed
if (tmp)
Tilghman Lesher
committed
ast_free(tmp);
return CLI_SUCCESS;
static char *console_active(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
switch (cmd) {
case CLI_INIT:
e->command = "console {set|show} active [<device>]";
e->usage =
"Usage: console active [device]\n"
" If used without a parameter, displays which device is the current\n"
" console. If a device is specified, the console sound device is changed to\n"
" the device specified.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
ast_cli(a->fd, "active console is [%s]\n", oss_active);
return CLI_SHOWUSAGE;
else {
struct chan_oss_pvt *o;
if (strcmp(a->argv[3], "show") == 0) {
for (o = oss_default.next; o; o = o->next)
ast_cli(a->fd, "device [%s] exists\n", o->name);
return CLI_SUCCESS;
ast_cli(a->fd, "No device [%s] exists\n", a->argv[3]);
else
oss_active = o->name;
}
return CLI_SUCCESS;
/*!
* \brief store the boost factor
*/
static void store_boost(struct chan_oss_pvt *o, const char *s)
{
double boost = 0;
ast_log(LOG_WARNING, "invalid boost <%s>\n", s);
return;
}
if (boost < -BOOST_MAX) {
ast_log(LOG_WARNING, "boost %s too small, using %d\n", s, -BOOST_MAX);
boost = -BOOST_MAX;
} else if (boost > BOOST_MAX) {
ast_log(LOG_WARNING, "boost %s too large, using %d\n", s, BOOST_MAX);
boost = BOOST_MAX;
}
boost = exp(log(10) * boost / 20) * BOOST_SCALE;
o->boost = boost;
ast_log(LOG_WARNING, "setting boost %s to %d\n", s, o->boost);
}
static char *console_boost(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct chan_oss_pvt *o = find_desc(oss_active);
switch (cmd) {
case CLI_INIT:
e->command = "console boost";
e->usage =
"Usage: console boost [boost in dB]\n"
" Sets or display mic boost in dB\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc == 2)
ast_cli(a->fd, "boost currently %5.1f\n", 20 * log10(((double) o->boost / (double) BOOST_SCALE)));
else if (a->argc == 3)
store_boost(o, a->argv[2]);
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_oss[] = {
Jason Parker
committed
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_flash, "Flash a call on the console"),
AST_CLI_DEFINE(console_dial, "Dial an extension on the console"),
AST_CLI_DEFINE(console_mute, "Disable/Enable mic input"),
AST_CLI_DEFINE(console_transfer, "Transfer a call to a different extension"),
AST_CLI_DEFINE(console_cmd, "Generic console command"),
Jason Parker
committed
AST_CLI_DEFINE(console_sendtext, "Send text to the remote device"),
AST_CLI_DEFINE(console_autoanswer, "Sets/displays autoanswer"),
AST_CLI_DEFINE(console_boost, "Sets/displays mic boost in dB"),
AST_CLI_DEFINE(console_active, "Sets/displays active console"),
Mark Spencer
committed
* store the mixer argument from the config file, filtering possibly
* invalid or dangerous values (the string is used as argument for
* system("mixer %s")
*/
static void store_mixer(struct chan_oss_pvt *o, const char *s)
Mark Spencer
committed
int i;
for (i = 0; i < strlen(s); i++) {
if (!isalnum(s[i]) && strchr(" \t-/", s[i]) == NULL) {
ast_log(LOG_WARNING, "Suspect char %c in mixer cmd, ignoring:\n\t%s\n", s[i], s);
Mark Spencer
committed
return;
}
Mark Spencer
committed
if (o->mixer_cmd)
Tilghman Lesher
committed
ast_free(o->mixer_cmd);
o->mixer_cmd = ast_strdup(s);
Mark Spencer
committed
ast_log(LOG_WARNING, "setting mixer %s\n", s);
}
static void store_callerid(struct chan_oss_pvt *o, const char *s)
{
ast_callerid_split(s, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num));
}
static void store_config_core(struct chan_oss_pvt *o, const char *var, const char *value)
{
CV_START(var, value);
/* handle jb conf */
return;
if (!console_video_config(&o->env, var, value))
return; /* matched there */
CV_BOOL("autoanswer", o->autoanswer);
CV_BOOL("autohangup", o->autohangup);
CV_BOOL("overridecontext", o->overridecontext);
CV_STR("device", o->device);
CV_UINT("frags", o->frags);
CV_UINT("debug", oss_debug);
CV_UINT("queuesize", o->queuesize);
CV_STR("context", o->ctx);
CV_STR("language", o->language);
CV_STR("mohinterpret", o->mohinterpret);
CV_STR("extension", o->ext);
CV_F("mixer", store_mixer(o, value));
CV_F("callerid", store_callerid(o, value)) ;
CV_F("boost", store_boost(o, value));
CV_END;
}
Mark Spencer
committed
* grab fields from the config file, init the descriptor and open the device.
*/
static struct chan_oss_pvt *store_config(struct ast_config *cfg, char *ctg)
Mark Spencer
committed
{
struct ast_variable *v;
struct chan_oss_pvt *o;
if (ctg == NULL) {
o = &oss_default;
ctg = "general";
} else {
if (!(o = ast_calloc(1, sizeof(*o))))
Mark Spencer
committed
return NULL;
*o = oss_default;
/* "general" is also the default thing */
if (strcmp(ctg, "general") == 0) {
o->name = ast_strdup("dsp");
Mark Spencer
committed
oss_active = o->name;
goto openit;
o->name = ast_strdup(ctg);
Mark Spencer
committed
Kevin P. Fleming
committed
strcpy(o->mohinterpret, "default");
o->lastopen = ast_tvnow(); /* don't leave it 0 or tvdiff may wrap */
Mark Spencer
committed
/* fill other fields from configuration */
for (v = ast_variable_browse(cfg, ctg); v; v = v->next) {
store_config_core(o, v->name, v->value);
Mark Spencer
committed
}
if (ast_strlen_zero(o->device))
ast_copy_string(o->device, DEV_DSP, sizeof(o->device));
if (o->mixer_cmd) {
char *cmd;
if (ast_asprintf(&cmd, "mixer %s", o->mixer_cmd) >= 0) {
Kevin P. Fleming
committed
ast_log(LOG_WARNING, "running [%s]\n", cmd);
if (system(cmd) < 0) {
ast_log(LOG_WARNING, "system() failed: %s\n", strerror(errno));
}
ast_free(cmd);
}
/* if the config file requested to start the GUI, do it */
if (get_gui_startup(o->env))
console_video_start(o->env, NULL);
if (o == &oss_default) /* we are done with the default */
Mark Spencer
committed
return NULL;
Kevin P. Fleming
committed
openit:
Kevin P. Fleming
committed
#ifdef TRYOPEN
Mark Spencer
committed
if (setformat(o, O_RDWR) < 0) { /* open device */
ast_verb(1, "Device %s not detected\n", ctg);
ast_verb(1, "Turn off OSS support by adding " "'noload=chan_oss.so' in /etc/asterisk/modules.conf\n");
Mark Spencer
committed
goto error;
}
if (o->duplex != M_FULL)
ast_log(LOG_WARNING, "XXX I don't work right with non " "full-duplex sound cards XXX\n");
Mark Spencer
committed
#endif /* TRYOPEN */
Kevin P. Fleming
committed
Mark Spencer
committed
/* link into list of devices */
if (o != &oss_default) {
o->next = oss_default.next;
oss_default.next = o;
}
return o;
Kevin P. Fleming
committed
#ifdef TRYOPEN
error:
Mark Spencer
committed
if (o != &oss_default)
Tilghman Lesher
committed
ast_free(o);
Mark Spencer
committed
return NULL;
Kevin P. Fleming
committed
#endif
Mark Spencer
committed
}
static int load_module(void)
Mark Spencer
committed
{
struct ast_config *cfg = NULL;
char *ctg = NULL;
struct ast_flags config_flags = { 0 };
struct ast_format tmpfmt;
Mark Spencer
committed
Russell Bryant
committed
/* Copy the default jb config over global_jbconf */
memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
Mark Spencer
committed
/* load config file */
if (!(cfg = ast_config_load(config, config_flags))) {
ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
return AST_MODULE_LOAD_DECLINE;
Tilghman Lesher
committed
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", config);
return AST_MODULE_LOAD_DECLINE;
do {
store_config(cfg, ctg);
} while ( (ctg = ast_category_browse(cfg, ctg)) != NULL);
ast_config_destroy(cfg);
Mark Spencer
committed
if (find_desc(oss_active) == NULL) {
ast_log(LOG_NOTICE, "Device %s not found\n", oss_active);
/* XXX we could default to 'dsp' perhaps ? */
/* XXX should cleanup allocated memory etc. */
Mark Spencer
committed
}
if (!(oss_tech.capabilities = ast_format_cap_alloc())) {
return AST_MODULE_LOAD_FAILURE;
}
ast_format_cap_add(oss_tech.capabilities, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
/* TODO XXX CONSOLE VIDEO IS DISABLE UNTIL IT HAS A MAINTAINER
* add console_video_formats to oss_tech.capabilities once this occurs. */
if (ast_channel_register(&oss_tech)) {
ast_log(LOG_ERROR, "Unable to register channel type 'OSS'\n");
return AST_MODULE_LOAD_DECLINE;
Mark Spencer
committed
}
ast_cli_register_multiple(cli_oss, ARRAY_LEN(cli_oss));
return AST_MODULE_LOAD_SUCCESS;
static int unload_module(void)
ast_channel_unregister(&oss_tech);
ast_cli_unregister_multiple(cli_oss, ARRAY_LEN(cli_oss));
Mark Spencer
committed
Mark Spencer
committed
close(o->sounddev);
if (o->owner)
ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD);
Mark Spencer
committed
return -1;
next = o->next;
ast_free(o->name);
ast_free(o);
o = next;
oss_tech.capabilities = ast_format_cap_destroy(oss_tech.capabilities);
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "OSS Console Channel Driver");