Skip to content
Snippets Groups Projects
chan_oss.c 43.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	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;
    
    	if (!o->owner) {
    
    		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;
    
    		f.datalen = i + 1;
    		ast_queue_frame(o->owner, &f);
    	}
    
    	return CLI_SUCCESS;
    
    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);
    
    	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)
    
    		ast_queue_hangup_with_cause(o->owner, AST_CAUSE_NORMAL_CLEARING);
    
    	setformat(o, O_CLOSE);
    
    	return CLI_SUCCESS;
    
    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;
    
    	return CLI_SUCCESS;
    
    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 } };
    
    		const char *digits;
    
    		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;
    
    		digits = a->argv[e->args];
    
    		/* 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);
    		}
    
    		return CLI_SUCCESS;
    
    	}
    	/* 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);
    
    	return CLI_SUCCESS;
    
    static char *console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	if (cmd == CLI_INIT) {
    
    		e->command = "console {mute|unmute} [toggle]";
    
    		e->usage =
    
    			"Usage: console {mute|unmute} [toggle]\n"
    
    			"       Mute/unmute the microphone.\n";
    		return NULL;
    	} else if (cmd == CLI_GENERATE)
    
    		return CLI_SHOWUSAGE;
    
    	if (a->argc == e->args) {
    		if (strcasecmp(a->argv[e->args-1], "toggle"))
    			return CLI_SHOWUSAGE;
    		toggle = 1;
    	}
    	s = a->argv[e->args-2];
    
    	if (!strcasecmp(s, "mute"))
    
    		o->mute = toggle ? !o->mute : 1;
    
    	else if (!strcasecmp(s, "unmute"))
    
    		o->mute = toggle ? !o->mute : 0;
    
    	else
    		return CLI_SHOWUSAGE;
    
    	ast_cli(a->fd, "Console mic is %s\n", o->mute ? "off" : "on");
    
    	return CLI_SUCCESS;
    
    static char *console_transfer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    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;
    
    	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;
    
    	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));
    
    	if (!ast_exists_extension(b, ctx, ext, 1,
    
    		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");
    
    	} else {
    
    		ast_cli(a->fd, "Whee, transferring %s to %s@%s.\n", ast_channel_name(b), ext, ctx);
    
    			ast_cli(a->fd, "Failed to transfer :(\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    static char *console_active(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    		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;
    	}
    
    
    	if (a->argc == 3)
    
    		ast_cli(a->fd, "active console is [%s]\n", oss_active);
    
    	else if (a->argc != 4)
    
    	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;
    
    		o = find_desc(a->argv[3]);
    
    		if (o == NULL)
    
    			ast_cli(a->fd, "No device [%s] exists\n", a->argv[3]);
    
    		else
    			oss_active = o->name;
    	}
    
    /*!
     * \brief store the boost factor
    
    static void store_boost(struct chan_oss_pvt *o, const char *s)
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	if (sscanf(s, "%30lf", &boost) != 1) {
    
    		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 = 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[] = {
    
    	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"),
    
    	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"),
    
     * 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's avatar
    Mark Spencer committed
    {
    
    	for (i = 0; i < strlen(s); i++) {
    
    Steve Murphy's avatar
    Steve Murphy committed
    		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's avatar
    Mark Spencer committed
    	}
    
    	o->mixer_cmd = ast_strdup(s);
    
    Russell Bryant's avatar
    Russell Bryant committed
     * store the callerid components
     */
    
    static void store_callerid(struct chan_oss_pvt *o, const char *s)
    
    Russell Bryant's avatar
    Russell Bryant committed
    {
    	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);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (!ast_jb_read_conf(&global_jbconf, var, value))
    
    	if (!console_video_config(&o->env, var, value))
    
    	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;
    
     * 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)
    
    {
    	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))))
    
    			return NULL;
    		*o = oss_default;
    		/* "general" is also the default thing */
    		if (strcmp(ctg, "general") == 0) {
    
    			o->name = ast_strdup("dsp");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	o->lastopen = ast_tvnow();	/* don't leave it 0 or tvdiff may wrap */
    
    	for (v = ast_variable_browse(cfg, ctg); v; v = v->next) {
    
    		store_config_core(o, v->name, v->value);
    
    	}
    	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) {
    
    			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);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    
    	/* 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 */
    
    	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");
    
    		ast_log(LOG_WARNING, "XXX I don't work right with non " "full-duplex sound cards XXX\n");
    
    	/* link into list of devices */
    	if (o != &oss_default) {
    		o->next = oss_default.next;
    		oss_default.next = o;
    	}
    	return o;
    
    
    static int load_module(void)
    
    	struct ast_config *cfg = NULL;
    	char *ctg = NULL;
    
    	struct ast_flags config_flags = { 0 };
    
    	/* Copy the default jb config over global_jbconf */
    	memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
    
    
    	if (!(cfg = ast_config_load(config, config_flags))) {
    
    		ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
    		return AST_MODULE_LOAD_DECLINE;
    
    	} 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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    
    	do {
    		store_config(cfg, ctg);
    	} while ( (ctg = ast_category_browse(cfg, ctg)) != NULL);
    
    	ast_config_destroy(cfg);
    
    
    	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. */
    
    		return AST_MODULE_LOAD_FAILURE;
    
    	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;
    
    	ast_cli_register_multiple(cli_oss, ARRAY_LEN(cli_oss));
    
    
    	return AST_MODULE_LOAD_SUCCESS;
    
    static int unload_module(void)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct chan_oss_pvt *o, *next;
    
    	ast_cli_unregister_multiple(cli_oss, ARRAY_LEN(cli_oss));
    
    	o = oss_default.next;
    	while (o) {
    
    		close(o->sounddev);
    		if (o->owner)
    			ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD);
    
    		next = o->next;
    		ast_free(o->name);
    		ast_free(o);
    		o = next;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	oss_tech.capabilities = ast_format_cap_destroy(oss_tech.capabilities);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "OSS Console Channel Driver");