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

📄 mmc_core.c

📁 linux下mmc_sd卡的驱动.rar
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  linux/drivers/mmc/mmc_core.c  *  	MultiMediaCard subsystem core implementation * *  Author:	Vladimir Shebordaev	 *  Copyright:	MontaVista Software Inc. * *	$Id: mmc_core.c,v 0.3.1.14 2002/09/27 17:36:09 ted Exp ted $ * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License version 2 as *  published by the Free Software Foundation. */#include <linux/version.h>#include <linux/config.h>#include <linux/init.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/slab.h>#include <asm/uaccess.h>#include <asm/semaphore.h>#ifdef CONFIG_PM#include <linux/pm.h>#endif//#define CONFIG_MMC_DEBUG//#define CONFIG_MMC_DEBUG_VERBOSE 0#include <mmc/types.h>#include <mmc/mmc.h>#include <mmc/ioctl.h>#include "types.h"#define __MMC_CORE_IMPLEMENTATION__#include "mmc.h"/* MMC controllers registered in the system */ static mmc_controller_t mmc_controller[MMC_CONTROLLERS_MAX];static int mmc_ncontrollers = 0;static rwsemaphore_t mmc_controller_sem; /* controller table lock */#ifdef CONFIG_PMstatic struct pm_dev *mmc_pm_dev = NULL;#endif/* users' notification list */static mmc_notifier_t mmc_notifier = NULL;static rwsemaphore_t mmc_notifier_sem; /* notifiers' list lock */ #ifdef CONFIG_PROC_FSstatic proc_dir_entry_t mmc_proc_dir = NULL;#endif/************************************************ * service function prototypes and declarations * ************************************************/static inline int mmc_acquire_io( mmc_controller_t ctrlr, mmc_card_t card ){	int ret = -EIO;	__ENTER0();		if ( !card || !ctrlr ) {		ret = -EINVAL;		goto error;	}#ifdef CONFIG_HOTPLUG/* TODO: account for controller removal */#endif	down( &ctrlr->io_sem );#if 0		down_read( &ctrlr->update_sem ); /* FIXME */	if ( card->state != MMC_CARD_STATE_UNPLUGGED )		ret = 0;	up_read( &ctrlr->update_sem );		if ( ret )		up( &ctrlr->io_sem );#else	ret = 0;#endiferror:	__LEAVE( "ret=%d", ret );	return ret;}static inline void mmc_release_io( mmc_controller_t ctrlr, mmc_card_t card ){	__ENTER0();#ifdef CONFIG_HOTPLUG/* TODO: account for controller removal */#endif	if ( !card && !ctrlr ) { /* FIXME */		MMC_DEBUG( MMC_DEBUG_LEVEL2, "bad card reference\n" );		goto error;	}	up( &ctrlr->io_sem );	error:	__LEAVE0();}/* TODO: there should be a separate context to be awaken  * by the card intertion interrupt; called under ctrlr->update_sem * held down by now */static int __mmc_update_card_stack( mmc_controller_t ctrlr ){	int ret = -1;	mmc_card_t card, prev;		__ENTER0();		if ( !ctrlr || !ctrlr->tmpl )		goto error;		/* check unplugged cards first... */	if ( (ret = ctrlr->tmpl->check_card_stack( ctrlr )) )		goto error;		/* unregister unplugged cards and free 'em immediately */	if ( ctrlr->stack.ncards > 0 ) {		prev = ctrlr->stack.first;		/* process the stack tail first */		if ( prev->next ) {			card = prev->next; 			while ( card ) {				if ( card->state == MMC_CARD_STATE_UNPLUGGED ) {					if ( ctrlr->stack.selected == card )						ctrlr->stack.selected = NULL;#ifdef CONFIG_PROC_FS					if ( card->proc ) {						remove_proc_entry( card->proc_name, ctrlr->proc );						card->proc = NULL;					}#endif					ctrlr->slot_next = card->slot; /* FIXME */					prev->next = card->next;					if ( ctrlr->stack.last == card )						ctrlr->stack.last = prev;					/* FIXME: controller use count */					mmc_notify_remove( card );					--ctrlr->stack.ncards;					if ( (ctrlr->usage > 0) && ctrlr->tmpl->owner ) {						--ctrlr->usage;						MMC_DEBUG( MMC_DEBUG_LEVEL2,							"'%s' use count "							"decreased (%d)\n",							ctrlr->tmpl->name,							ctrlr->usage );						__MOD_DEC_USE_COUNT( 							ctrlr->tmpl->owner );					}					__mmc_card_free( card );									card = prev->next;				}			}		}		/* then the head */				card = ctrlr->stack.first;		if ( card && (card->state == MMC_CARD_STATE_UNPLUGGED) ) {			if ( ctrlr->stack.selected == card )				ctrlr->stack.selected = NULL;#ifdef CONFIG_PROC_FS			if ( card->proc ) {				remove_proc_entry( card->proc_name, ctrlr->proc );				card->proc = NULL;			}#endif			ctrlr->slot_next = card->slot; /* FIXME */			mmc_notify_remove( card ); /* FIXME: should unregister here */			ctrlr->stack.first = card->next;			if ( ctrlr->stack.last == card )				ctrlr->stack.last = NULL;			/* FIXME: controller use count */			--ctrlr->stack.ncards;			if ( (ctrlr->usage > 0) && ctrlr->tmpl->owner ) {				--ctrlr->usage;				MMC_DEBUG( MMC_DEBUG_LEVEL2, "'%s' use count "					"decreased (%d)\n", ctrlr->tmpl->name,					ctrlr->usage );				__MOD_DEC_USE_COUNT( ctrlr->tmpl->owner );			}			__mmc_card_free( card );		}	}	MMC_DEBUG( MMC_DEBUG_LEVEL2, "after stack check: ncards=%d"			" first=0x%x last=0x%x\n", ctrlr->stack.ncards,			ctrlr->stack.first, ctrlr->stack.last );	/* ...then add newly inserted ones */	if ( (ret = ctrlr->tmpl->update_acq( ctrlr )) )		goto error;	/* ret = 0; */error:				__LEAVE( "ret=%d", ret );	return ret;}/* * 	1) check error code returned by controller; it's up to * 	   controller to detect error conditions reported by the card * 	   and to abort data transfer requests properly (e.g. send * 	   CMD12(STOP_TRANSMISSION) to abort ADDRESS_ERROR multiple * 	   block transfers) * 	2) arrange for card stack update when necessary *	   (all pending i/o requests must be held pending, *	   update procedure must start immediately after  *	   error has been detected) */static inline int __mmc_check_error( mmc_card_t card, int err ){	int ret = -EIO;	mmc_controller_t ctrlr;		__ENTER0();	if ( !card || !card->ctrlr )		goto error;		ctrlr = card->ctrlr;		if ( err < 0 ) {		switch ( err ) {		/* bus error occurred */		case MMC_ERROR_CRC_WRITE_ERROR:		case MMC_ERROR_CRC_READ_ERROR:		case MMC_ERROR_RES_CRC_ERROR:		case MMC_ERROR_READ_TIME_OUT:		case MMC_ERROR_TIME_OUT_RESPONSE:			down_write( &ctrlr->update_sem ); /* FIXME */			if ( !__mmc_update_card_stack( ctrlr ) )				ret = -ENXIO;			up_write( &ctrlr->update_sem );			break;		}	} else		ret = err;error:	__LEAVE( "ret=%d", ret );	return ret;}static inline void __mmc_free_controller( mmc_controller_t ctrlr ){	if ( ctrlr ) {		if ( ctrlr->stack.ncards > 0 )			__mmc_card_stack_free( &ctrlr->stack );		kfree( ctrlr );	}}#ifdef CONFIG_PROC_FSstatic int mmc_proc_read_card_info( char *page, char **start, off_t off, int count, int *eof, void *data ){	int ret = -EINVAL;	mmc_card_t card = (mmc_card_t)data;	char *cp = page;		if ( !card )		goto error;		down_read( &card->ctrlr->update_sem );/* TODO: proc report * Type: RO, RW or IO (by CCC) * MID: 0x%02x card->info.cid.mid * OID: 0x%04x card->info.cid.oid * PNM: %s card->info.pnm * PRV: %s card->info.prv * PSN: 0x%08x card->info.cid.psn * MDT: %s card->info.mdt * Capacity: card->info.capacity (Bytes) */#if 1	cp += sprintf( cp, "Capacity: %dKb.\n\n", (card->info.capacity>>10) );#else /* TODO */	cp += sprintf( cp, "Type    : %s\n", card->info.type );	cp += sprintf( cp, "MID     : 0x%02x\n", card->info.cid.mid );	cp += sprintf( cp, "OID     : 0x%04x\n", card->info.cid.oid );	cp += sprintf( cp, "PNM     : %s\n", card->info.pnm );	cp += sprintf( cp, "PRV     : %s\n", card->info.prv );	cp += sprintf( cp, "PSN     : 0x%08x\n", card->info.cid.psn );	cp += sprintf( cp, "MDT     : %s\n", card->info.mdt );	cp += sprintf( cp, "Capacity: %dKB\n", 			(card->info.capacity>>10) );#endif	up_read( &card->ctrlr->update_sem );		ret = cp - page;error:	return ret;}#endif/************************************* * MMC core interface implementation * *************************************/int mmc_notify_add( mmc_card_t card ){	int ret = 0;	mmc_notifier_t notifier;		__ENTER0();	if ( card ) {		for ( notifier = mmc_notifier; notifier;		      notifier = notifier->next )			if ( notifier->add )				if ( (ret = notifier->add( card )) )					break;	}	__LEAVE( "ret=%d", ret );	return ret;}EXPORT_SYMBOL( mmc_notify_add );int mmc_notify_remove( mmc_card_t card ){	int ret = 0;	mmc_notifier_t notifier;		__ENTER0();	if ( card ) {		for ( notifier = mmc_notifier; notifier; 		      notifier = notifier->next )			if ( notifier->remove )				if ( (ret = notifier->remove( card )) )					break;	}	__LEAVE( "ret=%d", ret );	return ret;}EXPORT_SYMBOL( mmc_notify_remove );int mmc_update_card_stack( int host ){	int ret = -EINVAL;	mmc_controller_t ctrlr;	__ENTER0();		if ( (host < 0) || (host >= MMC_CONTROLLERS_MAX) )		goto error;		down_read( &mmc_controller_sem );	if ( (ctrlr = mmc_controller[host]) ) {		down_write( &ctrlr->update_sem );		(void)__mmc_update_card_stack( ctrlr );		up_write( &ctrlr->update_sem );	}	up_read( &mmc_controller_sem );	ret = 0;error:	__LEAVE( "ret=%d", ret );	return ret;}EXPORT_SYMBOL( mmc_update_card_stack );ssize_t mmc_read( mmc_card_t card, mmc_transfer_mode_t mode, char *buf, size_t size, loff_t *paddr ){	ssize_t ret = -EIO;	mmc_controller_t ctrlr;		mmc_data_transfer_req_rec_t transfer; 			if ( !paddr ) {		ret = -EINVAL;		goto error;	}		if ( !card ) {		ret = -ENODEV;		goto error;	}	__ENTER( "card=%p usage=%d mode=%d buf=%p size=%d addr=%x",		card, card->usage, mode, buf, size, *paddr );		ctrlr = card->ctrlr;	if ( (ret = mmc_acquire_io( ctrlr, card )) )		goto error;		memset( &transfer, 0, sizeof( mmc_data_transfer_req_rec_t ) );	transfer.cmd = MMC_READ;	transfer.mode = mode;	transfer.type = MMC_USER; /* FIXME: buffer cache */	transfer.buf = buf;	transfer.addr = *paddr;	transfer.cnt = size;/* max block size defined by CSD[read_bl_len] */	transfer.blksz = card->info.read_bl_len;	transfer.nob = size / transfer.blksz;	if ( (size - (transfer.nob * transfer.blksz)) > 0 )		transfer.nob++;/* TODO: controller may restrict maximum block size; set block size * and number of blocks that their accumulated length fit to * CSD[READ_BL_LEN] not to bother with block misalignment in multiple * block transfers */	ctrlr = card->ctrlr;	if ( transfer.blksz > ctrlr->tmpl->block_size_max ) { 		ret = -EINVAL; /* FIXME */		goto error;	}		    	if ( ctrlr->stack.selected != card ) {		if ( (ret = ctrlr->tmpl->setup_card( ctrlr, card )) )			goto err_mmc;				ctrlr->stack.selected = card;	}		switch( mode ) {		case MMC_TRANSFER_MODE_STREAM:			if ( !ctrlr->tmpl->stream_read ) {				ret = -ENXIO;				goto err_down;			}/* TODO: The max clock frequency for stream read operation is given by   the following formula:   	max speed = min ( TRAN_SPEED, 8*2^(READ_BL_LEN) - NSAC/TAAC )   If the card is not able to sustain data transfer it will set the   UNDERRUN error bit in the status register, abort the transmission   and wait in the Data state for a stop command  */			ret = ctrlr->tmpl->stream_read( ctrlr, &transfer );			break;		case MMC_TRANSFER_MODE_BLOCK_SINGLE:			if ( !ctrlr->tmpl->read_block ) {				ret = -ENXIO;				goto err_down;			}/* TODO: buffer size and data alignment (v3.4, p.29): 	if CSD[READ_BL_PARTIAL] is set, smaller blocks whose starting	and ending address are entirely contained within one physical	block (as defined by CSD[READ_BL_LEN]) may also be transmitted */			transfer.type = MMC_KERNEL; /* FIXME */			ret = ctrlr->tmpl->read_block( ctrlr, &transfer );			break;					case MMC_TRANSFER_MODE_BLOCK_MULTIPLE:			if ( !ctrlr->tmpl->read_mblock ) {				ret = -ENXIO;				goto err_down;			}						if ( transfer.nob > ctrlr->tmpl->nob_max ) {				ret = -EINVAL;				goto error;			}/* TODO: buffer size and data alignment (v3.4, p.29): 	if the host uses patrial blocks whose accumulated length is	not block aligned and block misalignment is not allowed, the	card should detect a block misalignment error condition at the	beginning of the first misaligned block */			transfer.type = MMC_KERNEL; /* FIXME */			ret = ctrlr->tmpl->read_mblock( card->ctrlr, &transfer );			break;				default:			MMC_DEBUG( MMC_DEBUG_LEVEL0, "request for unknown transfer type\n" );			ret = -EINVAL;	}err_mmc:	ret = __mmc_check_error( card, ret );	if ( ret >= 0 ) {		ret = size - transfer.cnt;		*paddr += ret;	}err_down:	mmc_release_io( ctrlr, card );error:	__LEAVE("ret=%d", ret);	return ret;}EXPORT_SYMBOL( mmc_read );ssize_t mmc_write( mmc_card_t card, mmc_transfer_mode_t mode, const char *buf, size_t size, loff_t *paddr ){	ssize_t ret = -ESPIPE;	mmc_controller_t ctrlr;	mmc_data_transfer_req_rec_t transfer; 			if ( !paddr ) {		ret = -EINVAL;		goto error;	}		if ( !card ) {		ret = -ENODEV;		goto error;	}	__ENTER( "card=%p usage=%d mode=%d buf=%p size=%d addr=%llx",		card, card->usage, mode, buf, size, *paddr );	ctrlr = card->ctrlr;		if ( (ret = mmc_acquire_io( ctrlr, card )) )		goto error;		memset( &transfer, 0, sizeof( mmc_data_transfer_req_rec_t ) );	transfer.cmd = MMC_WRITE;	transfer.mode = mode;	transfer.type = MMC_USER; /* FIXME: buffer cache */	transfer.buf = (char *)buf;	transfer.addr = *paddr;	transfer.cnt = size;/* max block size defined by CSD[write_bl_len] */	transfer.blksz = card->info.write_bl_len;	transfer.nob = size / transfer.blksz;	if ( (size - (transfer.nob * transfer.blksz)) > 0 )		transfer.nob++;/* TODO: controller may restrict maximum block size; set block size * and number of blocks that their accumulated length fit to * CSD[WRITE_BL_LEN] not to bother with block misalignment in multiple * block transfers */	ctrlr = card->ctrlr;	if ( transfer.blksz > ctrlr->tmpl->block_size_max ) { 		ret = -EINVAL; /* FIXME */		goto error;	}			if ( ctrlr->stack.selected != card ) {		if ( (ret = ctrlr->tmpl->setup_card( ctrlr, card )) )			goto err_mmc;				ctrlr->stack.selected = card;	}		transfer.cmd = MMC_WRITE;	transfer.mode = mode;	transfer.type = MMC_USER;	switch( mode ) {		case MMC_TRANSFER_MODE_STREAM:			if ( !ctrlr->tmpl->stream_write ) {				ret = -ENXIO;				goto err_down;			}			ret = ctrlr->tmpl->stream_write( ctrlr, &transfer );			break;		case MMC_TRANSFER_MODE_BLOCK_SINGLE:			if ( !ctrlr->tmpl->write_block ) {				ret = -ENXIO;				goto err_down;			}			transfer.type = MMC_KERNEL; /* FIXME */			ret = ctrlr->tmpl->write_block( ctrlr, &transfer );			break;					case MMC_TRANSFER_MODE_BLOCK_MULTIPLE:			if ( !ctrlr->tmpl->write_mblock ) {				ret = -ENXIO;				goto err_down;			}			transfer.type = MMC_KERNEL; /* FIXME */			ret = ctrlr->tmpl->write_mblock( card->ctrlr, &transfer );			break;				default:			MMC_DEBUG( MMC_DEBUG_LEVEL0, "request for unknown transfer type\n" );

⌨️ 快捷键说明

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