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