cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

serport.c (7218B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Input device TTY line discipline
      4 *
      5 * Copyright (c) 1999-2002 Vojtech Pavlik
      6 *
      7 * This is a module that converts a tty line into a much simpler
      8 * 'serial io port' abstraction that the input device drivers use.
      9 */
     10
     11
     12#include <linux/uaccess.h>
     13#include <linux/kernel.h>
     14#include <linux/sched.h>
     15#include <linux/slab.h>
     16#include <linux/module.h>
     17#include <linux/init.h>
     18#include <linux/serio.h>
     19#include <linux/tty.h>
     20#include <linux/compat.h>
     21
     22MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
     23MODULE_DESCRIPTION("Input device TTY line discipline");
     24MODULE_LICENSE("GPL");
     25MODULE_ALIAS_LDISC(N_MOUSE);
     26
     27#define SERPORT_BUSY	1
     28#define SERPORT_ACTIVE	2
     29#define SERPORT_DEAD	3
     30
     31struct serport {
     32	struct tty_struct *tty;
     33	wait_queue_head_t wait;
     34	struct serio *serio;
     35	struct serio_device_id id;
     36	spinlock_t lock;
     37	unsigned long flags;
     38};
     39
     40/*
     41 * Callback functions from the serio code.
     42 */
     43
     44static int serport_serio_write(struct serio *serio, unsigned char data)
     45{
     46	struct serport *serport = serio->port_data;
     47	return -(serport->tty->ops->write(serport->tty, &data, 1) != 1);
     48}
     49
     50static int serport_serio_open(struct serio *serio)
     51{
     52	struct serport *serport = serio->port_data;
     53	unsigned long flags;
     54
     55	spin_lock_irqsave(&serport->lock, flags);
     56	set_bit(SERPORT_ACTIVE, &serport->flags);
     57	spin_unlock_irqrestore(&serport->lock, flags);
     58
     59	return 0;
     60}
     61
     62
     63static void serport_serio_close(struct serio *serio)
     64{
     65	struct serport *serport = serio->port_data;
     66	unsigned long flags;
     67
     68	spin_lock_irqsave(&serport->lock, flags);
     69	clear_bit(SERPORT_ACTIVE, &serport->flags);
     70	spin_unlock_irqrestore(&serport->lock, flags);
     71}
     72
     73/*
     74 * serport_ldisc_open() is the routine that is called upon setting our line
     75 * discipline on a tty. It prepares the serio struct.
     76 */
     77
     78static int serport_ldisc_open(struct tty_struct *tty)
     79{
     80	struct serport *serport;
     81
     82	if (!capable(CAP_SYS_ADMIN))
     83		return -EPERM;
     84
     85	serport = kzalloc(sizeof(struct serport), GFP_KERNEL);
     86	if (!serport)
     87		return -ENOMEM;
     88
     89	serport->tty = tty;
     90	spin_lock_init(&serport->lock);
     91	init_waitqueue_head(&serport->wait);
     92
     93	tty->disc_data = serport;
     94	tty->receive_room = 256;
     95	set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
     96
     97	return 0;
     98}
     99
    100/*
    101 * serport_ldisc_close() is the opposite of serport_ldisc_open()
    102 */
    103
    104static void serport_ldisc_close(struct tty_struct *tty)
    105{
    106	struct serport *serport = (struct serport *) tty->disc_data;
    107
    108	kfree(serport);
    109}
    110
    111/*
    112 * serport_ldisc_receive() is called by the low level tty driver when characters
    113 * are ready for us. We forward the characters and flags, one by one to the
    114 * 'interrupt' routine.
    115 */
    116
    117static void serport_ldisc_receive(struct tty_struct *tty,
    118		const unsigned char *cp, const char *fp, int count)
    119{
    120	struct serport *serport = (struct serport*) tty->disc_data;
    121	unsigned long flags;
    122	unsigned int ch_flags = 0;
    123	int i;
    124
    125	spin_lock_irqsave(&serport->lock, flags);
    126
    127	if (!test_bit(SERPORT_ACTIVE, &serport->flags))
    128		goto out;
    129
    130	for (i = 0; i < count; i++) {
    131		if (fp) {
    132			switch (fp[i]) {
    133			case TTY_FRAME:
    134				ch_flags = SERIO_FRAME;
    135				break;
    136
    137			case TTY_PARITY:
    138				ch_flags = SERIO_PARITY;
    139				break;
    140
    141			default:
    142				ch_flags = 0;
    143				break;
    144			}
    145		}
    146
    147		serio_interrupt(serport->serio, cp[i], ch_flags);
    148	}
    149
    150out:
    151	spin_unlock_irqrestore(&serport->lock, flags);
    152}
    153
    154/*
    155 * serport_ldisc_read() just waits indefinitely if everything goes well.
    156 * However, when the serio driver closes the serio port, it finishes,
    157 * returning 0 characters.
    158 */
    159
    160static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file,
    161				  unsigned char *kbuf, size_t nr,
    162				  void **cookie, unsigned long offset)
    163{
    164	struct serport *serport = (struct serport*) tty->disc_data;
    165	struct serio *serio;
    166
    167	if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
    168		return -EBUSY;
    169
    170	serport->serio = serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
    171	if (!serio)
    172		return -ENOMEM;
    173
    174	strlcpy(serio->name, "Serial port", sizeof(serio->name));
    175	snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty));
    176	serio->id = serport->id;
    177	serio->id.type = SERIO_RS232;
    178	serio->write = serport_serio_write;
    179	serio->open = serport_serio_open;
    180	serio->close = serport_serio_close;
    181	serio->port_data = serport;
    182	serio->dev.parent = tty->dev;
    183
    184	serio_register_port(serport->serio);
    185	printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty));
    186
    187	wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags));
    188	serio_unregister_port(serport->serio);
    189	serport->serio = NULL;
    190
    191	clear_bit(SERPORT_DEAD, &serport->flags);
    192	clear_bit(SERPORT_BUSY, &serport->flags);
    193
    194	return 0;
    195}
    196
    197static void serport_set_type(struct tty_struct *tty, unsigned long type)
    198{
    199	struct serport *serport = tty->disc_data;
    200
    201	serport->id.proto = type & 0x000000ff;
    202	serport->id.id    = (type & 0x0000ff00) >> 8;
    203	serport->id.extra = (type & 0x00ff0000) >> 16;
    204}
    205
    206/*
    207 * serport_ldisc_ioctl() allows to set the port protocol, and device ID
    208 */
    209
    210static int serport_ldisc_ioctl(struct tty_struct *tty, unsigned int cmd,
    211			       unsigned long arg)
    212{
    213	if (cmd == SPIOCSTYPE) {
    214		unsigned long type;
    215
    216		if (get_user(type, (unsigned long __user *) arg))
    217			return -EFAULT;
    218
    219		serport_set_type(tty, type);
    220		return 0;
    221	}
    222
    223	return -EINVAL;
    224}
    225
    226#ifdef CONFIG_COMPAT
    227#define COMPAT_SPIOCSTYPE	_IOW('q', 0x01, compat_ulong_t)
    228static int serport_ldisc_compat_ioctl(struct tty_struct *tty,
    229				       unsigned int cmd, unsigned long arg)
    230{
    231	if (cmd == COMPAT_SPIOCSTYPE) {
    232		void __user *uarg = compat_ptr(arg);
    233		compat_ulong_t compat_type;
    234
    235		if (get_user(compat_type, (compat_ulong_t __user *)uarg))
    236			return -EFAULT;
    237
    238		serport_set_type(tty, compat_type);
    239		return 0;
    240	}
    241
    242	return -EINVAL;
    243}
    244#endif
    245
    246static void serport_ldisc_hangup(struct tty_struct *tty)
    247{
    248	struct serport *serport = (struct serport *) tty->disc_data;
    249	unsigned long flags;
    250
    251	spin_lock_irqsave(&serport->lock, flags);
    252	set_bit(SERPORT_DEAD, &serport->flags);
    253	spin_unlock_irqrestore(&serport->lock, flags);
    254
    255	wake_up_interruptible(&serport->wait);
    256}
    257
    258static void serport_ldisc_write_wakeup(struct tty_struct * tty)
    259{
    260	struct serport *serport = (struct serport *) tty->disc_data;
    261	unsigned long flags;
    262
    263	spin_lock_irqsave(&serport->lock, flags);
    264	if (test_bit(SERPORT_ACTIVE, &serport->flags))
    265		serio_drv_write_wakeup(serport->serio);
    266	spin_unlock_irqrestore(&serport->lock, flags);
    267}
    268
    269/*
    270 * The line discipline structure.
    271 */
    272
    273static struct tty_ldisc_ops serport_ldisc = {
    274	.owner =	THIS_MODULE,
    275	.num =		N_MOUSE,
    276	.name =		"input",
    277	.open =		serport_ldisc_open,
    278	.close =	serport_ldisc_close,
    279	.read =		serport_ldisc_read,
    280	.ioctl =	serport_ldisc_ioctl,
    281#ifdef CONFIG_COMPAT
    282	.compat_ioctl =	serport_ldisc_compat_ioctl,
    283#endif
    284	.receive_buf =	serport_ldisc_receive,
    285	.hangup =	serport_ldisc_hangup,
    286	.write_wakeup =	serport_ldisc_write_wakeup
    287};
    288
    289/*
    290 * The functions for insering/removing us as a module.
    291 */
    292
    293static int __init serport_init(void)
    294{
    295	int retval;
    296	retval = tty_register_ldisc(&serport_ldisc);
    297	if (retval)
    298		printk(KERN_ERR "serport.c: Error registering line discipline.\n");
    299
    300	return  retval;
    301}
    302
    303static void __exit serport_exit(void)
    304{
    305	tty_unregister_ldisc(&serport_ldisc);
    306}
    307
    308module_init(serport_init);
    309module_exit(serport_exit);