2005-04-17 02:20:36 +04:00
/*
* Core registration and callback routines for MTD
* drivers and users .
*
2010-08-08 23:58:20 +04:00
* Copyright © 1999 - 2010 David Woodhouse < dwmw2 @ infradead . org >
* Copyright © 2006 Red Hat UK Limited
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/ptrace.h>
# include <linux/string.h>
# include <linux/timer.h>
# include <linux/major.h>
# include <linux/fs.h>
2006-10-11 15:52:44 +04:00
# include <linux/err.h>
2005-04-17 02:20:36 +04:00
# include <linux/ioctl.h>
# include <linux/init.h>
# include <linux/proc_fs.h>
2010-01-29 23:59:42 +03:00
# include <linux/idr.h>
2010-04-27 11:40:52 +04:00
# include <linux/backing-dev.h>
2010-03-29 21:52:39 +04:00
# include <linux/gfp.h>
2005-04-17 02:20:36 +04:00
# include <linux/mtd/mtd.h>
2007-05-28 23:28:34 +04:00
# include "mtdcore.h"
2010-04-27 11:40:52 +04:00
/*
* backing device capabilities for non - mappable devices ( such as NAND flash )
* - permits private mappings , copies are taken of the data
*/
2011-01-12 03:37:25 +03:00
static struct backing_dev_info mtd_bdi_unmappable = {
2010-04-27 11:40:52 +04:00
. capabilities = BDI_CAP_MAP_COPY ,
} ;
/*
* backing device capabilities for R / O mappable devices ( such as ROM )
* - permits private mappings , copies are taken of the data
* - permits non - writable shared mappings
*/
2011-01-12 03:37:25 +03:00
static struct backing_dev_info mtd_bdi_ro_mappable = {
2010-04-27 11:40:52 +04:00
. capabilities = ( BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP ) ,
} ;
/*
* backing device capabilities for writable mappable devices ( such as RAM )
* - permits private mappings , copies are taken of the data
* - permits non - writable shared mappings
*/
2011-01-12 03:37:25 +03:00
static struct backing_dev_info mtd_bdi_rw_mappable = {
2010-04-27 11:40:52 +04:00
. capabilities = ( BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP |
BDI_CAP_WRITE_MAP ) ,
} ;
2007-05-28 23:28:34 +04:00
2009-04-05 18:40:58 +04:00
static int mtd_cls_suspend ( struct device * dev , pm_message_t state ) ;
static int mtd_cls_resume ( struct device * dev ) ;
static struct class mtd_class = {
. name = " mtd " ,
. owner = THIS_MODULE ,
. suspend = mtd_cls_suspend ,
. resume = mtd_cls_resume ,
} ;
2009-03-26 10:42:41 +03:00
2010-01-29 23:59:42 +03:00
static DEFINE_IDR ( mtd_idr ) ;
2005-11-07 14:15:26 +03:00
/* These are exported solely for the purpose of mtd_blkdevs.c. You
2005-04-17 02:20:36 +04:00
should not use them for _anything_ else */
2006-03-31 14:29:41 +04:00
DEFINE_MUTEX ( mtd_table_mutex ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL_GPL ( mtd_table_mutex ) ;
2010-01-29 23:59:42 +03:00
struct mtd_info * __mtd_next_device ( int i )
{
return idr_get_next ( & mtd_idr , & i ) ;
}
EXPORT_SYMBOL_GPL ( __mtd_next_device ) ;
2005-04-17 02:20:36 +04:00
static LIST_HEAD ( mtd_notifiers ) ;
2009-03-26 10:42:41 +03:00
# if defined(CONFIG_MTD_CHAR) || defined(CONFIG_MTD_CHAR_MODULE)
# define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
# else
# define MTD_DEVT(index) 0
# endif
/* REVISIT once MTD uses the driver model better, whoever allocates
* the mtd_info will probably want to use the release ( ) hook . . .
*/
static void mtd_release ( struct device * dev )
{
2009-04-12 11:14:46 +04:00
dev_t index = MTD_DEVT ( dev_to_mtd ( dev ) - > index ) ;
2009-03-26 10:42:41 +03:00
/* remove /dev/mtdXro node if needed */
2009-04-12 11:14:46 +04:00
if ( index )
2009-04-05 18:40:58 +04:00
device_destroy ( & mtd_class , index + 1 ) ;
}
static int mtd_cls_suspend ( struct device * dev , pm_message_t state )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
2009-07-28 15:56:43 +04:00
if ( mtd & & mtd - > suspend )
2009-04-05 18:40:58 +04:00
return mtd - > suspend ( mtd ) ;
else
return 0 ;
}
static int mtd_cls_resume ( struct device * dev )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
2009-07-28 15:56:43 +04:00
if ( mtd & & mtd - > resume )
2009-04-05 18:40:58 +04:00
mtd - > resume ( mtd ) ;
return 0 ;
2009-03-26 10:42:41 +03:00
}
static ssize_t mtd_type_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
char * type ;
switch ( mtd - > type ) {
case MTD_ABSENT :
type = " absent " ;
break ;
case MTD_RAM :
type = " ram " ;
break ;
case MTD_ROM :
type = " rom " ;
break ;
case MTD_NORFLASH :
type = " nor " ;
break ;
case MTD_NANDFLASH :
type = " nand " ;
break ;
case MTD_DATAFLASH :
type = " dataflash " ;
break ;
case MTD_UBIVOLUME :
type = " ubi " ;
break ;
default :
type = " unknown " ;
}
return snprintf ( buf , PAGE_SIZE , " %s \n " , type ) ;
}
2009-04-04 00:00:45 +04:00
static DEVICE_ATTR ( type , S_IRUGO , mtd_type_show , NULL ) ;
static ssize_t mtd_flags_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " 0x%lx \n " , ( unsigned long ) mtd - > flags ) ;
}
static DEVICE_ATTR ( flags , S_IRUGO , mtd_flags_show , NULL ) ;
static ssize_t mtd_size_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %llu \n " ,
( unsigned long long ) mtd - > size ) ;
}
static DEVICE_ATTR ( size , S_IRUGO , mtd_size_show , NULL ) ;
static ssize_t mtd_erasesize_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %lu \n " , ( unsigned long ) mtd - > erasesize ) ;
}
static DEVICE_ATTR ( erasesize , S_IRUGO , mtd_erasesize_show , NULL ) ;
static ssize_t mtd_writesize_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %lu \n " , ( unsigned long ) mtd - > writesize ) ;
}
static DEVICE_ATTR ( writesize , S_IRUGO , mtd_writesize_show , NULL ) ;
2009-04-18 13:29:42 +04:00
static ssize_t mtd_subpagesize_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
unsigned int subpagesize = mtd - > writesize > > mtd - > subpage_sft ;
return snprintf ( buf , PAGE_SIZE , " %u \n " , subpagesize ) ;
}
static DEVICE_ATTR ( subpagesize , S_IRUGO , mtd_subpagesize_show , NULL ) ;
2009-04-04 00:00:45 +04:00
static ssize_t mtd_oobsize_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %lu \n " , ( unsigned long ) mtd - > oobsize ) ;
}
static DEVICE_ATTR ( oobsize , S_IRUGO , mtd_oobsize_show , NULL ) ;
static ssize_t mtd_numeraseregions_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %u \n " , mtd - > numeraseregions ) ;
}
static DEVICE_ATTR ( numeraseregions , S_IRUGO , mtd_numeraseregions_show ,
NULL ) ;
static ssize_t mtd_name_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mtd_info * mtd = dev_to_mtd ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " , mtd - > name ) ;
}
static DEVICE_ATTR ( name , S_IRUGO , mtd_name_show , NULL ) ;
2009-03-26 10:42:41 +03:00
static struct attribute * mtd_attrs [ ] = {
2009-04-04 00:00:45 +04:00
& dev_attr_type . attr ,
& dev_attr_flags . attr ,
& dev_attr_size . attr ,
& dev_attr_erasesize . attr ,
& dev_attr_writesize . attr ,
2009-04-18 13:29:42 +04:00
& dev_attr_subpagesize . attr ,
2009-04-04 00:00:45 +04:00
& dev_attr_oobsize . attr ,
& dev_attr_numeraseregions . attr ,
& dev_attr_name . attr ,
2009-03-26 10:42:41 +03:00
NULL ,
} ;
2009-08-07 03:05:32 +04:00
static struct attribute_group mtd_group = {
2009-03-26 10:42:41 +03:00
. attrs = mtd_attrs ,
} ;
2009-09-20 16:55:36 +04:00
static const struct attribute_group * mtd_groups [ ] = {
2009-03-26 10:42:41 +03:00
& mtd_group ,
NULL ,
} ;
static struct device_type mtd_devtype = {
. name = " mtd " ,
. groups = mtd_groups ,
. release = mtd_release ,
} ;
2005-04-17 02:20:36 +04:00
/**
* 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
2010-01-29 23:59:42 +03:00
* if there is insufficient memory or a sysfs error .
2005-04-17 02:20:36 +04:00
*/
int add_mtd_device ( struct mtd_info * mtd )
{
2010-01-29 23:59:42 +03:00
struct mtd_notifier * not ;
int i , error ;
2005-04-17 02:20:36 +04:00
2009-02-12 13:40:00 +03:00
if ( ! mtd - > backing_dev_info ) {
switch ( mtd - > type ) {
case MTD_RAM :
mtd - > backing_dev_info = & mtd_bdi_rw_mappable ;
break ;
case MTD_ROM :
mtd - > backing_dev_info = & mtd_bdi_ro_mappable ;
break ;
default :
mtd - > backing_dev_info = & mtd_bdi_unmappable ;
break ;
}
}
2006-06-14 19:53:44 +04:00
BUG_ON ( mtd - > writesize = = 0 ) ;
2006-03-31 14:29:41 +04:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04:00
2010-01-29 23:59:42 +03:00
do {
if ( ! idr_pre_get ( & mtd_idr , GFP_KERNEL ) )
goto fail_locked ;
error = idr_get_new ( & mtd_idr , mtd , & i ) ;
} while ( error = = - EAGAIN ) ;
2006-09-22 13:07:08 +04:00
2010-01-29 23:59:42 +03:00
if ( error )
goto fail_locked ;
2009-03-26 10:42:41 +03:00
2010-01-29 23:59:42 +03:00
mtd - > index = i ;
mtd - > usecount = 0 ;
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 ;
/* Some chips always power up locked. Unlock them now */
if ( ( mtd - > flags & MTD_WRITEABLE )
& & ( mtd - > flags & MTD_POWERUP_LOCK ) & & mtd - > unlock ) {
if ( mtd - > unlock ( mtd , 0 , mtd - > size ) )
printk ( KERN_WARNING
" %s: unlock failed, writes may not work \n " ,
mtd - > name ) ;
}
/* Caller should have set dev.parent to match the
* physical device .
*/
mtd - > dev . type = & mtd_devtype ;
mtd - > dev . class = & mtd_class ;
mtd - > dev . devt = MTD_DEVT ( i ) ;
dev_set_name ( & mtd - > dev , " mtd%d " , i ) ;
dev_set_drvdata ( & mtd - > dev , mtd ) ;
if ( device_register ( & mtd - > dev ) ! = 0 )
goto fail_added ;
if ( MTD_DEVT ( i ) )
device_create ( & mtd_class , mtd - > dev . parent ,
MTD_DEVT ( i ) + 1 ,
NULL , " mtd%dro " , i ) ;
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 */
list_for_each_entry ( not , & mtd_notifiers , list )
not - > add ( mtd ) ;
mutex_unlock ( & mtd_table_mutex ) ;
/* 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 14:15:26 +03:00
2010-01-29 23:59:42 +03:00
fail_added :
idr_remove ( & mtd_idr , i ) ;
fail_locked :
2006-03-31 14:29:41 +04:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04: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 ;
2010-02-22 21:39:32 +03:00
struct mtd_notifier * not ;
2005-11-07 14:15:26 +03:00
2006-03-31 14:29:41 +04:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04:00
2010-01-29 23:59:42 +03:00
if ( idr_find ( & mtd_idr , mtd - > index ) ! = mtd ) {
2005-04-17 02:20:36 +04:00
ret = - ENODEV ;
2010-02-22 21:39:32 +03:00
goto out_error ;
}
/* No need to get a refcount on the module containing
the notifier , since we hold the mtd_table_mutex */
list_for_each_entry ( not , & mtd_notifiers , list )
not - > remove ( mtd ) ;
if ( mtd - > usecount ) {
2005-11-07 14:15:26 +03:00
printk ( KERN_NOTICE " Removing MTD device #%d (%s) with use count %d \n " ,
2005-04-17 02:20:36 +04:00
mtd - > index , mtd - > name , mtd - > usecount ) ;
ret = - EBUSY ;
} else {
2009-04-04 00:00:45 +04:00
device_unregister ( & mtd - > dev ) ;
2010-01-29 23:59:42 +03:00
idr_remove ( & mtd_idr , mtd - > index ) ;
2005-04-17 02:20:36 +04:00
module_put ( THIS_MODULE ) ;
ret = 0 ;
}
2010-02-22 21:39:32 +03:00
out_error :
2006-03-31 14:29:41 +04:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04: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 )
{
2010-01-29 23:57:11 +03:00
struct mtd_info * mtd ;
2005-04-17 02:20:36 +04:00
2006-03-31 14:29:41 +04:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04:00
list_add ( & new - > list , & mtd_notifiers ) ;
__module_get ( THIS_MODULE ) ;
2005-11-07 14:15:26 +03:00
2010-01-29 23:57:11 +03:00
mtd_for_each_device ( mtd )
new - > add ( mtd ) ;
2005-04-17 02:20:36 +04:00
2006-03-31 14:29:41 +04:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04:00
}
/**
2005-02-18 17:34:54 +03:00
* unregister_mtd_user - unregister a ' user ' of MTD devices .
* @ old : pointer to notifier info structure
2005-04-17 02:20:36 +04: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 )
{
2010-01-29 23:57:11 +03:00
struct mtd_info * mtd ;
2005-04-17 02:20:36 +04:00
2006-03-31 14:29:41 +04:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04:00
module_put ( THIS_MODULE ) ;
2010-01-29 23:57:11 +03:00
mtd_for_each_device ( mtd )
old - > remove ( mtd ) ;
2005-11-07 14:15:26 +03:00
2005-04-17 02:20:36 +04:00
list_del ( & old - > list ) ;
2006-03-31 14:29:41 +04:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04: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 15:52:47 +04:00
* both , return the num ' th driver only if its address matches . Return
* error code if not .
2005-04-17 02:20:36 +04:00
*/
2005-11-07 14:15:26 +03:00
2005-04-17 02:20:36 +04:00
struct mtd_info * get_mtd_device ( struct mtd_info * mtd , int num )
{
2010-01-29 23:57:11 +03:00
struct mtd_info * ret = NULL , * other ;
int err = - ENODEV ;
2005-04-17 02:20:36 +04:00
2006-03-31 14:29:41 +04:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( num = = - 1 ) {
2010-01-29 23:57:11 +03:00
mtd_for_each_device ( other ) {
if ( other = = mtd ) {
ret = mtd ;
break ;
}
}
2010-01-29 23:59:42 +03:00
} else if ( num > = 0 ) {
ret = idr_find ( & mtd_idr , num ) ;
2005-04-17 02:20:36 +04:00
if ( mtd & & mtd ! = ret )
ret = NULL ;
}
2010-02-22 21:39:28 +03:00
if ( ! ret ) {
ret = ERR_PTR ( err ) ;
goto out ;
2006-10-11 15:52:45 +04:00
}
2005-04-17 02:20:36 +04:00
2010-02-22 21:39:28 +03:00
err = __get_mtd_device ( ret ) ;
if ( err )
ret = ERR_PTR ( err ) ;
out :
2006-10-11 15:52:47 +04:00
mutex_unlock ( & mtd_table_mutex ) ;
return ret ;
2010-02-22 21:39:28 +03:00
}
2005-04-17 02:20:36 +04:00
2010-02-22 21:39:28 +03:00
int __get_mtd_device ( struct mtd_info * mtd )
{
int err ;
if ( ! try_module_get ( mtd - > owner ) )
return - ENODEV ;
if ( mtd - > get_device ) {
err = mtd - > get_device ( mtd ) ;
if ( err ) {
module_put ( mtd - > owner ) ;
return err ;
}
}
mtd - > usecount + + ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-10-11 15:52:44 +04: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 )
{
2010-01-29 23:57:11 +03:00
int err = - ENODEV ;
struct mtd_info * mtd = NULL , * other ;
2006-10-11 15:52:44 +04:00
mutex_lock ( & mtd_table_mutex ) ;
2010-01-29 23:57:11 +03:00
mtd_for_each_device ( other ) {
if ( ! strcmp ( name , other - > name ) ) {
mtd = other ;
2006-10-11 15:52:44 +04:00
break ;
}
}
2006-10-11 15:52:45 +04:00
if ( ! mtd )
2006-10-11 15:52:44 +04:00
goto out_unlock ;
if ( ! try_module_get ( mtd - > owner ) )
goto out_unlock ;
2006-10-11 15:52:45 +04:00
if ( mtd - > get_device ) {
err = mtd - > get_device ( mtd ) ;
if ( err )
goto out_put ;
}
2006-10-11 15:52:44 +04:00
mtd - > usecount + + ;
2006-10-11 15:52:45 +04:00
mutex_unlock ( & mtd_table_mutex ) ;
return mtd ;
2006-10-11 15:52:44 +04:00
2006-10-11 15:52:45 +04:00
out_put :
module_put ( mtd - > owner ) ;
2006-10-11 15:52:44 +04:00
out_unlock :
mutex_unlock ( & mtd_table_mutex ) ;
2006-10-11 15:52:45 +04:00
return ERR_PTR ( err ) ;
2006-10-11 15:52:44 +04:00
}
2005-04-17 02:20:36 +04:00
void put_mtd_device ( struct mtd_info * mtd )
{
2006-03-31 14:29:41 +04:00
mutex_lock ( & mtd_table_mutex ) ;
2010-02-22 21:39:28 +03:00
__put_mtd_device ( mtd ) ;
mutex_unlock ( & mtd_table_mutex ) ;
}
void __put_mtd_device ( struct mtd_info * mtd )
{
- - mtd - > usecount ;
BUG_ON ( mtd - > usecount < 0 ) ;
2006-10-11 15:52:45 +04:00
if ( mtd - > put_device )
mtd - > put_device ( mtd ) ;
2005-04-17 02:20:36 +04:00
module_put ( mtd - > owner ) ;
}
/* default_mtd_writev - default mtd writev method for MTD devices that
2006-11-29 19:33:03 +03:00
* don ' t implement their own
2005-04-17 02:20:36 +04: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 19:33:03 +03: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 ) ;
2010-02-22 21:39:28 +03:00
EXPORT_SYMBOL_GPL ( __get_mtd_device ) ;
2006-11-29 19:33:03 +03:00
EXPORT_SYMBOL_GPL ( put_mtd_device ) ;
2010-02-22 21:39:28 +03:00
EXPORT_SYMBOL_GPL ( __put_mtd_device ) ;
2006-11-29 19:33:03 +03:00
EXPORT_SYMBOL_GPL ( register_mtd_user ) ;
EXPORT_SYMBOL_GPL ( unregister_mtd_user ) ;
EXPORT_SYMBOL_GPL ( default_mtd_writev ) ;
2005-04-17 02:20:36 +04:00
2006-03-31 14:29:49 +04:00
# ifdef CONFIG_PROC_FS
2005-04-17 02:20:36 +04:00
/*====================================================================*/
/* Support for /proc/mtd */
static struct proc_dir_entry * proc_mtd ;
2010-01-29 23:57:11 +03:00
static inline int mtd_proc_info ( char * buf , struct mtd_info * this )
2005-04-17 02:20:36 +04:00
{
2010-01-29 23:57:11 +03:00
return sprintf ( buf , " mtd%d: %8.8llx %8.8x \" %s \" \n " , this - > index ,
2008-12-10 16:37:21 +03:00
( unsigned long long ) this - > size ,
2005-04-17 02:20:36 +04:00
this - > erasesize , this - > name ) ;
}
static int mtd_read_proc ( char * page , char * * start , off_t off , int count ,
int * eof , void * data_unused )
{
2010-01-29 23:57:11 +03:00
struct mtd_info * mtd ;
int len , l ;
2005-04-17 02:20:36 +04:00
off_t begin = 0 ;
2006-03-31 14:29:41 +04:00
mutex_lock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04:00
len = sprintf ( page , " dev: size erasesize name \n " ) ;
2010-01-29 23:57:11 +03:00
mtd_for_each_device ( mtd ) {
l = mtd_proc_info ( page + len , mtd ) ;
2005-04-17 02:20:36 +04:00
len + = l ;
if ( len + begin > off + count )
goto done ;
if ( len + begin < off ) {
begin + = len ;
len = 0 ;
}
}
* eof = 1 ;
done :
2006-03-31 14:29:41 +04:00
mutex_unlock ( & mtd_table_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( off > = len + begin )
return 0 ;
* start = page + ( off - begin ) ;
return ( ( count < begin + len - off ) ? count : begin + len - off ) ;
}
2009-04-04 22:03:04 +04:00
# endif /* CONFIG_PROC_FS */
2005-04-17 02:20:36 +04:00
/*====================================================================*/
/* Init code */
2010-04-27 11:49:47 +04:00
static int __init mtd_bdi_init ( struct backing_dev_info * bdi , const char * name )
{
int ret ;
ret = bdi_init ( bdi ) ;
if ( ! ret )
ret = bdi_register ( bdi , NULL , name ) ;
if ( ret )
bdi_destroy ( bdi ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
static int __init init_mtd ( void )
{
2009-04-05 18:40:58 +04:00
int ret ;
2010-04-27 11:49:47 +04:00
2009-04-05 18:40:58 +04:00
ret = class_register ( & mtd_class ) ;
2010-04-27 11:49:47 +04:00
if ( ret )
goto err_reg ;
ret = mtd_bdi_init ( & mtd_bdi_unmappable , " mtd-unmap " ) ;
if ( ret )
goto err_bdi1 ;
ret = mtd_bdi_init ( & mtd_bdi_ro_mappable , " mtd-romap " ) ;
if ( ret )
goto err_bdi2 ;
ret = mtd_bdi_init ( & mtd_bdi_rw_mappable , " mtd-rwmap " ) ;
if ( ret )
goto err_bdi3 ;
2009-04-04 00:00:45 +04:00
2009-04-04 22:03:04 +04:00
# ifdef CONFIG_PROC_FS
2005-04-17 02:20:36 +04:00
if ( ( proc_mtd = create_proc_entry ( " mtd " , 0 , NULL ) ) )
proc_mtd - > read_proc = mtd_read_proc ;
2009-04-04 22:03:04 +04:00
# endif /* CONFIG_PROC_FS */
2005-04-17 02:20:36 +04:00
return 0 ;
2010-04-27 11:49:47 +04:00
err_bdi3 :
bdi_destroy ( & mtd_bdi_ro_mappable ) ;
err_bdi2 :
bdi_destroy ( & mtd_bdi_unmappable ) ;
err_bdi1 :
class_unregister ( & mtd_class ) ;
err_reg :
pr_err ( " Error registering mtd class or bdi: %d \n " , ret ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
static void __exit cleanup_mtd ( void )
{
2009-04-04 22:03:04 +04:00
# ifdef CONFIG_PROC_FS
2005-04-17 02:20:36 +04:00
if ( proc_mtd )
remove_proc_entry ( " mtd " , NULL ) ;
2009-04-04 22:03:04 +04:00
# endif /* CONFIG_PROC_FS */
2009-04-05 18:40:58 +04:00
class_unregister ( & mtd_class ) ;
2010-04-27 11:49:47 +04:00
bdi_destroy ( & mtd_bdi_unmappable ) ;
bdi_destroy ( & mtd_bdi_ro_mappable ) ;
bdi_destroy ( & mtd_bdi_rw_mappable ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( init_mtd ) ;
module_exit ( cleanup_mtd ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " David Woodhouse <dwmw2@infradead.org> " ) ;
MODULE_DESCRIPTION ( " Core MTD registration and access routines " ) ;