2005-04-16 15:20:36 -07:00
/*
* Core registration and callback routines for MTD
* drivers and users .
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/ptrace.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/timer.h>
# include <linux/major.h>
# include <linux/fs.h>
2006-10-11 14:52:44 +03:00
# include <linux/err.h>
2005-04-16 15:20:36 -07:00
# include <linux/ioctl.h>
# include <linux/init.h>
# include <linux/mtd/compatmac.h>
# include <linux/proc_fs.h>
# include <linux/mtd/mtd.h>
2007-05-28 20:28:34 +01:00
# include "mtdcore.h"
2005-11-07 11:15:26 +00:00
/* These are exported solely for the purpose of mtd_blkdevs.c. You
2005-04-16 15:20:36 -07:00
should not use them for _anything_ else */
2006-03-31 02:29:41 -08:00
DEFINE_MUTEX ( mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
struct mtd_info * mtd_table [ MAX_MTD_DEVICES ] ;
EXPORT_SYMBOL_GPL ( mtd_table_mutex ) ;
EXPORT_SYMBOL_GPL ( mtd_table ) ;
static LIST_HEAD ( mtd_notifiers ) ;
/**
* add_mtd_device - register an MTD device
* @ mtd : pointer to new MTD device info structure
*
* Add a device to the list of MTD devices present in the system , and
* notify each currently active MTD ' user ' of its arrival . Returns
* zero on success or 1 on failure , which currently will only happen
* if the number of present devices exceeds MAX_MTD_DEVICES ( i . e . 16 )
*/
int add_mtd_device ( struct mtd_info * mtd )
{
int i ;
2006-06-14 19:53:44 +04:00
BUG_ON ( mtd - > writesize = = 0 ) ;
2006-03-31 02:29:41 -08:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
for ( i = 0 ; i < MAX_MTD_DEVICES ; i + + )
if ( ! mtd_table [ i ] ) {
2008-05-31 15:28:09 +02:00
struct mtd_notifier * not ;
2005-04-16 15:20:36 -07:00
mtd_table [ i ] = mtd ;
mtd - > index = i ;
mtd - > usecount = 0 ;
2008-12-10 13:37:21 +00:00
if ( is_power_of_2 ( mtd - > erasesize ) )
mtd - > erasesize_shift = ffs ( mtd - > erasesize ) - 1 ;
else
mtd - > erasesize_shift = 0 ;
if ( is_power_of_2 ( mtd - > writesize ) )
mtd - > writesize_shift = ffs ( mtd - > writesize ) - 1 ;
else
mtd - > writesize_shift = 0 ;
mtd - > erasesize_mask = ( 1 < < mtd - > erasesize_shift ) - 1 ;
mtd - > writesize_mask = ( 1 < < mtd - > writesize_shift ) - 1 ;
2006-09-22 10:07:08 +01:00
/* Some chips always power up locked. Unlock them now */
if ( ( mtd - > flags & MTD_WRITEABLE )
2008-01-30 10:25:49 -08:00
& & ( mtd - > flags & MTD_POWERUP_LOCK ) & & mtd - > unlock ) {
2006-09-22 10:07:08 +01:00
if ( mtd - > unlock ( mtd , 0 , mtd - > size ) )
printk ( KERN_WARNING
" %s: unlock failed, "
" writes may not work \n " ,
mtd - > name ) ;
}
2005-04-16 15:20:36 -07:00
DEBUG ( 0 , " mtd: Giving out device %d to %s \n " , i , mtd - > name ) ;
/* No need to get a refcount on the module containing
the notifier , since we hold the mtd_table_mutex */
2008-05-19 20:11:50 +01:00
list_for_each_entry ( not , & mtd_notifiers , list )
2005-04-16 15:20:36 -07:00
not - > add ( mtd ) ;
2005-11-07 11:15:26 +00:00
2006-03-31 02:29:41 -08:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
/* We _know_ we aren't being removed, because
our caller is still holding us here . So none
of this try_ nonsense , and no bitching about it
either . : ) */
__module_get ( THIS_MODULE ) ;
return 0 ;
}
2005-11-07 11:15:26 +00:00
2006-03-31 02:29:41 -08:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
return 1 ;
}
/**
* del_mtd_device - unregister an MTD device
* @ mtd : pointer to MTD device info structure
*
* Remove a device from the list of MTD devices present in the system ,
* and notify each currently active MTD ' user ' of its departure .
* Returns zero on success or 1 on failure , which currently will happen
* if the requested device does not appear to be present in the list .
*/
int del_mtd_device ( struct mtd_info * mtd )
{
int ret ;
2005-11-07 11:15:26 +00:00
2006-03-31 02:29:41 -08:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
if ( mtd_table [ mtd - > index ] ! = mtd ) {
ret = - ENODEV ;
} else if ( mtd - > usecount ) {
2005-11-07 11:15:26 +00:00
printk ( KERN_NOTICE " Removing MTD device #%d (%s) with use count %d \n " ,
2005-04-16 15:20:36 -07:00
mtd - > index , mtd - > name , mtd - > usecount ) ;
ret = - EBUSY ;
} else {
2008-05-31 15:28:10 +02:00
struct mtd_notifier * not ;
2005-04-16 15:20:36 -07:00
/* No need to get a refcount on the module containing
the notifier , since we hold the mtd_table_mutex */
2008-05-19 20:11:50 +01:00
list_for_each_entry ( not , & mtd_notifiers , list )
2005-04-16 15:20:36 -07:00
not - > remove ( mtd ) ;
mtd_table [ mtd - > index ] = NULL ;
module_put ( THIS_MODULE ) ;
ret = 0 ;
}
2006-03-31 02:29:41 -08:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
/**
* register_mtd_user - register a ' user ' of MTD devices .
* @ new : pointer to notifier info structure
*
* Registers a pair of callbacks function to be called upon addition
* or removal of MTD devices . Causes the ' add ' callback to be immediately
* invoked for each MTD device currently present in the system .
*/
void register_mtd_user ( struct mtd_notifier * new )
{
int i ;
2006-03-31 02:29:41 -08:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
list_add ( & new - > list , & mtd_notifiers ) ;
__module_get ( THIS_MODULE ) ;
2005-11-07 11:15:26 +00:00
2005-04-16 15:20:36 -07:00
for ( i = 0 ; i < MAX_MTD_DEVICES ; i + + )
if ( mtd_table [ i ] )
new - > add ( mtd_table [ i ] ) ;
2006-03-31 02:29:41 -08:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
}
/**
2005-02-18 14:34:54 +00:00
* unregister_mtd_user - unregister a ' user ' of MTD devices .
* @ old : pointer to notifier info structure
2005-04-16 15:20:36 -07:00
*
* Removes a callback function pair from the list of ' users ' to be
* notified upon addition or removal of MTD devices . Causes the
* ' remove ' callback to be immediately invoked for each MTD device
* currently present in the system .
*/
int unregister_mtd_user ( struct mtd_notifier * old )
{
int i ;
2006-03-31 02:29:41 -08:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
module_put ( THIS_MODULE ) ;
for ( i = 0 ; i < MAX_MTD_DEVICES ; i + + )
if ( mtd_table [ i ] )
old - > remove ( mtd_table [ i ] ) ;
2005-11-07 11:15:26 +00:00
2005-04-16 15:20:36 -07:00
list_del ( & old - > list ) ;
2006-03-31 02:29:41 -08:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/**
* get_mtd_device - obtain a validated handle for an MTD device
* @ mtd : last known address of the required MTD device
* @ num : internal device number of the required MTD device
*
* Given a number and NULL address , return the num ' th entry in the device
* table , if any . Given an address and num = = - 1 , search the device table
* for a device with that address and return if it ' s still present . Given
2006-10-11 14:52:47 +03:00
* both , return the num ' th driver only if its address matches . Return
* error code if not .
2005-04-16 15:20:36 -07:00
*/
2005-11-07 11:15:26 +00:00
2005-04-16 15:20:36 -07:00
struct mtd_info * get_mtd_device ( struct mtd_info * mtd , int num )
{
struct mtd_info * ret = NULL ;
2006-10-11 14:52:47 +03:00
int i , err = - ENODEV ;
2005-04-16 15:20:36 -07:00
2006-03-31 02:29:41 -08:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
if ( num = = - 1 ) {
for ( i = 0 ; i < MAX_MTD_DEVICES ; i + + )
if ( mtd_table [ i ] = = mtd )
ret = mtd_table [ i ] ;
} else if ( num < MAX_MTD_DEVICES ) {
ret = mtd_table [ num ] ;
if ( mtd & & mtd ! = ret )
ret = NULL ;
}
2006-10-11 14:52:45 +03:00
if ( ! ret )
goto out_unlock ;
2006-10-11 14:52:47 +03:00
if ( ! try_module_get ( ret - > owner ) )
2006-10-11 14:52:45 +03:00
goto out_unlock ;
2006-10-11 14:52:47 +03:00
if ( ret - > get_device ) {
err = ret - > get_device ( ret ) ;
if ( err )
goto out_put ;
2006-10-11 14:52:45 +03:00
}
2005-04-16 15:20:36 -07:00
2006-10-11 14:52:45 +03:00
ret - > usecount + + ;
2006-10-11 14:52:47 +03:00
mutex_unlock ( & mtd_table_mutex ) ;
return ret ;
2005-04-16 15:20:36 -07:00
2006-10-11 14:52:47 +03:00
out_put :
module_put ( ret - > owner ) ;
2006-10-11 14:52:45 +03:00
out_unlock :
2006-03-31 02:29:41 -08:00
mutex_unlock ( & mtd_table_mutex ) ;
2006-10-11 14:52:47 +03:00
return ERR_PTR ( err ) ;
2005-04-16 15:20:36 -07:00
}
2006-10-11 14:52:44 +03:00
/**
* get_mtd_device_nm - obtain a validated handle for an MTD device by
* device name
* @ name : MTD device name to open
*
* This function returns MTD device description structure in case of
* success and an error code in case of failure .
*/
struct mtd_info * get_mtd_device_nm ( const char * name )
{
2006-10-11 14:52:45 +03:00
int i , err = - ENODEV ;
struct mtd_info * mtd = NULL ;
2006-10-11 14:52:44 +03:00
mutex_lock ( & mtd_table_mutex ) ;
for ( i = 0 ; i < MAX_MTD_DEVICES ; i + + ) {
if ( mtd_table [ i ] & & ! strcmp ( name , mtd_table [ i ] - > name ) ) {
mtd = mtd_table [ i ] ;
break ;
}
}
2006-10-11 14:52:45 +03:00
if ( ! mtd )
2006-10-11 14:52:44 +03:00
goto out_unlock ;
if ( ! try_module_get ( mtd - > owner ) )
goto out_unlock ;
2006-10-11 14:52:45 +03:00
if ( mtd - > get_device ) {
err = mtd - > get_device ( mtd ) ;
if ( err )
goto out_put ;
}
2006-10-11 14:52:44 +03:00
mtd - > usecount + + ;
2006-10-11 14:52:45 +03:00
mutex_unlock ( & mtd_table_mutex ) ;
return mtd ;
2006-10-11 14:52:44 +03:00
2006-10-11 14:52:45 +03:00
out_put :
module_put ( mtd - > owner ) ;
2006-10-11 14:52:44 +03:00
out_unlock :
mutex_unlock ( & mtd_table_mutex ) ;
2006-10-11 14:52:45 +03:00
return ERR_PTR ( err ) ;
2006-10-11 14:52:44 +03:00
}
2005-04-16 15:20:36 -07:00
void put_mtd_device ( struct mtd_info * mtd )
{
int c ;
2006-03-31 02:29:41 -08:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
c = - - mtd - > usecount ;
2006-10-11 14:52:45 +03:00
if ( mtd - > put_device )
mtd - > put_device ( mtd ) ;
2006-03-31 02:29:41 -08:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
BUG_ON ( c < 0 ) ;
module_put ( mtd - > owner ) ;
}
/* default_mtd_writev - default mtd writev method for MTD devices that
2006-11-29 16:33:03 +00:00
* don ' t implement their own
2005-04-16 15:20:36 -07:00
*/
int default_mtd_writev ( struct mtd_info * mtd , const struct kvec * vecs ,
unsigned long count , loff_t to , size_t * retlen )
{
unsigned long i ;
size_t totlen = 0 , thislen ;
int ret = 0 ;
if ( ! mtd - > write ) {
ret = - EROFS ;
} else {
for ( i = 0 ; i < count ; i + + ) {
if ( ! vecs [ i ] . iov_len )
continue ;
ret = mtd - > write ( mtd , to , vecs [ i ] . iov_len , & thislen , vecs [ i ] . iov_base ) ;
totlen + = thislen ;
if ( ret | | thislen ! = vecs [ i ] . iov_len )
break ;
to + = vecs [ i ] . iov_len ;
}
}
if ( retlen )
* retlen = totlen ;
return ret ;
}
2006-11-29 16:33:03 +00:00
EXPORT_SYMBOL_GPL ( add_mtd_device ) ;
EXPORT_SYMBOL_GPL ( del_mtd_device ) ;
EXPORT_SYMBOL_GPL ( get_mtd_device ) ;
EXPORT_SYMBOL_GPL ( get_mtd_device_nm ) ;
EXPORT_SYMBOL_GPL ( put_mtd_device ) ;
EXPORT_SYMBOL_GPL ( register_mtd_user ) ;
EXPORT_SYMBOL_GPL ( unregister_mtd_user ) ;
EXPORT_SYMBOL_GPL ( default_mtd_writev ) ;
2005-04-16 15:20:36 -07:00
2006-03-31 02:29:49 -08:00
# ifdef CONFIG_PROC_FS
2005-04-16 15:20:36 -07:00
/*====================================================================*/
/* Support for /proc/mtd */
static struct proc_dir_entry * proc_mtd ;
static inline int mtd_proc_info ( char * buf , int i )
{
struct mtd_info * this = mtd_table [ i ] ;
if ( ! this )
return 0 ;
2008-12-10 13:37:21 +00:00
return sprintf ( buf , " mtd%d: %8.8llx %8.8x \" %s \" \n " , i ,
( unsigned long long ) this - > size ,
2005-04-16 15:20:36 -07:00
this - > erasesize , this - > name ) ;
}
static int mtd_read_proc ( char * page , char * * start , off_t off , int count ,
int * eof , void * data_unused )
{
int len , l , i ;
off_t begin = 0 ;
2006-03-31 02:29:41 -08:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
len = sprintf ( page , " dev: size erasesize name \n " ) ;
for ( i = 0 ; i < MAX_MTD_DEVICES ; i + + ) {
l = mtd_proc_info ( page + len , i ) ;
len + = l ;
if ( len + begin > off + count )
goto done ;
if ( len + begin < off ) {
begin + = len ;
len = 0 ;
}
}
* eof = 1 ;
done :
2006-03-31 02:29:41 -08:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-16 15:20:36 -07:00
if ( off > = len + begin )
return 0 ;
* start = page + ( off - begin ) ;
return ( ( count < begin + len - off ) ? count : begin + len - off ) ;
}
/*====================================================================*/
/* Init code */
static int __init init_mtd ( void )
{
if ( ( proc_mtd = create_proc_entry ( " mtd " , 0 , NULL ) ) )
proc_mtd - > read_proc = mtd_read_proc ;
return 0 ;
}
static void __exit cleanup_mtd ( void )
{
if ( proc_mtd )
remove_proc_entry ( " mtd " , NULL ) ;
}
module_init ( init_mtd ) ;
module_exit ( cleanup_mtd ) ;
2006-03-31 02:29:49 -08:00
# endif /* CONFIG_PROC_FS */
2005-04-16 15:20:36 -07:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " David Woodhouse <dwmw2@infradead.org> " ) ;
MODULE_DESCRIPTION ( " Core MTD registration and access routines " ) ;