2005-04-17 02:20:36 +04:00
/*
* linux / drivers / char / raw . c
*
* Front - end raw character devices . These can be bound to any block
* devices to provide genuine Unix raw character device semantics .
*
* We reserve minor number 0 for a control interface . ioctl ( ) s on this
* device are used to bind the other minor numbers to block devices .
*/
# include <linux/init.h>
# include <linux/fs.h>
# include <linux/major.h>
# include <linux/blkdev.h>
# include <linux/module.h>
# include <linux/raw.h>
# include <linux/capability.h>
# include <linux/uio.h>
# include <linux/cdev.h>
# include <linux/device.h>
2006-03-23 14:00:21 +03:00
# include <linux/mutex.h>
2008-05-16 23:54:46 +04:00
# include <linux/smp_lock.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
struct raw_device_data {
struct block_device * binding ;
int inuse ;
} ;
2005-03-23 20:53:09 +03:00
static struct class * raw_class ;
2005-04-17 02:20:36 +04:00
static struct raw_device_data raw_devices [ MAX_RAW_MINORS ] ;
2006-03-23 14:00:21 +03:00
static DEFINE_MUTEX ( raw_mutex ) ;
2006-07-03 11:24:21 +04:00
static const struct file_operations raw_ctl_fops ; /* forward declaration */
2005-04-17 02:20:36 +04:00
/*
* Open / close code for raw IO .
*
* We just rewrite the i_mapping for the / dev / raw / rawN file descriptor to
* point at the blockdev ' s address_space and set the file handle to use
* O_DIRECT .
*
* Set the device ' s soft blocksize to the minimum possible . This gives the
* finest possible alignment and has no adverse impact on performance .
*/
static int raw_open ( struct inode * inode , struct file * filp )
{
const int minor = iminor ( inode ) ;
struct block_device * bdev ;
int err ;
if ( minor = = 0 ) { /* It is the control device */
filp - > f_op = & raw_ctl_fops ;
return 0 ;
}
2008-05-16 23:54:46 +04:00
lock_kernel ( ) ;
2006-03-23 14:00:21 +03:00
mutex_lock ( & raw_mutex ) ;
2005-04-17 02:20:36 +04:00
/*
* All we need to do on open is check that the device is bound .
*/
bdev = raw_devices [ minor ] . binding ;
err = - ENODEV ;
if ( ! bdev )
goto out ;
igrab ( bdev - > bd_inode ) ;
err = blkdev_get ( bdev , filp - > f_mode , 0 ) ;
if ( err )
goto out ;
err = bd_claim ( bdev , raw_open ) ;
if ( err )
goto out1 ;
err = set_blocksize ( bdev , bdev_hardsect_size ( bdev ) ) ;
if ( err )
goto out2 ;
filp - > f_flags | = O_DIRECT ;
filp - > f_mapping = bdev - > bd_inode - > i_mapping ;
if ( + + raw_devices [ minor ] . inuse = = 1 )
2006-12-08 13:36:55 +03:00
filp - > f_path . dentry - > d_inode - > i_mapping =
2005-04-17 02:20:36 +04:00
bdev - > bd_inode - > i_mapping ;
filp - > private_data = bdev ;
2006-03-23 14:00:21 +03:00
mutex_unlock ( & raw_mutex ) ;
2008-05-16 23:54:46 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
out2 :
bd_release ( bdev ) ;
out1 :
blkdev_put ( bdev ) ;
out :
2006-03-23 14:00:21 +03:00
mutex_unlock ( & raw_mutex ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
/*
* When the final fd which refers to this character - special node is closed , we
* make its - > mapping point back at its own i_data .
*/
static int raw_release ( struct inode * inode , struct file * filp )
{
const int minor = iminor ( inode ) ;
struct block_device * bdev ;
2006-03-23 14:00:21 +03:00
mutex_lock ( & raw_mutex ) ;
2005-04-17 02:20:36 +04:00
bdev = raw_devices [ minor ] . binding ;
if ( - - raw_devices [ minor ] . inuse = = 0 ) {
/* Here inode->i_mapping == bdev->bd_inode->i_mapping */
inode - > i_mapping = & inode - > i_data ;
inode - > i_mapping - > backing_dev_info = & default_backing_dev_info ;
}
2006-03-23 14:00:21 +03:00
mutex_unlock ( & raw_mutex ) ;
2005-04-17 02:20:36 +04:00
bd_release ( bdev ) ;
blkdev_put ( bdev ) ;
return 0 ;
}
/*
* Forward ioctls to the underlying block device .
*/
static int
raw_ioctl ( struct inode * inode , struct file * filp ,
unsigned int command , unsigned long arg )
{
struct block_device * bdev = filp - > private_data ;
2005-05-18 19:22:31 +04:00
return blkdev_ioctl ( bdev - > bd_inode , NULL , command , arg ) ;
2005-04-17 02:20:36 +04:00
}
static void bind_device ( struct raw_config_request * rq )
{
2006-08-08 09:19:37 +04:00
device_destroy ( raw_class , MKDEV ( RAW_MAJOR , rq - > raw_minor ) ) ;
2008-05-21 23:52:33 +04:00
device_create_drvdata ( raw_class , NULL , MKDEV ( RAW_MAJOR , rq - > raw_minor ) ,
NULL , " raw%d " , rq - > raw_minor ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Deal with ioctls against the raw - device control interface , to bind
* and unbind other raw devices .
*/
static int raw_ctl_ioctl ( struct inode * inode , struct file * filp ,
unsigned int command , unsigned long arg )
{
struct raw_config_request rq ;
struct raw_device_data * rawdev ;
int err = 0 ;
switch ( command ) {
case RAW_SETBIND :
case RAW_GETBIND :
/* First, find out which raw minor we want */
if ( copy_from_user ( & rq , ( void __user * ) arg , sizeof ( rq ) ) ) {
err = - EFAULT ;
goto out ;
}
2007-02-10 12:46:10 +03:00
if ( rq . raw_minor < = 0 | | rq . raw_minor > = MAX_RAW_MINORS ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto out ;
}
rawdev = & raw_devices [ rq . raw_minor ] ;
if ( command = = RAW_SETBIND ) {
dev_t dev ;
/*
* This is like making block devices , so demand the
* same capability
*/
if ( ! capable ( CAP_SYS_ADMIN ) ) {
err = - EPERM ;
goto out ;
}
/*
* For now , we don ' t need to check that the underlying
* block device is present or not : we can do that when
* the raw device is opened . Just check that the
* major / minor numbers make sense .
*/
dev = MKDEV ( rq . block_major , rq . block_minor ) ;
if ( ( rq . block_major = = 0 & & rq . block_minor ! = 0 ) | |
MAJOR ( dev ) ! = rq . block_major | |
MINOR ( dev ) ! = rq . block_minor ) {
err = - EINVAL ;
goto out ;
}
2006-03-23 14:00:21 +03:00
mutex_lock ( & raw_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( rawdev - > inuse ) {
2006-03-23 14:00:21 +03:00
mutex_unlock ( & raw_mutex ) ;
2005-04-17 02:20:36 +04:00
err = - EBUSY ;
goto out ;
}
if ( rawdev - > binding ) {
bdput ( rawdev - > binding ) ;
module_put ( THIS_MODULE ) ;
}
if ( rq . block_major = = 0 & & rq . block_minor = = 0 ) {
/* unbind */
rawdev - > binding = NULL ;
2006-08-08 09:19:37 +04:00
device_destroy ( raw_class ,
2005-03-23 20:53:09 +03:00
MKDEV ( RAW_MAJOR , rq . raw_minor ) ) ;
2005-04-17 02:20:36 +04:00
} else {
rawdev - > binding = bdget ( dev ) ;
if ( rawdev - > binding = = NULL )
err = - ENOMEM ;
else {
__module_get ( THIS_MODULE ) ;
bind_device ( & rq ) ;
}
}
2006-03-23 14:00:21 +03:00
mutex_unlock ( & raw_mutex ) ;
2005-04-17 02:20:36 +04:00
} else {
struct block_device * bdev ;
2006-03-23 14:00:21 +03:00
mutex_lock ( & raw_mutex ) ;
2005-04-17 02:20:36 +04:00
bdev = rawdev - > binding ;
if ( bdev ) {
rq . block_major = MAJOR ( bdev - > bd_dev ) ;
rq . block_minor = MINOR ( bdev - > bd_dev ) ;
} else {
rq . block_major = rq . block_minor = 0 ;
}
2006-03-23 14:00:21 +03:00
mutex_unlock ( & raw_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( copy_to_user ( ( void __user * ) arg , & rq , sizeof ( rq ) ) ) {
err = - EFAULT ;
goto out ;
}
}
break ;
default :
err = - EINVAL ;
break ;
}
out :
return err ;
}
2006-07-03 11:24:21 +04:00
static const struct file_operations raw_fops = {
2006-10-01 10:28:48 +04:00
. read = do_sync_read ,
2005-04-17 02:20:36 +04:00
. aio_read = generic_file_aio_read ,
2006-10-01 10:28:48 +04:00
. write = do_sync_write ,
2006-10-01 10:28:46 +04:00
. aio_write = generic_file_aio_write_nolock ,
2005-04-17 02:20:36 +04:00
. open = raw_open ,
. release = raw_release ,
. ioctl = raw_ioctl ,
. owner = THIS_MODULE ,
} ;
2006-07-03 11:24:21 +04:00
static const struct file_operations raw_ctl_fops = {
2005-04-17 02:20:36 +04:00
. ioctl = raw_ctl_ioctl ,
. open = raw_open ,
. owner = THIS_MODULE ,
} ;
2007-09-13 02:06:57 +04:00
static struct cdev raw_cdev ;
2005-04-17 02:20:36 +04:00
static int __init raw_init ( void )
{
dev_t dev = MKDEV ( RAW_MAJOR , 0 ) ;
2006-09-29 13:00:46 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
2006-09-29 13:00:46 +04:00
ret = register_chrdev_region ( dev , MAX_RAW_MINORS , " raw " ) ;
if ( ret )
2005-04-17 02:20:36 +04:00
goto error ;
cdev_init ( & raw_cdev , & raw_fops ) ;
2006-09-29 13:00:46 +04:00
ret = cdev_add ( & raw_cdev , dev , MAX_RAW_MINORS ) ;
if ( ret ) {
2005-04-17 02:20:36 +04:00
kobject_put ( & raw_cdev . kobj ) ;
2006-09-29 13:00:46 +04:00
goto error_region ;
2005-04-17 02:20:36 +04:00
}
2005-03-23 20:53:09 +03:00
raw_class = class_create ( THIS_MODULE , " raw " ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( raw_class ) ) {
printk ( KERN_ERR " Error creating raw class. \n " ) ;
cdev_del ( & raw_cdev ) ;
2006-09-29 13:00:46 +04:00
ret = PTR_ERR ( raw_class ) ;
goto error_region ;
2005-04-17 02:20:36 +04:00
}
2008-05-21 23:52:33 +04:00
device_create_drvdata ( raw_class , NULL , MKDEV ( RAW_MAJOR , 0 ) , NULL ,
" rawctl " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2006-09-29 13:00:46 +04:00
error_region :
unregister_chrdev_region ( dev , MAX_RAW_MINORS ) ;
2005-04-17 02:20:36 +04:00
error :
2006-09-29 13:00:46 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
static void __exit raw_exit ( void )
{
2006-08-08 09:19:37 +04:00
device_destroy ( raw_class , MKDEV ( RAW_MAJOR , 0 ) ) ;
2005-03-23 20:53:09 +03:00
class_destroy ( raw_class ) ;
2005-04-17 02:20:36 +04:00
cdev_del ( & raw_cdev ) ;
unregister_chrdev_region ( MKDEV ( RAW_MAJOR , 0 ) , MAX_RAW_MINORS ) ;
}
module_init ( raw_init ) ;
module_exit ( raw_exit ) ;
MODULE_LICENSE ( " GPL " ) ;