]> Git Repo - linux.git/blob - drivers/auxdisplay/line-display.c
crypto: akcipher - Drop sign/verify operations
[linux.git] / drivers / auxdisplay / line-display.c
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 <[email protected]>
7  *
8  * Copyright (C) 2021 Glider bv
9  */
10
11 #ifndef CONFIG_PANEL_BOOT_MESSAGE
12 #include <generated/utsrelease.h>
13 #endif
14
15 #include <linux/container_of.h>
16 #include <linux/device.h>
17 #include <linux/export.h>
18 #include <linux/idr.h>
19 #include <linux/jiffies.h>
20 #include <linux/kstrtox.h>
21 #include <linux/module.h>
22 #include <linux/slab.h>
23 #include <linux/string.h>
24 #include <linux/sysfs.h>
25 #include <linux/timer.h>
26
27 #include <linux/map_to_7segment.h>
28 #include <linux/map_to_14segment.h>
29
30 #include "line-display.h"
31
32 #define DEFAULT_SCROLL_RATE     (HZ / 2)
33
34 /**
35  * linedisp_scroll() - scroll the display by a character
36  * @t: really a pointer to the private data structure
37  *
38  * Scroll the current message along the display by one character, rearming the
39  * timer if required.
40  */
41 static void linedisp_scroll(struct timer_list *t)
42 {
43         struct linedisp *linedisp = from_timer(linedisp, t, timer);
44         unsigned int i, ch = linedisp->scroll_pos;
45         unsigned int num_chars = linedisp->num_chars;
46
47         /* update the current message string */
48         for (i = 0; i < num_chars;) {
49                 /* copy as many characters from the string as possible */
50                 for (; i < num_chars && ch < linedisp->message_len; i++, ch++)
51                         linedisp->buf[i] = linedisp->message[ch];
52
53                 /* wrap around to the start of the string */
54                 ch = 0;
55         }
56
57         /* update the display */
58         linedisp->ops->update(linedisp);
59
60         /* move on to the next character */
61         linedisp->scroll_pos++;
62         linedisp->scroll_pos %= linedisp->message_len;
63
64         /* rearm the timer */
65         if (linedisp->message_len > num_chars && linedisp->scroll_rate)
66                 mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate);
67 }
68
69 /**
70  * linedisp_display() - set the message to be displayed
71  * @linedisp: pointer to the private data structure
72  * @msg: the message to display
73  * @count: length of msg, or -1
74  *
75  * Display a new message @msg on the display. @msg can be longer than the
76  * number of characters the display can display, in which case it will begin
77  * scrolling across the display.
78  *
79  * Return: 0 on success, -ENOMEM on memory allocation failure
80  */
81 static int linedisp_display(struct linedisp *linedisp, const char *msg,
82                             ssize_t count)
83 {
84         char *new_msg;
85
86         /* stop the scroll timer */
87         del_timer_sync(&linedisp->timer);
88
89         if (count == -1)
90                 count = strlen(msg);
91
92         /* if the string ends with a newline, trim it */
93         if (msg[count - 1] == '\n')
94                 count--;
95
96         if (!count) {
97                 /* Clear the display */
98                 kfree(linedisp->message);
99                 linedisp->message = NULL;
100                 linedisp->message_len = 0;
101                 memset(linedisp->buf, ' ', linedisp->num_chars);
102                 linedisp->ops->update(linedisp);
103                 return 0;
104         }
105
106         new_msg = kmemdup_nul(msg, count, GFP_KERNEL);
107         if (!new_msg)
108                 return -ENOMEM;
109
110         kfree(linedisp->message);
111
112         linedisp->message = new_msg;
113         linedisp->message_len = count;
114         linedisp->scroll_pos = 0;
115
116         /* update the display */
117         linedisp_scroll(&linedisp->timer);
118
119         return 0;
120 }
121
122 /**
123  * message_show() - read message via sysfs
124  * @dev: the display device
125  * @attr: the display message attribute
126  * @buf: the buffer to read the message into
127  *
128  * Read the current message being displayed or scrolled across the display into
129  * @buf, for reads from sysfs.
130  *
131  * Return: the number of characters written to @buf
132  */
133 static ssize_t message_show(struct device *dev, struct device_attribute *attr,
134                             char *buf)
135 {
136         struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
137
138         return sysfs_emit(buf, "%s\n", linedisp->message);
139 }
140
141 /**
142  * message_store() - write a new message via sysfs
143  * @dev: the display device
144  * @attr: the display message attribute
145  * @buf: the buffer containing the new message
146  * @count: the size of the message in @buf
147  *
148  * Write a new message to display or scroll across the display from sysfs.
149  *
150  * Return: the size of the message on success, else -ERRNO
151  */
152 static ssize_t message_store(struct device *dev, struct device_attribute *attr,
153                              const char *buf, size_t count)
154 {
155         struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
156         int err;
157
158         err = linedisp_display(linedisp, buf, count);
159         return err ?: count;
160 }
161
162 static DEVICE_ATTR_RW(message);
163
164 static ssize_t scroll_step_ms_show(struct device *dev,
165                                    struct device_attribute *attr, char *buf)
166 {
167         struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
168
169         return sysfs_emit(buf, "%u\n", jiffies_to_msecs(linedisp->scroll_rate));
170 }
171
172 static ssize_t scroll_step_ms_store(struct device *dev,
173                                     struct device_attribute *attr,
174                                     const char *buf, size_t count)
175 {
176         struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
177         unsigned int ms;
178         int err;
179
180         err = kstrtouint(buf, 10, &ms);
181         if (err)
182                 return err;
183
184         linedisp->scroll_rate = msecs_to_jiffies(ms);
185         if (linedisp->message && linedisp->message_len > linedisp->num_chars) {
186                 del_timer_sync(&linedisp->timer);
187                 if (linedisp->scroll_rate)
188                         linedisp_scroll(&linedisp->timer);
189         }
190
191         return count;
192 }
193
194 static DEVICE_ATTR_RW(scroll_step_ms);
195
196 static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, char *buf)
197 {
198         struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
199         struct linedisp_map *map = linedisp->map;
200
201         memcpy(buf, &map->map, map->size);
202         return map->size;
203 }
204
205 static ssize_t map_seg_store(struct device *dev, struct device_attribute *attr,
206                              const char *buf, size_t count)
207 {
208         struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
209         struct linedisp_map *map = linedisp->map;
210
211         if (count != map->size)
212                 return -EINVAL;
213
214         memcpy(&map->map, buf, count);
215         return count;
216 }
217
218 static const SEG7_DEFAULT_MAP(initial_map_seg7);
219 static DEVICE_ATTR(map_seg7, 0644, map_seg_show, map_seg_store);
220
221 static const SEG14_DEFAULT_MAP(initial_map_seg14);
222 static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store);
223
224 static struct attribute *linedisp_attrs[] = {
225         &dev_attr_message.attr,
226         &dev_attr_scroll_step_ms.attr,
227         &dev_attr_map_seg7.attr,
228         &dev_attr_map_seg14.attr,
229         NULL
230 };
231
232 static umode_t linedisp_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
233 {
234         struct device *dev = kobj_to_dev(kobj);
235         struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
236         struct linedisp_map *map = linedisp->map;
237         umode_t mode = attr->mode;
238
239         if (attr == &dev_attr_map_seg7.attr) {
240                 if (!map)
241                         return 0;
242                 if (map->type != LINEDISP_MAP_SEG7)
243                         return 0;
244         }
245
246         if (attr == &dev_attr_map_seg14.attr) {
247                 if (!map)
248                         return 0;
249                 if (map->type != LINEDISP_MAP_SEG14)
250                         return 0;
251         }
252
253         return mode;
254 };
255
256 static const struct attribute_group linedisp_group = {
257         .is_visible     = linedisp_attr_is_visible,
258         .attrs          = linedisp_attrs,
259 };
260 __ATTRIBUTE_GROUPS(linedisp);
261
262 static DEFINE_IDA(linedisp_id);
263
264 static void linedisp_release(struct device *dev)
265 {
266         struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
267
268         kfree(linedisp->map);
269         kfree(linedisp->message);
270         kfree(linedisp->buf);
271         ida_free(&linedisp_id, linedisp->id);
272 }
273
274 static const struct device_type linedisp_type = {
275         .groups = linedisp_groups,
276         .release = linedisp_release,
277 };
278
279 static int linedisp_init_map(struct linedisp *linedisp)
280 {
281         struct linedisp_map *map;
282         int err;
283
284         if (!linedisp->ops->get_map_type)
285                 return 0;
286
287         err = linedisp->ops->get_map_type(linedisp);
288         if (err < 0)
289                 return err;
290
291         map = kmalloc(sizeof(*map), GFP_KERNEL);
292         if (!map)
293                 return -ENOMEM;
294
295         map->type = err;
296
297         /* assign initial mapping */
298         switch (map->type) {
299         case LINEDISP_MAP_SEG7:
300                 map->map.seg7 = initial_map_seg7;
301                 map->size = sizeof(map->map.seg7);
302                 break;
303         case LINEDISP_MAP_SEG14:
304                 map->map.seg14 = initial_map_seg14;
305                 map->size = sizeof(map->map.seg14);
306                 break;
307         default:
308                 kfree(map);
309                 return -EINVAL;
310         }
311
312         linedisp->map = map;
313
314         return 0;
315 }
316
317 #ifdef CONFIG_PANEL_BOOT_MESSAGE
318 #define LINEDISP_INIT_TEXT CONFIG_PANEL_BOOT_MESSAGE
319 #else
320 #define LINEDISP_INIT_TEXT "Linux " UTS_RELEASE "       "
321 #endif
322
323 /**
324  * linedisp_register - register a character line display
325  * @linedisp: pointer to character line display structure
326  * @parent: parent device
327  * @num_chars: the number of characters that can be displayed
328  * @ops: character line display operations
329  *
330  * Return: zero on success, else a negative error code.
331  */
332 int linedisp_register(struct linedisp *linedisp, struct device *parent,
333                       unsigned int num_chars, const struct linedisp_ops *ops)
334 {
335         int err;
336
337         memset(linedisp, 0, sizeof(*linedisp));
338         linedisp->dev.parent = parent;
339         linedisp->dev.type = &linedisp_type;
340         linedisp->ops = ops;
341         linedisp->num_chars = num_chars;
342         linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
343
344         err = ida_alloc(&linedisp_id, GFP_KERNEL);
345         if (err < 0)
346                 return err;
347         linedisp->id = err;
348
349         device_initialize(&linedisp->dev);
350         dev_set_name(&linedisp->dev, "linedisp.%u", linedisp->id);
351
352         err = -ENOMEM;
353         linedisp->buf = kzalloc(linedisp->num_chars, GFP_KERNEL);
354         if (!linedisp->buf)
355                 goto out_put_device;
356
357         /* initialise a character mapping, if required */
358         err = linedisp_init_map(linedisp);
359         if (err)
360                 goto out_put_device;
361
362         /* initialise a timer for scrolling the message */
363         timer_setup(&linedisp->timer, linedisp_scroll, 0);
364
365         err = device_add(&linedisp->dev);
366         if (err)
367                 goto out_del_timer;
368
369         /* display a default message */
370         err = linedisp_display(linedisp, LINEDISP_INIT_TEXT, -1);
371         if (err)
372                 goto out_del_dev;
373
374         return 0;
375
376 out_del_dev:
377         device_del(&linedisp->dev);
378 out_del_timer:
379         del_timer_sync(&linedisp->timer);
380 out_put_device:
381         put_device(&linedisp->dev);
382         return err;
383 }
384 EXPORT_SYMBOL_NS_GPL(linedisp_register, LINEDISP);
385
386 /**
387  * linedisp_unregister - unregister a character line display
388  * @linedisp: pointer to character line display structure registered previously
389  *            with linedisp_register()
390  */
391 void linedisp_unregister(struct linedisp *linedisp)
392 {
393         device_del(&linedisp->dev);
394         del_timer_sync(&linedisp->timer);
395         put_device(&linedisp->dev);
396 }
397 EXPORT_SYMBOL_NS_GPL(linedisp_unregister, LINEDISP);
398
399 MODULE_DESCRIPTION("Character line display core support");
400 MODULE_LICENSE("GPL");
This page took 0.055199 seconds and 4 git commands to generate.