brintos

brintos / linux-shallow public Read only

0
0
Text · 13.7 KiB · 6d44682 Raw
492 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* speakup_soft.c - speakup driver to register and make available3 * a user space device for software synthesizers.  written by: Kirk4 * Reiser <kirk@braille.uwo.ca>5 *6 * Copyright (C) 2003  Kirk Reiser.7 *8 * this code is specifically written as a driver for the speakup screenreview9 * package and is not a general device driver.10 */11 12#include <linux/unistd.h>13#include <linux/miscdevice.h>	/* for misc_register, and MISC_DYNAMIC_MINOR */14#include <linux/poll.h>		/* for poll_wait() */15 16/* schedule(), signal_pending(), TASK_INTERRUPTIBLE */17#include <linux/sched/signal.h>18 19#include "spk_priv.h"20#include "speakup.h"21 22#define DRV_VERSION "2.6"23#define PROCSPEECH 0x0d24#define CLEAR_SYNTH 0x1825 26static int softsynth_probe(struct spk_synth *synth);27static void softsynth_release(struct spk_synth *synth);28static int softsynth_is_alive(struct spk_synth *synth);29static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var);30static unsigned char get_index(struct spk_synth *synth);31 32static struct miscdevice synth_device, synthu_device;33static int init_pos;34static int misc_registered;35 36 37enum default_vars_id {38	DIRECT_ID = 0, CAPS_START_ID, CAPS_STOP_ID,39	PAUSE_ID, RATE_ID, PITCH_ID, INFLECTION_ID,40	VOL_ID, TONE_ID, PUNCT_ID, VOICE_ID,41	FREQUENCY_ID, V_LAST_VAR_ID,42	 NB_ID43};44 45 46static struct var_t vars[NB_ID] = {47 48	[DIRECT_ID]  = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },49	[CAPS_START_ID] = { CAPS_START, .u.s = {"\x01+3p" } },50	[CAPS_STOP_ID]  = { CAPS_STOP, .u.s = {"\x01-3p" } },51	[PAUSE_ID]  = { PAUSE, .u.n = {"\x01P" } },52	[RATE_ID]  = { RATE, .u.n = {"\x01%ds", 2, 0, 9, 0, 0, NULL } },53	[PITCH_ID]  = { PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL } },54	[INFLECTION_ID]  = { INFLECTION, .u.n = {"\x01%dr", 5, 0, 9, 0, 0, NULL } },55	[VOL_ID]  = { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },56	[TONE_ID]  = { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },57	[PUNCT_ID]  = { PUNCT, .u.n = {"\x01%db", 0, 0, 3, 0, 0, NULL } },58	[VOICE_ID]  = { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },59	[FREQUENCY_ID]  = { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },60	V_LAST_VAR61};62 63/* These attributes will appear in /sys/accessibility/speakup/soft. */64 65static struct kobj_attribute caps_start_attribute =66	__ATTR(caps_start, 0644, spk_var_show, spk_var_store);67static struct kobj_attribute caps_stop_attribute =68	__ATTR(caps_stop, 0644, spk_var_show, spk_var_store);69static struct kobj_attribute freq_attribute =70	__ATTR(freq, 0644, spk_var_show, spk_var_store);71static struct kobj_attribute pitch_attribute =72	__ATTR(pitch, 0644, spk_var_show, spk_var_store);73static struct kobj_attribute inflection_attribute =74	__ATTR(inflection, 0644, spk_var_show, spk_var_store);75static struct kobj_attribute punct_attribute =76	__ATTR(punct, 0644, spk_var_show, spk_var_store);77static struct kobj_attribute rate_attribute =78	__ATTR(rate, 0644, spk_var_show, spk_var_store);79static struct kobj_attribute tone_attribute =80	__ATTR(tone, 0644, spk_var_show, spk_var_store);81static struct kobj_attribute voice_attribute =82	__ATTR(voice, 0644, spk_var_show, spk_var_store);83static struct kobj_attribute vol_attribute =84	__ATTR(vol, 0644, spk_var_show, spk_var_store);85 86/*87 * We should uncomment the following definition, when we agree on a88 * method of passing a language designation to the software synthesizer.89 * static struct kobj_attribute lang_attribute =90 *	__ATTR(lang, 0644, spk_var_show, spk_var_store);91 */92 93static struct kobj_attribute delay_time_attribute =94	__ATTR(delay_time, 0644, spk_var_show, spk_var_store);95static struct kobj_attribute direct_attribute =96	__ATTR(direct, 0644, spk_var_show, spk_var_store);97static struct kobj_attribute full_time_attribute =98	__ATTR(full_time, 0644, spk_var_show, spk_var_store);99static struct kobj_attribute jiffy_delta_attribute =100	__ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);101static struct kobj_attribute trigger_time_attribute =102	__ATTR(trigger_time, 0644, spk_var_show, spk_var_store);103 104/*105 * Create a group of attributes so that we can create and destroy them all106 * at once.107 */108static struct attribute *synth_attrs[] = {109	&caps_start_attribute.attr,110	&caps_stop_attribute.attr,111	&freq_attribute.attr,112/*	&lang_attribute.attr, */113	&pitch_attribute.attr,114	&inflection_attribute.attr,115	&punct_attribute.attr,116	&rate_attribute.attr,117	&tone_attribute.attr,118	&voice_attribute.attr,119	&vol_attribute.attr,120	&delay_time_attribute.attr,121	&direct_attribute.attr,122	&full_time_attribute.attr,123	&jiffy_delta_attribute.attr,124	&trigger_time_attribute.attr,125	NULL,	/* need to NULL terminate the list of attributes */126};127 128static struct spk_synth synth_soft = {129	.name = "soft",130	.version = DRV_VERSION,131	.long_name = "software synth",132	.init = "\01@\x01\x31y\n",133	.procspeech = PROCSPEECH,134	.delay = 0,135	.trigger = 0,136	.jiffies = 0,137	.full = 0,138	.startup = SYNTH_START,139	.checkval = SYNTH_CHECK,140	.vars = vars,141	.io_ops = NULL,142	.probe = softsynth_probe,143	.release = softsynth_release,144	.synth_immediate = NULL,145	.catch_up = NULL,146	.flush = NULL,147	.is_alive = softsynth_is_alive,148	.synth_adjust = softsynth_adjust,149	.read_buff_add = NULL,150	.get_index = get_index,151	.indexing = {152		.command = "\x01%di",153		.lowindex = 1,154		.highindex = 5,155		.currindex = 1,156	},157	.attributes = {158		.attrs = synth_attrs,159		.name = "soft",160	},161};162 163static char *get_initstring(void)164{165	static char buf[40];166	char *cp;167	struct var_t *var;168	size_t len;169	size_t n;170 171	memset(buf, 0, sizeof(buf));172	cp = buf;173	len = sizeof(buf);174 175	var = synth_soft.vars;176	while (var->var_id != MAXVARS) {177		if (var->var_id != CAPS_START && var->var_id != CAPS_STOP &&178		    var->var_id != PAUSE && var->var_id != DIRECT) {179			n = scnprintf(cp, len, var->u.n.synth_fmt,180				      var->u.n.value);181			cp = cp + n;182			len = len - n;183		}184		var++;185	}186	cp = cp + scnprintf(cp, len, "\n");187	return buf;188}189 190static int softsynth_open(struct inode *inode, struct file *fp)191{192	unsigned long flags;193	/*if ((fp->f_flags & O_ACCMODE) != O_RDONLY) */194	/*	return -EPERM; */195	spin_lock_irqsave(&speakup_info.spinlock, flags);196	if (synth_soft.alive) {197		spin_unlock_irqrestore(&speakup_info.spinlock, flags);198		return -EBUSY;199	}200	synth_soft.alive = 1;201	spin_unlock_irqrestore(&speakup_info.spinlock, flags);202	return 0;203}204 205static int softsynth_close(struct inode *inode, struct file *fp)206{207	unsigned long flags;208 209	spin_lock_irqsave(&speakup_info.spinlock, flags);210	synth_soft.alive = 0;211	init_pos = 0;212	spin_unlock_irqrestore(&speakup_info.spinlock, flags);213	/* Make sure we let applications go before leaving */214	speakup_start_ttys();215	return 0;216}217 218static ssize_t softsynthx_read(struct file *fp, char __user *buf, size_t count,219			       loff_t *pos, int unicode)220{221	int chars_sent = 0;222	char __user *cp;223	char *init;224	size_t bytes_per_ch = unicode ? 3 : 1;225	u16 ch;226	int empty;227	unsigned long flags;228	DEFINE_WAIT(wait);229 230	if (count < bytes_per_ch)231		return -EINVAL;232 233	spin_lock_irqsave(&speakup_info.spinlock, flags);234	synth_soft.alive = 1;235	while (1) {236		prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE);237		if (synth_current() == &synth_soft) {238			if (!unicode)239				synth_buffer_skip_nonlatin1();240			if (!synth_buffer_empty() || speakup_info.flushing)241				break;242		}243		spin_unlock_irqrestore(&speakup_info.spinlock, flags);244		if (fp->f_flags & O_NONBLOCK) {245			finish_wait(&speakup_event, &wait);246			return -EAGAIN;247		}248		if (signal_pending(current)) {249			finish_wait(&speakup_event, &wait);250			return -ERESTARTSYS;251		}252		schedule();253		spin_lock_irqsave(&speakup_info.spinlock, flags);254	}255	finish_wait(&speakup_event, &wait);256 257	cp = buf;258	init = get_initstring();259 260	/* Keep 3 bytes available for a 16bit UTF-8-encoded character */261	while (chars_sent <= count - bytes_per_ch) {262		if (synth_current() != &synth_soft)263			break;264		if (speakup_info.flushing) {265			speakup_info.flushing = 0;266			ch = '\x18';267		} else if (init[init_pos]) {268			ch = init[init_pos++];269		} else {270			if (!unicode)271				synth_buffer_skip_nonlatin1();272			if (synth_buffer_empty())273				break;274			ch = synth_buffer_getc();275		}276		spin_unlock_irqrestore(&speakup_info.spinlock, flags);277 278		if ((!unicode && ch < 0x100) || (unicode && ch < 0x80)) {279			u_char c = ch;280 281			if (copy_to_user(cp, &c, 1))282				return -EFAULT;283 284			chars_sent++;285			cp++;286		} else if (unicode && ch < 0x800) {287			u_char s[2] = {288				0xc0 | (ch >> 6),289				0x80 | (ch & 0x3f)290			};291 292			if (copy_to_user(cp, s, sizeof(s)))293				return -EFAULT;294 295			chars_sent += sizeof(s);296			cp += sizeof(s);297		} else if (unicode) {298			u_char s[3] = {299				0xe0 | (ch >> 12),300				0x80 | ((ch >> 6) & 0x3f),301				0x80 | (ch & 0x3f)302			};303 304			if (copy_to_user(cp, s, sizeof(s)))305				return -EFAULT;306 307			chars_sent += sizeof(s);308			cp += sizeof(s);309		}310 311		spin_lock_irqsave(&speakup_info.spinlock, flags);312	}313	*pos += chars_sent;314	empty = synth_buffer_empty();315	spin_unlock_irqrestore(&speakup_info.spinlock, flags);316	if (empty) {317		speakup_start_ttys();318		*pos = 0;319	}320	return chars_sent;321}322 323static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count,324			      loff_t *pos)325{326	return softsynthx_read(fp, buf, count, pos, 0);327}328 329static ssize_t softsynthu_read(struct file *fp, char __user *buf, size_t count,330			       loff_t *pos)331{332	return softsynthx_read(fp, buf, count, pos, 1);333}334 335static int last_index;336 337static ssize_t softsynth_write(struct file *fp, const char __user *buf,338			       size_t count, loff_t *pos)339{340	unsigned long supplied_index = 0;341	int converted;342 343	converted = kstrtoul_from_user(buf, count, 0, &supplied_index);344 345	if (converted < 0)346		return converted;347 348	last_index = supplied_index;349	return count;350}351 352static __poll_t softsynth_poll(struct file *fp, struct poll_table_struct *wait)353{354	unsigned long flags;355	__poll_t ret = 0;356 357	poll_wait(fp, &speakup_event, wait);358 359	spin_lock_irqsave(&speakup_info.spinlock, flags);360	if (synth_current() == &synth_soft &&361	    (!synth_buffer_empty() || speakup_info.flushing))362		ret = EPOLLIN | EPOLLRDNORM;363	spin_unlock_irqrestore(&speakup_info.spinlock, flags);364	return ret;365}366 367static unsigned char get_index(struct spk_synth *synth)368{369	int rv;370 371	rv = last_index;372	last_index = 0;373	return rv;374}375 376static const struct file_operations softsynth_fops = {377	.owner = THIS_MODULE,378	.poll = softsynth_poll,379	.read = softsynth_read,380	.write = softsynth_write,381	.open = softsynth_open,382	.release = softsynth_close,383};384 385static const struct file_operations softsynthu_fops = {386	.owner = THIS_MODULE,387	.poll = softsynth_poll,388	.read = softsynthu_read,389	.write = softsynth_write,390	.open = softsynth_open,391	.release = softsynth_close,392};393 394static int softsynth_probe(struct spk_synth *synth)395{396	if (misc_registered != 0)397		return 0;398	memset(&synth_device, 0, sizeof(synth_device));399	synth_device.minor = MISC_DYNAMIC_MINOR;400	synth_device.name = "softsynth";401	synth_device.fops = &softsynth_fops;402	if (misc_register(&synth_device)) {403		pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n");404		return -ENODEV;405	}406 407	memset(&synthu_device, 0, sizeof(synthu_device));408	synthu_device.minor = MISC_DYNAMIC_MINOR;409	synthu_device.name = "softsynthu";410	synthu_device.fops = &softsynthu_fops;411	if (misc_register(&synthu_device)) {412		misc_deregister(&synth_device);413		pr_warn("Couldn't initialize miscdevice /dev/softsynthu.\n");414		return -ENODEV;415	}416 417	misc_registered = 1;418	pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR %d)\n",419		synth_device.minor);420	pr_info("initialized device: /dev/softsynthu, node (MAJOR 10, MINOR %d)\n",421		synthu_device.minor);422	return 0;423}424 425static void softsynth_release(struct spk_synth *synth)426{427	misc_deregister(&synth_device);428	misc_deregister(&synthu_device);429	misc_registered = 0;430	pr_info("unregistered /dev/softsynth\n");431	pr_info("unregistered /dev/softsynthu\n");432}433 434static int softsynth_is_alive(struct spk_synth *synth)435{436	if (synth_soft.alive)437		return 1;438	return 0;439}440 441static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var)442{443	struct st_var_header *punc_level_var;444	struct var_t *var_data;445 446	if (var->var_id != PUNC_LEVEL)447		return 0;448 449	/* We want to set the the speech synthesis punctuation level450	 * accordingly, so it properly tunes speaking A_PUNC characters */451	var_data = var->data;452	if (!var_data)453		return 0;454	punc_level_var = spk_get_var_header(PUNCT);455	if (!punc_level_var)456		return 0;457	spk_set_num_var(var_data->u.n.value, punc_level_var, E_SET);458 459	return 1;460}461 462module_param_named(start, synth_soft.startup, short, 0444);463module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);464module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);465module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);466module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444);467module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);468module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);469module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);470module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444);471module_param_named(frequency, vars[FREQUENCY_ID].u.n.default_val, int, 0444);472 473 474 475MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");476MODULE_PARM_DESC(direct, "Set the direct variable on load.");477MODULE_PARM_DESC(rate, "Sets the rate of the synthesizer.");478MODULE_PARM_DESC(pitch, "Sets the pitch of the synthesizer.");479MODULE_PARM_DESC(inflection, "Sets the inflection of the synthesizer.");480MODULE_PARM_DESC(vol, "Sets the volume of the speech synthesizer.");481MODULE_PARM_DESC(tone, "Sets the tone of the speech synthesizer.");482MODULE_PARM_DESC(punct, "Sets the amount of punctuation spoken by the synthesizer.");483MODULE_PARM_DESC(voice, "Sets the voice used by the synthesizer.");484MODULE_PARM_DESC(frequency, "Sets the frequency of speech synthesizer.");485 486module_spk_synth(synth_soft);487 488MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");489MODULE_DESCRIPTION("Speakup userspace software synthesizer support");490MODULE_LICENSE("GPL");491MODULE_VERSION(DRV_VERSION);492