/*
- * $Id: mtdblock_ro.c,v 1.9 2001/10/02 15:05:11 dwmw2 Exp $
+ * $Id: mtdblock_ro.c,v 1.13 2002/03/11 16:03:29 sioux Exp $
*
- * Read-only version of the mtdblock device, without the
- * read/erase/modify/writeback stuff
+ * Read-only flash, read-write RAM version of the mtdblock device,
+ * without caching.
*/
#ifdef MTDBLOCK_DEBUG
#define LOCAL_END_REQUEST
#define MAJOR_NR MTD_BLOCK_MAJOR
#define DEVICE_NAME "mtdblock"
-#define DEVICE_NR(device) (device)
#include <linux/blk.h>
-#define RQFUNC_ARG request_queue_t *q
-
#ifdef MTDBLOCK_DEBUG
static int debug = MTDBLOCK_DEBUG;
MODULE_PARM(debug, "i");
#endif
-static struct gendisk *mtd_disks[MAX_MTD_DEVICES];
+struct mtdro_dev {
+ struct gendisk *disk;
+ struct mtd_info *mtd;
+ int open;
+};
+
+static struct mtdro_dev mtd_dev[MAX_MTD_DEVICES];
+static DECLARE_MUTEX(mtd_sem);
+
+static struct request_queue mtdro_queue;
+static spinlock_t mtdro_lock = SPIN_LOCK_UNLOCKED;
static int mtdblock_open(struct inode *inode, struct file *file)
{
- struct mtd_info *mtd = NULL;
- int dev = minor(inode->i_rdev);
- struct gendisk *disk = mtd_disks[dev];
+ struct mtdro_dev *mdev = inode->i_bdev->bd_disk->private_data;
+ int ret = 0;
DEBUG(1,"mtdblock_open\n");
- mtd = get_mtd_device(NULL, dev);
- if (!mtd)
- return -EINVAL;
- if (MTD_ABSENT == mtd->type) {
- put_mtd_device(mtd);
- return -EINVAL;
+ down(&mtd_sem);
+ if (mdev->mtd == NULL) {
+ mdev->mtd = get_mtd_device(NULL, minor(inode->i_rdev));
+ if (!mdev->mtd || mdev->mtd->type == MTD_ABSENT) {
+ if (mdev->mtd)
+ put_mtd_device(mdev->mtd);
+ ret = -ENODEV;
+ }
}
- set_capacity(disk, mtd->size>>9);
- add_disk(disk);
+ if (ret == 0) {
+ set_device_ro(inode->i_rdev, !(mdev->mtd->flags & MTD_CAP_RAM));
+ mdev->open++;
+ }
+ up(&mtd_sem);
- DEBUG(1, "ok\n");
+ DEBUG(1, "%s\n", ret ? "ok" : "nodev");
- return 0;
+ return ret;
}
static release_t mtdblock_release(struct inode *inode, struct file *file)
{
- int dev;
- struct mtd_info *mtd;
+ struct mtdro_dev *mdev = inode->i_bdev->bd_disk->private_data;
DEBUG(1, "mtdblock_release\n");
- if (inode == NULL)
- release_return(-ENODEV);
-
- dev = minor(inode->i_rdev);
- mtd = __get_mtd_device(NULL, dev);
-
- if (!mtd) {
- printk(KERN_WARNING "MTD device is absent on mtd_release!\n");
- release_return(-ENODEV);
- }
+ down(&mtd_sem);
+ if (mdev->open-- == 0) {
+ struct mtd_info *mtd = mdev->mtd;
- del_gendisk(mtd_disks[dev]);
-
- if (mtd->sync)
- mtd->sync(mtd);
+ mdev->mtd = NULL;
+ if (mtd->sync)
+ mtd->sync(mtd);
- put_mtd_device(mtd);
+ put_mtd_device(mtd);
+ }
+ up(&mtd_sem);
DEBUG(1, "ok\n");
release_return(0);
}
-static inline void mtdblock_end_request(struct request *req, int uptodate)
+static void mtdblock_request(request_queue_t *q)
{
- if (end_that_request_first(req, uptodate, req->hard_cur_sectors))
- return;
- blkdev_dequeue_request(req);
- end_that_request_last(req);
-}
-
-static void mtdblock_request(RQFUNC_ARG)
-{
- struct request *current_request;
- unsigned int res = 0;
- struct mtd_info *mtd;
-
- while (1)
- {
- /* Grab the Request and unlink it from the request list, we
- will execute a return if we are done. */
- if (blk_queue_empty(QUEUE))
- return;
-
- current_request = CURRENT;
-
- if (minor(current_request->rq_dev) >= MAX_MTD_DEVICES)
- {
- printk("mtd: Unsupported device!\n");
- mtdblock_end_request(current_request, 0);
- continue;
- }
-
- // Grab our MTD structure
-
- mtd = __get_mtd_device(NULL, minor(current_request->rq_dev));
- if (!mtd) {
- printk("MTD device %s doesn't appear to exist any more\n", kdevname(DEVICE_NR(CURRENT->rq_dev)));
- mtdblock_end_request(current_request, 0);
- }
-
- if (current_request->sector << 9 > mtd->size ||
- (current_request->sector + current_request->nr_sectors) << 9 > mtd->size)
- {
- printk("mtd: Attempt to read past end of device!\n");
- printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size, current_request->sector, current_request->nr_sectors);
- mtdblock_end_request(current_request, 0);
- continue;
- }
+ while (!blk_queue_empty(q)) {
+ struct request *req = elv_next_request(q);
+ struct mtdro_dev *mdev = req->rq_disk->private_data;
+ struct mtd_info *mtd = mdev->mtd;
+ unsigned int res;
+
+ if (!(req->flags & REQ_CMD)) {
+ res = 0;
+ goto end_req;
+ }
+
+ if ((req->sector + req->current_nr_sectors) > (mtd->size >> 9)) {
+ printk("mtd: Attempt to read past end of device!\n");
+ printk("size: %x, sector: %lx, nr_sectors: %x\n",
+ mtd->size, req->sector, req->current_nr_sectors);
+ res = 0;
+ goto end_req;
+ }
- /* Remove the request we are handling from the request list so nobody messes
- with it */
- /* Now drop the lock that the ll_rw_blk functions grabbed for us
- and process the request. This is necessary due to the extreme time
- we spend processing it. */
- spin_unlock_irq(&io_request_lock);
-
- // Handle the request
- switch (current_request->cmd)
- {
- size_t retlen;
-
- case READ:
- if (MTD_READ(mtd,current_request->sector<<9,
- current_request->nr_sectors << 9,
- &retlen, current_request->buffer) == 0)
- res = 1;
- else
- res = 0;
- break;
-
- case WRITE:
-
- /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector,
- current_request->nr_sectors);
- */
-
- // Read only device
- if ((mtd->flags & MTD_CAP_RAM) == 0)
- {
- res = 0;
- break;
- }
-
- // Do the write
- if (MTD_WRITE(mtd,current_request->sector<<9,
- current_request->nr_sectors << 9,
- &retlen, current_request->buffer) == 0)
- res = 1;
- else
- res = 0;
- break;
-
- // Shouldn't happen
- default:
- printk("mtd: unknown request\n");
- break;
- }
-
- // Grab the lock and re-thread the item onto the linked list
- spin_lock_irq(&io_request_lock);
- mtdblock_end_request(current_request, res);
- }
+ /* Now drop the lock that the ll_rw_blk functions grabbed for
+ us and process the request. This is necessary due to the
+ extreme time we spend processing it. */
+ spin_unlock_irq(q->queue_lock);
+
+ /* Handle the request */
+ switch (rq_data_dir(req)) {
+ size_t retlen;
+
+ case READ:
+ if (MTD_READ(mtd, req->sector << 9,
+ req->current_nr_sectors << 9,
+ &retlen, req->buffer) == 0)
+ res = 1;
+ else
+ res = 0;
+ break;
+
+ case WRITE:
+ /* printk("mtdblock_request WRITE sector=%d(%d)\n",
+ req->sector, req->current_nr_sectors);
+ */
+
+ /* Read only device */
+ if ((mtd->flags & MTD_CAP_RAM) == 0) {
+ res = 0;
+ break;
+ }
+
+ /* Do the write */
+ if (MTD_WRITE(mtd, req->sector << 9,
+ req->current_nr_sectors << 9,
+ &retlen, req->buffer) == 0)
+ res = 1;
+ else
+ res = 0;
+ break;
+
+ /* Shouldn't happen */
+ default:
+ printk("mtd: unknown request\n");
+ res = 0;
+ break;
+ }
+
+ /* Grab the lock and re-thread the item onto the linked list */
+ spin_lock_irq(q->queue_lock);
+ end_req:
+ if (!end_that_request_first(req, res, req->hard_cur_sectors)) {
+ blkdev_dequeue_request(req);
+ end_that_request_last(req);
+ }
+ }
}
-
-
static int mtdblock_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
- struct mtd_info *mtd;
-
- mtd = __get_mtd_device(NULL, minor(inode->i_rdev));
+ struct mtdro_dev *mdev = inode->i_bdev->bd_disk->private_data;
- if (!mtd || cmd != BLKFLSBUF)
+ if (cmd != BLKFLSBUF)
return -EINVAL;
fsync_bdev(inode->i_bdev);
invalidate_bdev(inode->i_bdev, 0);
- if (mtd->sync)
- mtd->sync(mtd);
+ if (mdev->mtd->sync)
+ mdev->mtd->sync(mdev->mtd);
+
return 0;
}
-static struct block_device_operations mtd_fops =
-{
+static struct block_device_operations mtd_fops = {
.owner = THIS_MODULE,
.open = mtdblock_open,
.release = mtdblock_release,
.ioctl = mtdblock_ioctl
};
-int __init init_mtdblock(void)
+/* Called with mtd_table_mutex held. */
+static void mtd_notify_add(struct mtd_info* mtd)
{
- int err = -ENOMEM;
- int i;
+ struct gendisk *disk;
- for (i = 0; i < MAX_MTD_DEVICES; i++) {
- struct gendisk *disk = alloc_disk(1);
- if (!disk)
- goto out;
+ if (!mtd || mtd->type == MTD_ABSENT || mtd->index >= MAX_MTD_DEVICES)
+ return;
+
+ disk = alloc_disk(1);
+ if (disk) {
disk->major = MAJOR_NR;
- disk->first_minor = i;
- sprintf(disk->disk_name, "mtdblock%d", i);
+ disk->first_minor = mtd->index;
disk->fops = &mtd_fops;
- mtd_disks[i] = disk;
+ sprintf(disk->disk_name, "mtdblock%d", mtd->index);
+
+ mtd_dev[mtd->index].disk = disk;
+ set_capacity(disk, mtd->size / 512);
+ disk->queue = &mtdro_queue;
+ disk->private_data = &mtd_dev[mtd->index];
+ add_disk(disk);
}
+}
+
+/* Called with mtd_table_mutex held. */
+static void mtd_notify_remove(struct mtd_info* mtd)
+{
+ struct mtdro_dev *mdev;
+ struct gendisk *disk;
+
+ if (!mtd || mtd->type == MTD_ABSENT || mtd->index >= MAX_MTD_DEVICES)
+ return;
+
+ mdev = &mtd_dev[mtd->index];
+
+ disk = mdev->disk;
+ mdev->disk = NULL;
+
+ if (disk) {
+ del_gendisk(disk);
+ put_disk(disk);
+ }
+}
+
+static struct mtd_notifier notifier = {
+ .add = mtd_notify_add,
+ .remove = mtd_notify_remove,
+};
+
+int __init init_mtdblock(void)
+{
+ int err;
if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
goto out;
}
- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
- return 0;
-out:
- while (i--)
- put_disk(mtd_disks[i]);
+ blk_init_queue(&mtdro_queue, &mtdblock_request, &mtdro_lock);
+ register_mtd_user(¬ifier);
+ err = 0;
+ out:
return err;
}
static void __exit cleanup_mtdblock(void)
{
- int i;
+ unregister_mtd_user(¬ifier);
unregister_blkdev(MAJOR_NR,DEVICE_NAME);
- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
- for (i = 0; i < MAX_MTD_DEVICES; i++)
- put_disk(mtd_disks[i]);
+ blk_cleanup_queue(&mtdro_queue);
}
module_init(init_mtdblock);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Erwin Authried <eauth@softsys.co.at> et al.");
-MODULE_DESCRIPTION("Simple read-only block device emulation access to MTD devices");
+MODULE_DESCRIPTION("Simple uncached block device emulation access to MTD devices");