📄 mmc_disk.c
字号:
/* * drivers/mmc/mmc_disk.c * * Block Device Driver for MMC * * Copyright (C) 2001-2003 MIZI Research, Inc. * * Author: Yong-iL Joh <tolkien@mizi.com> * $Id: mmc_disk.c,v 1.2 2004/01/26 08:29:56 laputa Exp $ * * Revision History: * * 2001-XX-XX Yong-iL Joh <tolkien@mizi.com> * - initial release * * 2002-07-25 Chan Gyun Jeong <cgjeong@mizi.com> * - code cleanup and restructuring * * 2003-01-13 Chan Gyun Jeong <cgjeong@mizi.com> * - remove task_schedule() * * * 2004-01-06 kwang hyun La <nala.la@samsung.com> * - removed a delay fn for a performance */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/devfs_fs_kernel.h>#include <linux/blkpg.h>#include <linux/hdreg.h>#include <linux/genhd.h>#include <linux/sched.h>#include <linux/delay.h>#include <asm/uaccess.h>#include <asm/hardware.h>#include "mmc.h"#define MMC_SHIFT 2 /* max 4 partition */#define MMC_NRDEV (1 << MMC_SHIFT)#define MMC_NDISK (MAX_MMC_SLOTS << MMC_SHIFT)#define MAJOR_NR 60#define DEVICE_NAME "mmc"#define DEVICE_REQUEST do_mmc_request#define DEVICE_NR(device) (MINOR(device) >> MMC_SHIFT)#define DEVICE_ON(device)#define DEVICE_OFF(device)#define DEVICE_NO_RANDOM#include <linux/blk.h>static int mmc_sizes[MMC_NDISK];static int mmc_blksizes[MMC_NDISK];static struct hd_struct mmc_part[MMC_NDISK];static char mmc_gendisk_flags[MAX_MMC_SLOTS];static struct gendisk mmc_gendisk = { major: MAJOR_NR, major_name: DEVICE_NAME, minor_shift: MMC_SHIFT, max_p: MMC_NRDEV, part: mmc_part, sizes: mmc_sizes, flags: mmc_gendisk_flags,};static struct mmc_disk { int use_cnt; struct mmc_slot *slot;} mmc_disk[MAX_MMC_SLOTS];#define SD_PRIV(x) ((struct mmc_disk *)(mmc_disk) + (x))#define SD_SLOT(x) (SD_PRIV(x)->slot)#define IS_EMPTY(x) (SD_SLOT(x) == NULL)static int do_mmc_rw(struct request *req, int rd){ int ret = 0; int dev = DEVICE_NR(req->rq_dev); unsigned int sect_first, sect_last; unsigned long blksize, from; char *buffer; /* read_len must be 512 bytes */ blksize = SD_SLOT(dev)->read_len; buffer = req->buffer; sect_first = req->sector + mmc_part[MINOR(req->rq_dev)].start_sect; sect_last = sect_first + req->current_nr_sectors; from = sect_first * blksize; while (sect_first < sect_last) { ret = SD_SLOT(dev)->transfer1b(SD_SLOT(dev), rd, from, buffer); if (ret) { if (ret == -EBUSY) { DEBUG2(3, "[%s] MMC busy\n", __FUNCTION__); schedule_timeout(HZ/20); /* prevent busy waiting */ continue; } else { DEBUG2(1, "[%s] error: %d\n", __FUNCTION__, ret); break; } } sect_first++; from += blksize; buffer += blksize; } return ret;}static void do_mmc_request(request_queue_t * q){ int dev, ret, res; struct request *req; int rd; while (1) { INIT_REQUEST; /* blk.h */ req = CURRENT; /* We can do this because the generic code knows not to touch the request at the head of the queue */ spin_unlock_irq(&io_request_lock); /* check if the device is valid */ dev = DEVICE_NR(req->rq_dev); if (dev >= MAX_MMC_SLOTS || IS_EMPTY(dev) || mmc_sizes[MINOR(req->rq_dev)] == 0) { res = 0; goto endreq; } if (req->sector + req->current_nr_sectors > mmc_part[MINOR(req->rq_dev)].nr_sects) { /* request past end of device */ res = 0; goto endreq; } if (req->cmd == READ) { rd = 1; } else if (req->cmd == WRITE) { rd = 0; } else { /* invalid request command */ res = 0; goto endreq; } ret = do_mmc_rw(req, rd); if (ret) { res = 0; goto endreq; } res = 1; endreq: spin_lock_irq(&io_request_lock); end_request(res); }}static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ int dev = DEVICE_NR(inode->i_rdev); int minor = MINOR(inode->i_rdev); switch (cmd) { case BLKGETSIZE: return put_user(mmc_gendisk.part[minor].nr_sects, (long *) arg);#ifdef BLKGETSIZE64 case BLKGETSIZE64: return put_user((u64)mmc_gendisk.part[minor].nr_sects, (u64 *)arg);#endif case BLKFLSBUF: if (!capable(CAP_SYS_ADMIN)) return -EACCES; invalidate_device(inode->i_rdev, 1); return 0; case HDIO_GETGEO: { struct hd_geometry geo; geo.cylinders = 1; geo.heads = 1; geo.sectors = mmc_gendisk.part[minor].nr_sects; geo.start = mmc_gendisk.part[minor].start_sect; return copy_to_user((void *)arg, &geo, sizeof(geo)) ? -EFAULT : 0; } case BLKRRPART: { int i; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (SD_PRIV(dev)->use_cnt > 1) return -EBUSY; for (i = mmc_gendisk.max_p - 1; i >= 0; i--) { if (mmc_gendisk.part[i].nr_sects) { kdev_t devid = MKDEV(MAJOR(inode->i_rdev), minor + i); invalidate_device(devid, 1); mmc_gendisk.part[minor + i].start_sect = 0; mmc_gendisk.part[minor + i].nr_sects = 0; } } grok_partitions(&mmc_gendisk, dev, mmc_gendisk.max_p, SD_SLOT(dev)->size / SD_SLOT(dev)->read_len); return 0; } case BLKROSET: case BLKROGET: case BLKSSZGET: return blk_ioctl(inode->i_rdev, cmd, arg); default: return -EINVAL; }}static int mmc_open(struct inode *inode, struct file *filp){ int dev = DEVICE_NR(inode->i_rdev); if (dev >= MAX_MMC_SLOTS) return -ENODEV; if (IS_EMPTY(dev)) return -ENODEV; if ((filp->f_mode & FMODE_WRITE) && SD_SLOT(dev)->readonly) { DEBUG2(2, "[%s] it's readonly", __FUNCTION__); return -EROFS; } SD_PRIV(dev)->use_cnt++; MOD_INC_USE_COUNT; return 0;}static int mmc_release(struct inode * inode, struct file * filp){ int dev = DEVICE_NR(inode->i_rdev); invalidate_device(inode->i_rdev, 1); SD_PRIV(dev)->use_cnt--; if (!mmc_sizes[MINOR(inode->i_rdev)]) { SD_SLOT(dev) = NULL; } MOD_DEC_USE_COUNT; return 0;}static struct block_device_operations mmc_fops = { owner: THIS_MODULE, open: mmc_open, release: mmc_release, ioctl: mmc_ioctl,};static void mmc_notify_add(struct mmc_slot *slot){ int i, dev, minor; for (i = 0, dev = -1; i < MAX_MMC_SLOTS; i++) { if (IS_EMPTY(i)) { if (dev == -1) dev = i; } else if (SD_SLOT(i)->id == slot->id) { /* already mounted */ dev = i; break; } } if (dev == -1) return; SD_SLOT(dev) = slot; minor = dev << mmc_gendisk.minor_shift; mmc_gendisk.part[minor].start_sect = 0; mmc_gendisk.nr_real++; register_disk(&mmc_gendisk, MKDEV(MAJOR_NR, minor), mmc_gendisk.max_p, mmc_gendisk.fops, SD_SLOT(dev)->size / SD_SLOT(dev)->read_len);#ifdef CONFIG_MIZI event_notify(EXT_DEV_INSERT);#endif DEBUG2(1, "Register %s: %ldMB\n", "MultiMediaCard", SD_SLOT(dev)->size/(1024 * 1024));}static void mmc_notify_remove(struct mmc_slot *slot){ int dev, minor; for (dev = 0; dev < MAX_MMC_SLOTS; dev++) { if (SD_SLOT(dev) == slot) { break; } } if (dev >= MAX_MMC_SLOTS) return; minor = dev << mmc_gendisk.minor_shift; /* Unregister the related device files at the /dev/mmc directory, grok_partitions(*, *, *, 0) does not work here */ devfs_register_partitions (&mmc_gendisk, minor, 1); mmc_gendisk.nr_real--; if (!(SD_PRIV(dev)->use_cnt)) { SD_SLOT(dev) = NULL; } for (; minor < (dev + 1) << mmc_gendisk.minor_shift; minor++) { if (mmc_sizes[minor]) { mmc_sizes[minor] = 0; } }#ifdef CONFIG_MIZI event_notify(EXT_DEV_REMOVE);#endif DEBUG2(1, "Unregister %s: remain %s\n", "MultiMediaCard", IS_EMPTY(dev) ? "unmounted":"mounted");}static struct mmc_notifier disk_notifier = { add: mmc_notify_add, remove: mmc_notify_remove,};static int __init init_mmc_disk(void){ if (devfs_register_blkdev(MAJOR_NR, DEVICE_NAME, &mmc_fops)) { printk("MMC Disk: failed to register major %d\n", MAJOR_NR); return -EBUSY; } mmc_gendisk.fops = &mmc_fops; memset(mmc_gendisk.flags, GENHD_FL_REMOVABLE, MAX_MMC_SLOTS); blksize_size[MAJOR_NR] = mmc_blksizes; blk_size[MAJOR_NR] = mmc_sizes; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); add_gendisk(&mmc_gendisk); register_mmc_user(&disk_notifier); return 0;}static void __exit exit_mmc_disk(void){ unregister_mmc_user(&disk_notifier); devfs_unregister_blkdev(MAJOR_NR, DEVICE_NAME); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); del_gendisk(&mmc_gendisk); blk_size[MAJOR_NR] = NULL; blksize_size[MAJOR_NR] = NULL;}module_init(init_mmc_disk);module_exit(exit_mmc_disk);MODULE_AUTHOR("Yong-iL Joh <tolkien@mizi.com>");MODULE_LICENSE("Not GPL, Proprietary License");MODULE_DESCRIPTION("MMC Memory Card block device driver");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -