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;
}