2005-04-16 15:20:36 -07:00
/*
* fbsysfs . c - framebuffer device class and attributes
*
* Copyright ( c ) 2004 James Simmons < jsimmons @ infradead . org >
*
* This program is free software you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
/*
* Note : currently there ' s only stubs for framebuffer_alloc and
* framebuffer_release here . The reson for that is that until all drivers
* are converted to use it a sysfsification will open OOPSable races .
*/
# include <linux/kernel.h>
# include <linux/fb.h>
# include <linux/console.h>
/**
* framebuffer_alloc - creates a new frame buffer info structure
*
* @ size : size of driver private data , can be zero
* @ dev : pointer to the device for this fb , this can be NULL
*
* Creates a new frame buffer info structure . Also reserves @ size bytes
* for driver private data ( info - > par ) . info - > par ( if any ) will be
* aligned to sizeof ( long ) .
*
* Returns the new structure , or NULL if an error occured .
*
*/
struct fb_info * framebuffer_alloc ( size_t size , struct device * dev )
{
# define BYTES_PER_LONG (BITS_PER_LONG / 8)
# define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
int fb_info_size = sizeof ( struct fb_info ) ;
struct fb_info * info ;
char * p ;
if ( size )
fb_info_size + = PADDING ;
2006-01-09 20:53:45 -08:00
p = kzalloc ( fb_info_size + size , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! p )
return NULL ;
2006-01-09 20:53:45 -08:00
2005-04-16 15:20:36 -07:00
info = ( struct fb_info * ) p ;
if ( size )
info - > par = p + fb_info_size ;
info - > device = dev ;
return info ;
# undef PADDING
# undef BYTES_PER_LONG
}
EXPORT_SYMBOL ( framebuffer_alloc ) ;
/**
* framebuffer_release - marks the structure available for freeing
*
* @ info : frame buffer info structure
*
* Drop the reference count of the class_device embedded in the
* framebuffer info structure .
*
*/
void framebuffer_release ( struct fb_info * info )
{
kfree ( info ) ;
}
EXPORT_SYMBOL ( framebuffer_release ) ;
static int activate ( struct fb_info * fb_info , struct fb_var_screeninfo * var )
{
int err ;
var - > activate | = FB_ACTIVATE_FORCE ;
acquire_console_sem ( ) ;
fb_info - > flags | = FBINFO_MISC_USEREVENT ;
err = fb_set_var ( fb_info , var ) ;
fb_info - > flags & = ~ FBINFO_MISC_USEREVENT ;
release_console_sem ( ) ;
if ( err )
return err ;
return 0 ;
}
static int mode_string ( char * buf , unsigned int offset ,
const struct fb_videomode * mode )
{
char m = ' U ' ;
if ( mode - > flag & FB_MODE_IS_DETAILED )
m = ' D ' ;
if ( mode - > flag & FB_MODE_IS_VESA )
m = ' V ' ;
if ( mode - > flag & FB_MODE_IS_STANDARD )
m = ' S ' ;
return snprintf ( & buf [ offset ] , PAGE_SIZE - offset , " %c:%dx%d-%d \n " , m , mode - > xres , mode - > yres , mode - > refresh ) ;
}
static ssize_t store_mode ( struct class_device * class_device , const char * buf ,
size_t count )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
char mstr [ 100 ] ;
struct fb_var_screeninfo var ;
struct fb_modelist * modelist ;
struct fb_videomode * mode ;
struct list_head * pos ;
size_t i ;
int err ;
memset ( & var , 0 , sizeof ( var ) ) ;
list_for_each ( pos , & fb_info - > modelist ) {
modelist = list_entry ( pos , struct fb_modelist , list ) ;
mode = & modelist - > mode ;
i = mode_string ( mstr , 0 , mode ) ;
if ( strncmp ( mstr , buf , max ( count , i ) ) = = 0 ) {
var = fb_info - > var ;
fb_videomode_to_var ( & var , mode ) ;
if ( ( err = activate ( fb_info , & var ) ) )
return err ;
fb_info - > mode = mode ;
return count ;
}
}
return - EINVAL ;
}
static ssize_t show_mode ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
if ( ! fb_info - > mode )
return 0 ;
return mode_string ( buf , 0 , fb_info - > mode ) ;
}
static ssize_t store_modes ( struct class_device * class_device , const char * buf ,
size_t count )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
LIST_HEAD ( old_list ) ;
int i = count / sizeof ( struct fb_videomode ) ;
if ( i * sizeof ( struct fb_videomode ) ! = count )
return - EINVAL ;
acquire_console_sem ( ) ;
list_splice ( & fb_info - > modelist , & old_list ) ;
fb_videomode_to_modelist ( ( struct fb_videomode * ) buf , i ,
& fb_info - > modelist ) ;
if ( fb_new_modelist ( fb_info ) ) {
fb_destroy_modelist ( & fb_info - > modelist ) ;
list_splice ( & old_list , & fb_info - > modelist ) ;
} else
fb_destroy_modelist ( & old_list ) ;
release_console_sem ( ) ;
return 0 ;
}
static ssize_t show_modes ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
unsigned int i ;
struct list_head * pos ;
struct fb_modelist * modelist ;
const struct fb_videomode * mode ;
i = 0 ;
list_for_each ( pos , & fb_info - > modelist ) {
modelist = list_entry ( pos , struct fb_modelist , list ) ;
mode = & modelist - > mode ;
i + = mode_string ( buf , i , mode ) ;
}
return i ;
}
static ssize_t store_bpp ( struct class_device * class_device , const char * buf ,
size_t count )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
struct fb_var_screeninfo var ;
char * * last = NULL ;
int err ;
var = fb_info - > var ;
var . bits_per_pixel = simple_strtoul ( buf , last , 0 ) ;
if ( ( err = activate ( fb_info , & var ) ) )
return err ;
return count ;
}
static ssize_t show_bpp ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
return snprintf ( buf , PAGE_SIZE , " %d \n " , fb_info - > var . bits_per_pixel ) ;
}
2005-11-08 21:39:15 -08:00
static ssize_t store_rotate ( struct class_device * class_device , const char * buf ,
size_t count )
{
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
struct fb_var_screeninfo var ;
char * * last = NULL ;
int err ;
var = fb_info - > var ;
var . rotate = simple_strtoul ( buf , last , 0 ) ;
if ( ( err = activate ( fb_info , & var ) ) )
return err ;
return count ;
}
static ssize_t show_rotate ( struct class_device * class_device , char * buf )
{
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , fb_info - > var . rotate ) ;
}
static ssize_t store_con_rotate ( struct class_device * class_device ,
const char * buf , size_t count )
{
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
int rotate ;
char * * last = NULL ;
acquire_console_sem ( ) ;
rotate = simple_strtoul ( buf , last , 0 ) ;
fb_con_duit ( fb_info , FB_EVENT_SET_CON_ROTATE , & rotate ) ;
release_console_sem ( ) ;
return count ;
}
static ssize_t store_con_rotate_all ( struct class_device * class_device ,
const char * buf , size_t count )
{
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
int rotate ;
char * * last = NULL ;
acquire_console_sem ( ) ;
rotate = simple_strtoul ( buf , last , 0 ) ;
fb_con_duit ( fb_info , FB_EVENT_SET_CON_ROTATE_ALL , & rotate ) ;
release_console_sem ( ) ;
return count ;
}
static ssize_t show_con_rotate ( struct class_device * class_device , char * buf )
{
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
int rotate ;
acquire_console_sem ( ) ;
rotate = fb_con_duit ( fb_info , FB_EVENT_GET_CON_ROTATE , NULL ) ;
release_console_sem ( ) ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , rotate ) ;
}
2005-04-16 15:20:36 -07:00
static ssize_t store_virtual ( struct class_device * class_device ,
const char * buf , size_t count )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
struct fb_var_screeninfo var ;
char * last = NULL ;
int err ;
var = fb_info - > var ;
var . xres_virtual = simple_strtoul ( buf , & last , 0 ) ;
last + + ;
if ( last - buf > = count )
return - EINVAL ;
var . yres_virtual = simple_strtoul ( last , & last , 0 ) ;
if ( ( err = activate ( fb_info , & var ) ) )
return err ;
return count ;
}
static ssize_t show_virtual ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
return snprintf ( buf , PAGE_SIZE , " %d,%d \n " , fb_info - > var . xres_virtual ,
2005-06-13 15:52:36 -07:00
fb_info - > var . yres_virtual ) ;
2005-04-16 15:20:36 -07:00
}
2005-10-24 21:46:21 +01:00
static ssize_t show_stride ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-10-24 21:46:21 +01:00
return snprintf ( buf , PAGE_SIZE , " %d \n " , fb_info - > fix . line_length ) ;
}
2005-07-27 11:46:05 -07:00
/* Format for cmap is "%02x%c%4x%4x%4x\n" */
/* %02x entry %c transp %4x red %4x blue %4x green \n */
2005-07-28 21:16:19 -07:00
/* 256 rows at 16 chars equals 4096, the normal page size */
/* the code will automatically adjust for different page sizes */
2005-07-27 11:46:05 -07:00
static ssize_t store_cmap ( struct class_device * class_device , const char * buf ,
2005-04-16 15:20:36 -07:00
size_t count )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-07-27 11:46:05 -07:00
int rc , i , start , length , transp = 0 ;
2005-07-28 21:16:19 -07:00
if ( ( count > PAGE_SIZE ) | | ( ( count % 16 ) ! = 0 ) )
2005-07-27 11:46:05 -07:00
return - EINVAL ;
if ( ! fb_info - > fbops - > fb_setcolreg & & ! fb_info - > fbops - > fb_setcmap )
return - EINVAL ;
sscanf ( buf , " %02x " , & start ) ;
length = count / 16 ;
for ( i = 0 ; i < length ; i + + )
if ( buf [ i * 16 + 2 ] ! = ' ' )
transp = 1 ;
/* If we can batch, do it */
if ( fb_info - > fbops - > fb_setcmap & & length > 1 ) {
struct fb_cmap umap ;
memset ( & umap , 0 , sizeof ( umap ) ) ;
if ( ( rc = fb_alloc_cmap ( & umap , length , transp ) ) )
return rc ;
umap . start = start ;
for ( i = 0 ; i < length ; i + + ) {
sscanf ( & buf [ i * 16 + 3 ] , " %4hx " , & umap . red [ i ] ) ;
sscanf ( & buf [ i * 16 + 7 ] , " %4hx " , & umap . blue [ i ] ) ;
sscanf ( & buf [ i * 16 + 11 ] , " %4hx " , & umap . green [ i ] ) ;
if ( transp )
umap . transp [ i ] = ( buf [ i * 16 + 2 ] ! = ' ' ) ;
}
rc = fb_info - > fbops - > fb_setcmap ( & umap , fb_info ) ;
fb_copy_cmap ( & umap , & fb_info - > cmap ) ;
fb_dealloc_cmap ( & umap ) ;
2006-03-27 01:17:30 -08:00
return rc ? : count ;
2005-07-27 11:46:05 -07:00
}
for ( i = 0 ; i < length ; i + + ) {
u16 red , blue , green , tsp ;
sscanf ( & buf [ i * 16 + 3 ] , " %4hx " , & red ) ;
sscanf ( & buf [ i * 16 + 7 ] , " %4hx " , & blue ) ;
sscanf ( & buf [ i * 16 + 11 ] , " %4hx " , & green ) ;
tsp = ( buf [ i * 16 + 2 ] ! = ' ' ) ;
if ( ( rc = fb_info - > fbops - > fb_setcolreg ( start + + ,
red , green , blue , tsp , fb_info ) ) )
return rc ;
fb_info - > cmap . red [ i ] = red ;
fb_info - > cmap . blue [ i ] = blue ;
fb_info - > cmap . green [ i ] = green ;
if ( transp )
fb_info - > cmap . transp [ i ] = tsp ;
}
2006-03-27 01:17:30 -08:00
return count ;
2005-04-16 15:20:36 -07:00
}
static ssize_t show_cmap ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-07-27 11:46:05 -07:00
unsigned int i ;
2005-04-16 15:20:36 -07:00
if ( ! fb_info - > cmap . red | | ! fb_info - > cmap . blue | |
2005-07-27 11:46:05 -07:00
! fb_info - > cmap . green )
return - EINVAL ;
2005-07-28 21:16:19 -07:00
if ( fb_info - > cmap . len > PAGE_SIZE / 16 )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2005-07-27 11:46:05 -07:00
/* don't mess with the format, the buffer is PAGE_SIZE */
2005-07-28 21:16:19 -07:00
/* 256 entries at 16 chars per line equals 4096 = PAGE_SIZE */
2005-04-16 15:20:36 -07:00
for ( i = 0 ; i < fb_info - > cmap . len ; i + + ) {
2005-07-28 21:16:19 -07:00
snprintf ( & buf [ i * 16 ] , PAGE_SIZE - i * 16 , " %02x%c%4x%4x%4x \n " , i + fb_info - > cmap . start ,
2005-07-27 11:46:05 -07:00
( ( fb_info - > cmap . transp & & fb_info - > cmap . transp [ i ] ) ? ' * ' : ' ' ) ,
fb_info - > cmap . red [ i ] , fb_info - > cmap . blue [ i ] ,
fb_info - > cmap . green [ i ] ) ;
2005-04-16 15:20:36 -07:00
}
2005-07-28 21:16:19 -07:00
return 16 * fb_info - > cmap . len ;
2005-04-16 15:20:36 -07:00
}
static ssize_t store_blank ( struct class_device * class_device , const char * buf ,
size_t count )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
char * last = NULL ;
int err ;
acquire_console_sem ( ) ;
fb_info - > flags | = FBINFO_MISC_USEREVENT ;
err = fb_blank ( fb_info , simple_strtoul ( buf , & last , 0 ) ) ;
fb_info - > flags & = ~ FBINFO_MISC_USEREVENT ;
release_console_sem ( ) ;
if ( err < 0 )
return err ;
return count ;
}
static ssize_t show_blank ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
// struct fb_info *fb_info = class_get_devdata(class_device);
2005-04-16 15:20:36 -07:00
return 0 ;
}
static ssize_t store_console ( struct class_device * class_device ,
const char * buf , size_t count )
{
2006-01-09 20:53:12 -08:00
// struct fb_info *fb_info = class_get_devdata(class_device);
2005-04-16 15:20:36 -07:00
return 0 ;
}
static ssize_t show_console ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
// struct fb_info *fb_info = class_get_devdata(class_device);
2005-04-16 15:20:36 -07:00
return 0 ;
}
static ssize_t store_cursor ( struct class_device * class_device ,
const char * buf , size_t count )
{
2006-01-09 20:53:12 -08:00
// struct fb_info *fb_info = class_get_devdata(class_device);
2005-04-16 15:20:36 -07:00
return 0 ;
}
static ssize_t show_cursor ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
// struct fb_info *fb_info = class_get_devdata(class_device);
2005-04-16 15:20:36 -07:00
return 0 ;
}
static ssize_t store_pan ( struct class_device * class_device , const char * buf ,
size_t count )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
struct fb_var_screeninfo var ;
char * last = NULL ;
int err ;
var = fb_info - > var ;
var . xoffset = simple_strtoul ( buf , & last , 0 ) ;
last + + ;
if ( last - buf > = count )
return - EINVAL ;
var . yoffset = simple_strtoul ( last , & last , 0 ) ;
acquire_console_sem ( ) ;
err = fb_pan_display ( fb_info , & var ) ;
release_console_sem ( ) ;
if ( err < 0 )
return err ;
return count ;
}
static ssize_t show_pan ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-04-16 15:20:36 -07:00
return snprintf ( buf , PAGE_SIZE , " %d,%d \n " , fb_info - > var . xoffset ,
fb_info - > var . xoffset ) ;
}
2005-07-30 23:49:54 +01:00
static ssize_t show_name ( struct class_device * class_device , char * buf )
{
2006-01-09 20:53:12 -08:00
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
2005-07-30 23:49:54 +01:00
return snprintf ( buf , PAGE_SIZE , " %s \n " , fb_info - > fix . id ) ;
}
2006-01-09 20:53:01 -08:00
static ssize_t store_fbstate ( struct class_device * class_device ,
const char * buf , size_t count )
{
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
u32 state ;
char * last = NULL ;
state = simple_strtoul ( buf , & last , 0 ) ;
acquire_console_sem ( ) ;
fb_set_suspend ( fb_info , ( int ) state ) ;
release_console_sem ( ) ;
return count ;
}
static ssize_t show_fbstate ( struct class_device * class_device , char * buf )
{
struct fb_info * fb_info = class_get_devdata ( class_device ) ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , fb_info - > state ) ;
}
2005-05-05 16:16:09 -07:00
static struct class_device_attribute class_device_attrs [ ] = {
2005-04-16 15:20:36 -07:00
__ATTR ( bits_per_pixel , S_IRUGO | S_IWUSR , show_bpp , store_bpp ) ,
__ATTR ( blank , S_IRUGO | S_IWUSR , show_blank , store_blank ) ,
__ATTR ( color_map , S_IRUGO | S_IWUSR , show_cmap , store_cmap ) ,
__ATTR ( console , S_IRUGO | S_IWUSR , show_console , store_console ) ,
__ATTR ( cursor , S_IRUGO | S_IWUSR , show_cursor , store_cursor ) ,
__ATTR ( mode , S_IRUGO | S_IWUSR , show_mode , store_mode ) ,
__ATTR ( modes , S_IRUGO | S_IWUSR , show_modes , store_modes ) ,
__ATTR ( pan , S_IRUGO | S_IWUSR , show_pan , store_pan ) ,
__ATTR ( virtual_size , S_IRUGO | S_IWUSR , show_virtual , store_virtual ) ,
2005-07-30 23:49:54 +01:00
__ATTR ( name , S_IRUGO , show_name , NULL ) ,
2005-10-24 21:46:21 +01:00
__ATTR ( stride , S_IRUGO , show_stride , NULL ) ,
2005-11-08 21:39:15 -08:00
__ATTR ( rotate , S_IRUGO | S_IWUSR , show_rotate , store_rotate ) ,
__ATTR ( con_rotate , S_IRUGO | S_IWUSR , show_con_rotate , store_con_rotate ) ,
__ATTR ( con_rotate_all , S_IWUSR , NULL , store_con_rotate_all ) ,
2006-01-09 20:53:01 -08:00
__ATTR ( state , S_IRUGO | S_IWUSR , show_fbstate , store_fbstate ) ,
2005-04-16 15:20:36 -07:00
} ;
int fb_init_class_device ( struct fb_info * fb_info )
{
unsigned int i ;
class_set_devdata ( fb_info - > class_device , fb_info ) ;
for ( i = 0 ; i < ARRAY_SIZE ( class_device_attrs ) ; i + + )
class_device_create_file ( fb_info - > class_device ,
& class_device_attrs [ i ] ) ;
return 0 ;
}
void fb_cleanup_class_device ( struct fb_info * fb_info )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( class_device_attrs ) ; i + + )
class_device_remove_file ( fb_info - > class_device ,
& class_device_attrs [ i ] ) ;
}