⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mmc_media.c

📁 三星公司ARM芯片S3C2410 SD/MMC LINUX 驱动程序源码。
💻 C
字号:
/* * Block driver for media (i.e., flash cards) * * Copyright 2002 Hewlett-Packard Company * * Use consistent with the GNU GPL is permitted, * provided that this copyright notice is * preserved in its entirety in all copies and derived works. * * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS * FITNESS FOR ANY PARTICULAR PURPOSE. * * Many thanks to Alessandro Rubini and Jonathan Corbet! * * Author:  Andrew Christian *          28 May 2002 */#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/kernel.h> /* printk() */#include <linux/fs.h>     /* everything... */#include <linux/errno.h>  /* error codes */#include <linux/types.h>  /* size_t */#include <linux/fcntl.h>  /* O_ACCMODE */#include <linux/hdreg.h>  /* HDIO_GETGEO */#include <linux/init.h>#include <linux/devfs_fs_kernel.h>#include <asm/system.h>#include <asm/uaccess.h>#include "mmc_media.h"#define MAJOR_NR mmc_major /* force definitions on in blk.h */static int mmc_major; /* must be declared before including blk.h */#define MMC_SHIFT           3             /* max 8 partitions per card */#define DEVICE_NR(device)   (MINOR(device)>>MMC_SHIFT)#define DEVICE_NAME         "mmc"         /* name for messaging */#define DEVICE_INTR         mmc_intrptr   /* pointer to the bottom half */#define DEVICE_NO_RANDOM                  /* no entropy to contribute */#define DEVICE_REQUEST      mmc_media_request#define DEVICE_OFF(d) /* do-nothing */#include <linux/blk.h>#include <linux/blkpg.h>static int rahead     = 8;static int maxsectors = 4;MODULE_PARM(maxsectors,"i");MODULE_PARM_DESC(maxsectors,"Maximum number of sectors for a single request");MODULE_PARM(rahead,"i");MODULE_PARM_DESC(rahead,"Default sector read ahead");#define MMC_NDISK	(MMC_MAX_SLOTS << MMC_SHIFT)/*    Don't bother messing with blksize_size....it gets changed by various filesystems.   You're better off dealing with arbitrary blksize's*/ static int              mmc_blk[MMC_NDISK];  /* Used for hardsect_size - should be 512 bytes */static int              mmc_max[MMC_NDISK];  /* Used for max_sectors[] - limit size of individual request */static int              mmc_sizes[MMC_NDISK];        /* Used in gendisk - gives whole size of partition */static struct hd_struct mmc_partitions[MMC_NDISK];   /* Used in gendisk - gives particular partition information */static char             mmc_gendisk_flags;static devfs_handle_t   mmc_devfs_handle;// There is one mmc_media_dev per inserted cardstruct mmc_media_dev {	int              usage;	struct mmc_slot *slot;	spinlock_t       lock;	int              changed;	long             nr_sects;   // In total number of sectors	int              read_block_len;      // Valid read block length	int              write_block_len;     // Valid write block length};static struct mmc_media_dev   g_media_dev[MMC_MAX_SLOTS];static struct mmc_io_request  g_io_request;static int                    g_busy;static struct gendisk mmc_gendisk = {        major:	        0,               /* major number dynamically assigned */	major_name:	DEVICE_NAME,	minor_shift:	MMC_SHIFT,	 /* shift to get device number */	max_p:	        1 << MMC_SHIFT,	 /* Number of partiions */	/* The remainder will be filled in dynamically */};/*************************************************************************//* TODO: O_EXCL, O_NDELAY, invalidate_buffers */static int mmc_media_open( struct inode *inode, struct file *filp ){	struct mmc_media_dev *dev;	int num = DEVICE_NR(inode->i_rdev);	MMC_DEBUG(1,": num=%d", num);	if ( num >= MMC_MAX_SLOTS) 		return -ENODEV;	dev = &g_media_dev[num];	if ( !dev->slot ) 		return -ENODEV;	spin_lock(&dev->lock);	if (!dev->usage)		check_disk_change(inode->i_rdev);	dev->usage++;	MOD_INC_USE_COUNT;	spin_unlock(&dev->lock);	return 0;}static int mmc_media_release( struct inode *inode, struct file *filep ){	struct mmc_media_dev *dev = &g_media_dev[DEVICE_NR(inode->i_rdev)];	MMC_DEBUG(1,": num=%d", DEVICE_NR(inode->i_rdev));	spin_lock(&dev->lock);	dev->usage--;	/* Is this worth doing? */	if (!dev->usage) {			fsync_dev(inode->i_rdev);		invalidate_buffers(inode->i_rdev);	}	MOD_DEC_USE_COUNT;	spin_unlock(&dev->lock);	return 0;}static int mmc_media_revalidate(kdev_t i_rdev){	int index, max_p, start, i;	struct mmc_media_dev *dev;	index = DEVICE_NR(i_rdev);	MMC_DEBUG(2,": index=%d", index);	max_p = mmc_gendisk.max_p;	start = index << MMC_SHIFT;	dev   = &g_media_dev[index];	// not right: devfs_register_partitions(&mmc_gendisk, start, 1);	for ( i = max_p - 1 ; i >= 0 ; i-- ) {		int item = start + i;		invalidate_device(MKDEV(mmc_major,item),1);		mmc_gendisk.part[item].start_sect = 0;		mmc_gendisk.part[item].nr_sects   = 0;		/* TODO: Fix the blocksize? */	}	register_disk(&mmc_gendisk, i_rdev, 1 << MMC_SHIFT, mmc_gendisk.fops, dev->nr_sects);	return 0;}static int mmc_media_ioctl (struct inode *inode, struct file *filp,			    unsigned int cmd, unsigned long arg){	int num = DEVICE_NR(inode->i_rdev);	int size;	struct hd_geometry geo;	MMC_DEBUG(1," ioctl 0x%x 0x%lx", cmd, arg);	switch(cmd) {	case BLKGETSIZE:		/* Return the device size, expressed in sectors */		/* Not really necessary, but this is faster than walking the gendisk list */		if (!access_ok(VERIFY_WRITE, arg, sizeof(long)))			return -EFAULT;		return put_user(mmc_partitions[MINOR(inode->i_rdev)].nr_sects, (long *)arg);	case BLKRRPART: /* re-read partition table */		if (!capable(CAP_SYS_ADMIN)) 			return -EACCES;		return mmc_media_revalidate(inode->i_rdev);	case HDIO_GETGEO:		if (!access_ok(VERIFY_WRITE, arg, sizeof(geo)))			return -EFAULT;		/* Grab the size from the 0 partition for this minor */		/* TODO: is this the right thing?  Or should we do it by partition??? */		size = mmc_sizes[num << MMC_SHIFT] / mmc_blk[num << MMC_SHIFT];		geo.cylinders = (size & ~0x3f) >> 6;		geo.heads     = 4;		geo.sectors   = 16;		geo.start     = mmc_partitions[MINOR(inode->i_rdev)].start_sect;		if (copy_to_user((void *) arg, &geo, sizeof(geo)))			return -EFAULT;		return 0;	default:		return blk_ioctl(inode->i_rdev, cmd, arg);	}	return -ENOTTY; /* should never get here */}static int mmc_media_check_change(kdev_t i_rdev) {	int                   index, retval;	struct mmc_media_dev *dev;	unsigned long         flags;	index = DEVICE_NR(i_rdev);	MMC_DEBUG(2," device=%d", index);	if (index >= MMC_MAX_SLOTS) 		return 0;	dev = &g_media_dev[index];	spin_lock_irqsave(&dev->lock, flags);	retval = (dev->changed ? 1 : 0);	dev->changed = 0;	spin_unlock_irqrestore(&dev->lock, flags);	return retval;}static struct mmc_media_dev * mmc_media_locate_device(const struct request *req){	int num = DEVICE_NR(req->rq_dev);	if ( num >= MMC_MAX_SLOTS) {		static int count = 0;		if (count++ < 5) /* print the message at most five times */			printk(KERN_WARNING "mmc: request for unknown device\n");		return NULL;	}	return &g_media_dev[num];}static int mmc_media_transfer( struct mmc_media_dev *dev, const struct request *req ){	int minor = MINOR(req->rq_dev);	unsigned long flags;	MMC_DEBUG(2,": minor=%d", minor);	if ( req->sector + req->current_nr_sectors > mmc_partitions[minor].nr_sects ) {		static int count = 0;		if (count++ < 5)			printk(KERN_WARNING "%s: request past end of partition\n", __FUNCTION__);		return 0;	}		spin_lock_irqsave(&dev->lock, flags);	g_io_request.id         = DEVICE_NR(req->rq_dev);	g_io_request.cmd        = req->cmd;	g_io_request.sector     = mmc_partitions[minor].start_sect + req->sector;	g_io_request.nr_sectors = req->current_nr_sectors;	g_io_request.block_len  = mmc_blk[minor];	g_io_request.buffer     = req->buffer;	MMC_DEBUG(2,": id=%d cmd=%d sector=%ld nr_sectors=%ld block_len=%ld buf=%p",	      g_io_request.id, g_io_request.cmd, g_io_request.sector, g_io_request.nr_sectors,	      g_io_request.block_len, g_io_request.buffer );	mmc_handle_io_request(&g_io_request);	spin_unlock_irqrestore(&dev->lock, flags);	return 1;}static void mmc_media_request( request_queue_t *q ){	struct mmc_media_dev *dev;	if ( g_busy )		return;	while(1) {		INIT_REQUEST;  /* returns when queue is empty */		dev = mmc_media_locate_device(CURRENT);		if ( !dev ) {			end_request(0);			continue;		}		MMC_DEBUG(2," (%p): cmd %i sec %li (nr. %li)", CURRENT,		      CURRENT->cmd, CURRENT->sector, CURRENT->current_nr_sectors);		if ( mmc_media_transfer(dev,CURRENT) ) {			g_busy = 1;			return;		}		end_request(0);  /* There was a problem with the request */	}}static void mmc_media_transfer_done( struct mmc_io_request *trans, int result ){	unsigned long flags;	MMC_DEBUG(3,": result=%d", result);	spin_lock_irqsave(&io_request_lock, flags);	end_request(result);	g_busy = 0;	if (!QUEUE_EMPTY)		mmc_media_request(NULL);  // Start the next transfer	spin_unlock_irqrestore(&io_request_lock, flags);}static struct block_device_operations mmc_bdops = {	open:               mmc_media_open,	release:            mmc_media_release,	ioctl:              mmc_media_ioctl,	check_media_change: mmc_media_check_change,	revalidate:         mmc_media_revalidate};/******************************************************************//* TODO:   We have a race condition if two slots need to be revalidated at the same   time.  Perhaps we should walk the list of devices and look for change   flags?*/static void mmc_media_load_task_handler( void *nr ){	int slot_id = (int) nr;	MMC_DEBUG(2," slot_id=%d", slot_id );	mmc_media_revalidate(MKDEV(mmc_major,(slot_id<<MMC_SHIFT)));}static struct tq_struct mmc_media_load_task = {	routine:  mmc_media_load_task_handler};static void mmc_media_load( struct mmc_slot *slot ){	unsigned long flags;	struct mmc_media_dev *dev  = &g_media_dev[slot->id];	int i;	long nr_sects;	int  write_block_len;	int  read_block_len;	spin_lock_irqsave(&dev->lock, flags);	nr_sects        = (1 + slot->csd.c_size) * (1 << (slot->csd.c_size_mult + 2));	write_block_len = 1 << slot->csd.write_bl_len;	read_block_len  = 1 << slot->csd.read_bl_len;	MOD_INC_USE_COUNT;	MMC_DEBUG(1, " slot=%p nr_sect=%ld write_block_length=%d read_block_len=%d", 	      slot, nr_sects, write_block_len, read_block_len );	dev->slot            = slot;	dev->nr_sects        = nr_sects;	dev->read_block_len  = read_block_len;	dev->write_block_len = write_block_len;	dev->changed         = 1;	mmc_gendisk.nr_real++;	/* Fix up the block size to match read_block_len */	/* TODO: can we really do this?  Right now we're affecting blksize_size and hardsect_size */	for ( i = 0 ; i < (1 << MMC_SHIFT) ; i++ )		mmc_blk[(slot->id << MMC_SHIFT) + i] = read_block_len;	mmc_media_load_task.data = (void *) slot->id;	schedule_task( &mmc_media_load_task );	spin_unlock_irqrestore(&dev->lock, flags);}/* TODO: This is a problem area.  We've lost our card, so we'd like   to flush all outstanding buffers and requests, remove the partitions from   the file system, and generally shut everything down.*/static void mmc_media_unload( struct mmc_slot *slot ){	unsigned long flags;	struct mmc_media_dev *dev = &g_media_dev[slot->id];	spin_lock_irqsave(&dev->lock, flags);//	for ( i = 0 ; i < MMC_SHIFT ; i++ )//		fsync_dev(MKDEV(mmc_major,slot->id,i));	MOD_DEC_USE_COUNT;	MMC_DEBUG(1," slot=%p id=%d", slot, slot->id);	dev->slot            = NULL;	dev->nr_sects        = 0;	dev->changed         = 1;	mmc_gendisk.nr_real--;	mmc_media_load_task.data = (void *) slot->id;	schedule_task( &mmc_media_load_task );	spin_unlock_irqrestore(&dev->lock, flags);}/*    Called once the device has a valid CSD structure   In the future this should determine what type of card we have   For the moment, everything is a memory card */static int mmc_media_probe( struct mmc_slot *slot ){	return 1;}static struct mmc_media_driver mmc_driver = {	name:            "flash",	load:            mmc_media_load,	unload:          mmc_media_unload,	probe:           mmc_media_probe,	io_request_done: mmc_media_transfer_done,};/******************************************************************/static int __init mmc_media_init( void ){	int i, result;	MMC_DEBUG(0,"");	mmc_devfs_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);	if (!mmc_devfs_handle) return -EBUSY;	result = devfs_register_blkdev(mmc_major, DEVICE_NAME, &mmc_bdops);	if (result < 0) {		printk(KERN_WARNING "Unable to get major %d for MMC media\n", mmc_major);		return result;	}	if ( !mmc_major ) mmc_major = result;	/* Set up global block arrays */	read_ahead[mmc_major]    = rahead;	for(i=0 ; i < MMC_NDISK; i++)		mmc_blk[i] = 512;	hardsect_size[mmc_major] = mmc_blk;	for(i=0; i < MMC_NDISK; i++)		mmc_max[i] = maxsectors;	max_sectors[mmc_major]   = mmc_max;	/* Start with zero-sized partitions : we'll fix this later */	memset(mmc_sizes, 0, sizeof(int) * MMC_NDISK);	blk_size[mmc_major] = mmc_sizes;	/* Fix up the gendisk structure */	mmc_gendisk.part    = mmc_partitions;	mmc_gendisk.sizes   = mmc_sizes;	mmc_gendisk.nr_real = 0;	mmc_gendisk.de_arr  = &mmc_devfs_handle;	mmc_gendisk.flags   = &mmc_gendisk_flags;	mmc_gendisk.fops    = &mmc_bdops;	/* Add ourselves to the global list */	mmc_gendisk.major = mmc_major;	add_gendisk(&mmc_gendisk);		blk_init_queue(BLK_DEFAULT_QUEUE(mmc_major), DEVICE_REQUEST);	return mmc_register_media_driver(&mmc_driver);}static void __exit mmc_media_cleanup( void ){	int i;	MMC_DEBUG(0,"");	flush_scheduled_tasks();	unregister_blkdev(mmc_major, DEVICE_NAME);	for ( i = 0 ; i < MMC_NDISK; i++ )		fsync_dev(MKDEV(mmc_major,i));	mmc_unregister_media_driver(&mmc_driver);	blk_cleanup_queue(BLK_DEFAULT_QUEUE(mmc_major));	blk_size[mmc_major]      = NULL;	hardsect_size[mmc_major] = NULL;	max_sectors[mmc_major]   = NULL;	del_gendisk(&mmc_gendisk);	devfs_unregister(mmc_devfs_handle);}struct mmc_media_module media_module = {	init:    mmc_media_init,	cleanup: mmc_media_cleanup};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -