static ssize_t softsynthx_read()

in speakup/speakup_soft.c [206:309]


static ssize_t softsynthx_read(struct file *fp, char __user *buf, size_t count,
			       loff_t *pos, int unicode)
{
	int chars_sent = 0;
	char __user *cp;
	char *init;
	size_t bytes_per_ch = unicode ? 3 : 1;
	u16 ch;
	int empty;
	unsigned long flags;
	DEFINE_WAIT(wait);

	if (count < bytes_per_ch)
		return -EINVAL;

	spin_lock_irqsave(&speakup_info.spinlock, flags);
	synth_soft.alive = 1;
	while (1) {
		prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE);
		if (synth_current() == &synth_soft) {
			if (!unicode)
				synth_buffer_skip_nonlatin1();
			if (!synth_buffer_empty() || speakup_info.flushing)
				break;
		}
		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
		if (fp->f_flags & O_NONBLOCK) {
			finish_wait(&speakup_event, &wait);
			return -EAGAIN;
		}
		if (signal_pending(current)) {
			finish_wait(&speakup_event, &wait);
			return -ERESTARTSYS;
		}
		schedule();
		spin_lock_irqsave(&speakup_info.spinlock, flags);
	}
	finish_wait(&speakup_event, &wait);

	cp = buf;
	init = get_initstring();

	/* Keep 3 bytes available for a 16bit UTF-8-encoded character */
	while (chars_sent <= count - bytes_per_ch) {
		if (synth_current() != &synth_soft)
			break;
		if (speakup_info.flushing) {
			speakup_info.flushing = 0;
			ch = '\x18';
		} else if (init[init_pos]) {
			ch = init[init_pos++];
		} else {
			if (!unicode)
				synth_buffer_skip_nonlatin1();
			if (synth_buffer_empty())
				break;
			ch = synth_buffer_getc();
		}
		spin_unlock_irqrestore(&speakup_info.spinlock, flags);

		if ((!unicode && ch < 0x100) || (unicode && ch < 0x80)) {
			u_char c = ch;

			if (copy_to_user(cp, &c, 1))
				return -EFAULT;

			chars_sent++;
			cp++;
		} else if (unicode && ch < 0x800) {
			u_char s[2] = {
				0xc0 | (ch >> 6),
				0x80 | (ch & 0x3f)
			};

			if (copy_to_user(cp, s, sizeof(s)))
				return -EFAULT;

			chars_sent += sizeof(s);
			cp += sizeof(s);
		} else if (unicode) {
			u_char s[3] = {
				0xe0 | (ch >> 12),
				0x80 | ((ch >> 6) & 0x3f),
				0x80 | (ch & 0x3f)
			};

			if (copy_to_user(cp, s, sizeof(s)))
				return -EFAULT;

			chars_sent += sizeof(s);
			cp += sizeof(s);
		}

		spin_lock_irqsave(&speakup_info.spinlock, flags);
	}
	*pos += chars_sent;
	empty = synth_buffer_empty();
	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
	if (empty) {
		speakup_start_ttys();
		*pos = 0;
	}
	return chars_sent;
}