2005-04-17 02:20:36 +04:00
/*
* linux / fs / char_dev . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*/
# include <linux/init.h>
# include <linux/fs.h>
2007-02-21 00:57:48 +03:00
# include <linux/kdev_t.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/major.h>
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/smp_lock.h>
2006-03-31 14:30:32 +04:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/kobject.h>
# include <linux/kobj_map.h>
# include <linux/cdev.h>
2006-02-07 01:12:43 +03:00
# include <linux/mutex.h>
2006-09-27 12:50:16 +04:00
# include <linux/backing-dev.h>
2005-04-17 02:20:36 +04:00
2006-09-30 22:52:18 +04:00
# include "internal.h"
2005-04-17 02:20:36 +04:00
2006-09-27 12:50:16 +04:00
/*
* capabilities for / dev / mem , / dev / kmem and similar directly mappable character
* devices
* - permits shared - mmap for read , write and / or exec
* - does not permit private mmap in NOMMU mode ( can ' t do COW )
* - no readahead or I / O queue unplugging required
*/
struct backing_dev_info directly_mappable_cdev_bdi = {
. capabilities = (
# ifdef CONFIG_MMU
/* permit private copies of the data to be taken */
BDI_CAP_MAP_COPY |
# endif
/* permit direct mmap, for read, write or exec */
BDI_CAP_MAP_DIRECT |
BDI_CAP_READ_MAP | BDI_CAP_WRITE_MAP | BDI_CAP_EXEC_MAP ) ,
} ;
2005-04-17 02:20:36 +04:00
static struct kobj_map * cdev_map ;
2006-02-07 01:12:43 +03:00
static DEFINE_MUTEX ( chrdevs_lock ) ;
2005-04-17 02:20:36 +04:00
static struct char_device_struct {
struct char_device_struct * next ;
unsigned int major ;
unsigned int baseminor ;
int minorct ;
2006-01-15 00:20:38 +03:00
char name [ 64 ] ;
2005-04-17 02:20:36 +04:00
struct cdev * cdev ; /* will die */
2006-03-31 14:30:32 +04:00
} * chrdevs [ CHRDEV_MAJOR_HASH_SIZE ] ;
2005-04-17 02:20:36 +04:00
/* index in the above */
static inline int major_to_index ( int major )
{
2006-03-31 14:30:32 +04:00
return major % CHRDEV_MAJOR_HASH_SIZE ;
2006-01-15 00:20:38 +03:00
}
2006-03-31 14:30:32 +04:00
# ifdef CONFIG_PROC_FS
2006-01-15 00:20:38 +03:00
2006-03-31 14:30:32 +04:00
void chrdev_show ( struct seq_file * f , off_t offset )
2006-01-15 00:20:38 +03:00
{
struct char_device_struct * cd ;
2006-03-31 14:30:32 +04:00
if ( offset < CHRDEV_MAJOR_HASH_SIZE ) {
mutex_lock ( & chrdevs_lock ) ;
for ( cd = chrdevs [ offset ] ; cd ; cd = cd - > next )
seq_printf ( f , " %3d %s \n " , cd - > major , cd - > name ) ;
mutex_unlock ( & chrdevs_lock ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-15 00:20:38 +03:00
}
2006-03-31 14:30:32 +04:00
# endif /* CONFIG_PROC_FS */
2005-04-17 02:20:36 +04:00
/*
* Register a single major with a specified minor range .
*
* If major = = 0 this functions will dynamically allocate a major and return
* its number .
*
* If major > 0 this function will attempt to reserve the passed range of
* minors and will return zero on success .
*
* Returns a - ve errno on failure .
*/
static struct char_device_struct *
__register_chrdev_region ( unsigned int major , unsigned int baseminor ,
int minorct , const char * name )
{
struct char_device_struct * cd , * * cp ;
int ret = 0 ;
int i ;
2006-03-25 14:08:13 +03:00
cd = kzalloc ( sizeof ( struct char_device_struct ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( cd = = NULL )
return ERR_PTR ( - ENOMEM ) ;
2006-02-07 01:12:43 +03:00
mutex_lock ( & chrdevs_lock ) ;
2005-04-17 02:20:36 +04:00
/* temporary */
if ( major = = 0 ) {
for ( i = ARRAY_SIZE ( chrdevs ) - 1 ; i > 0 ; i - - ) {
if ( chrdevs [ i ] = = NULL )
break ;
}
if ( i = = 0 ) {
ret = - EBUSY ;
goto out ;
}
major = i ;
ret = major ;
}
cd - > major = major ;
cd - > baseminor = baseminor ;
cd - > minorct = minorct ;
2006-01-15 00:20:38 +03:00
strncpy ( cd - > name , name , 64 ) ;
2005-04-17 02:20:36 +04:00
i = major_to_index ( major ) ;
for ( cp = & chrdevs [ i ] ; * cp ; cp = & ( * cp ) - > next )
if ( ( * cp ) - > major > major | |
2006-09-29 13:00:08 +04:00
( ( * cp ) - > major = = major & &
( ( ( * cp ) - > baseminor > = baseminor ) | |
( ( * cp ) - > baseminor + ( * cp ) - > minorct > baseminor ) ) ) )
2005-04-17 02:20:36 +04:00
break ;
2006-09-29 13:00:08 +04:00
/* Check for overlapping minor ranges. */
if ( * cp & & ( * cp ) - > major = = major ) {
int old_min = ( * cp ) - > baseminor ;
int old_max = ( * cp ) - > baseminor + ( * cp ) - > minorct - 1 ;
int new_min = baseminor ;
int new_max = baseminor + minorct - 1 ;
/* New driver overlaps from the left. */
if ( new_max > = old_min & & new_max < = old_max ) {
ret = - EBUSY ;
goto out ;
}
/* New driver overlaps from the right. */
if ( new_min < = old_max & & new_min > = old_min ) {
ret = - EBUSY ;
goto out ;
}
2005-04-17 02:20:36 +04:00
}
2006-09-29 13:00:08 +04:00
2005-04-17 02:20:36 +04:00
cd - > next = * cp ;
* cp = cd ;
2006-02-07 01:12:43 +03:00
mutex_unlock ( & chrdevs_lock ) ;
2005-04-17 02:20:36 +04:00
return cd ;
out :
2006-02-07 01:12:43 +03:00
mutex_unlock ( & chrdevs_lock ) ;
2005-04-17 02:20:36 +04:00
kfree ( cd ) ;
return ERR_PTR ( ret ) ;
}
static struct char_device_struct *
__unregister_chrdev_region ( unsigned major , unsigned baseminor , int minorct )
{
struct char_device_struct * cd = NULL , * * cp ;
int i = major_to_index ( major ) ;
2006-02-07 01:12:43 +03:00
mutex_lock ( & chrdevs_lock ) ;
2005-04-17 02:20:36 +04:00
for ( cp = & chrdevs [ i ] ; * cp ; cp = & ( * cp ) - > next )
if ( ( * cp ) - > major = = major & &
( * cp ) - > baseminor = = baseminor & &
( * cp ) - > minorct = = minorct )
break ;
if ( * cp ) {
cd = * cp ;
* cp = cd - > next ;
}
2006-02-07 01:12:43 +03:00
mutex_unlock ( & chrdevs_lock ) ;
2005-04-17 02:20:36 +04:00
return cd ;
}
2006-09-29 13:00:44 +04:00
/**
* register_chrdev_region ( ) - register a range of device numbers
* @ from : the first in the desired range of device numbers ; must include
* the major number .
* @ count : the number of consecutive device numbers required
* @ name : the name of the device or driver .
*
* Return value is zero on success , a negative error code on failure .
*/
2005-04-17 02:20:36 +04:00
int register_chrdev_region ( dev_t from , unsigned count , const char * name )
{
struct char_device_struct * cd ;
dev_t to = from + count ;
dev_t n , next ;
for ( n = from ; n < to ; n = next ) {
next = MKDEV ( MAJOR ( n ) + 1 , 0 ) ;
if ( next > to )
next = to ;
cd = __register_chrdev_region ( MAJOR ( n ) , MINOR ( n ) ,
next - n , name ) ;
if ( IS_ERR ( cd ) )
goto fail ;
}
return 0 ;
fail :
to = n ;
for ( n = from ; n < to ; n = next ) {
next = MKDEV ( MAJOR ( n ) + 1 , 0 ) ;
kfree ( __unregister_chrdev_region ( MAJOR ( n ) , MINOR ( n ) , next - n ) ) ;
}
return PTR_ERR ( cd ) ;
}
2006-09-29 13:00:44 +04:00
/**
* alloc_chrdev_region ( ) - register a range of char device numbers
* @ dev : output parameter for first assigned number
* @ baseminor : first of the requested range of minor numbers
* @ count : the number of minor numbers required
* @ name : the name of the associated device or driver
*
* Allocates a range of char device numbers . The major number will be
* chosen dynamically , and returned ( along with the first minor number )
* in @ dev . Returns zero or a negative error code .
*/
2005-04-17 02:20:36 +04:00
int alloc_chrdev_region ( dev_t * dev , unsigned baseminor , unsigned count ,
const char * name )
{
struct char_device_struct * cd ;
cd = __register_chrdev_region ( 0 , baseminor , count , name ) ;
if ( IS_ERR ( cd ) )
return PTR_ERR ( cd ) ;
* dev = MKDEV ( cd - > major , cd - > baseminor ) ;
return 0 ;
}
2006-07-14 11:24:23 +04:00
/**
* register_chrdev ( ) - Register a major number for character devices .
* @ major : major device number or 0 for dynamic allocation
* @ name : name of this range of devices
* @ fops : file operations associated with this devices
*
* If @ major = = 0 this functions will dynamically allocate a major and return
* its number .
*
* If @ major > 0 this function will attempt to reserve a device with the given
* major number and will return zero on success .
*
* Returns a - ve errno on failure .
*
* The name of this device has nothing to do with the name of the device in
* / dev . It only helps to keep track of the different owners of devices . If
* your module name has only one type of devices it ' s ok to use e . g . the name
* of the module here .
*
* This function registers a range of 256 minor numbers . The first minor number
* is 0.
*/
2005-04-17 02:20:36 +04:00
int register_chrdev ( unsigned int major , const char * name ,
2006-03-28 13:56:41 +04:00
const struct file_operations * fops )
2005-04-17 02:20:36 +04:00
{
struct char_device_struct * cd ;
struct cdev * cdev ;
char * s ;
int err = - ENOMEM ;
cd = __register_chrdev_region ( major , 0 , 256 , name ) ;
if ( IS_ERR ( cd ) )
return PTR_ERR ( cd ) ;
cdev = cdev_alloc ( ) ;
if ( ! cdev )
goto out2 ;
cdev - > owner = fops - > owner ;
cdev - > ops = fops ;
kobject_set_name ( & cdev - > kobj , " %s " , name ) ;
for ( s = strchr ( kobject_name ( & cdev - > kobj ) , ' / ' ) ; s ; s = strchr ( s , ' / ' ) )
* s = ' ! ' ;
err = cdev_add ( cdev , MKDEV ( cd - > major , 0 ) , 256 ) ;
if ( err )
goto out ;
cd - > cdev = cdev ;
return major ? 0 : cd - > major ;
out :
kobject_put ( & cdev - > kobj ) ;
out2 :
kfree ( __unregister_chrdev_region ( cd - > major , 0 , 256 ) ) ;
return err ;
}
2006-09-29 13:00:44 +04:00
/**
* unregister_chrdev_region ( ) - return a range of device numbers
* @ from : the first in the range of numbers to unregister
* @ count : the number of device numbers to unregister
*
* This function will unregister a range of @ count device numbers ,
* starting with @ from . The caller should normally be the one who
* allocated those numbers in the first place . . .
*/
2005-04-17 02:20:36 +04:00
void unregister_chrdev_region ( dev_t from , unsigned count )
{
dev_t to = from + count ;
dev_t n , next ;
for ( n = from ; n < to ; n = next ) {
next = MKDEV ( MAJOR ( n ) + 1 , 0 ) ;
if ( next > to )
next = to ;
kfree ( __unregister_chrdev_region ( MAJOR ( n ) , MINOR ( n ) , next - n ) ) ;
}
}
2007-07-19 12:47:51 +04:00
void unregister_chrdev ( unsigned int major , const char * name )
2005-04-17 02:20:36 +04:00
{
struct char_device_struct * cd ;
cd = __unregister_chrdev_region ( major , 0 , 256 ) ;
if ( cd & & cd - > cdev )
cdev_del ( cd - > cdev ) ;
kfree ( cd ) ;
}
static DEFINE_SPINLOCK ( cdev_lock ) ;
static struct kobject * cdev_get ( struct cdev * p )
{
struct module * owner = p - > owner ;
struct kobject * kobj ;
if ( owner & & ! try_module_get ( owner ) )
return NULL ;
kobj = kobject_get ( & p - > kobj ) ;
if ( ! kobj )
module_put ( owner ) ;
return kobj ;
}
void cdev_put ( struct cdev * p )
{
if ( p ) {
2005-07-13 00:58:30 +04:00
struct module * owner = p - > owner ;
2005-04-17 02:20:36 +04:00
kobject_put ( & p - > kobj ) ;
2005-07-13 00:58:30 +04:00
module_put ( owner ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
* Called every time a character special file is opened
*/
2008-02-08 15:22:00 +03:00
static int chrdev_open ( struct inode * inode , struct file * filp )
2005-04-17 02:20:36 +04:00
{
struct cdev * p ;
struct cdev * new = NULL ;
int ret = 0 ;
spin_lock ( & cdev_lock ) ;
p = inode - > i_cdev ;
if ( ! p ) {
struct kobject * kobj ;
int idx ;
spin_unlock ( & cdev_lock ) ;
kobj = kobj_lookup ( cdev_map , inode - > i_rdev , & idx ) ;
if ( ! kobj )
return - ENXIO ;
new = container_of ( kobj , struct cdev , kobj ) ;
spin_lock ( & cdev_lock ) ;
2008-05-19 01:39:11 +04:00
/* Check i_cdev again in case somebody beat us to it while
we dropped the lock . */
2005-04-17 02:20:36 +04:00
p = inode - > i_cdev ;
if ( ! p ) {
inode - > i_cdev = p = new ;
inode - > i_cindex = idx ;
list_add ( & inode - > i_devices , & p - > list ) ;
new = NULL ;
} else if ( ! cdev_get ( p ) )
ret = - ENXIO ;
} else if ( ! cdev_get ( p ) )
ret = - ENXIO ;
spin_unlock ( & cdev_lock ) ;
cdev_put ( new ) ;
if ( ret )
return ret ;
2008-08-11 17:34:22 +04:00
ret = - ENXIO ;
2005-04-17 02:20:36 +04:00
filp - > f_op = fops_get ( p - > ops ) ;
2008-08-11 17:34:22 +04:00
if ( ! filp - > f_op )
goto out_cdev_put ;
if ( filp - > f_op - > open ) {
2005-04-17 02:20:36 +04:00
ret = filp - > f_op - > open ( inode , filp ) ;
2008-08-11 17:34:22 +04:00
if ( ret )
goto out_cdev_put ;
}
return 0 ;
out_cdev_put :
cdev_put ( p ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
void cd_forget ( struct inode * inode )
{
spin_lock ( & cdev_lock ) ;
list_del_init ( & inode - > i_devices ) ;
inode - > i_cdev = NULL ;
spin_unlock ( & cdev_lock ) ;
}
2005-05-06 03:16:09 +04:00
static void cdev_purge ( struct cdev * cdev )
2005-04-17 02:20:36 +04:00
{
spin_lock ( & cdev_lock ) ;
while ( ! list_empty ( & cdev - > list ) ) {
struct inode * inode ;
inode = container_of ( cdev - > list . next , struct inode , i_devices ) ;
list_del_init ( & inode - > i_devices ) ;
inode - > i_cdev = NULL ;
}
spin_unlock ( & cdev_lock ) ;
}
/*
* Dummy default file - operations : the only thing this does
* is contain the open that then fills in the correct operations
* depending on the special file . . .
*/
2006-03-28 13:56:42 +04:00
const struct file_operations def_chr_fops = {
2005-04-17 02:20:36 +04:00
. open = chrdev_open ,
} ;
static struct kobject * exact_match ( dev_t dev , int * part , void * data )
{
struct cdev * p = data ;
return & p - > kobj ;
}
static int exact_lock ( dev_t dev , void * data )
{
struct cdev * p = data ;
return cdev_get ( p ) ? 0 : - 1 ;
}
2006-09-29 13:00:44 +04:00
/**
* cdev_add ( ) - add a char device to the system
* @ p : the cdev structure for the device
* @ dev : the first device number for which this device is responsible
* @ count : the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add ( ) adds the device represented by @ p to the system , making it
* live immediately . A negative error code is returned on failure .
*/
2005-04-17 02:20:36 +04:00
int cdev_add ( struct cdev * p , dev_t dev , unsigned count )
{
p - > dev = dev ;
p - > count = count ;
return kobj_map ( cdev_map , dev , count , NULL , exact_match , exact_lock , p ) ;
}
static void cdev_unmap ( dev_t dev , unsigned count )
{
kobj_unmap ( cdev_map , dev , count ) ;
}
2006-09-29 13:00:44 +04:00
/**
* cdev_del ( ) - remove a cdev from the system
* @ p : the cdev structure to be removed
*
* cdev_del ( ) removes @ p from the system , possibly freeing the structure
* itself .
*/
2005-04-17 02:20:36 +04:00
void cdev_del ( struct cdev * p )
{
cdev_unmap ( p - > dev , p - > count ) ;
kobject_put ( & p - > kobj ) ;
}
static void cdev_default_release ( struct kobject * kobj )
{
struct cdev * p = container_of ( kobj , struct cdev , kobj ) ;
cdev_purge ( p ) ;
}
static void cdev_dynamic_release ( struct kobject * kobj )
{
struct cdev * p = container_of ( kobj , struct cdev , kobj ) ;
cdev_purge ( p ) ;
kfree ( p ) ;
}
static struct kobj_type ktype_cdev_default = {
. release = cdev_default_release ,
} ;
static struct kobj_type ktype_cdev_dynamic = {
. release = cdev_dynamic_release ,
} ;
2006-09-29 13:00:44 +04:00
/**
* cdev_alloc ( ) - allocate a cdev structure
*
* Allocates and returns a cdev structure , or NULL on failure .
*/
2005-04-17 02:20:36 +04:00
struct cdev * cdev_alloc ( void )
{
2006-03-25 14:08:13 +03:00
struct cdev * p = kzalloc ( sizeof ( struct cdev ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( p ) {
INIT_LIST_HEAD ( & p - > list ) ;
2007-12-18 09:05:35 +03:00
kobject_init ( & p - > kobj , & ktype_cdev_dynamic ) ;
2005-04-17 02:20:36 +04:00
}
return p ;
}
2006-09-29 13:00:44 +04:00
/**
* cdev_init ( ) - initialize a cdev structure
* @ cdev : the structure to initialize
* @ fops : the file_operations for this device
*
* Initializes @ cdev , remembering @ fops , making it ready to add to the
* system with cdev_add ( ) .
*/
2006-03-28 13:56:41 +04:00
void cdev_init ( struct cdev * cdev , const struct file_operations * fops )
2005-04-17 02:20:36 +04:00
{
memset ( cdev , 0 , sizeof * cdev ) ;
INIT_LIST_HEAD ( & cdev - > list ) ;
2007-12-18 09:05:35 +03:00
kobject_init ( & cdev - > kobj , & ktype_cdev_default ) ;
2005-04-17 02:20:36 +04:00
cdev - > ops = fops ;
}
static struct kobject * base_probe ( dev_t dev , int * part , void * data )
{
if ( request_module ( " char-major-%d-%d " , MAJOR ( dev ) , MINOR ( dev ) ) > 0 )
/* Make old-style 2.4 aliases work */
request_module ( " char-major-%d " , MAJOR ( dev ) ) ;
return NULL ;
}
void __init chrdev_init ( void )
{
cdev_map = kobj_map_init ( base_probe , & chrdevs_lock ) ;
2007-10-17 10:25:46 +04:00
bdi_init ( & directly_mappable_cdev_bdi ) ;
2005-04-17 02:20:36 +04:00
}
/* Let modules do char dev stuff */
EXPORT_SYMBOL ( register_chrdev_region ) ;
EXPORT_SYMBOL ( unregister_chrdev_region ) ;
EXPORT_SYMBOL ( alloc_chrdev_region ) ;
EXPORT_SYMBOL ( cdev_init ) ;
EXPORT_SYMBOL ( cdev_alloc ) ;
EXPORT_SYMBOL ( cdev_del ) ;
EXPORT_SYMBOL ( cdev_add ) ;
EXPORT_SYMBOL ( register_chrdev ) ;
EXPORT_SYMBOL ( unregister_chrdev ) ;
2006-09-27 12:50:16 +04:00
EXPORT_SYMBOL ( directly_mappable_cdev_bdi ) ;