Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /*
3 : * fbsysfs.c - framebuffer device class and attributes
4 : *
5 : * Copyright (c) 2004 James Simmons <jsimmons@infradead.org>
6 : */
7 :
8 : #include <linux/console.h>
9 : #include <linux/fb.h>
10 : #include <linux/fbcon.h>
11 : #include <linux/major.h>
12 :
13 : #include "fb_internal.h"
14 :
15 : #define FB_SYSFS_FLAG_ATTR 1
16 :
17 0 : static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
18 : {
19 : int err;
20 :
21 0 : var->activate |= FB_ACTIVATE_FORCE;
22 0 : console_lock();
23 0 : lock_fb_info(fb_info);
24 0 : err = fb_set_var(fb_info, var);
25 : if (!err)
26 : fbcon_update_vcs(fb_info, var->activate & FB_ACTIVATE_ALL);
27 0 : unlock_fb_info(fb_info);
28 0 : console_unlock();
29 0 : if (err)
30 : return err;
31 0 : return 0;
32 : }
33 :
34 0 : static int mode_string(char *buf, unsigned int offset,
35 : const struct fb_videomode *mode)
36 : {
37 0 : char m = 'U';
38 0 : char v = 'p';
39 :
40 0 : if (mode->flag & FB_MODE_IS_DETAILED)
41 0 : m = 'D';
42 0 : if (mode->flag & FB_MODE_IS_VESA)
43 0 : m = 'V';
44 0 : if (mode->flag & FB_MODE_IS_STANDARD)
45 0 : m = 'S';
46 :
47 0 : if (mode->vmode & FB_VMODE_INTERLACED)
48 0 : v = 'i';
49 0 : if (mode->vmode & FB_VMODE_DOUBLE)
50 0 : v = 'd';
51 :
52 0 : return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
53 : m, mode->xres, mode->yres, v, mode->refresh);
54 : }
55 :
56 0 : static ssize_t store_mode(struct device *device, struct device_attribute *attr,
57 : const char *buf, size_t count)
58 : {
59 0 : struct fb_info *fb_info = dev_get_drvdata(device);
60 : char mstr[100];
61 : struct fb_var_screeninfo var;
62 : struct fb_modelist *modelist;
63 : struct fb_videomode *mode;
64 : struct list_head *pos;
65 : size_t i;
66 : int err;
67 :
68 0 : memset(&var, 0, sizeof(var));
69 :
70 0 : list_for_each(pos, &fb_info->modelist) {
71 0 : modelist = list_entry(pos, struct fb_modelist, list);
72 0 : mode = &modelist->mode;
73 0 : i = mode_string(mstr, 0, mode);
74 0 : if (strncmp(mstr, buf, max(count, i)) == 0) {
75 :
76 0 : var = fb_info->var;
77 0 : fb_videomode_to_var(&var, mode);
78 0 : if ((err = activate(fb_info, &var)))
79 0 : return err;
80 0 : fb_info->mode = mode;
81 0 : return count;
82 : }
83 : }
84 : return -EINVAL;
85 : }
86 :
87 0 : static ssize_t show_mode(struct device *device, struct device_attribute *attr,
88 : char *buf)
89 : {
90 0 : struct fb_info *fb_info = dev_get_drvdata(device);
91 :
92 0 : if (!fb_info->mode)
93 : return 0;
94 :
95 0 : return mode_string(buf, 0, fb_info->mode);
96 : }
97 :
98 0 : static ssize_t store_modes(struct device *device,
99 : struct device_attribute *attr,
100 : const char *buf, size_t count)
101 : {
102 0 : struct fb_info *fb_info = dev_get_drvdata(device);
103 0 : LIST_HEAD(old_list);
104 0 : int i = count / sizeof(struct fb_videomode);
105 :
106 0 : if (i * sizeof(struct fb_videomode) != count)
107 : return -EINVAL;
108 :
109 0 : console_lock();
110 0 : lock_fb_info(fb_info);
111 :
112 0 : list_splice(&fb_info->modelist, &old_list);
113 0 : fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
114 : &fb_info->modelist);
115 0 : if (fb_new_modelist(fb_info)) {
116 0 : fb_destroy_modelist(&fb_info->modelist);
117 0 : list_splice(&old_list, &fb_info->modelist);
118 : } else
119 0 : fb_destroy_modelist(&old_list);
120 :
121 0 : unlock_fb_info(fb_info);
122 0 : console_unlock();
123 :
124 0 : return 0;
125 : }
126 :
127 0 : static ssize_t show_modes(struct device *device, struct device_attribute *attr,
128 : char *buf)
129 : {
130 0 : struct fb_info *fb_info = dev_get_drvdata(device);
131 : unsigned int i;
132 : struct list_head *pos;
133 : struct fb_modelist *modelist;
134 : const struct fb_videomode *mode;
135 :
136 0 : i = 0;
137 0 : list_for_each(pos, &fb_info->modelist) {
138 0 : modelist = list_entry(pos, struct fb_modelist, list);
139 0 : mode = &modelist->mode;
140 0 : i += mode_string(buf, i, mode);
141 : }
142 0 : return i;
143 : }
144 :
145 0 : static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
146 : const char *buf, size_t count)
147 : {
148 0 : struct fb_info *fb_info = dev_get_drvdata(device);
149 : struct fb_var_screeninfo var;
150 0 : char ** last = NULL;
151 : int err;
152 :
153 0 : var = fb_info->var;
154 0 : var.bits_per_pixel = simple_strtoul(buf, last, 0);
155 0 : if ((err = activate(fb_info, &var)))
156 0 : return err;
157 0 : return count;
158 : }
159 :
160 0 : static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
161 : char *buf)
162 : {
163 0 : struct fb_info *fb_info = dev_get_drvdata(device);
164 0 : return sysfs_emit(buf, "%d\n", fb_info->var.bits_per_pixel);
165 : }
166 :
167 0 : static ssize_t store_rotate(struct device *device,
168 : struct device_attribute *attr,
169 : const char *buf, size_t count)
170 : {
171 0 : struct fb_info *fb_info = dev_get_drvdata(device);
172 : struct fb_var_screeninfo var;
173 0 : char **last = NULL;
174 : int err;
175 :
176 0 : var = fb_info->var;
177 0 : var.rotate = simple_strtoul(buf, last, 0);
178 :
179 0 : if ((err = activate(fb_info, &var)))
180 0 : return err;
181 :
182 0 : return count;
183 : }
184 :
185 :
186 0 : static ssize_t show_rotate(struct device *device,
187 : struct device_attribute *attr, char *buf)
188 : {
189 0 : struct fb_info *fb_info = dev_get_drvdata(device);
190 :
191 0 : return sysfs_emit(buf, "%d\n", fb_info->var.rotate);
192 : }
193 :
194 0 : static ssize_t store_virtual(struct device *device,
195 : struct device_attribute *attr,
196 : const char *buf, size_t count)
197 : {
198 0 : struct fb_info *fb_info = dev_get_drvdata(device);
199 : struct fb_var_screeninfo var;
200 0 : char *last = NULL;
201 : int err;
202 :
203 0 : var = fb_info->var;
204 0 : var.xres_virtual = simple_strtoul(buf, &last, 0);
205 0 : last++;
206 0 : if (last - buf >= count)
207 : return -EINVAL;
208 0 : var.yres_virtual = simple_strtoul(last, &last, 0);
209 :
210 0 : if ((err = activate(fb_info, &var)))
211 0 : return err;
212 0 : return count;
213 : }
214 :
215 0 : static ssize_t show_virtual(struct device *device,
216 : struct device_attribute *attr, char *buf)
217 : {
218 0 : struct fb_info *fb_info = dev_get_drvdata(device);
219 0 : return sysfs_emit(buf, "%d,%d\n", fb_info->var.xres_virtual,
220 : fb_info->var.yres_virtual);
221 : }
222 :
223 0 : static ssize_t show_stride(struct device *device,
224 : struct device_attribute *attr, char *buf)
225 : {
226 0 : struct fb_info *fb_info = dev_get_drvdata(device);
227 0 : return sysfs_emit(buf, "%d\n", fb_info->fix.line_length);
228 : }
229 :
230 0 : static ssize_t store_blank(struct device *device,
231 : struct device_attribute *attr,
232 : const char *buf, size_t count)
233 : {
234 0 : struct fb_info *fb_info = dev_get_drvdata(device);
235 0 : char *last = NULL;
236 : int err, arg;
237 :
238 0 : arg = simple_strtoul(buf, &last, 0);
239 0 : console_lock();
240 0 : err = fb_blank(fb_info, arg);
241 : /* might again call into fb_blank */
242 0 : fbcon_fb_blanked(fb_info, arg);
243 0 : console_unlock();
244 0 : if (err < 0)
245 0 : return err;
246 0 : return count;
247 : }
248 :
249 0 : static ssize_t show_blank(struct device *device,
250 : struct device_attribute *attr, char *buf)
251 : {
252 : // struct fb_info *fb_info = dev_get_drvdata(device);
253 0 : return 0;
254 : }
255 :
256 0 : static ssize_t store_console(struct device *device,
257 : struct device_attribute *attr,
258 : const char *buf, size_t count)
259 : {
260 : // struct fb_info *fb_info = dev_get_drvdata(device);
261 0 : return 0;
262 : }
263 :
264 0 : static ssize_t show_console(struct device *device,
265 : struct device_attribute *attr, char *buf)
266 : {
267 : // struct fb_info *fb_info = dev_get_drvdata(device);
268 0 : return 0;
269 : }
270 :
271 0 : static ssize_t store_cursor(struct device *device,
272 : struct device_attribute *attr,
273 : const char *buf, size_t count)
274 : {
275 : // struct fb_info *fb_info = dev_get_drvdata(device);
276 0 : return 0;
277 : }
278 :
279 0 : static ssize_t show_cursor(struct device *device,
280 : struct device_attribute *attr, char *buf)
281 : {
282 : // struct fb_info *fb_info = dev_get_drvdata(device);
283 0 : return 0;
284 : }
285 :
286 0 : static ssize_t store_pan(struct device *device,
287 : struct device_attribute *attr,
288 : const char *buf, size_t count)
289 : {
290 0 : struct fb_info *fb_info = dev_get_drvdata(device);
291 : struct fb_var_screeninfo var;
292 0 : char *last = NULL;
293 : int err;
294 :
295 0 : var = fb_info->var;
296 0 : var.xoffset = simple_strtoul(buf, &last, 0);
297 0 : last++;
298 0 : if (last - buf >= count)
299 : return -EINVAL;
300 0 : var.yoffset = simple_strtoul(last, &last, 0);
301 :
302 0 : console_lock();
303 0 : err = fb_pan_display(fb_info, &var);
304 0 : console_unlock();
305 :
306 0 : if (err < 0)
307 0 : return err;
308 0 : return count;
309 : }
310 :
311 0 : static ssize_t show_pan(struct device *device,
312 : struct device_attribute *attr, char *buf)
313 : {
314 0 : struct fb_info *fb_info = dev_get_drvdata(device);
315 0 : return sysfs_emit(buf, "%d,%d\n", fb_info->var.xoffset,
316 : fb_info->var.yoffset);
317 : }
318 :
319 0 : static ssize_t show_name(struct device *device,
320 : struct device_attribute *attr, char *buf)
321 : {
322 0 : struct fb_info *fb_info = dev_get_drvdata(device);
323 :
324 0 : return sysfs_emit(buf, "%s\n", fb_info->fix.id);
325 : }
326 :
327 0 : static ssize_t store_fbstate(struct device *device,
328 : struct device_attribute *attr,
329 : const char *buf, size_t count)
330 : {
331 0 : struct fb_info *fb_info = dev_get_drvdata(device);
332 : u32 state;
333 0 : char *last = NULL;
334 :
335 0 : state = simple_strtoul(buf, &last, 0);
336 :
337 0 : console_lock();
338 0 : lock_fb_info(fb_info);
339 :
340 0 : fb_set_suspend(fb_info, (int)state);
341 :
342 0 : unlock_fb_info(fb_info);
343 0 : console_unlock();
344 :
345 0 : return count;
346 : }
347 :
348 0 : static ssize_t show_fbstate(struct device *device,
349 : struct device_attribute *attr, char *buf)
350 : {
351 0 : struct fb_info *fb_info = dev_get_drvdata(device);
352 0 : return sysfs_emit(buf, "%d\n", fb_info->state);
353 : }
354 :
355 : #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
356 : static ssize_t store_bl_curve(struct device *device,
357 : struct device_attribute *attr,
358 : const char *buf, size_t count)
359 : {
360 : struct fb_info *fb_info = dev_get_drvdata(device);
361 : u8 tmp_curve[FB_BACKLIGHT_LEVELS];
362 : unsigned int i;
363 :
364 : /* Some drivers don't use framebuffer_alloc(), but those also
365 : * don't have backlights.
366 : */
367 : if (!fb_info || !fb_info->bl_dev)
368 : return -ENODEV;
369 :
370 : if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
371 : return -EINVAL;
372 :
373 : for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
374 : if (sscanf(&buf[i * 24],
375 : "%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
376 : &tmp_curve[i * 8 + 0],
377 : &tmp_curve[i * 8 + 1],
378 : &tmp_curve[i * 8 + 2],
379 : &tmp_curve[i * 8 + 3],
380 : &tmp_curve[i * 8 + 4],
381 : &tmp_curve[i * 8 + 5],
382 : &tmp_curve[i * 8 + 6],
383 : &tmp_curve[i * 8 + 7]) != 8)
384 : return -EINVAL;
385 :
386 : /* If there has been an error in the input data, we won't
387 : * reach this loop.
388 : */
389 : mutex_lock(&fb_info->bl_curve_mutex);
390 : for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
391 : fb_info->bl_curve[i] = tmp_curve[i];
392 : mutex_unlock(&fb_info->bl_curve_mutex);
393 :
394 : return count;
395 : }
396 :
397 : static ssize_t show_bl_curve(struct device *device,
398 : struct device_attribute *attr, char *buf)
399 : {
400 : struct fb_info *fb_info = dev_get_drvdata(device);
401 : ssize_t len = 0;
402 : unsigned int i;
403 :
404 : /* Some drivers don't use framebuffer_alloc(), but those also
405 : * don't have backlights.
406 : */
407 : if (!fb_info || !fb_info->bl_dev)
408 : return -ENODEV;
409 :
410 : mutex_lock(&fb_info->bl_curve_mutex);
411 : for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
412 : len += scnprintf(&buf[len], PAGE_SIZE - len, "%8ph\n",
413 : fb_info->bl_curve + i);
414 : mutex_unlock(&fb_info->bl_curve_mutex);
415 :
416 : return len;
417 : }
418 : #endif
419 :
420 : /* When cmap is added back in it should be a binary attribute
421 : * not a text one. Consideration should also be given to converting
422 : * fbdev to use configfs instead of sysfs */
423 : static struct device_attribute device_attrs[] = {
424 : __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
425 : __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
426 : __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
427 : __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
428 : __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
429 : __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
430 : __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
431 : __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
432 : __ATTR(name, S_IRUGO, show_name, NULL),
433 : __ATTR(stride, S_IRUGO, show_stride, NULL),
434 : __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
435 : __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
436 : #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
437 : __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
438 : #endif
439 : };
440 :
441 0 : static int fb_init_device(struct fb_info *fb_info)
442 : {
443 0 : int i, error = 0;
444 :
445 0 : dev_set_drvdata(fb_info->dev, fb_info);
446 :
447 0 : fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
448 :
449 0 : for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
450 0 : error = device_create_file(fb_info->dev, &device_attrs[i]);
451 :
452 0 : if (error)
453 : break;
454 : }
455 :
456 0 : if (error) {
457 0 : while (--i >= 0)
458 0 : device_remove_file(fb_info->dev, &device_attrs[i]);
459 0 : fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
460 : }
461 :
462 0 : return 0;
463 : }
464 :
465 0 : static void fb_cleanup_device(struct fb_info *fb_info)
466 : {
467 : unsigned int i;
468 :
469 0 : if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
470 0 : for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
471 0 : device_remove_file(fb_info->dev, &device_attrs[i]);
472 :
473 0 : fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
474 : }
475 0 : }
476 :
477 0 : int fb_device_create(struct fb_info *fb_info)
478 : {
479 0 : int node = fb_info->node;
480 0 : dev_t devt = MKDEV(FB_MAJOR, node);
481 : int ret;
482 :
483 0 : fb_info->dev = device_create(fb_class, fb_info->device, devt, NULL, "fb%d", node);
484 0 : if (IS_ERR(fb_info->dev)) {
485 : /* Not fatal */
486 0 : ret = PTR_ERR(fb_info->dev);
487 0 : pr_warn("Unable to create device for framebuffer %d; error %d\n", node, ret);
488 0 : fb_info->dev = NULL;
489 : } else {
490 0 : fb_init_device(fb_info);
491 : }
492 :
493 0 : return 0;
494 : }
495 :
496 0 : void fb_device_destroy(struct fb_info *fb_info)
497 : {
498 0 : dev_t devt = MKDEV(FB_MAJOR, fb_info->node);
499 :
500 0 : if (!fb_info->dev)
501 : return;
502 :
503 0 : fb_cleanup_device(fb_info);
504 0 : device_destroy(fb_class, devt);
505 0 : fb_info->dev = NULL;
506 : }
|