Here is the big set of char/misc and a number of other driver subsystem updates for 6.9-rc1. Included in here are: - IIO driver updates, loads of new ones and evolution of existing ones - coresight driver updates - const cleanups for many driver subsystems - speakup driver additions - platform remove callback void cleanups - mei driver updates - mhi driver updates - cdx driver updates for MSI interrupt handling - nvmem driver updates - other smaller driver updates and cleanups, full details in the shortlog All of these have been in linux-next for a long time with no reported issue, other than a build warning with some older versions of gcc for a speakup driver, fix for that will come in a few days when I catch up with my pending patch queues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZfwuLg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ynKVACgjvR1cD8NYk9PcGWc9ZaXAZ6zSnwAn260kMoe lLFtwszo7m0N6ZULBWBd =y3yz -----END PGP SIGNATURE----- Merge tag 'char-misc-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc and other driver subsystem updates from Greg KH: "Here is the big set of char/misc and a number of other driver subsystem updates for 6.9-rc1. Included in here are: - IIO driver updates, loads of new ones and evolution of existing ones - coresight driver updates - const cleanups for many driver subsystems - speakup driver additions - platform remove callback void cleanups - mei driver updates - mhi driver updates - cdx driver updates for MSI interrupt handling - nvmem driver updates - other smaller driver updates and cleanups, full details in the shortlog All of these have been in linux-next for a long time with no reported issue, other than a build warning for the speakup driver" The build warning hits clang and is a gcc (and C23) extension, and is fixed up in the merge. Link: https://lore.kernel.org/all/20240321134831.GA2762840@dev-arch.thelio-3990X/ * tag 'char-misc-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (279 commits) binder: remove redundant variable page_addr uio_dmem_genirq: UIO_MEM_DMA_COHERENT conversion uio_pruss: UIO_MEM_DMA_COHERENT conversion cnic,bnx2,bnx2x: use UIO_MEM_DMA_COHERENT uio: introduce UIO_MEM_DMA_COHERENT type cdx: add MSI support for CDX bus pps: use cflags-y instead of EXTRA_CFLAGS speakup: Add /dev/synthu device speakup: Fix 8bit characters from direct synth parport: sunbpp: Convert to platform remove callback returning void parport: amiga: Convert to platform remove callback returning void char: xillybus: Convert to platform remove callback returning void vmw_balloon: change maintainership MAINTAINERS: change the maintainer for hpilo driver char: xilinx_hwicap: Fix NULL vs IS_ERR() bug hpet: remove hpets::hp_clocksource platform: goldfish: move the separate 'default' propery for CONFIG_GOLDFISH char: xilinx_hwicap: drop casting to void in dev_set_drvdata greybus: move is_gb_* functions out of greybus.h greybus: Remove usage of the deprecated ida_simple_xx() API ...
212 lines
4.5 KiB
C
212 lines
4.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/errno.h>
|
|
#include <linux/miscdevice.h> /* for misc_register, and MISC_DYNAMIC_MINOR */
|
|
#include <linux/types.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include "speakup.h"
|
|
#include "spk_priv.h"
|
|
|
|
static int synth_registered, synthu_registered;
|
|
static int dev_opened;
|
|
|
|
/* Latin1 version */
|
|
static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
|
|
size_t nbytes, loff_t *ppos)
|
|
{
|
|
size_t count = nbytes;
|
|
const char __user *ptr = buffer;
|
|
size_t bytes;
|
|
unsigned long flags;
|
|
u_char buf[256];
|
|
|
|
if (!synth)
|
|
return -ENODEV;
|
|
while (count > 0) {
|
|
bytes = min(count, sizeof(buf));
|
|
if (copy_from_user(buf, ptr, bytes))
|
|
return -EFAULT;
|
|
count -= bytes;
|
|
ptr += bytes;
|
|
spin_lock_irqsave(&speakup_info.spinlock, flags);
|
|
synth_write(buf, bytes);
|
|
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
|
|
}
|
|
return (ssize_t)nbytes;
|
|
}
|
|
|
|
/* UTF-8 version */
|
|
static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,
|
|
size_t nbytes, loff_t *ppos)
|
|
{
|
|
size_t count = nbytes, want;
|
|
const char __user *ptr = buffer;
|
|
size_t bytes;
|
|
unsigned long flags;
|
|
unsigned char buf[256];
|
|
u16 ubuf[256];
|
|
size_t in, in2, out;
|
|
|
|
if (!synth)
|
|
return -ENODEV;
|
|
|
|
want = 1;
|
|
while (count >= want) {
|
|
/* Copy some UTF-8 piece from userland */
|
|
bytes = min(count, sizeof(buf));
|
|
if (copy_from_user(buf, ptr, bytes))
|
|
return -EFAULT;
|
|
|
|
/* Convert to u16 */
|
|
for (in = 0, out = 0; in < bytes; in++) {
|
|
unsigned char c = buf[in];
|
|
int nbytes = 8 - fls(c ^ 0xff);
|
|
u32 value;
|
|
|
|
switch (nbytes) {
|
|
case 8: /* 0xff */
|
|
case 7: /* 0xfe */
|
|
case 1: /* 0x80 */
|
|
/* Invalid, drop */
|
|
goto drop;
|
|
|
|
case 0:
|
|
/* ASCII, copy */
|
|
ubuf[out++] = c;
|
|
continue;
|
|
|
|
default:
|
|
/* 2..6-byte UTF-8 */
|
|
|
|
if (bytes - in < nbytes) {
|
|
/* We don't have it all yet, stop here
|
|
* and wait for the rest
|
|
*/
|
|
bytes = in;
|
|
want = nbytes;
|
|
continue;
|
|
}
|
|
|
|
/* First byte */
|
|
value = c & ((1u << (7 - nbytes)) - 1);
|
|
|
|
/* Other bytes */
|
|
for (in2 = 2; in2 <= nbytes; in2++) {
|
|
c = buf[in + 1];
|
|
if ((c & 0xc0) != 0x80) {
|
|
/* Invalid, drop the head */
|
|
want = 1;
|
|
goto drop;
|
|
}
|
|
value = (value << 6) | (c & 0x3f);
|
|
in++;
|
|
}
|
|
|
|
if (value < 0x10000)
|
|
ubuf[out++] = value;
|
|
want = 1;
|
|
break;
|
|
}
|
|
drop:
|
|
/* empty statement */;
|
|
}
|
|
|
|
count -= bytes;
|
|
ptr += bytes;
|
|
|
|
/* And speak this up */
|
|
if (out) {
|
|
spin_lock_irqsave(&speakup_info.spinlock, flags);
|
|
for (in = 0; in < out; in++)
|
|
synth_buffer_add(ubuf[in]);
|
|
synth_start();
|
|
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
|
|
}
|
|
}
|
|
|
|
return (ssize_t)(nbytes - count);
|
|
}
|
|
|
|
static ssize_t speakup_file_read(struct file *fp, char __user *buf,
|
|
size_t nbytes, loff_t *ppos)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int speakup_file_open(struct inode *ip, struct file *fp)
|
|
{
|
|
if (!synth)
|
|
return -ENODEV;
|
|
if (xchg(&dev_opened, 1))
|
|
return -EBUSY;
|
|
return 0;
|
|
}
|
|
|
|
static int speakup_file_release(struct inode *ip, struct file *fp)
|
|
{
|
|
dev_opened = 0;
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations synth_fops = {
|
|
.read = speakup_file_read,
|
|
.write = speakup_file_write,
|
|
.open = speakup_file_open,
|
|
.release = speakup_file_release,
|
|
};
|
|
|
|
static const struct file_operations synthu_fops = {
|
|
.read = speakup_file_read,
|
|
.write = speakup_file_writeu,
|
|
.open = speakup_file_open,
|
|
.release = speakup_file_release,
|
|
};
|
|
|
|
static struct miscdevice synth_device = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "synth",
|
|
.fops = &synth_fops,
|
|
};
|
|
|
|
static struct miscdevice synthu_device = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "synthu",
|
|
.fops = &synthu_fops,
|
|
};
|
|
|
|
void speakup_register_devsynth(void)
|
|
{
|
|
if (!synth_registered) {
|
|
if (misc_register(&synth_device)) {
|
|
pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
|
|
} else {
|
|
pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
|
|
MISC_MAJOR, synth_device.minor);
|
|
synth_registered = 1;
|
|
}
|
|
}
|
|
if (!synthu_registered) {
|
|
if (misc_register(&synthu_device)) {
|
|
pr_warn("Couldn't initialize miscdevice /dev/synthu.\n");
|
|
} else {
|
|
pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n",
|
|
MISC_MAJOR, synthu_device.minor);
|
|
synthu_registered = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void speakup_unregister_devsynth(void)
|
|
{
|
|
if (synth_registered) {
|
|
pr_info("speakup: unregistering synth device /dev/synth\n");
|
|
misc_deregister(&synth_device);
|
|
synth_registered = 0;
|
|
}
|
|
if (synthu_registered) {
|
|
pr_info("speakup: unregistering synth device /dev/synthu\n");
|
|
misc_deregister(&synthu_device);
|
|
synthu_registered = 0;
|
|
}
|
|
}
|