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

📄 sdcard.c

📁 嵌入式linux系统下hi3510平台的osd开发源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* 
 * extdrv/interface/spi_sd/sdcard.c
 *
 * Copyright (c) 2006 Hisilicon Co., Ltd. 
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.
 *
 * 
 * History: 
 *      2006-4-11 create this file
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/fs.h>    
#include <linux/errno.h> 
#include <linux/types.h> 
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/wait.h>
#include <linux/workqueue.h>

#include "sdcard.h"
#include "../peripheral/misc_gpio/misc_gpio.h"
#include "hi_ssp.h"

static void sd_do_work(void *);
static DECLARE_WORK(sd_work, sd_do_work, NULL);
static struct workqueue_struct *sd_workqueue;

static struct request_queue *Queue;
static struct sd_device Device;

static int hardsect_size = 512;
static int nsectors = 16*(2*1024);
static int minors=16;

static int thread_quit = 0;

/*
 * sd_wakup routine.
 *
 */
void sd_wakup(void)
{
	wake_up(&Device.dma_wq);
}
/*
 * sd_add_wait routine.
 *
 * @param wait: pointer wait_queue_t
 */
void sd_add_wait(wait_queue_t *wait)
{
	add_wait_queue(&Device.dma_wq, wait);
	set_current_state(TASK_INTERRUPTIBLE);
}
/*
 * sd_rm_wait routine.
 *
 * @param wait: pointer wait_queue_t
 */
void sd_rm_wait(wait_queue_t *wait)
{
	remove_wait_queue(&Device.dma_wq, wait);
}


/*
 * write operation routine.
 * @param dev: pointer sd_device
 *   
 * @return value: 0-- write operation failture      num-- num block of write operation successed
 *
 */
static int sd_do_tran(struct sd_device *dev)
{
	unsigned int offset,num,ret=0,retry=0;
	char *buf;

	offset=dev->waddr;
	num=dev->wblock_num;
	#ifdef CONFIG_SSP_DMA
		buf=dev->writebuf;
	#else
		buf=&dev->writebuf[0];
	#endif
	if(num && num <=MAXNUM)
	{
		retry=0;
		do
		{
			if(Device.sd_online==0)
			{
				ret=100;break;
			}
			#ifdef CONFIG_SSP_DMA
				ret=sdcard_write(offset,0,num);
			#else
				ret=sdcard_write(offset,buf,num);
			#endif
			msleep(SSPDELAYW);
			if(ret!=0)
			{
				/*
				DBG(" sdcard_write error offset=%u num=%u ret=%u \n",offset/KERNEL_SECTOR_SIZE,num,ret);
				*/
				if(retry++ >10) break;
				sdcard_init();
				#ifdef CONFIG_SSP_DMA
					hi_ssp_set_serialclock(0,10);
				#else
					hi_ssp_set_serialclock(0,6);
				#endif
			}
		}while(ret!=0);
		
		if(ret!=0)
		{

			dev->wblock_num=0;
			dev->woffset=0;

			if(dev->sd_error ==0)
				DBG(" sdcard_write error offset=%u num=%u ret=%u \n",offset/KERNEL_SECTOR_SIZE,num,ret);
			
			dev->sd_error=1;
			return(num);
		}
		else
		{
			/*
			DBG(" sdcard_write ok offset=%u num=%u \n",offset/KERNEL_SECTOR_SIZE,num);
			*/
			dev->wblock_num=0;
			dev->woffset=0;
			dev->sd_error=0;
		}
	}
	
	return(num);
}

/*
 * read operation routine.
 * @param dev: pointer sd_device
 *   
 * @return value: 0-- read operation failture      num-- num block of read operation successed
 *
 */
