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

line-display.c (6972B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Character line display core support
      4 *
      5 * Copyright (C) 2016 Imagination Technologies
      6 * Author: Paul Burton <paul.burton@mips.com>
      7 *
      8 * Copyright (C) 2021 Glider bv
      9 */
     10
     11#include <generated/utsrelease.h>
     12
     13#include <linux/device.h>
     14#include <linux/module.h>
     15#include <linux/slab.h>
     16#include <linux/string.h>
     17#include <linux/sysfs.h>
     18#include <linux/timer.h>
     19
     20#include "line-display.h"
     21
     22#define DEFAULT_SCROLL_RATE	(HZ / 2)
     23
     24/**
     25 * linedisp_scroll() - scroll the display by a character
     26 * @t: really a pointer to the private data structure
     27 *
     28 * Scroll the current message along the display by one character, rearming the
     29 * timer if required.
     30 */
     31static void linedisp_scroll(struct timer_list *t)
     32{
     33	struct linedisp *linedisp = from_timer(linedisp, t, timer);
     34	unsigned int i, ch = linedisp->scroll_pos;
     35	unsigned int num_chars = linedisp->num_chars;
     36
     37	/* update the current message string */
     38	for (i = 0; i < num_chars;) {
     39		/* copy as many characters from the string as possible */
     40		for (; i < num_chars && ch < linedisp->message_len; i++, ch++)
     41			linedisp->buf[i] = linedisp->message[ch];
     42
     43		/* wrap around to the start of the string */
     44		ch = 0;
     45	}
     46
     47	/* update the display */
     48	linedisp->update(linedisp);
     49
     50	/* move on to the next character */
     51	linedisp->scroll_pos++;
     52	linedisp->scroll_pos %= linedisp->message_len;
     53
     54	/* rearm the timer */
     55	if (linedisp->message_len > num_chars && linedisp->scroll_rate)
     56		mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate);
     57}
     58
     59/**
     60 * linedisp_display() - set the message to be displayed
     61 * @linedisp: pointer to the private data structure
     62 * @msg: the message to display
     63 * @count: length of msg, or -1
     64 *
     65 * Display a new message @msg on the display. @msg can be longer than the
     66 * number of characters the display can display, in which case it will begin
     67 * scrolling across the display.
     68 *
     69 * Return: 0 on success, -ENOMEM on memory allocation failure
     70 */
     71static int linedisp_display(struct linedisp *linedisp, const char *msg,
     72			    ssize_t count)
     73{
     74	char *new_msg;
     75
     76	/* stop the scroll timer */
     77	del_timer_sync(&linedisp->timer);
     78
     79	if (count == -1)
     80		count = strlen(msg);
     81
     82	/* if the string ends with a newline, trim it */
     83	if (msg[count - 1] == '\n')
     84		count--;
     85
     86	if (!count) {
     87		/* Clear the display */
     88		kfree(linedisp->message);
     89		linedisp->message = NULL;
     90		linedisp->message_len = 0;
     91		memset(linedisp->buf, ' ', linedisp->num_chars);
     92		linedisp->update(linedisp);
     93		return 0;
     94	}
     95
     96	new_msg = kmemdup_nul(msg, count, GFP_KERNEL);
     97	if (!new_msg)
     98		return -ENOMEM;
     99
    100	kfree(linedisp->message);
    101
    102	linedisp->message = new_msg;
    103	linedisp->message_len = count;
    104	linedisp->scroll_pos = 0;
    105
    106	/* update the display */
    107	linedisp_scroll(&linedisp->timer);
    108
    109	return 0;
    110}
    111
    112/**
    113 * message_show() - read message via sysfs
    114 * @dev: the display device
    115 * @attr: the display message attribute
    116 * @buf: the buffer to read the message into
    117 *
    118 * Read the current message being displayed or scrolled across the display into
    119 * @buf, for reads from sysfs.
    120 *
    121 * Return: the number of characters written to @buf
    122 */
    123static ssize_t message_show(struct device *dev, struct device_attribute *attr,
    124			    char *buf)
    125{
    126	struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
    127
    128	return sysfs_emit(buf, "%s\n", linedisp->message);
    129}
    130
    131/**
    132 * message_store() - write a new message via sysfs
    133 * @dev: the display device
    134 * @attr: the display message attribute
    135 * @buf: the buffer containing the new message
    136 * @count: the size of the message in @buf
    137 *
    138 * Write a new message to display or scroll across the display from sysfs.
    139 *
    140 * Return: the size of the message on success, else -ERRNO
    141 */
    142static ssize_t message_store(struct device *dev, struct device_attribute *attr,
    143			     const char *buf, size_t count)
    144{
    145	struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
    146	int err;
    147
    148	err = linedisp_display(linedisp, buf, count);
    149	return err ?: count;
    150}
    151
    152static DEVICE_ATTR_RW(message);
    153
    154static ssize_t scroll_step_ms_show(struct device *dev,
    155				   struct device_attribute *attr, char *buf)
    156{
    157	struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
    158
    159	return sysfs_emit(buf, "%u\n", jiffies_to_msecs(linedisp->scroll_rate));
    160}
    161
    162static ssize_t scroll_step_ms_store(struct device *dev,
    163				    struct device_attribute *attr,
    164				    const char *buf, size_t count)
    165{
    166	struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
    167	unsigned int ms;
    168
    169	if (kstrtouint(buf, 10, &ms) != 0)
    170		return -EINVAL;
    171
    172	linedisp->scroll_rate = msecs_to_jiffies(ms);
    173	if (linedisp->message && linedisp->message_len > linedisp->num_chars) {
    174		del_timer_sync(&linedisp->timer);
    175		if (linedisp->scroll_rate)
    176			linedisp_scroll(&linedisp->timer);
    177	}
    178
    179	return count;
    180}
    181
    182static DEVICE_ATTR_RW(scroll_step_ms);
    183
    184static struct attribute *linedisp_attrs[] = {
    185	&dev_attr_message.attr,
    186	&dev_attr_scroll_step_ms.attr,
    187	NULL,
    188};
    189ATTRIBUTE_GROUPS(linedisp);
    190
    191static const struct device_type linedisp_type = {
    192	.groups	= linedisp_groups,
    193};
    194
    195/**
    196 * linedisp_register - register a character line display
    197 * @linedisp: pointer to character line display structure
    198 * @parent: parent device
    199 * @num_chars: the number of characters that can be displayed
    200 * @buf: pointer to a buffer that can hold @num_chars characters
    201 * @update: Function called to update the display.  This must not sleep!
    202 *
    203 * Return: zero on success, else a negative error code.
    204 */
    205int linedisp_register(struct linedisp *linedisp, struct device *parent,
    206		      unsigned int num_chars, char *buf,
    207		      void (*update)(struct linedisp *linedisp))
    208{
    209	static atomic_t linedisp_id = ATOMIC_INIT(-1);
    210	int err;
    211
    212	memset(linedisp, 0, sizeof(*linedisp));
    213	linedisp->dev.parent = parent;
    214	linedisp->dev.type = &linedisp_type;
    215	linedisp->update = update;
    216	linedisp->buf = buf;
    217	linedisp->num_chars = num_chars;
    218	linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
    219
    220	device_initialize(&linedisp->dev);
    221	dev_set_name(&linedisp->dev, "linedisp.%lu",
    222		     (unsigned long)atomic_inc_return(&linedisp_id));
    223
    224	/* initialise a timer for scrolling the message */
    225	timer_setup(&linedisp->timer, linedisp_scroll, 0);
    226
    227	err = device_add(&linedisp->dev);
    228	if (err)
    229		goto out_del_timer;
    230
    231	/* display a default message */
    232	err = linedisp_display(linedisp, "Linux " UTS_RELEASE "       ", -1);
    233	if (err)
    234		goto out_del_dev;
    235
    236	return 0;
    237
    238out_del_dev:
    239	device_del(&linedisp->dev);
    240out_del_timer:
    241	del_timer_sync(&linedisp->timer);
    242	put_device(&linedisp->dev);
    243	return err;
    244}
    245EXPORT_SYMBOL_GPL(linedisp_register);
    246
    247/**
    248 * linedisp_unregister - unregister a character line display
    249 * @linedisp: pointer to character line display structure registered previously
    250 *	      with linedisp_register()
    251 */
    252void linedisp_unregister(struct linedisp *linedisp)
    253{
    254	device_del(&linedisp->dev);
    255	del_timer_sync(&linedisp->timer);
    256	kfree(linedisp->message);
    257	put_device(&linedisp->dev);
    258}
    259EXPORT_SYMBOL_GPL(linedisp_unregister);
    260
    261MODULE_LICENSE("GPL");