0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <generated/utsrelease.h>
0012
0013 #include <linux/device.h>
0014 #include <linux/module.h>
0015 #include <linux/slab.h>
0016 #include <linux/string.h>
0017 #include <linux/sysfs.h>
0018 #include <linux/timer.h>
0019
0020 #include "line-display.h"
0021
0022 #define DEFAULT_SCROLL_RATE (HZ / 2)
0023
0024
0025
0026
0027
0028
0029
0030
0031 static void linedisp_scroll(struct timer_list *t)
0032 {
0033 struct linedisp *linedisp = from_timer(linedisp, t, timer);
0034 unsigned int i, ch = linedisp->scroll_pos;
0035 unsigned int num_chars = linedisp->num_chars;
0036
0037
0038 for (i = 0; i < num_chars;) {
0039
0040 for (; i < num_chars && ch < linedisp->message_len; i++, ch++)
0041 linedisp->buf[i] = linedisp->message[ch];
0042
0043
0044 ch = 0;
0045 }
0046
0047
0048 linedisp->update(linedisp);
0049
0050
0051 linedisp->scroll_pos++;
0052 linedisp->scroll_pos %= linedisp->message_len;
0053
0054
0055 if (linedisp->message_len > num_chars && linedisp->scroll_rate)
0056 mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate);
0057 }
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071 static int linedisp_display(struct linedisp *linedisp, const char *msg,
0072 ssize_t count)
0073 {
0074 char *new_msg;
0075
0076
0077 del_timer_sync(&linedisp->timer);
0078
0079 if (count == -1)
0080 count = strlen(msg);
0081
0082
0083 if (msg[count - 1] == '\n')
0084 count--;
0085
0086 if (!count) {
0087
0088 kfree(linedisp->message);
0089 linedisp->message = NULL;
0090 linedisp->message_len = 0;
0091 memset(linedisp->buf, ' ', linedisp->num_chars);
0092 linedisp->update(linedisp);
0093 return 0;
0094 }
0095
0096 new_msg = kmemdup_nul(msg, count, GFP_KERNEL);
0097 if (!new_msg)
0098 return -ENOMEM;
0099
0100 kfree(linedisp->message);
0101
0102 linedisp->message = new_msg;
0103 linedisp->message_len = count;
0104 linedisp->scroll_pos = 0;
0105
0106
0107 linedisp_scroll(&linedisp->timer);
0108
0109 return 0;
0110 }
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123 static ssize_t message_show(struct device *dev, struct device_attribute *attr,
0124 char *buf)
0125 {
0126 struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
0127
0128 return sysfs_emit(buf, "%s\n", linedisp->message);
0129 }
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142 static ssize_t message_store(struct device *dev, struct device_attribute *attr,
0143 const char *buf, size_t count)
0144 {
0145 struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
0146 int err;
0147
0148 err = linedisp_display(linedisp, buf, count);
0149 return err ?: count;
0150 }
0151
0152 static DEVICE_ATTR_RW(message);
0153
0154 static ssize_t scroll_step_ms_show(struct device *dev,
0155 struct device_attribute *attr, char *buf)
0156 {
0157 struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
0158
0159 return sysfs_emit(buf, "%u\n", jiffies_to_msecs(linedisp->scroll_rate));
0160 }
0161
0162 static ssize_t scroll_step_ms_store(struct device *dev,
0163 struct device_attribute *attr,
0164 const char *buf, size_t count)
0165 {
0166 struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
0167 unsigned int ms;
0168
0169 if (kstrtouint(buf, 10, &ms) != 0)
0170 return -EINVAL;
0171
0172 linedisp->scroll_rate = msecs_to_jiffies(ms);
0173 if (linedisp->message && linedisp->message_len > linedisp->num_chars) {
0174 del_timer_sync(&linedisp->timer);
0175 if (linedisp->scroll_rate)
0176 linedisp_scroll(&linedisp->timer);
0177 }
0178
0179 return count;
0180 }
0181
0182 static DEVICE_ATTR_RW(scroll_step_ms);
0183
0184 static struct attribute *linedisp_attrs[] = {
0185 &dev_attr_message.attr,
0186 &dev_attr_scroll_step_ms.attr,
0187 NULL,
0188 };
0189 ATTRIBUTE_GROUPS(linedisp);
0190
0191 static const struct device_type linedisp_type = {
0192 .groups = linedisp_groups,
0193 };
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205 int linedisp_register(struct linedisp *linedisp, struct device *parent,
0206 unsigned int num_chars, char *buf,
0207 void (*update)(struct linedisp *linedisp))
0208 {
0209 static atomic_t linedisp_id = ATOMIC_INIT(-1);
0210 int err;
0211
0212 memset(linedisp, 0, sizeof(*linedisp));
0213 linedisp->dev.parent = parent;
0214 linedisp->dev.type = &linedisp_type;
0215 linedisp->update = update;
0216 linedisp->buf = buf;
0217 linedisp->num_chars = num_chars;
0218 linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
0219
0220 device_initialize(&linedisp->dev);
0221 dev_set_name(&linedisp->dev, "linedisp.%lu",
0222 (unsigned long)atomic_inc_return(&linedisp_id));
0223
0224
0225 timer_setup(&linedisp->timer, linedisp_scroll, 0);
0226
0227 err = device_add(&linedisp->dev);
0228 if (err)
0229 goto out_del_timer;
0230
0231
0232 err = linedisp_display(linedisp, "Linux " UTS_RELEASE " ", -1);
0233 if (err)
0234 goto out_del_dev;
0235
0236 return 0;
0237
0238 out_del_dev:
0239 device_del(&linedisp->dev);
0240 out_del_timer:
0241 del_timer_sync(&linedisp->timer);
0242 put_device(&linedisp->dev);
0243 return err;
0244 }
0245 EXPORT_SYMBOL_GPL(linedisp_register);
0246
0247
0248
0249
0250
0251
0252 void linedisp_unregister(struct linedisp *linedisp)
0253 {
0254 device_del(&linedisp->dev);
0255 del_timer_sync(&linedisp->timer);
0256 kfree(linedisp->message);
0257 put_device(&linedisp->dev);
0258 }
0259 EXPORT_SYMBOL_GPL(linedisp_unregister);
0260
0261 MODULE_LICENSE("GPL");