static int sd_do_tran_read(struct sd_device *dev)
{
	unsigned int offset,num,i,ret=0,rbaddr=0,res=0,retry=0;
	char *rb;
	
	for(i=0;i<dev->readlist_offset;i++)
	{
		offset=dev->readlist[i][0];
		num=dev->readlist[i][1];
		if(i==0)
			rbaddr=0;
		else
			rbaddr += KERNEL_SECTOR_SIZE * num;
		retry=0;
		do
		{
			if(Device.sd_online==0)
			{
				ret=100;break;
			}
			#ifdef CONFIG_SSP_DMA
				rb = dev->readbuf+rbaddr;
				ret=sdcard_read(offset,rbaddr,num);
			#else
				rb = & dev->readbuf[rbaddr];
				ret=sdcard_read(offset,rb,num);
			#endif
			msleep(SSPDELAYR);
			if(ret!=0)
			{
				/*
				DBG(" sdcard_read  error offset=%u num=%u ret=%u \n",offset/KERNEL_SECTOR_SIZE,num,ret);
				*/
				if(retry++ >10) break;
				sdcard_init();
				#ifdef CONFIG_SSP_DMA
					hi_ssp_set_serialclock(0,10);
				#else
					hi_ssp_set_serialclock(0,6);
				#endif
			}
		}while(ret!=0);

		if(ret!=0)
		{
			dev->raddr = 0;
			dev->readlist_offset=0;

			if(dev->sd_error ==0)
				DBG(" sdcard_read  error offset=%u num=%u ret=%u \n",offset/KERNEL_SECTOR_SIZE,num,ret);
			
			res += num;
			dev->sd_error=1;
		}
		else
		{
			/*
			DBG(" sdcard_read ok offset=%u num=%u \n",offset/KERNEL_SECTOR_SIZE,num);
			*/
			res += num;
			dev->sd_error=0;
			
		}
	}		
				
	dev->raddr = 0;
	dev->readlist_offset=0;
	
	return(res);
}
/*
 * save data writed to buf routine.
 * @param dev   : pointer sd_device
 * @param sector: data start sector
 * @param nsect : sector num
 * @param buffer: data buffer
 * @param write : write or read flag
 *
 * @return value: 0-- error   1--success
 *
 */
static int sd_transfer(struct sd_device *dev, unsigned int sector,unsigned int nsect, char *buffer, int write)
{
	unsigned int offset = sector*KERNEL_SECTOR_SIZE;
	unsigned int nbytes = nsect*KERNEL_SECTOR_SIZE;
	unsigned int i;
	unsigned char *buf=buffer;
    
	if ((offset + nbytes) > dev->size) 
	{
		printk (KERN_NOTICE "sd: Beyond-end write (%u %u)\n", offset, nbytes);
		return(0);
	}
	
	if (write)
	{
		if(dev->wblock_num == 0)
			dev->waddr = offset;
		for(i=0;i<nbytes;i++)
		#ifdef CONFIG_SSP_DMA	
			*(dev->writebuf+i + dev->woffset) = *buf++;
		#else
			dev->writebuf[i + dev->woffset] = *buf++;
		#endif
		dev->wblock_num += nsect;
		dev->woffset    += nbytes;
		buf = buffer;
	}
	else
	{
		if(dev->readlist_offset ==0)
		{
			dev->readlist[0][0]=offset;
			dev->readlist[0][1]=nsect;
			dev->readlist_offset++;
		}
		else
		{
			if(offset == (dev->readlist[dev->readlist_offset-1][0] + KERNEL_SECTOR_SIZE*dev->readlist[dev->readlist_offset-1][1]) )
			{
				dev->readlist[dev->readlist_offset-1][1]+=nsect;
			}
			else
			{
				dev->readlist[dev->readlist_offset][0]=offset;
				dev->readlist[dev->readlist_offset][1]=nsect;
				dev->readlist_offset++;
			}
		}
	}
	
	return(1);
}
/*
 * copy read data to request queue routine.
 * @param dev   : pointer sd_device
 * @param sector: data start sector
 * @param nsect : sector num
 * @param buffer: data buffer
 * @param write : write or read flag
 *
 * @return value: 0-- error   1--success
 *
 */
