2005-04-16 15:20:36 -07:00
/*
* bin . c - binary file operations for sysfs .
*
* Copyright ( c ) 2003 Patrick Mochel
* Copyright ( c ) 2003 Matthew Wilcox
* Copyright ( c ) 2004 Silicon Graphics , Inc .
*/
# undef DEBUG
# include <linux/errno.h>
# include <linux/fs.h>
2006-07-10 23:05:25 -07:00
# include <linux/kernel.h>
2005-04-16 15:20:36 -07:00
# include <linux/kobject.h>
# include <linux/module.h>
# include <linux/slab.h>
2007-07-26 14:53:53 +00:00
# include <linux/mutex.h>
2005-04-16 15:20:36 -07:00
# include <asm/uaccess.h>
# include "sysfs.h"
2007-06-14 03:45:16 +09:00
struct bin_buffer {
struct mutex mutex ;
void * buffer ;
2007-06-14 03:45:16 +09:00
int mmapped ;
2007-06-14 03:45:16 +09:00
} ;
2005-04-16 15:20:36 -07:00
static int
fill_read ( struct dentry * dentry , char * buffer , loff_t off , size_t count )
{
2007-06-14 03:45:15 +09:00
struct sysfs_dirent * attr_sd = dentry - > d_fsdata ;
struct bin_attribute * attr = attr_sd - > s_elem . bin_attr . bin_attr ;
2007-06-14 03:45:16 +09:00
struct kobject * kobj = attr_sd - > s_parent - > s_elem . dir . kobj ;
int rc ;
/* need attr_sd for attr, its parent for kobj */
if ( ! sysfs_get_active_two ( attr_sd ) )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
rc = - EIO ;
if ( attr - > read )
2007-06-09 13:57:22 +08:00
rc = attr - > read ( kobj , attr , buffer , off , count ) ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
sysfs_put_active_two ( attr_sd ) ;
return rc ;
2005-04-16 15:20:36 -07:00
}
static ssize_t
2007-06-14 03:45:13 +09:00
read ( struct file * file , char __user * userbuf , size_t bytes , loff_t * off )
2005-04-16 15:20:36 -07:00
{
2007-06-14 03:45:16 +09:00
struct bin_buffer * bb = file - > private_data ;
2006-12-08 02:36:36 -08:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-16 15:20:36 -07:00
int size = dentry - > d_inode - > i_size ;
loff_t offs = * off ;
2007-06-14 03:45:13 +09:00
int count = min_t ( size_t , bytes , PAGE_SIZE ) ;
2005-04-16 15:20:36 -07:00
if ( size ) {
if ( offs > size )
return 0 ;
if ( offs + count > size )
count = size - offs ;
}
2007-06-14 03:45:16 +09:00
mutex_lock ( & bb - > mutex ) ;
count = fill_read ( dentry , bb - > buffer , offs , count ) ;
2007-06-14 03:45:13 +09:00
if ( count < 0 )
2007-06-14 03:45:16 +09:00
goto out_unlock ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
if ( copy_to_user ( userbuf , bb - > buffer , count ) ) {
count = - EFAULT ;
goto out_unlock ;
}
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:13 +09:00
pr_debug ( " offs = %lld, *off = %lld, count = %d \n " , offs , * off , count ) ;
2005-04-16 15:20:36 -07:00
* off = offs + count ;
2007-06-14 03:45:16 +09:00
out_unlock :
mutex_unlock ( & bb - > mutex ) ;
2005-04-16 15:20:36 -07:00
return count ;
}
static int
flush_write ( struct dentry * dentry , char * buffer , loff_t offset , size_t count )
{
2007-06-14 03:45:15 +09:00
struct sysfs_dirent * attr_sd = dentry - > d_fsdata ;
struct bin_attribute * attr = attr_sd - > s_elem . bin_attr . bin_attr ;
2007-06-14 03:45:16 +09:00
struct kobject * kobj = attr_sd - > s_parent - > s_elem . dir . kobj ;
int rc ;
/* need attr_sd for attr, its parent for kobj */
if ( ! sysfs_get_active_two ( attr_sd ) )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
rc = - EIO ;
if ( attr - > write )
2007-06-09 13:57:22 +08:00
rc = attr - > write ( kobj , attr , buffer , offset , count ) ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
sysfs_put_active_two ( attr_sd ) ;
return rc ;
2005-04-16 15:20:36 -07:00
}
2007-06-14 03:45:13 +09:00
static ssize_t write ( struct file * file , const char __user * userbuf ,
size_t bytes , loff_t * off )
2005-04-16 15:20:36 -07:00
{
2007-06-14 03:45:16 +09:00
struct bin_buffer * bb = file - > private_data ;
2006-12-08 02:36:36 -08:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-16 15:20:36 -07:00
int size = dentry - > d_inode - > i_size ;
loff_t offs = * off ;
2007-06-14 03:45:13 +09:00
int count = min_t ( size_t , bytes , PAGE_SIZE ) ;
2005-04-16 15:20:36 -07:00
if ( size ) {
if ( offs > size )
return 0 ;
if ( offs + count > size )
count = size - offs ;
}
2007-06-14 03:45:16 +09:00
mutex_lock ( & bb - > mutex ) ;
if ( copy_from_user ( bb - > buffer , userbuf , count ) ) {
count = - EFAULT ;
goto out_unlock ;
}
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
count = flush_write ( dentry , bb - > buffer , offs , count ) ;
2005-04-16 15:20:36 -07:00
if ( count > 0 )
* off = offs + count ;
2007-06-14 03:45:16 +09:00
out_unlock :
mutex_unlock ( & bb - > mutex ) ;
2005-04-16 15:20:36 -07:00
return count ;
}
static int mmap ( struct file * file , struct vm_area_struct * vma )
{
2007-06-14 03:45:16 +09:00
struct bin_buffer * bb = file - > private_data ;
2007-06-14 03:45:15 +09:00
struct sysfs_dirent * attr_sd = file - > f_path . dentry - > d_fsdata ;
struct bin_attribute * attr = attr_sd - > s_elem . bin_attr . bin_attr ;
2007-06-14 03:45:16 +09:00
struct kobject * kobj = attr_sd - > s_parent - > s_elem . dir . kobj ;
2007-06-14 03:45:16 +09:00
int rc ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
mutex_lock ( & bb - > mutex ) ;
2007-06-14 03:45:16 +09:00
/* need attr_sd for attr, its parent for kobj */
if ( ! sysfs_get_active_two ( attr_sd ) )
return - ENODEV ;
rc = - EINVAL ;
if ( attr - > mmap )
rc = attr - > mmap ( kobj , attr , vma ) ;
if ( rc = = 0 & & ! bb - > mmapped )
bb - > mmapped = 1 ;
else
sysfs_put_active_two ( attr_sd ) ;
2007-06-14 03:45:16 +09:00
mutex_unlock ( & bb - > mutex ) ;
return rc ;
2005-04-16 15:20:36 -07:00
}
static int open ( struct inode * inode , struct file * file )
{
2007-06-14 03:45:15 +09:00
struct sysfs_dirent * attr_sd = file - > f_path . dentry - > d_fsdata ;
struct bin_attribute * attr = attr_sd - > s_elem . bin_attr . bin_attr ;
2007-06-14 03:45:16 +09:00
struct bin_buffer * bb = NULL ;
2007-06-14 03:45:16 +09:00
int error ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
/* need attr_sd for attr */
if ( ! sysfs_get_active ( attr_sd ) )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
error = - EACCES ;
if ( ( file - > f_mode & FMODE_WRITE ) & & ! ( attr - > write | | attr - > mmap ) )
2007-06-14 03:45:17 +09:00
goto err_out ;
2005-04-16 15:20:36 -07:00
if ( ( file - > f_mode & FMODE_READ ) & & ! ( attr - > read | | attr - > mmap ) )
2007-06-14 03:45:17 +09:00
goto err_out ;
2005-04-16 15:20:36 -07:00
error = - ENOMEM ;
2007-06-14 03:45:16 +09:00
bb = kzalloc ( sizeof ( * bb ) , GFP_KERNEL ) ;
if ( ! bb )
2007-06-14 03:45:17 +09:00
goto err_out ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
bb - > buffer = kmalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( ! bb - > buffer )
2007-06-14 03:45:17 +09:00
goto err_out ;
2007-06-14 03:45:16 +09:00
mutex_init ( & bb - > mutex ) ;
file - > private_data = bb ;
2007-09-20 16:05:10 +09:00
/* open succeeded, put active reference */
2007-06-14 03:45:16 +09:00
sysfs_put_active ( attr_sd ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:17 +09:00
err_out :
2007-06-14 03:45:16 +09:00
sysfs_put_active ( attr_sd ) ;
kfree ( bb ) ;
2005-04-16 15:20:36 -07:00
return error ;
}
static int release ( struct inode * inode , struct file * file )
{
2007-06-14 03:45:15 +09:00
struct sysfs_dirent * attr_sd = file - > f_path . dentry - > d_fsdata ;
2007-06-14 03:45:16 +09:00
struct bin_buffer * bb = file - > private_data ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
if ( bb - > mmapped )
sysfs_put_active_two ( attr_sd ) ;
2007-06-14 03:45:16 +09:00
kfree ( bb - > buffer ) ;
kfree ( bb ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2006-03-28 01:56:42 -08:00
const struct file_operations bin_fops = {
2005-04-16 15:20:36 -07:00
. read = read ,
. write = write ,
. mmap = mmap ,
. llseek = generic_file_llseek ,
. open = open ,
. release = release ,
} ;
/**
* sysfs_create_bin_file - create binary file for object .
* @ kobj : object .
* @ attr : attribute descriptor .
*/
int sysfs_create_bin_file ( struct kobject * kobj , struct bin_attribute * attr )
{
2007-06-14 04:27:22 +09:00
BUG_ON ( ! kobj | | ! kobj - > sd | | ! attr ) ;
2005-04-16 15:20:36 -07:00
2007-06-14 04:27:22 +09:00
return sysfs_add_file ( kobj - > sd , & attr - > attr , SYSFS_KOBJ_BIN_ATTR ) ;
2005-04-16 15:20:36 -07:00
}
/**
* sysfs_remove_bin_file - remove binary file for object .
* @ kobj : object .
* @ attr : attribute descriptor .
*/
2006-07-10 23:05:25 -07:00
void sysfs_remove_bin_file ( struct kobject * kobj , struct bin_attribute * attr )
2005-04-16 15:20:36 -07:00
{
2007-08-16 16:13:06 -04:00
sysfs_hash_and_remove ( kobj - > sd , attr - > attr . name ) ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL_GPL ( sysfs_create_bin_file ) ;
EXPORT_SYMBOL_GPL ( sysfs_remove_bin_file ) ;