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