linux-old/drivers/mtd/mtdcore.c
<<
>>
Prefs
   1/*
   2 * $Id: mtdcore.c,v 1.34 2003/01/24 23:32:25 dwmw2 Exp $
   3 *
   4 * Core registration and callback routines for MTD
   5 * drivers and users.
   6 *
   7 */
   8
   9#include <linux/config.h>
  10#include <linux/module.h>
  11#include <linux/kernel.h>
  12#include <linux/sched.h>
  13#include <linux/ptrace.h>
  14#include <linux/slab.h>
  15#include <linux/string.h>
  16#include <linux/timer.h>
  17#include <linux/major.h>
  18#include <linux/fs.h>
  19#include <linux/ioctl.h>
  20#include <linux/mtd/compatmac.h>
  21#ifdef CONFIG_PROC_FS
  22#include <linux/proc_fs.h>
  23#endif
  24
  25#include <linux/mtd/mtd.h>
  26
  27static DECLARE_MUTEX(mtd_table_mutex);
  28static struct mtd_info *mtd_table[MAX_MTD_DEVICES];
  29static struct mtd_notifier *mtd_notifiers = NULL;
  30
  31/**
  32 *      add_mtd_device - register an MTD device
  33 *      @mtd: pointer to new MTD device info structure
  34 *
  35 *      Add a device to the list of MTD devices present in the system, and
  36 *      notify each currently active MTD 'user' of its arrival. Returns
  37 *      zero on success or 1 on failure, which currently will only happen
  38 *      if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16)
  39 */
  40
  41int add_mtd_device(struct mtd_info *mtd)
  42{
  43        int i;
  44
  45        down(&mtd_table_mutex);
  46
  47        for (i=0; i< MAX_MTD_DEVICES; i++)
  48                if (!mtd_table[i])
  49                {
  50                        struct mtd_notifier *not=mtd_notifiers;
  51
  52                        mtd_table[i] = mtd;
  53                        mtd->index = i;
  54                        DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
  55                        while (not)
  56                        {
  57                                (*(not->add))(mtd);
  58                                not = not->next;
  59                        }
  60                        up(&mtd_table_mutex);
  61                        MOD_INC_USE_COUNT;
  62                        return 0;
  63                }
  64        
  65        up(&mtd_table_mutex);
  66        return 1;
  67}
  68
  69/**
  70 *      del_mtd_device - unregister an MTD device
  71 *      @mtd: pointer to MTD device info structure
  72 *
  73 *      Remove a device from the list of MTD devices present in the system,
  74 *      and notify each currently active MTD 'user' of its departure.
  75 *      Returns zero on success or 1 on failure, which currently will happen
  76 *      if the requested device does not appear to be present in the list.
  77 */
  78
  79int del_mtd_device (struct mtd_info *mtd)
  80{
  81        struct mtd_notifier *not=mtd_notifiers;
  82        int i;
  83        
  84        down(&mtd_table_mutex);
  85
  86        for (i=0; i < MAX_MTD_DEVICES; i++)
  87        {
  88                if (mtd_table[i] == mtd)
  89                {
  90                        while (not)
  91                        {
  92                                (*(not->remove))(mtd);
  93                                not = not->next;
  94                        }
  95                        mtd_table[i] = NULL;
  96                        up (&mtd_table_mutex);
  97                        MOD_DEC_USE_COUNT;
  98                        return 0;
  99                }
 100        }
 101
 102        up(&mtd_table_mutex);
 103        return 1;
 104}
 105
 106/**
 107 *      register_mtd_user - register a 'user' of MTD devices.
 108 *      @new: pointer to notifier info structure
 109 *
 110 *      Registers a pair of callbacks function to be called upon addition
 111 *      or removal of MTD devices. Causes the 'add' callback to be immediately
 112 *      invoked for each MTD device currently present in the system.
 113 */
 114
 115void register_mtd_user (struct mtd_notifier *new)
 116{
 117        int i;
 118
 119        down(&mtd_table_mutex);
 120
 121        new->next = mtd_notifiers;
 122        mtd_notifiers = new;
 123
 124        MOD_INC_USE_COUNT;
 125        
 126        for (i=0; i< MAX_MTD_DEVICES; i++)
 127                if (mtd_table[i])
 128                        new->add(mtd_table[i]);
 129
 130        up(&mtd_table_mutex);
 131}
 132
 133/**
 134 *      register_mtd_user - unregister a 'user' of MTD devices.
 135 *      @new: pointer to notifier info structure
 136 *
 137 *      Removes a callback function pair from the list of 'users' to be
 138 *      notified upon addition or removal of MTD devices. Causes the
 139 *      'remove' callback to be immediately invoked for each MTD device
 140 *      currently present in the system.
 141 */
 142
 143int unregister_mtd_user (struct mtd_notifier *old)
 144{
 145        struct mtd_notifier **prev = &mtd_notifiers;
 146        struct mtd_notifier *cur;
 147        int i;
 148
 149        down(&mtd_table_mutex);
 150
 151        while ((cur = *prev)) {
 152                if (cur == old) {
 153                        *prev = cur->next;
 154
 155                        MOD_DEC_USE_COUNT;
 156
 157                        for (i=0; i< MAX_MTD_DEVICES; i++)
 158                                if (mtd_table[i])
 159                                        old->remove(mtd_table[i]);
 160                        
 161                        up(&mtd_table_mutex);
 162                        return 0;
 163                }
 164                prev = &cur->next;
 165        }
 166        up(&mtd_table_mutex);
 167        return 1;
 168}
 169
 170
 171/**
 172 *      __get_mtd_device - obtain a validated handle for an MTD device
 173 *      @mtd: last known address of the required MTD device
 174 *      @num: internal device number of the required MTD device
 175 *
 176 *      Given a number and NULL address, return the num'th entry in the device
 177 *      table, if any.  Given an address and num == -1, search the device table
 178 *      for a device with that address and return if it's still present. Given
 179 *      both, return the num'th driver only if its address matches. Return NULL
 180 *      if not. get_mtd_device() increases the use count, but
 181 *      __get_mtd_device() doesn't - you should generally use get_mtd_device().
 182 */
 183        
 184struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num)
 185{
 186        struct mtd_info *ret = NULL;
 187        int i;
 188
 189        down(&mtd_table_mutex);
 190
 191        if (num == -1) {
 192                for (i=0; i< MAX_MTD_DEVICES; i++)
 193                        if (mtd_table[i] == mtd)
 194                                ret = mtd_table[i];
 195        } else if (num < MAX_MTD_DEVICES) {
 196                ret = mtd_table[num];
 197                if (mtd && mtd != ret)
 198                        ret = NULL;
 199        }
 200        
 201        up(&mtd_table_mutex);
 202        return ret;
 203}
 204
 205
 206/* default_mtd_writev - default mtd writev method for MTD devices that
 207 *                      dont implement their own
 208 */
 209
 210int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
 211                       unsigned long count, loff_t to, size_t *retlen)
 212{
 213        unsigned long i;
 214        size_t totlen = 0, thislen;
 215        int ret = 0;
 216
 217        if(!mtd->write) {
 218                ret = -EROFS;
 219        } else {
 220                for (i=0; i<count; i++) {
 221                        if (!vecs[i].iov_len)
 222                                continue;
 223                        ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
 224                        totlen += thislen;
 225                        if (ret || thislen != vecs[i].iov_len)
 226                                break;
 227                        to += vecs[i].iov_len;
 228                }
 229        }
 230        if (retlen)
 231                *retlen = totlen;
 232        return ret;
 233}
 234
 235
 236/* default_mtd_readv - default mtd readv method for MTD devices that dont
 237 *                     implement their own
 238 */
 239
 240int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
 241                      unsigned long count, loff_t from, size_t *retlen)
 242{
 243        unsigned long i;
 244        size_t totlen = 0, thislen;
 245        int ret = 0;
 246
 247        if(!mtd->read) {
 248                ret = -EIO;
 249        } else {
 250                for (i=0; i<count; i++) {
 251                        if (!vecs[i].iov_len)
 252                                continue;
 253                        ret = mtd->read(mtd, from, vecs[i].iov_len, &thislen, vecs[i].iov_base);
 254                        totlen += thislen;
 255                        if (ret || thislen != vecs[i].iov_len)
 256                                break;
 257                        from += vecs[i].iov_len;
 258                }
 259        }
 260        if (retlen)
 261                *retlen = totlen;
 262        return ret;
 263}
 264
 265
 266EXPORT_SYMBOL(add_mtd_device);
 267EXPORT_SYMBOL(del_mtd_device);
 268EXPORT_SYMBOL(__get_mtd_device);
 269EXPORT_SYMBOL(register_mtd_user);
 270EXPORT_SYMBOL(unregister_mtd_user);
 271EXPORT_SYMBOL(default_mtd_writev);
 272EXPORT_SYMBOL(default_mtd_readv);
 273
 274/*====================================================================*/
 275/* Power management code */
 276
 277#ifdef CONFIG_PM
 278
 279#include <linux/pm.h>
 280
 281static struct pm_dev *mtd_pm_dev = NULL;
 282
 283static int mtd_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
 284{
 285        int ret = 0, i;
 286
 287        if (down_trylock(&mtd_table_mutex))
 288                return -EAGAIN;
 289        if (rqst == PM_SUSPEND) {
 290                for (i = 0; ret == 0 && i < MAX_MTD_DEVICES; i++) {
 291                        if (mtd_table[i] && mtd_table[i]->suspend)
 292                                ret = mtd_table[i]->suspend(mtd_table[i]);
 293                }
 294        } else i = MAX_MTD_DEVICES-1;
 295
 296        if (rqst == PM_RESUME || ret) {
 297                for ( ; i >= 0; i--) {
 298                        if (mtd_table[i] && mtd_table[i]->resume)
 299                                mtd_table[i]->resume(mtd_table[i]);
 300                }
 301        }
 302        up(&mtd_table_mutex);
 303        return ret;
 304}
 305#endif
 306
 307/*====================================================================*/
 308/* Support for /proc/mtd */
 309
 310#ifdef CONFIG_PROC_FS
 311
 312#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 313static struct proc_dir_entry *proc_mtd;
 314#endif
 315
 316static inline int mtd_proc_info (char *buf, int i)
 317{
 318        struct mtd_info *this = mtd_table[i];
 319
 320        if (!this)
 321                return 0;
 322
 323        return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size,
 324                       this->erasesize, this->name);
 325}
 326
 327static int mtd_read_proc ( char *page, char **start, off_t off,int count
 328#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 329                       ,int *eof, void *data_unused
 330#else
 331                        ,int unused
 332#endif
 333                        )
 334{
 335        int len, l, i;
 336        off_t   begin = 0;
 337
 338        down(&mtd_table_mutex);
 339
 340        len = sprintf(page, "dev:    size   erasesize  name\n");
 341        for (i=0; i< MAX_MTD_DEVICES; i++) {
 342
 343                l = mtd_proc_info(page + len, i);
 344                len += l;
 345                if (len+begin > off+count)
 346                        goto done;
 347                if (len+begin < off) {
 348                        begin += len;
 349                        len = 0;
 350                }
 351        }
 352
 353#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 354        *eof = 1;
 355#endif
 356
 357done:
 358        up(&mtd_table_mutex);
 359        if (off >= len+begin)
 360                return 0;
 361        *start = page + (off-begin);
 362        return ((count < begin+len-off) ? count : begin+len-off);
 363}
 364
 365#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
 366struct proc_dir_entry mtd_proc_entry = {
 367        0,                 /* low_ino: the inode -- dynamic */
 368        3, "mtd",     /* len of name and name */
 369        S_IFREG | S_IRUGO, /* mode */
 370        1, 0, 0,           /* nlinks, owner, group */
 371        0, NULL,           /* size - unused; operations -- use default */
 372        &mtd_read_proc,   /* function used to read data */
 373        /* nothing more */
 374    };
 375#endif
 376
 377#endif /* CONFIG_PROC_FS */
 378
 379/*====================================================================*/
 380/* Init code */
 381
 382int __init init_mtd(void)
 383{
 384#ifdef CONFIG_PROC_FS
 385#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 386        if ((proc_mtd = create_proc_entry( "mtd", 0, 0 )))
 387          proc_mtd->read_proc = mtd_read_proc;
 388#else
 389        proc_register_dynamic(&proc_root,&mtd_proc_entry);
 390#endif
 391#endif
 392
 393#if LINUX_VERSION_CODE < 0x20212
 394        init_mtd_devices();
 395#endif
 396
 397#ifdef CONFIG_PM
 398        mtd_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, mtd_pm_callback);
 399#endif
 400        return 0;
 401}
 402
 403static void __exit cleanup_mtd(void)
 404{
 405#ifdef CONFIG_PM
 406        if (mtd_pm_dev) {
 407                pm_unregister(mtd_pm_dev);
 408                mtd_pm_dev = NULL;
 409        }
 410#endif
 411
 412#ifdef CONFIG_PROC_FS
 413#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 414        if (proc_mtd)
 415          remove_proc_entry( "mtd", 0);
 416#else
 417        proc_unregister(&proc_root,mtd_proc_entry.low_ino);
 418#endif
 419#endif
 420}
 421
 422module_init(init_mtd);
 423module_exit(cleanup_mtd);
 424
 425
 426MODULE_LICENSE("GPL");
 427MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 428MODULE_DESCRIPTION("Core MTD registration and access routines");
 429