Branch data 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 : : /*
9 : : * Note: currently there's only stubs for framebuffer_alloc and
10 : : * framebuffer_release here. The reson for that is that until all drivers
11 : : * are converted to use it a sysfsification will open OOPSable races.
12 : : */
13 : :
14 : : #include <linux/kernel.h>
15 : : #include <linux/slab.h>
16 : : #include <linux/fb.h>
17 : : #include <linux/fbcon.h>
18 : : #include <linux/console.h>
19 : : #include <linux/module.h>
20 : :
21 : : #define FB_SYSFS_FLAG_ATTR 1
22 : :
23 : : /**
24 : : * framebuffer_alloc - creates a new frame buffer info structure
25 : : *
26 : : * @size: size of driver private data, can be zero
27 : : * @dev: pointer to the device for this fb, this can be NULL
28 : : *
29 : : * Creates a new frame buffer info structure. Also reserves @size bytes
30 : : * for driver private data (info->par). info->par (if any) will be
31 : : * aligned to sizeof(long).
32 : : *
33 : : * Returns the new structure, or NULL if an error occurred.
34 : : *
35 : : */
36 : 0 : struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
37 : : {
38 : : #define BYTES_PER_LONG (BITS_PER_LONG/8)
39 : : #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
40 : : int fb_info_size = sizeof(struct fb_info);
41 : : struct fb_info *info;
42 : : char *p;
43 : :
44 [ # # ]: 0 : if (size)
45 : : fb_info_size += PADDING;
46 : :
47 : 0 : p = kzalloc(fb_info_size + size, GFP_KERNEL);
48 : :
49 [ # # ]: 0 : if (!p)
50 : : return NULL;
51 : :
52 : : info = (struct fb_info *) p;
53 : :
54 [ # # ]: 0 : if (size)
55 : 0 : info->par = p + fb_info_size;
56 : :
57 : 0 : info->device = dev;
58 : 0 : info->fbcon_rotate_hint = -1;
59 : :
60 : : #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
61 : 0 : mutex_init(&info->bl_curve_mutex);
62 : : #endif
63 : :
64 : 0 : return info;
65 : : #undef PADDING
66 : : #undef BYTES_PER_LONG
67 : : }
68 : : EXPORT_SYMBOL(framebuffer_alloc);
69 : :
70 : : /**
71 : : * framebuffer_release - marks the structure available for freeing
72 : : *
73 : : * @info: frame buffer info structure
74 : : *
75 : : * Drop the reference count of the device embedded in the
76 : : * framebuffer info structure.
77 : : *
78 : : */
79 : 0 : void framebuffer_release(struct fb_info *info)
80 : : {
81 [ # # ]: 0 : if (!info)
82 : 0 : return;
83 : 0 : kfree(info->apertures);
84 : 0 : kfree(info);
85 : : }
86 : : EXPORT_SYMBOL(framebuffer_release);
87 : :
88 : 0 : static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
89 : : {
90 : : int err;
91 : :
92 : 0 : var->activate |= FB_ACTIVATE_FORCE;
93 : 0 : console_lock();
94 : 0 : fb_info->flags |= FBINFO_MISC_USEREVENT;
95 : 0 : err = fb_set_var(fb_info, var);
96 : 0 : fb_info->flags &= ~FBINFO_MISC_USEREVENT;
97 : 0 : console_unlock();
98 [ # # ]: 0 : if (err)
99 : 0 : return err;
100 : : return 0;
101 : : }
102 : :
103 : 0 : static int mode_string(char *buf, unsigned int offset,
104 : : const struct fb_videomode *mode)
105 : : {
106 : : char m = 'U';
107 : : char v = 'p';
108 : :
109 [ # # ]: 0 : if (mode->flag & FB_MODE_IS_DETAILED)
110 : : m = 'D';
111 [ # # ]: 0 : if (mode->flag & FB_MODE_IS_VESA)
112 : : m = 'V';
113 [ # # ]: 0 : if (mode->flag & FB_MODE_IS_STANDARD)
114 : : m = 'S';
115 : :
116 [ # # ]: 0 : if (mode->vmode & FB_VMODE_INTERLACED)
117 : : v = 'i';
118 [ # # ]: 0 : if (mode->vmode & FB_VMODE_DOUBLE)
119 : : v = 'd';
120 : :
121 : 0 : return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
122 : : m, mode->xres, mode->yres, v, mode->refresh);
123 : : }
124 : :
125 : 0 : static ssize_t store_mode(struct device *device, struct device_attribute *attr,
126 : : const char *buf, size_t count)
127 : : {
128 : : struct fb_info *fb_info = dev_get_drvdata(device);
129 : : char mstr[100];
130 : : struct fb_var_screeninfo var;
131 : : struct fb_modelist *modelist;
132 : : struct fb_videomode *mode;
133 : : struct list_head *pos;
134 : : size_t i;
135 : : int err;
136 : :
137 : 0 : memset(&var, 0, sizeof(var));
138 : :
139 [ # # ]: 0 : list_for_each(pos, &fb_info->modelist) {
140 : : modelist = list_entry(pos, struct fb_modelist, list);
141 : 0 : mode = &modelist->mode;
142 : 0 : i = mode_string(mstr, 0, mode);
143 [ # # ]: 0 : if (strncmp(mstr, buf, max(count, i)) == 0) {
144 : :
145 : 0 : var = fb_info->var;
146 : 0 : fb_videomode_to_var(&var, mode);
147 [ # # ]: 0 : if ((err = activate(fb_info, &var)))
148 : : return err;
149 : 0 : fb_info->mode = mode;
150 : 0 : return count;
151 : : }
152 : : }
153 : : return -EINVAL;
154 : : }
155 : :
156 : 0 : static ssize_t show_mode(struct device *device, struct device_attribute *attr,
157 : : char *buf)
158 : : {
159 : : struct fb_info *fb_info = dev_get_drvdata(device);
160 : :
161 [ # # ]: 0 : if (!fb_info->mode)
162 : : return 0;
163 : :
164 : 0 : return mode_string(buf, 0, fb_info->mode);
165 : : }
166 : :
167 : 0 : static ssize_t store_modes(struct device *device,
168 : : struct device_attribute *attr,
169 : : const char *buf, size_t count)
170 : : {
171 : : struct fb_info *fb_info = dev_get_drvdata(device);
172 : 0 : LIST_HEAD(old_list);
173 : 0 : int i = count / sizeof(struct fb_videomode);
174 : :
175 [ # # ]: 0 : if (i * sizeof(struct fb_videomode) != count)
176 : : return -EINVAL;
177 : :
178 : 0 : console_lock();
179 : : lock_fb_info(fb_info);
180 : :
181 : 0 : list_splice(&fb_info->modelist, &old_list);
182 : 0 : fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
183 : : &fb_info->modelist);
184 [ # # ]: 0 : if (fb_new_modelist(fb_info)) {
185 : 0 : fb_destroy_modelist(&fb_info->modelist);
186 : : list_splice(&old_list, &fb_info->modelist);
187 : : } else
188 : 0 : fb_destroy_modelist(&old_list);
189 : :
190 : : unlock_fb_info(fb_info);
191 : 0 : console_unlock();
192 : :
193 : 0 : return 0;
194 : : }
195 : :
196 : 0 : static ssize_t show_modes(struct device *device, struct device_attribute *attr,
197 : : char *buf)
198 : : {
199 : : struct fb_info *fb_info = dev_get_drvdata(device);
200 : : unsigned int i;
201 : : struct list_head *pos;
202 : : struct fb_modelist *modelist;
203 : : const struct fb_videomode *mode;
204 : :
205 : : i = 0;
206 [ # # ]: 0 : list_for_each(pos, &fb_info->modelist) {
207 : : modelist = list_entry(pos, struct fb_modelist, list);
208 : 0 : mode = &modelist->mode;
209 : 0 : i += mode_string(buf, i, mode);
210 : : }
211 : 0 : return i;
212 : : }
213 : :
214 : 0 : static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
215 : : const char *buf, size_t count)
216 : : {
217 : : struct fb_info *fb_info = dev_get_drvdata(device);
218 : : struct fb_var_screeninfo var;
219 : : char ** last = NULL;
220 : : int err;
221 : :
222 : 0 : var = fb_info->var;
223 : 0 : var.bits_per_pixel = simple_strtoul(buf, last, 0);
224 [ # # ]: 0 : if ((err = activate(fb_info, &var)))
225 : : return err;
226 : 0 : return count;
227 : : }
228 : :
229 : 0 : static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
230 : : char *buf)
231 : : {
232 : : struct fb_info *fb_info = dev_get_drvdata(device);
233 : 0 : return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel);
234 : : }
235 : :
236 : 0 : static ssize_t store_rotate(struct device *device,
237 : : struct device_attribute *attr,
238 : : const char *buf, size_t count)
239 : : {
240 : : struct fb_info *fb_info = dev_get_drvdata(device);
241 : : struct fb_var_screeninfo var;
242 : : char **last = NULL;
243 : : int err;
244 : :
245 : 0 : var = fb_info->var;
246 : 0 : var.rotate = simple_strtoul(buf, last, 0);
247 : :
248 [ # # ]: 0 : if ((err = activate(fb_info, &var)))
249 : : return err;
250 : :
251 : 0 : return count;
252 : : }
253 : :
254 : :
255 : 0 : static ssize_t show_rotate(struct device *device,
256 : : struct device_attribute *attr, char *buf)
257 : : {
258 : : struct fb_info *fb_info = dev_get_drvdata(device);
259 : :
260 : 0 : return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.rotate);
261 : : }
262 : :
263 : 0 : static ssize_t store_virtual(struct device *device,
264 : : struct device_attribute *attr,
265 : : const char *buf, size_t count)
266 : : {
267 : : struct fb_info *fb_info = dev_get_drvdata(device);
268 : : struct fb_var_screeninfo var;
269 : 0 : char *last = NULL;
270 : : int err;
271 : :
272 : 0 : var = fb_info->var;
273 : 0 : var.xres_virtual = simple_strtoul(buf, &last, 0);
274 : 0 : last++;
275 [ # # ]: 0 : if (last - buf >= count)
276 : : return -EINVAL;
277 : 0 : var.yres_virtual = simple_strtoul(last, &last, 0);
278 : :
279 [ # # ]: 0 : if ((err = activate(fb_info, &var)))
280 : : return err;
281 : 0 : return count;
282 : : }
283 : :
284 : 0 : static ssize_t show_virtual(struct device *device,
285 : : struct device_attribute *attr, char *buf)
286 : : {
287 : : struct fb_info *fb_info = dev_get_drvdata(device);
288 : 0 : return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xres_virtual,
289 : : fb_info->var.yres_virtual);
290 : : }
291 : :
292 : 0 : static ssize_t show_stride(struct device *device,
293 : : struct device_attribute *attr, char *buf)
294 : : {
295 : : struct fb_info *fb_info = dev_get_drvdata(device);
296 : 0 : return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length);
297 : : }
298 : :
299 : 0 : static ssize_t store_blank(struct device *device,
300 : : struct device_attribute *attr,
301 : : const char *buf, size_t count)
302 : : {
303 : : struct fb_info *fb_info = dev_get_drvdata(device);
304 : 0 : char *last = NULL;
305 : : int err, arg;
306 : :
307 : 0 : arg = simple_strtoul(buf, &last, 0);
308 : 0 : console_lock();
309 : 0 : err = fb_blank(fb_info, arg);
310 : : /* might again call into fb_blank */
311 : 0 : fbcon_fb_blanked(fb_info, arg);
312 : 0 : console_unlock();
313 [ # # ]: 0 : if (err < 0)
314 : : return err;
315 : 0 : return count;
316 : : }
317 : :
318 : 0 : static ssize_t show_blank(struct device *device,
319 : : struct device_attribute *attr, char *buf)
320 : : {
321 : : // struct fb_info *fb_info = dev_get_drvdata(device);
322 : 0 : return 0;
323 : : }
324 : :
325 : 0 : static ssize_t store_console(struct device *device,
326 : : struct device_attribute *attr,
327 : : const char *buf, size_t count)
328 : : {
329 : : // struct fb_info *fb_info = dev_get_drvdata(device);
330 : 0 : return 0;
331 : : }
332 : :
333 : 0 : static ssize_t show_console(struct device *device,
334 : : struct device_attribute *attr, char *buf)
335 : : {
336 : : // struct fb_info *fb_info = dev_get_drvdata(device);
337 : 0 : return 0;
338 : : }
339 : :
340 : 0 : static ssize_t store_cursor(struct device *device,
341 : : struct device_attribute *attr,
342 : : const char *buf, size_t count)
343 : : {
344 : : // struct fb_info *fb_info = dev_get_drvdata(device);
345 : 0 : return 0;
346 : : }
347 : :
348 : 0 : static ssize_t show_cursor(struct device *device,
349 : : struct device_attribute *attr, char *buf)
350 : : {
351 : : // struct fb_info *fb_info = dev_get_drvdata(device);
352 : 0 : return 0;
353 : : }
354 : :
355 : 0 : static ssize_t store_pan(struct device *device,
356 : : struct device_attribute *attr,
357 : : const char *buf, size_t count)
358 : : {
359 : : struct fb_info *fb_info = dev_get_drvdata(device);
360 : : struct fb_var_screeninfo var;
361 : 0 : char *last = NULL;
362 : : int err;
363 : :
364 : 0 : var = fb_info->var;
365 : 0 : var.xoffset = simple_strtoul(buf, &last, 0);
366 : 0 : last++;
367 [ # # ]: 0 : if (last - buf >= count)
368 : : return -EINVAL;
369 : 0 : var.yoffset = simple_strtoul(last, &last, 0);
370 : :
371 : 0 : console_lock();
372 : 0 : err = fb_pan_display(fb_info, &var);
373 : 0 : console_unlock();
374 : :
375 [ # # ]: 0 : if (err < 0)
376 : : return err;
377 : 0 : return count;
378 : : }
379 : :
380 : 0 : static ssize_t show_pan(struct device *device,
381 : : struct device_attribute *attr, char *buf)
382 : : {
383 : : struct fb_info *fb_info = dev_get_drvdata(device);
384 : 0 : return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xoffset,
385 : : fb_info->var.yoffset);
386 : : }
387 : :
388 : 0 : static ssize_t show_name(struct device *device,
389 : : struct device_attribute *attr, char *buf)
390 : : {
391 : : struct fb_info *fb_info = dev_get_drvdata(device);
392 : :
393 : 0 : return snprintf(buf, PAGE_SIZE, "%s\n", fb_info->fix.id);
394 : : }
395 : :
396 : 0 : static ssize_t store_fbstate(struct device *device,
397 : : struct device_attribute *attr,
398 : : const char *buf, size_t count)
399 : : {
400 : : struct fb_info *fb_info = dev_get_drvdata(device);
401 : : u32 state;
402 : 0 : char *last = NULL;
403 : :
404 : 0 : state = simple_strtoul(buf, &last, 0);
405 : :
406 : 0 : console_lock();
407 : : lock_fb_info(fb_info);
408 : :
409 : 0 : fb_set_suspend(fb_info, (int)state);
410 : :
411 : : unlock_fb_info(fb_info);
412 : 0 : console_unlock();
413 : :
414 : 0 : return count;
415 : : }
416 : :
417 : 0 : static ssize_t show_fbstate(struct device *device,
418 : : struct device_attribute *attr, char *buf)
419 : : {
420 : : struct fb_info *fb_info = dev_get_drvdata(device);
421 : 0 : return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state);
422 : : }
423 : :
424 : : #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
425 : 0 : static ssize_t store_bl_curve(struct device *device,
426 : : struct device_attribute *attr,
427 : : const char *buf, size_t count)
428 : : {
429 : : struct fb_info *fb_info = dev_get_drvdata(device);
430 : : u8 tmp_curve[FB_BACKLIGHT_LEVELS];
431 : : unsigned int i;
432 : :
433 : : /* Some drivers don't use framebuffer_alloc(), but those also
434 : : * don't have backlights.
435 : : */
436 [ # # # # ]: 0 : if (!fb_info || !fb_info->bl_dev)
437 : : return -ENODEV;
438 : :
439 [ # # ]: 0 : if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
440 : : return -EINVAL;
441 : :
442 [ # # ]: 0 : for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
443 [ # # ]: 0 : if (sscanf(&buf[i * 24],
444 : : "%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
445 : 0 : &tmp_curve[i * 8 + 0],
446 : 0 : &tmp_curve[i * 8 + 1],
447 : 0 : &tmp_curve[i * 8 + 2],
448 : 0 : &tmp_curve[i * 8 + 3],
449 : 0 : &tmp_curve[i * 8 + 4],
450 : 0 : &tmp_curve[i * 8 + 5],
451 : 0 : &tmp_curve[i * 8 + 6],
452 : 0 : &tmp_curve[i * 8 + 7]) != 8)
453 : : return -EINVAL;
454 : :
455 : : /* If there has been an error in the input data, we won't
456 : : * reach this loop.
457 : : */
458 : 0 : mutex_lock(&fb_info->bl_curve_mutex);
459 [ # # ]: 0 : for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
460 : 0 : fb_info->bl_curve[i] = tmp_curve[i];
461 : 0 : mutex_unlock(&fb_info->bl_curve_mutex);
462 : :
463 : 0 : return count;
464 : : }
465 : :
466 : 0 : static ssize_t show_bl_curve(struct device *device,
467 : : struct device_attribute *attr, char *buf)
468 : : {
469 : : struct fb_info *fb_info = dev_get_drvdata(device);
470 : : ssize_t len = 0;
471 : : unsigned int i;
472 : :
473 : : /* Some drivers don't use framebuffer_alloc(), but those also
474 : : * don't have backlights.
475 : : */
476 [ # # # # ]: 0 : if (!fb_info || !fb_info->bl_dev)
477 : : return -ENODEV;
478 : :
479 : 0 : mutex_lock(&fb_info->bl_curve_mutex);
480 [ # # ]: 0 : for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
481 : 0 : len += scnprintf(&buf[len], PAGE_SIZE - len, "%8ph\n",
482 : : fb_info->bl_curve + i);
483 : 0 : mutex_unlock(&fb_info->bl_curve_mutex);
484 : :
485 : 0 : return len;
486 : : }
487 : : #endif
488 : :
489 : : /* When cmap is added back in it should be a binary attribute
490 : : * not a text one. Consideration should also be given to converting
491 : : * fbdev to use configfs instead of sysfs */
492 : : static struct device_attribute device_attrs[] = {
493 : : __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
494 : : __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
495 : : __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
496 : : __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
497 : : __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
498 : : __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
499 : : __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
500 : : __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
501 : : __ATTR(name, S_IRUGO, show_name, NULL),
502 : : __ATTR(stride, S_IRUGO, show_stride, NULL),
503 : : __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
504 : : __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
505 : : #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
506 : : __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
507 : : #endif
508 : : };
509 : :
510 : 1212 : int fb_init_device(struct fb_info *fb_info)
511 : : {
512 : : int i, error = 0;
513 : :
514 : 1212 : dev_set_drvdata(fb_info->dev, fb_info);
515 : :
516 : 1212 : fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
517 : :
518 [ + + ]: 16968 : for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
519 : 15756 : error = device_create_file(fb_info->dev, &device_attrs[i]);
520 : :
521 [ + - ]: 15756 : if (error)
522 : : break;
523 : : }
524 : :
525 [ - + ]: 1212 : if (error) {
526 [ # # ]: 0 : while (--i >= 0)
527 : 0 : device_remove_file(fb_info->dev, &device_attrs[i]);
528 : 0 : fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
529 : : }
530 : :
531 : 1212 : return 0;
532 : : }
533 : :
534 : 0 : void fb_cleanup_device(struct fb_info *fb_info)
535 : : {
536 : : unsigned int i;
537 : :
538 [ # # ]: 0 : if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
539 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
540 : 0 : device_remove_file(fb_info->dev, &device_attrs[i]);
541 : :
542 : 0 : fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
543 : : }
544 : 0 : }
545 : :
546 : : #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
547 : : /* This function generates a linear backlight curve
548 : : *
549 : : * 0: off
550 : : * 1-7: min
551 : : * 8-127: linear from min to max
552 : : */
553 : 0 : void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
554 : : {
555 : 0 : unsigned int i, flat, count, range = (max - min);
556 : :
557 : 0 : mutex_lock(&fb_info->bl_curve_mutex);
558 : :
559 : 0 : fb_info->bl_curve[0] = off;
560 : :
561 [ # # ]: 0 : for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
562 : 0 : fb_info->bl_curve[flat] = min;
563 : :
564 : : count = FB_BACKLIGHT_LEVELS * 15 / 16;
565 [ # # ]: 0 : for (i = 0; i < count; ++i)
566 : 0 : fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count);
567 : :
568 : 0 : mutex_unlock(&fb_info->bl_curve_mutex);
569 : 0 : }
570 : : EXPORT_SYMBOL_GPL(fb_bl_default_curve);
571 : : #endif
|