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

ledtrig-tty.c (4099B)


      1// SPDX-License-Identifier: GPL-2.0
      2
      3#include <linux/delay.h>
      4#include <linux/leds.h>
      5#include <linux/module.h>
      6#include <linux/slab.h>
      7#include <linux/tty.h>
      8#include <uapi/linux/serial.h>
      9
     10struct ledtrig_tty_data {
     11	struct led_classdev *led_cdev;
     12	struct delayed_work dwork;
     13	struct mutex mutex;
     14	const char *ttyname;
     15	struct tty_struct *tty;
     16	int rx, tx;
     17};
     18
     19static void ledtrig_tty_restart(struct ledtrig_tty_data *trigger_data)
     20{
     21	schedule_delayed_work(&trigger_data->dwork, 0);
     22}
     23
     24static ssize_t ttyname_show(struct device *dev,
     25			    struct device_attribute *attr, char *buf)
     26{
     27	struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
     28	ssize_t len = 0;
     29
     30	mutex_lock(&trigger_data->mutex);
     31
     32	if (trigger_data->ttyname)
     33		len = sprintf(buf, "%s\n", trigger_data->ttyname);
     34
     35	mutex_unlock(&trigger_data->mutex);
     36
     37	return len;
     38}
     39
     40static ssize_t ttyname_store(struct device *dev,
     41			     struct device_attribute *attr, const char *buf,
     42			     size_t size)
     43{
     44	struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
     45	char *ttyname;
     46	ssize_t ret = size;
     47	bool running;
     48
     49	if (size > 0 && buf[size - 1] == '\n')
     50		size -= 1;
     51
     52	if (size) {
     53		ttyname = kmemdup_nul(buf, size, GFP_KERNEL);
     54		if (!ttyname)
     55			return -ENOMEM;
     56	} else {
     57		ttyname = NULL;
     58	}
     59
     60	mutex_lock(&trigger_data->mutex);
     61
     62	running = trigger_data->ttyname != NULL;
     63
     64	kfree(trigger_data->ttyname);
     65	tty_kref_put(trigger_data->tty);
     66	trigger_data->tty = NULL;
     67
     68	trigger_data->ttyname = ttyname;
     69
     70	mutex_unlock(&trigger_data->mutex);
     71
     72	if (ttyname && !running)
     73		ledtrig_tty_restart(trigger_data);
     74
     75	return ret;
     76}
     77static DEVICE_ATTR_RW(ttyname);
     78
     79static void ledtrig_tty_work(struct work_struct *work)
     80{
     81	struct ledtrig_tty_data *trigger_data =
     82		container_of(work, struct ledtrig_tty_data, dwork.work);
     83	struct serial_icounter_struct icount;
     84	int ret;
     85
     86	mutex_lock(&trigger_data->mutex);
     87
     88	if (!trigger_data->ttyname) {
     89		/* exit without rescheduling */
     90		mutex_unlock(&trigger_data->mutex);
     91		return;
     92	}
     93
     94	/* try to get the tty corresponding to $ttyname */
     95	if (!trigger_data->tty) {
     96		dev_t devno;
     97		struct tty_struct *tty;
     98		int ret;
     99
    100		ret = tty_dev_name_to_number(trigger_data->ttyname, &devno);
    101		if (ret < 0)
    102			/*
    103			 * A device with this name might appear later, so keep
    104			 * retrying.
    105			 */
    106			goto out;
    107
    108		tty = tty_kopen_shared(devno);
    109		if (IS_ERR(tty) || !tty)
    110			/* What to do? retry or abort */
    111			goto out;
    112
    113		trigger_data->tty = tty;
    114	}
    115
    116	ret = tty_get_icount(trigger_data->tty, &icount);
    117	if (ret) {
    118		dev_info(trigger_data->tty->dev, "Failed to get icount, stopped polling\n");
    119		mutex_unlock(&trigger_data->mutex);
    120		return;
    121	}
    122
    123	if (icount.rx != trigger_data->rx ||
    124	    icount.tx != trigger_data->tx) {
    125		led_set_brightness_sync(trigger_data->led_cdev, LED_ON);
    126
    127		trigger_data->rx = icount.rx;
    128		trigger_data->tx = icount.tx;
    129	} else {
    130		led_set_brightness_sync(trigger_data->led_cdev, LED_OFF);
    131	}
    132
    133out:
    134	mutex_unlock(&trigger_data->mutex);
    135	schedule_delayed_work(&trigger_data->dwork, msecs_to_jiffies(100));
    136}
    137
    138static struct attribute *ledtrig_tty_attrs[] = {
    139	&dev_attr_ttyname.attr,
    140	NULL
    141};
    142ATTRIBUTE_GROUPS(ledtrig_tty);
    143
    144static int ledtrig_tty_activate(struct led_classdev *led_cdev)
    145{
    146	struct ledtrig_tty_data *trigger_data;
    147
    148	trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL);
    149	if (!trigger_data)
    150		return -ENOMEM;
    151
    152	led_set_trigger_data(led_cdev, trigger_data);
    153
    154	INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_tty_work);
    155	trigger_data->led_cdev = led_cdev;
    156	mutex_init(&trigger_data->mutex);
    157
    158	return 0;
    159}
    160
    161static void ledtrig_tty_deactivate(struct led_classdev *led_cdev)
    162{
    163	struct ledtrig_tty_data *trigger_data = led_get_trigger_data(led_cdev);
    164
    165	cancel_delayed_work_sync(&trigger_data->dwork);
    166
    167	kfree(trigger_data);
    168}
    169
    170static struct led_trigger ledtrig_tty = {
    171	.name = "tty",
    172	.activate = ledtrig_tty_activate,
    173	.deactivate = ledtrig_tty_deactivate,
    174	.groups = ledtrig_tty_groups,
    175};
    176module_led_trigger(ledtrig_tty);
    177
    178MODULE_AUTHOR("Uwe Kleine-König <u.kleine-koenig@pengutronix.de>");
    179MODULE_DESCRIPTION("UART LED trigger");
    180MODULE_LICENSE("GPL v2");