static int sd_transfer_read(struct sd_device *dev, unsigned int sector,unsigned int nsect, char *buffer, int write)
{
	unsigned int offset = sector*KERNEL_SECTOR_SIZE;
	unsigned int nbytes = nsect*KERNEL_SECTOR_SIZE;
	unsigned int i;
	unsigned char *buf=buffer;
    
	if ((offset + nbytes) > dev->size) 
	{
		printk (KERN_NOTICE "sd: Beyond-end write (%u %u)\n", offset, nbytes);
		return(0);
	}
	
	for(i=0;i<nbytes;i++)
	{
		#ifdef CONFIG_SSP_DMA
			*buf++ = *(dev->readbuf + dev->raddr++);
		#else
			*buf++ = dev->readbuf[dev->raddr++];
		#endif
	}
	return(1);
}
/*
 * sd xfer bio request routine.
 * @param dev : pointer sd_device
 * @param bio : pointer bio
 *
 * @return value: 0-- success
 *
 */
static int sd_xfer_bio(struct sd_device *dev, struct bio *bio)
{
	unsigned int i;
	struct bio_vec *bvec;
	sector_t sector = bio->bi_sector;

	bio_for_each_segment(bvec, bio, i) 
	{
		char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
		sd_transfer(dev, sector, bio_cur_sectors(bio), buffer, bio_data_dir(bio) == WRITE);
		sector += bio_cur_sectors(bio);
		__bio_kunmap_atomic(bio, KM_USER0);
	}
	
	return 0;
}
/*
 * sd xfer bio2 request routine.
 * @param dev : pointer sd_device
 * @param bio : pointer bio
 *
 * @return value: 0-- success
 *
 */
static int sd_xfer_bio2(struct sd_device *dev, struct bio *bio)
{
	unsigned int i;
	struct bio_vec *bvec;
	sector_t sector = bio->bi_sector;

	bio_for_each_segment(bvec, bio, i) 
	{
		char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
		sd_transfer_read(dev, sector, bio_cur_sectors(bio), buffer, bio_data_dir(bio) == WRITE);
		sector += bio_cur_sectors(bio);
		__bio_kunmap_atomic(bio, KM_USER0);
	}
	
	return 0;
}
/*
 * sd xfer request routine.
 * @param dev : pointer sd_device
 * @param req : pointer request
 *
 * @return value: success multiple block write/read num (sector num)
 *
 */
static int sd_xfer_request(struct sd_device *dev, struct request *req)
{
	struct bio *bio;
	unsigned int nsect = 0,ret,wflag=0;
	struct request *req2=req;

	rq_for_each_bio(bio, req) 
	{
		sd_xfer_bio(dev, bio);
		nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
		if( bio_data_dir(bio) == WRITE) 
			wflag=1;
		else 
			wflag=0;
	}

	if(wflag)
	{
		ret = sd_do_tran(&Device);
		return ret;
	}
	else
	{
		ret = sd_do_tran_read(&Device);
		if(ret)
		{
			nsect=0;
			rq_for_each_bio(bio, req2) 
			{
				sd_xfer_bio2(dev, bio);
				nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
			}
		}
		return ret;
	}
	
}
/*
 * blk device request routine. 
 * @param q: pointer request_queue_t
 *
 */
static void sd_request(request_queue_t *q)
{
	wake_up(&Device.wq);
}

/*
 * get geo.heads routine.
 * @param size: sdcard size;
 *
 * @return value:heads 
 *
 */
static unsigned int get_heads(unsigned long size)
{
	unsigned int temp=0;
	
	if( size<(25*1000*1000) )
		temp = 2;
	else if( (25*1000*1000)<=size && size<(100*1000*1000) )
		temp = 4;
	else if( (100*1000*1000)<=size && size<(200*1000*1000) )
		temp = 8;
	else if( (200*1000*1000)<=size && size<(1000*1000*1000) )
		temp = 16;

⌨️ 快捷键说明

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