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