OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序

引言

前一段时间,我们分别在eCos和linux上实现了ORPSoC中的sd卡控制器的基本操作。在eCos上实现了完整的,支持文件系统的driver,在linux上,也实现了基本的IO操作,但是还没有实现基于文件系统的操作,本小节就解决这个问题。

关于之前我们所做的工作,请参考:

OpenRisc-30-SD card controller模块分析与验证:http://blog.csdn.net/rill_zhen/article/details/9111213

OpenRisc-35-基于orpsoc,eCos的sd card controller的测试实验:http://blog.csdn.net/rill_zhen/article/details/9365477

OpenRisc-38-基于orpsoc,linux的sd card controller的测试实验:http://blog.csdn.net/rill_zhen/article/details/9419651


1,基本思想

前面的基于linux的工作实现了基本的SD卡的IO,通过之前的努力,我们已经可以对SD卡进行读写操作,但是这个读写操作只能基于block,不能以文件的形式操作SD卡。

所以为了实现以文件的操作,需要文件系统的支持。

为了提高传输速度,驱动中仔细设计了中断处理程序的上半部分和下半部分。

一般情况下,上半部分和下半部分通过信号量来进行同步,我们这次使用了两外一种机制,就是completion。这种机制主要通过complete和wait_for_completion两个函数实现。

Completion是一种轻量级的机制,他允许一个线程告诉另一个线程某个工作已经完成。

此外,还需要注意的是,本驱动并没有使用linux专门为SD卡提供的framework,而是采用标准的块设备的framework。这需要重新配置linux的编译选项,如何配置内核,请参考附录部分。


2,工作机制

为了便于读者更容易理解,这里简单介绍一下本驱动的工作机制。

1,本驱动是以model的形式编译的,编译会生成ko文件,通过insmod命令加载到内核,并会生成/dev/sdcmsc的设备节点。

2,在模块初始化部分,会对sd卡进行初始化,并注册块设备驱动,注册中断处理程序。关于中断的使用,如中断号的确定,我们之前介绍过,请参考 http://blog.csdn.net/rill_zhen/article/details/8894856

3,通过mount -t vfat /dev/sdcmmc /sdc 来挂载SD卡,其中/sdc是通过mkdir命令创建的挂载点。

4,一旦挂载成功,我们就可以使用cd ls cat cp mv rm 等命令来对SD卡进行操作了。

5,在linux进行IO操作时,首先会启动DMA传输过程,然后会唤醒一个名字为sdcmsc_queue_thread的内核线程,这个内核线程会等待DMA传输完毕之后的中断信号。一旦DMA传输完毕,中断处理程序会告诉这个内核线程。这样做的好处是,在进行DMA传输时,驱动程序会通过schedule()让出CPU,减少了CPU的占用时间。


3,编码

了解了本驱动的基本思想和大体的工作机制之后,就可以开始编码了,这里直接给出代码清单。

这里需要说明的是,其实,一切细节都可以从代码中获得,所以还是建议读者在使用前仔细阅读本代码。

code list:


1>sdcmsc.c


/*
* rill create at 2013-09-18
* [email protected]
*/


#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/sched.h>
#include <linux/kernel.h>	/* printk() */
#include <linux/slab.h>		/* kmalloc() */
#include <linux/fs.h>		/* everything... */
#include <linux/errno.h>	/* error codes */
#include <linux/timer.h>
#include <linux/types.h>	/* size_t */
#include <linux/fcntl.h>	/* O_ACCMODE */
#include <linux/hdreg.h>	/* HDIO_GETGEO */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>	/* invalidate_bdev */
#include <linux/bio.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/kthread.h>
#include <linux/interrupt.h>
#include "sdcmsc.h"
MODULE_LICENSE("Dual BSD/GPL");

static struct sdcmsc_card* sdcmsc_dev_p = NULL;
static DEFINE_MUTEX(block_mutex);

static DEFINE_MUTEX(open_lock);
DECLARE_COMPLETION(comp_dma);

static struct sdcmsc_card* sdcmsc_blk_get(struct gendisk *disk)
{
	struct sdcmsc_card* sdev;

	mutex_lock(&open_lock);
	sdev = disk->private_data;
	if(sdev && sdev->usage ==0)
		sdev = NULL;
	if(sdev)
		sdev->usage++;
	mutex_unlock(&open_lock);

	return sdev;
}
static void sdcmsc_blk_put(struct sdcmsc_card *dev)
{
	mutex_lock(&open_lock);
	dev->usage--;
	if(dev->usage == 0)
	{
		blk_cleanup_queue(dev->mq.queue);
		put_disk(dev->gd);
		kfree(dev);
	}
	mutex_unlock(&open_lock);
}
static irqreturn_t sdcmsc_irq_dat(int irq, void *dev_id)
{
	struct sdcmsc_card * dev= dev_id;
	void __iomem * sdcmsc_base = dev->sdcmsc_base;
	unsigned reg;
//	printk(KERN_ALERT "I'm in interrupt!");
	reg = ioread32(sdcmsc_base + SDCMSC_DAT_INT_STATUS);
	reg = cpu_to_le32(reg);
	iowrite32(0, sdcmsc_base + SDCMSC_DAT_INT_STATUS);
	//Check for errors
	if(reg !=  SDCMSC_DAT_INT_STATUS_TRS)
	{
		if(reg & (1 << 5)) printk(KERN_ALERT "Transmission error\n");
		if(reg & (1 << 4)) printk(KERN_ALERT "Command error\n");
		if(reg & (1 << 2)) printk(KERN_ALERT "FIFO error\n");
		if(reg & (1 << 1)) printk(KERN_ALERT "Retry error\n");
	}
//	printk(KERN_ALERT "before complete!");
	complete(&comp_dma);
	return IRQ_HANDLED;

}

static int sdcmsc_card_queue(struct sdcmsc_card* dev, int direction_transmit,
	unsigned long block_addr, unsigned long  buffer_addr)
{
	unsigned long reg;
	//int i;
	void __iomem *sdcmsc_base = dev->sdcmsc_base;
//	printk(KERN_ALERT "sdcmsc_base:0x%x\n", sdcmsc_base);
//	printk(KERN_ALERT "block_addr:%d\n", block_addr-8192);
	if(direction_transmit) {
//		printk(KERN_ALERT "I'm in writing");
		reg = le32_to_cpu(buffer_addr);
		iowrite32(reg, sdcmsc_base + SDCMSC_BD_TX);
		block_addr = le32_to_cpu(block_addr);
		iowrite32(block_addr, sdcmsc_base + SDCMSC_BD_TX);
	}
	else
	{
//		printk(KERN_ALERT "I'm in Reading\n");
//		printk(KERN_ALERT "buffer_addr:0x%x\n", buffer_addr);
		reg= le32_to_cpu(buffer_addr);
		iowrite32(reg, sdcmsc_base + SDCMSC_BD_RX);
//		printk(KERN_ALERT "block_addr:%d\n", block_addr);
		block_addr = le32_to_cpu(block_addr);
		iowrite32(block_addr, sdcmsc_base + SDCMSC_BD_RX);
	}
//	printk(KERN_ALERT "before wait_for_completion");
	wait_for_completion(&comp_dma);
//	printk(KERN_ALERT "after wait_for_completion");
	return 1;
}
static int sdcmsc_disk_read(struct sdcmsc_card* dev, unsigned long  buf, 
	unsigned long blocks, unsigned long first_block){
	void __iomem *sdcmsc_base =dev->sdcmsc_base;

	int i;
	int result;
	unsigned int reg;
//	printk(KERN_ALERT "buf addr:0x%x\n", buf);
//	printk(KERN_ALERT "block num:%d\n", blocks);
//	printk(KERN_ALERT "first_block:%d\n", first_block);
	for(i=0; i < blocks; i++) {
		//Check for free receive buffers
		reg= ioread32(sdcmsc_base + SDCMSC_BD_BUFFER_STATUS);
		reg = cpu_to_le32(reg);
		reg >>= 8;
		reg &= 0xFF;
		if(reg == 0) {
			printk(KERN_ALERT "IO ERROR \n");
			return -1;
		}
//		result = sdcmsc_card_queue(dev, 0, first_block, buf );
		result = sdcmsc_card_queue(dev, 0, first_block+i, buf+i*512 );
		if(!result){
			printk(KERN_ALERT "IO ERROR \n");
			return -1;
		}
	}
	return 1;
}

static int sdcmsc_disk_write(struct sdcmsc_card* dev, unsigned long  buf, 
	unsigned long blocks, unsigned long first_block){
	void __iomem *sdcmsc_base = dev->sdcmsc_base;

	int i;
	int result;
	unsigned int reg;

	for(i = 0; i < blocks;i++) {

		//Check for free transmit buffers
		reg = ioread32(sdcmsc_base + SDCMSC_BD_BUFFER_STATUS);
		reg = cpu_to_le32(reg);
		reg &= 0xFF;
		if(reg == 0) {
			printk(KERN_ALERT "IO ERROR \n");
			return -1;
		}
		//result = sdcmsc_card_queue(dev, 1, first_block, buf );
		result = sdcmsc_card_queue(dev, 1, first_block+i, buf + i*512 );
		if(!result){
			printk(KERN_ALERT "IO ERROR \n");
			return -1;
		}
	}
	return 1;	
}
static void sdcmsc_transfer(struct sdcmsc_card *dev, unsigned long sector,
	unsigned long nsect, unsigned long buffer, int write)
{
	if(write)
		sdcmsc_disk_write(dev, buffer, nsect, sector);
	else
		sdcmsc_disk_read(dev, buffer, nsect, sector);
}

static int sdcmsc_ioctl(struct block_device *bdev, fmode_t mode,
	unsigned int  cmd, unsigned long arg)
{
	return 1;
}
static int sdcmsc_open(struct block_device *bdev, fmode_t mode)
{
//	printk(KERN_ALERT "I'm in sdcmsc_open\n");
	struct sdcmsc_card* dev= sdcmsc_blk_get(bdev->bd_disk);
	int ret = -ENXIO;

	mutex_lock(&block_mutex);
	if(dev){
		if(dev->usage == 2)
			check_disk_change(bdev);
		ret = 0;
	}
	mutex_unlock(&block_mutex);
//	printk(KERN_ALERT "sdcmsc_open ok!\n");
	return ret;
}

static int sdcmsc_release(struct gendisk *disk, fmode_t mode)
{
	struct sdcmsc_card* dev =  disk->private_data;

	mutex_lock(&block_mutex);
	sdcmsc_blk_put(dev);
	mutex_unlock(&block_mutex);
	return 0;
}
/*unsigned long bytes_to_sectors_checked(unsigned long size_bytes)
{
	if(size_bytes%512)
	{
		printk(KERN_ALERT "What's the fuck!\n************");
	}
	return size_bytes/512;
}*/
static int sdcmsc_xfer_bio(struct sdcmsc_card* dev, struct bio* bio)
{
	int i;
	unsigned long tmp=0;
	struct bio_vec *bvec;
	struct page *bv_page;
	unsigned long phy_addr;
//	int bvecNum =1;
	sector_t sector = bio->bi_sector;
	sector += 8192; 
	/* Do each segment independently. */
	bio_for_each_segment(bvec, bio, i){
//		printk(KERN_ALERT "bvecNum:%d", bvecNum++);
		//char * buffer = __bio_kmap_atomic(bio, i, KM_USER0);
//		bv_page = bio_page(bio);
		bv_page = bvec->bv_page;
		phy_addr = page_to_phys(bv_page);
		phy_addr += bvec->bv_offset;
//		printk(KERN_ALERT "phy_addr:0x%x\n", phy_addr);
//		tmp=bytes_to_sectors_checked(bio_cur_bytes(bio));
//		printk(KERN_ALERT "sector num:%d\n", tmp);
//		printk(KERN_ALERT "bv_len:%d\n", bvec->bv_len>>9);
		tmp = bvec->bv_len>>9;
//		printk(KERN_ALERT "direction:%d\n",bio_data_dir(bio)==WRITE);
		sdcmsc_transfer(dev, sector, /*bytes_to_sectors_checked(bio_cur_bytes(bio))*/tmp, 
			phy_addr, bio_data_dir(bio) == WRITE);
		sector += /*(bytes_to_sectors_checked(bio_cur_bytes(bio)))*/tmp;
		//int j;
		/*for(j=0; j<512;j++)
		{	printk(KERN_ALERT "%.2x\t", buffer[j]);
		}*/
		//__bio_kunmap_atomic(bio, KM_USER0);
	}
	return 0;
}

static int sdcmsc_xfer_request(struct sdcmsc_card* dev, struct request *req)
{
	struct bio *bio;
	unsigned long  bytes_tranf=0;
//	int bioNum =1;
	__rq_for_each_bio(bio,req){
//		printk(KERN_ALERT "bioNum:%d", bioNum++);
		sdcmsc_xfer_bio(dev, bio);
		bytes_tranf +=bio->bi_size;
	}
	return bytes_tranf;
}
static int sdcmsc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
	struct sdcmsc_card * dev = mq->data;
	unsigned long bytes_tranf =0;
	int ret =0;
	if(req)
	{
		if(req->cmd_type != REQ_TYPE_FS){
			printk(KERN_ALERT "Skip non-fs request\n");
			ret= -EIO;
		
		}else
		{
			bytes_tranf=sdcmsc_xfer_request(dev, req);
		}
	//__blk_end_request(req, 0,bytes_tranf);
		spin_lock_irq(&dev->lock);
	//	__blk_end_request_cur(req, ret);
		__blk_end_request(req, 0,bytes_tranf);
		spin_unlock_irq(&dev->lock);
	}
	return 0;
}

static int sdcmsc_queue_thread(void *d)
{
	struct mmc_queue *mq = d;
	struct request_queue *q = mq->queue;

//	current->flags |= PF_MEMALLOC;

	down(&mq->thread_sem);
	do{
		struct request *req =NULL;

		spin_lock_irq(q->queue_lock);
		set_current_state(TASK_INTERRUPTIBLE);
		req = blk_fetch_request(q);
		mq->mqrq_cur = req;
		spin_unlock_irq(q->queue_lock);

		if(req || mq->mqrq_prev){
			set_current_state(TASK_RUNNING);
//			printk(KERN_ALERT "before issue_fn");
			mq->issue_fn(mq, req);
		} else {
//	printk(	KERN_ALERT "I will sleep or stop");
			if(kthread_should_stop()){
//				printk(KERN_ALERT "I will stop");
				set_current_state(TASK_RUNNING);
				break;
			}
			up(&mq->thread_sem);
			schedule();
//			printk(KERN_ALERT "after schedule");
			down(&mq->thread_sem);
		}

		mq->mqrq_prev = mq->mqrq_cur;
		mq->mqrq_cur = NULL;
	}while(1);

	up(&mq->thread_sem);

	return 0;
}

static void sdcmsc_request(struct request_queue *q)
{
	struct mmc_queue *mq = q->queuedata;
	struct request *req;

	if(!mq) {
		while((req = blk_fetch_request(q)) != NULL) {
			req->cmd_flags |= REQ_QUIET;
			__blk_end_request_all(req, -EIO);
		}
		return;
	}
//	printk(KERN_ALERT "before wake_up_process");
	if(!mq->mqrq_cur && !mq->mqrq_prev)
	{
//		printk(KERN_ALERT "I will wake up process");
		wake_up_process(mq->thread);
	}
}

static int sdcmsc_card_cmd(void __iomem* sdcmsc_base,unsigned cmd, unsigned arg, 
				unsigned *response){
	// Send command to card
	cmd = le32_to_cpu(cmd);
	iowrite32(cmd, sdcmsc_base + SDCMSC_COMMAND);
	arg = le32_to_cpu(arg);
	iowrite32(arg, sdcmsc_base + SDCMSC_ARGUMENT);

	// Wait for response
	unsigned reg;
	unsigned mask = SDCMSC_NORMAL_INT_STATUS_EI | SDCMSC_NORMAL_INT_STATUS_CC;

	do {
	reg = ioread32(sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);
	reg = cpu_to_le32(reg);
	} while(!(reg&mask));
	iowrite32(0, sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);

	//Optionally read response register
	if(response) {
		unsigned tmp;
		tmp = ioread32(sdcmsc_base + SDCMSC_RESPONSE);
		tmp = cpu_to_le32(tmp);
		*response =tmp;
	}
	
	// Check for errors
	if(reg & SDCMSC_NORMAL_INT_STATUS_EI) {
		reg = ioread32(sdcmsc_base + SDCMSC_ERROR_INT_STATUS);
		reg = cpu_to_le32(reg);
		if(reg & (1 << 3)) printk(KERN_ALERT "Command index error\n");
		if(reg & (1 << 1)) printk(KERN_ALERT "Command CRC error\n");
		if(reg & (1 << 0)) printk(KERN_ALERT "Command timeout\n");
		iowrite32(0, sdcmsc_base+ SDCMSC_ERROR_INT_STATUS);
		return 0;
	}
	else{
		return 1;
	}
}

int sdcmsc_dev_probe(void __iomem * sdcmsc_base)
{
	
	unsigned cmd;
	unsigned reg;
	unsigned arg;
	unsigned card_capacity;
	unsigned int param;
	
	// Set highest possible timeout
	param = le32_to_cpu(0xFFFE);
	iowrite32(param,sdcmsc_base + SDCMSC_TIMEOUT);	

	//Reset the peripheral
	param = le32_to_cpu(1);
	iowrite32(param, sdcmsc_base + SDCMSC_SOFTWARE_RESET);
	param = le32_to_cpu(2);
	iowrite32(param, sdcmsc_base + SDCMSC_CLOCK_DIVIDER);
	param = le32_to_cpu(0);
	iowrite32(param, sdcmsc_base + SDCMSC_SOFTWARE_RESET);

	//Send CMD0 to switch the card to idle state
	cmd = SDCMSC_COMMAND_CMDI(0);
	if(!sdcmsc_card_cmd(sdcmsc_base,cmd, 0, NULL)) return 0;

	// Send CMD8 offering 2.7V to 3.6V range
	// If the card doesn't response it means either:
	// 1. Card supports v2.0 but can't communicate using current voltage levels
	// 2. Card doesn't support v2.0
	cmd = SDCMSC_COMMAND_CMDI(8) |
		SDCMSC_COMMAND_CICE | 
		SDCMSC_COMMAND_CIRC |
		SDCMSC_COMMAND_RTS_48;
	sdcmsc_dev_p->is_v20 = sdcmsc_card_cmd(sdcmsc_base, cmd, 0x1AA, NULL);
//	if(sdcmsc_dev_p->is_v20)
//		 printk(KERN_ALERT "This is sd version 2.0\n");
//	else
//		printk(KERN_ALERT "This isn't sd version 2.0\n");
		
	do {
		reg = ioread32(sdcmsc_base + SDCMSC_CARD_STATUS);
		reg = cpu_to_le32(reg);
	} while(reg & SDCMSC_CARD_STATUS_CICMD);

	// Repeat ACMD41 until card set the busy bit to 1
	// Since ACMD is an extended command, it must be preceded
	// by CMD55
	do {
		cmd = SDCMSC_COMMAND_CMDI(55) |
			SDCMSC_COMMAND_CICE |
			SDCMSC_COMMAND_CIRC |
			SDCMSC_COMMAND_RTS_48;
		if(!sdcmsc_card_cmd(sdcmsc_base, cmd, 0, NULL)) return 0;

		cmd = SDCMSC_COMMAND_CMDI(41)
			| SDCMSC_COMMAND_RTS_48;
		arg = sdcmsc_dev_p->is_v20 ? 
			0x40FF8000 :
			0x00FF8000;
		if(!sdcmsc_card_cmd(sdcmsc_base, cmd, arg, ®)) return 0;
	} while(!(reg & 0x80000000));
	
	sdcmsc_dev_p->is_sdhc = !!(reg & 0x40000000);
//	if(sdcmsc_dev_p->is_sdhc)
//		printk(KERN_ALERT "This is sdhc\n");
//	else
//		printk(KERN_ALERT "This isn't sdhc\n");
	// Issue CMD2 to switch from ready state to ident. Unfortunately, it is
	// not possible to read whole CID because the command can be issued only
	// once, and the peripheral can store only 32bit of the command at once.
	cmd = SDCMSC_COMMAND_CMDI(2) |
		SDCMSC_COMMAND_RTS_136;
	if(!sdcmsc_card_cmd(sdcmsc_base, cmd, 0, NULL)) return 0;

	//Issue CMD3 to get RCA and switch from ident state to stby
	cmd = SDCMSC_COMMAND_CMDI(3) |
		SDCMSC_COMMAND_CICE |
		SDCMSC_COMMAND_CIRC |
		SDCMSC_COMMAND_RTS_48;
	if(!sdcmsc_card_cmd(sdcmsc_base, cmd, 0, ®)) return 0;
	sdcmsc_dev_p->rca = reg & 0xFFFF0000;

	//Calculate card capacity. Use information stored in CSD register.
	if(sdcmsc_dev_p->is_sdhc) {
		cmd = SDCMSC_COMMAND_CMDI(9) |
			SDCMSC_COMMAND_CMDW(1) |
			SDCMSC_COMMAND_RTS_136;
		if(!sdcmsc_card_cmd(sdcmsc_base, cmd, sdcmsc_dev_p->rca, ®))  return 0;
		card_capacity = reg & 0x3F;
		card_capacity <<=16;

		cmd = SDCMSC_COMMAND_CMDI(9) |
			SDCMSC_COMMAND_CMDW(2) |
			SDCMSC_COMMAND_RTS_136;
		if(!sdcmsc_card_cmd(sdcmsc_base, cmd, sdcmsc_dev_p->rca, ®)) return 0;
		reg >>=16;
		card_capacity |= reg;
		card_capacity +=1;
		card_capacity *=1000;
	}
	else {
		cmd = SDCMSC_COMMAND_CMDI(9) |
			SDCMSC_COMMAND_CMDW(1) |
			SDCMSC_COMMAND_RTS_136;
		if(!sdcmsc_card_cmd(sdcmsc_base, cmd, sdcmsc_dev_p->rca, ®)) return 0;
		unsigned read_bl_len = (reg >>16) & 0x0F;
		unsigned c_size = reg & 0x3FF;
		c_size <<= 2;

		cmd = SDCMSC_COMMAND_CMDI(9) |
			SDCMSC_COMMAND_CMDW(2) |
			SDCMSC_COMMAND_RTS_136;
		if(!sdcmsc_card_cmd(sdcmsc_base, cmd, sdcmsc_dev_p->rca, ®)) return 0;
		c_size |= (reg >>30) & 0x03;
		unsigned c_size_mult = (reg >> 15) & 0x07;
		card_capacity = c_size + 1;
		card_capacity *= 1 << (c_size_mult + 2);
		card_capacity *= 1 << (read_bl_len);
		card_capacity >>= 9;
	}
	sdcmsc_dev_p->card_capacity = card_capacity;
//	printk(	KERN_ALERT "card capacity:%d\n", sdcmsc_dev_p->card_capacity);
	// Put card in transfer state
	cmd = SDCMSC_COMMAND_CMDI(7) |
		SDCMSC_COMMAND_CICE |
		SDCMSC_COMMAND_CIRC |
		SDCMSC_COMMAND_RTS_48;
	if(!sdcmsc_card_cmd(sdcmsc_base, cmd, sdcmsc_dev_p->rca, ®)) return 0;
	if(reg != 0x700) return 0;

	// Set block size to 512
	cmd = SDCMSC_COMMAND_CMDI(16) |
		SDCMSC_COMMAND_CICE |
		SDCMSC_COMMAND_CIRC |
		SDCMSC_COMMAND_RTS_48;
	if(!sdcmsc_card_cmd(sdcmsc_base, cmd ,512, NULL)) return 0;

	// Set 4-bits bus mode
	cmd = SDCMSC_COMMAND_CMDI(55) |
		SDCMSC_COMMAND_CICE |
		SDCMSC_COMMAND_CIRC |
		SDCMSC_COMMAND_RTS_48;
	if(!sdcmsc_card_cmd(sdcmsc_base, cmd, sdcmsc_dev_p->rca, NULL)) return 0;

	cmd = SDCMSC_COMMAND_CMDI(6) |
		SDCMSC_COMMAND_CICE |
		SDCMSC_COMMAND_CIRC |
		SDCMSC_COMMAND_RTS_48;
	if(!sdcmsc_card_cmd(sdcmsc_base, cmd, 0x02, NULL)) return 0;
//	printk(KERN_ALERT "test done!\n");
	return 1;

	
}
int sdcmsc_init_queue(struct mmc_queue *mq, spinlock_t *lock )
{
	int ret;
	
	mq->queue = blk_init_queue(sdcmsc_request, lock);
	if(!mq->queue)
		return -ENOMEM;
	mq->mqrq_cur = &mq->mqrq[0];
	mq->mqrq_prev = &mq->mqrq[1];

	mq->queue->queuedata = mq;
	sema_init(&mq->thread_sem, 1);
	mq->thread = kthread_run(sdcmsc_queue_thread, mq, "sdcmscThread");
	if(IS_ERR(mq->thread)){
		ret = PTR_ERR(mq->thread);
		goto cleanup_queue;
	}
	return 0;
cleanup_queue:
	blk_cleanup_queue(mq->queue);
	return ret;
}
int sdcmsc_irq_init(void __iomem* sdcmsc_base)
{
	iowrite32(0,sdcmsc_base+ SDCMSC_NORMAL_INT_ENABLE);
	iowrite32(0, sdcmsc_base+SDCMSC_ERROR_INT_ENABLE);
	iowrite32(le32_to_cpu(0xFF),sdcmsc_base+ SDCMSC_DAT_INT_ENABLE);	
	iowrite32(0, sdcmsc_base + SDCMSC_DAT_INT_STATUS);
	return 1;
}

static struct block_device_operations sdcmsc_ops = {
	.owner	= THIS_MODULE,
	.open	= sdcmsc_open,
	.release= sdcmsc_release,
	//.media_changed = sdcmsc_media_changed,
	//.revalidate_disk = sdcmsc_revalidate,
	.ioctl	= sdcmsc_ioctl
};
void sdcmsc_cleanup_queue(struct mmc_queue *mq)
{
	struct request_queue *q = mq->queue;
	unsigned long  flags;
	/* Then terminate our worker thread */
	kthread_stop(mq->thread);

	/* Empty the queue */
	spin_lock_irqsave(q->queue_lock, flags);
	q->queuedata = NULL;
	blk_start_queue(q);
	spin_unlock_irqrestore(q->queue_lock, flags);
}

int sdcmsc_dev_init(void)
{
	int ret, err;
	if(!request_mem_region(SDCMSC_BASE,SDCMSC_ADR_LEN,"ocores-sdcmsc"))
	{
		printk(KERN_ALERT "ocores-sdcmsc request_mem_region fails!");
		goto out;
	}
	sdcmsc_dev_p->sdcmsc_base= ioremap(SDCMSC_BASE,SDCMSC_ADR_LEN);
//	printk(KERN_ALERT "sdcmsc_dev_p->sdcmsc_base:0x%x\n", sdcmsc_dev_p->sdcmsc_base);
	if(!sdcmsc_dev_p->sdcmsc_base)
	{
		printk(KERN_ALERT "ocores-sdcmsc ioremap failed!");
		goto out_release_mem_region;
	}
	if(!sdcmsc_dev_probe(sdcmsc_dev_p->sdcmsc_base))
		goto out_iounmap;
	
	sdcmsc_dev_p->irq_dat = SDCMSC_DAT_INT_NO;
	sdcmsc_irq_init(sdcmsc_dev_p->sdcmsc_base);
//	printk(KERN_ALERT "before request_irq");
	err = request_irq(SDCMSC_DAT_INT_NO, sdcmsc_irq_dat, IRQF_DISABLED, "sdcmsc", sdcmsc_dev_p);
	if(err)
	{
		printk(KERN_ALERT "request irq error!");
		goto out_iounmap;

	}
	spin_lock_init(&sdcmsc_dev_p->lock);
	
	sdcmsc_init_queue(&sdcmsc_dev_p->mq, &sdcmsc_dev_p->lock);
	if(ret)
		goto out_freeirq;
	sdcmsc_dev_p->mq.issue_fn = sdcmsc_blk_issue_rq;
	sdcmsc_dev_p->mq.data = sdcmsc_dev_p;
	
	blk_queue_logical_block_size(sdcmsc_dev_p->mq.queue, 512);
	
	sdcmsc_dev_p->gd = alloc_disk(/*SDCMSC_MINORS*/1);
	if(!sdcmsc_dev_p->gd)
	{
		printk(KERN_ALERT "alloc_disk failure\n");
		goto out_cleanqueue;
	}
	sdcmsc_dev_p->gd->major = MMC_BLOCK_MAJOR;
	sdcmsc_dev_p->gd->first_minor = 0;
	sdcmsc_dev_p->gd->fops = &sdcmsc_ops;
	snprintf(sdcmsc_dev_p->gd->disk_name, 32, "sdcmsc");
	sdcmsc_dev_p->gd->queue = sdcmsc_dev_p->mq.queue;
	set_capacity(sdcmsc_dev_p->gd, sdcmsc_dev_p->card_capacity);
	sdcmsc_dev_p->gd->private_data = sdcmsc_dev_p;
	sdcmsc_dev_p->usage = 1;
//	printk(KERN_ALERT "Before add_disk");
	add_disk(sdcmsc_dev_p->gd);
//	printk(KERN_ALERT "OK after add_disk\n");
	return 1;
	out_cleanqueue:
		sdcmsc_cleanup_queue(&sdcmsc_dev_p->mq);
	out_freeirq:
		free_irq(SDCMSC_DAT_INT_NO, sdcmsc_dev_p);
	out_iounmap:
		iounmap(sdcmsc_dev_p->sdcmsc_base);
	out_release_mem_region:
		release_mem_region(SDCMSC_BASE, SDCMSC_ADR_LEN);
	out:
		return -1;
	
}	

	
static int ocores_sdcmsc_init(void)
{
	int res;
	
	res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
	if (res)
		goto out;
	sdcmsc_dev_p = kzalloc(sizeof(struct sdcmsc_card), GFP_KERNEL);
	if(sdcmsc_dev_p == NULL)
		goto out_unregister;

	res=sdcmsc_dev_init();
	if(res < 0)
		goto out_kfree;
	return 0;
	out_kfree:
		kfree(sdcmsc_dev_p);
	
	out_unregister:
		unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
	
	out:
		return res;
}

static void ocores_sdcmsc_exit(void)
{
	del_gendisk(sdcmsc_dev_p->gd);
	put_disk(sdcmsc_dev_p->gd);
	sdcmsc_cleanup_queue(&sdcmsc_dev_p->mq);
	unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
}


module_init(ocores_sdcmsc_init);
module_exit(ocores_sdcmsc_exit);


2>sdcmsc.h


/*
* rill create at 2013-09-18
* [email protected]
*/


#ifndef MMC_BLOCK_MAJOR
	#define MMC_BLOCK_MAJOR		179
#endif
#define SDCMSC_MINORS	4

// SDCMSC address space
#define SDCMSC_BASE 0x9e000000
#define SDCMSC_ADR_LEN 0xa0
// Register space
#define SDCMSC_ARGUMENT           0x00
#define SDCMSC_COMMAND            0x04
#define SDCMSC_CARD_STATUS        0x08
#define SDCMSC_RESPONSE           0x0C
#define SDCMSC_CONTROLLER_SETTING 0x1C
#define SDCMSC_BLOCK_SIZE         0x20
#define SDCMSC_POWER_CONTROL      0x24
#define SDCMSC_SOFTWARE_RESET     0x28
#define SDCMSC_TIMEOUT            0x2C
#define SDCMSC_NORMAL_INT_STATUS  0x30
#define SDCMSC_ERROR_INT_STATUS   0x34
#define SDCMSC_NORMAL_INT_ENABLE  0x38
#define SDCMSC_ERROR_INT_ENABLE   0x3C
#define SDCMSC_CAPABILITY         0x48
#define SDCMSC_CLOCK_DIVIDER      0x4C
#define SDCMSC_BD_BUFFER_STATUS   0x50
#define SDCMSC_DAT_INT_STATUS     0x54
#define SDCMSC_DAT_INT_ENABLE     0x58
#define SDCMSC_BD_RX              0x60
#define SDCMSC_BD_TX              0x80

// SDCMSC_COMMAND bits
#define SDCMSC_COMMAND_CMDI(x) (x << 8)
#define SDCMSC_COMMAND_CMDW(x) (x << 6)
#define SDCMSC_COMMAND_CICE    0x10
#define SDCMSC_COMMAND_CIRC    0x08
#define SDCMSC_COMMAND_RTS_48  0x02
#define SDCMSC_COMMAND_RTS_136 0x01

//SDCMSC_CARD_STATUS bits
#define SDCMSC_CARD_STATUS_CICMD 0x01

// SDCMSC_NORMAL_INT_STATUS bits
#define SDCMSC_NORMAL_INT_STATUS_EI 0x8000
#define SDCMSC_NORMAL_INT_STATUS_CC 0x0001

// SDCMSC_DAT_INT_STATUS
#define SDCMSC_DAT_INT_STATUS_TRS 0x01

#define SDCMSC_DAT_INT_NO 16


struct mmc_queue {
	//struct mmc_card		*card;
	struct task_struct	*thread;
	struct semaphore	thread_sem;
	unsigned int		flags;
	int			(*issue_fn)(struct mmc_queue *, struct request *);
	void			*data;
	struct request_queue	*queue;
	struct request	mqrq[2];
	struct request	*mqrq_cur;
	struct request	*mqrq_prev;
};

struct sdcmsc_card {
	spinlock_t lock;
	struct mmc_queue mq;
	struct gendisk *gd;

	void __iomem * sdcmsc_base;
	unsigned int rca; /* relative card address of device */
	int is_v20;
	int is_sdhc;
	int connected;
	unsigned card_capacity;
	unsigned int	usage;
	int irq_dat;
};


3>Makefile


ifneq ($(KERNELRELEASE), )
	obj-m := sdcmsc.o
else
	KERNELDIR ?= /home/openrisc/soc-design/linux
	PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules ARCH=openrisc CROSS_COMPILE=or32-linux-
clean:
	rm -rf .*.cmd *.o *.mod.c *.ko .tm_versions *.order *.symvers
endif


4,验证

关于驱动模块的加载步骤,之前已经介绍过了,这里不再赘述,如有疑问请参考: http://blog.csdn.net/rill_zhen/article/details/8700937

需要注意的是,在ko模块加载成功之后,需要使用mount命令挂载SD卡。

如果一切顺利的话,我们可以事先用电脑向SD卡里存放一些文件,然后插到ORPSoC的FPGA开发板上进行测试。

测试过程中使用的命令如下:


# mount -t nfs -o nolock 192.168.1.101:/home/openrisc/nfs nfs
# insmod sdcmsc.ko 
# cd ..
# mkdir sdcmsc
# mount -t vfat /dev/sdcmsc /sdcmsc/
# cd sdcmsc/
# ls
# cat boot_record.txt
# ls
We Are The Champions.mp3  fatfs1.c
boot_record.txt           presentation.pdf
# cp presentation.pdf presentation_back.pdf
# ls
# cat fatfs1.c
# cp We\ Are\ The\ Champions.mp3 We_gss.mp3
# ls
# ls -l



下面是整个测试过程中的终端完整打印结果,通过和电脑中事先存放的文件,进行对比,可以确认所有操作都已经正确执行了。


openrisc@openrisc-VirtualBox:~$ picocom --b 115200 --p n --d 8 --f xon /dev/ttyUSB2
picocom v1.4

port is        : /dev/ttyUSB2
flowcontrol    : xon/xoff
baudrate is    : 115200
parity is      : none
databits are   : 8
escape is      : C-a
noinit is      : no
noreset is     : no
nolock is      : no
send_cmd is    : ascii_xfr -s -v -l10
receive_cmd is : rz -vv

Terminal ready
Compiled-in FDT at 0xc02b04e0
Linux version 3.1.0-rc6-00002-g0da8eb1-dirty (openrisc@openrisc-VirtualBox) (gcc version 4.5.1-or32-1.0rc4 (OpenRISC 32-bit toolchain for or32-linux (built 20111017)) ) #76 Mon Aug 26 14:38:03 CEST 2013
CPU: OpenRISC-12 (revision 8) @50 MHz
-- dcache: 4096 bytes total, 16 bytes/line, 1 way(s)
-- icache: 8192 bytes total, 16 bytes/line, 1 way(s)
-- dmmu:   64 entries, 1 way(s)
-- immu:   64 entries, 1 way(s)
-- additional features:
-- debug unit
-- PIC
-- timer
setup_memory: Memory: 0x0-0x2000000
Reserved - 0x01ffda90-0x00002570
Setting up paging and PTEs.
map_ram: Memory: 0x0-0x2000000
On node 0 totalpages: 4096
free_area_init_node: node 0, pgdat c0294bd0, node_mem_map c0410000
  Normal zone: 16 pages used for memmap
  Normal zone: 0 pages reserved
  Normal zone: 4080 pages, LIFO batch:0
dtlb_miss_handler c0002000
itlb_miss_handler c0002108
OpenRISC Linux -- http://openrisc.net
pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
pcpu-alloc: [0] 0 
Built 1 zonelists in Zone order, mobility grouping off.  Total pages: 4080
Kernel command line: console=uart,mmio,0x90000000,115200
Early serial console at MMIO 0x90000000 (options '115200')
bootconsole [uart0] enabled
PID hash table entries: 128 (order: -4, 512 bytes)
Dentry cache hash table entries: 4096 (order: 1, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 0, 8192 bytes)
Memory: 28376k/32768k available (2307k kernel code, 4392k reserved, 338k data, 1416k init, 0k highmem)
mem_init_done ...........................................
NR_IRQS:32
100.00 BogoMIPS (lpj=500000)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024
gss done after type->mount
gss done before return in mount_fs
gss done after type->mount
gss done before return in mount_fs
gss done after type->mount
gss done before return in mount_fs
gss done after type->mount
gss done before return in mount_fs
gss done after type->mount
gss done before return in mount_fs
gss done after type->mount
gss done before return in mount_fs
devtmpfs: initialized
gss done after type->mount
gss done before return in mount_fs
NET: Registered protocol family 16
bio: create slab <bio-0> at 0
Switching to clocksource openrisc_timer
gss done after type->mount
gss done before return in mount_fs
gss done after type->mount
Switched to NOHz mode on CPU #0
gss done before return in mount_fs
NET: Registered protocol family 2
IP route cache hash table entries: 2048 (order: 0, 8192 bytes)
TCP established hash table entries: 1024 (order: 0, 8192 bytes)
TCP bind hash table entries: 1024 (order: -1, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
TCP reno registered
UDP hash table entries: 512 (order: 0, 8192 bytes)
UDP-Lite hash table entries: 512 (order: 0, 8192 bytes)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
Unpacking initramfs
gss done after type->mount
gss done before return in mount_fs
gss done after type->mount
gss done before return in mount_fs
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254)
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
90000000.serial: ttyS0 at MMIO 0x90000000 (irq = 2) is a 16550A
console [ttyS0] enabled, bootconsole disabled
console [ttyS0] enabled, bootconsole disabled
ethoc-mdio: probed
NET: Registered protocol family 17
Freeing unused kernel memory: 1416k freed
init started: BusyBox v1.19.0.git (2011-02-16 08:10:12 CET)
gss done after type->mount
gss done before return in mount_fs
gss done after type->mount
gss done before return in mount_fs
gss done after type->mount
gss done before return in mount_fsConfiguring loopback device
rill add to  mount nfs 2013-04-10
<--mount start-->

gss done after type->mount
gss done before return in mount_fsmount: RPC: Unable to receive; errno = Connection refused
mount: mounting 192.168.1.101:/home/openrisc/nfs on nfs failed: Bad file descriptor
<--mount end-->

Please press Enter to activate this console. 
# mount -t nfs -o nolock 192.168.1.101:/home/openrisc/nfs nfs

gss done after type->mount
gss done before return in mount_fs# cd nfs
# insmod sdcmsc.ko 
# cd ..
# mkdir sdcmsc
# mount -t vfat /dev/sdcmsc /sdcmsc/

b->fats:2
b->fat32_length:-383975424
gss done after fill_super
gss done before mount_bdev
gss done after type->mount
gss done before return in mount_fs# 
# cd sdcmsc/
# ls
We Are The Champions.mp3  fatfs1.c
boot_record.txt           presentation.pdf
# cat boot_record.txt 
openrisc@openrisc-VirtualBox:~/share/ecos_sdc_test$ picocom --b 115200 --p n --d 8 --f xon /dev/ttyUSB2
picocom v1.4

port is        : /dev/ttyUSB2
flowcontrol    : xon/xoff
baudrate is    : 115200
parity is      : none
databits are   : 8
escape is      : C-a
noinit is      : no
noreset is     : no
nolock is      : no
send_cmd is    : ascii_xfr -s -v -l10
receive_cmd is : rz -vv

Terminal ready
is_v20:0x1
is_sdhc:0x0
rca:0xe6240000
card capacity:0x3af000
sdc init success!
sddr:0x10e00,sector:135------------->
tmp[0]:0xeb
tmp[1]:0x58
tmp[2]:0x90
tmp[3]:0x4d
tmp[4]:0x53
tmp[5]:0x44
tmp[6]:0x4f
tmp[7]:0x53
tmp[8]:0x35
tmp[9]:0x2e
tmp[10]:0x30
tmp[11]:0x0
tmp[12]:0x2
tmp[13]:0x1
tmp[14]:0x7e
tmp[15]:0x18
tmp[16]:0x2
tmp[17]:0x0
tmp[18]:0x0
tmp[19]:0x0
tmp[20]:0x0
tmp[21]:0xf8
tmp[22]:0x0
tmp[23]:0x0
tmp[24]:0x3f
tmp[25]:0x0
tmp[26]:0xff
tmp[27]:0x0
tmp[28]:0x87
tmp[29]:0x0
tmp[30]:0x0
tmp[31]:0x0
tmp[32]:0x39
tmp[33]:0xe0
tmp[34]:0x3a
tmp[35]:0x0
tmp[36]:0xc1
tmp[37]:0x73
tmp[38]:0x0
tmp[39]:0x0
tmp[40]:0x0
tmp[41]:0x0
tmp[42]:0x0
tmp[43]:0x0
tmp[44]:0x2
tmp[45]:0x0
tmp[46]:0x0
tmp[47]:0x0
tmp[48]:0x1
tmp[49]:0x0
tmp[50]:0x6
tmp[51]:0x0
tmp[52]:0x0
tmp[53]:0x0
tmp[54]:0x0
tmp[55]:0x0
tmp[56]:0x0
tmp[57]:0x0
tmp[58]:0x0
tmp[59]:0x0
tmp[60]:0x0
tmp[61]:0x0
tmp[62]:0x0
tmp[63]:0x0
tmp[64]:0x80
tmp[65]:0x0
tmp[66]:0x29
tmp[67]:0x36
tmp[68]:0x8d
tmp[69]:0x5a
tmp[70]:0x76
tmp[71]:0x4e
tmp[72]:0x4f
tmp[73]:0x20
tmp[74]:0x4e
tmp[75]:0x41
tmp[76]:0x4d
tmp[77]:0x45
tmp[78]:0x20
tmp[79]:0x20
tmp[80]:0x20
tmp[81]:0x20
tmp[82]:0x46
tmp[83]:0x41
tmp[84]:0x54
tmp[85]:0x33
tmp[86]:0x32
tmp[87]:0x20
tmp[88]:0x20
tmp[89]:0x20
tmp[90]:0x96
tmp[91]:0xc
tmp[92]:0x0
tmp[93]:0x0
tmp[94]:0x0
tmp[95]:0x5a
tmp[96]:0x0
tmp[97]:0x0
tmp[98]:0x0
tmp[99]:0x0
tmp[100]:0x0
tmp[101]:0x2
tmp[102]:0xfc
tmp[103]:0xf8
tmp[104]:0x0
tmp[105]:0x2
tmp[106]:0xae
tmp[107]:0x78
tmp[108]:0x0
tmp[109]:0x2
tmp[110]:0xfd
tmp[111]:0x3c
tmp[112]:0x0
tmp[113]:0x2
tmp[114]:0x96
tmp[115]:0x19
tmp[116]:0x11
tmp[117]:0x11
tmp[118]:0x0
tmp[119]:0x18
tmp[120]:0x11
tmp[121]:0x11
tmp[122]:0x0
tmp[123]:0x1a
tmp[124]:0x11
tmp[125]:0x11
tmp[126]:0x0
tmp[127]:0x1c
tmp[128]:0x11
tmp[129]:0x11
tmp[130]:0x0
tmp[131]:0x1e
tmp[132]:0x0
tmp[133]:0x2
tmp[134]:0xf2
tmp[135]:0xec
tmp[136]:0x0
tmp[137]:0x0
tmp[138]:0xf4
tmp[139]:0x64
tmp[140]:0x0
tmp[141]:0x0
tmp[142]:0x0
tmp[143]:0x0
tmp[144]:0x0
tmp[145]:0x0
tmp[146]:0x0
tmp[147]:0x0
tmp[148]:0x0
tmp[149]:0x0
tmp[150]:0x0
tmp[151]:0x0
tmp[152]:0x0
tmp[153]:0x0
tmp[154]:0x0
tmp[155]:0x0
tmp[156]:0x0
tmp[157]:0x0
tmp[158]:0x0
tmp[159]:0x0
tmp[160]:0x0
tmp[161]:0x0
tmp[162]:0x0
tmp[163]:0x0
tmp[164]:0x0
tmp[165]:0x0
tmp[166]:0x0
tmp[167]:0x0
tmp[168]:0x0
tmp[169]:0x0
tmp[170]:0x0
tmp[171]:0x0
tmp[172]:0x0
tmp[173]:0x0
tmp[174]:0x0
tmp[175]:0x0
tmp[176]:0x0
tmp[177]:0x0
tmp[178]:0x0
tmp[179]:0x0
tmp[180]:0x0
tmp[181]:0x0
tmp[182]:0x0
tmp[183]:0x0
tmp[184]:0x0
tmp[185]:0x0
tmp[186]:0x0
tmp[187]:0x0
tmp[188]:0x0
tmp[189]:0x0
tmp[190]:0x0
tmp[191]:0x0
tmp[192]:0x0
tmp[193]:0x0
tmp[194]:0x0
tmp[195]:0x0
tmp[196]:0x0
tmp[197]:0x0
tmp[198]:0x0
tmp[199]:0x0
tmp[200]:0x0
tmp[201]:0x2
tmp[202]:0xad
tmp[203]:0x78
tmp[204]:0x0
tmp[205]:0x2
tmp[206]:0xae
tmp[207]:0x78
tmp[208]:0x0
tmp[209]:0x2
tmp[210]:0xae
tmp[211]:0x18
tmp[212]:0x0
tmp[213]:0x2
tmp[214]:0x96
tmp[215]:0x1b
tmp[216]:0x0
tmp[217]:0x2
tmp[218]:0x96
tmp[219]:0xc
tmp[220]:0x0
tmp[221]:0x2
tmp[222]:0xf3
tmp[223]:0x8
tmp[224]:0x0
tmp[225]:0x0
tmp[226]:0xd1
tmp[227]:0x68
tmp[228]:0x0
tmp[229]:0x2
tmp[230]:0xd3
tmp[231]:0x10
tmp[232]:0x11
tmp[233]:0x11
tmp[234]:0x0
tmp[235]:0x10
tmp[236]:0x11
tmp[237]:0x11
tmp[238]:0x0
tmp[239]:0x12
tmp[240]:0x11
tmp[241]:0x11
tmp[242]:0x0
tmp[243]:0x14
tmp[244]:0x11
tmp[245]:0x11
tmp[246]:0x0
tmp[247]:0x16
tmp[248]:0x0
tmp[249]:0x2
tmp[250]:0xf3
tmp[251]:0x94
tmp[252]:0x0
tmp[253]:0x0
tmp[254]:0x90
tmp[255]:0xa4
tmp[256]:0x0
tmp[257]:0x0
tmp[258]:0x0
tmp[259]:0x0
tmp[260]:0x0
tmp[261]:0x0
tmp[262]:0x0
tmp[263]:0x0
tmp[264]:0x0
tmp[265]:0x0
tmp[266]:0x0
tmp[267]:0x0
tmp[268]:0x0
tmp[269]:0x0
tmp[270]:0x0
tmp[271]:0x0
tmp[272]:0x0
tmp[273]:0x0
tmp[274]:0x0
tmp[275]:0x0
tmp[276]:0x0
tmp[277]:0x0
tmp[278]:0x0
tmp[279]:0x0
tmp[280]:0x0
tmp[281]:0x0
tmp[282]:0x0
tmp[283]:0x0
tmp[284]:0x0
tmp[285]:0x2
tmp[286]:0xf3
tmp[287]:0xb0
tmp[288]:0x0
tmp[289]:0x2
tmp[290]:0xf3
tmp[291]:0xb0
tmp[292]:0x0
tmp[293]:0x2
tmp[294]:0xd3
tmp[295]:0x10
tmp[296]:0x11
tmp[297]:0x11
tmp[298]:0x0
tmp[299]:0x4
tmp[300]:0x11
tmp[301]:0x11
tmp[302]:0x0
tmp[303]:0x5
tmp[304]:0x11
tmp[305]:0x11
tmp[306]:0x0
tmp[307]:0x6
tmp[308]:0x11
tmp[309]:0x11
tmp[310]:0x0
tmp[311]:0x7
tmp[312]:0x11
tmp[313]:0x11
tmp[314]:0x0
tmp[315]:0x8
tmp[316]:0x0
tmp[317]:0x1
tmp[318]:0x8d
tmp[319]:0xf0
tmp[320]:0x11
tmp[321]:0x11
tmp[322]:0x0
tmp[323]:0xa
tmp[324]:0x11
tmp[325]:0x11
tmp[326]:0x0
tmp[327]:0xb
tmp[328]:0x11
tmp[329]:0x11
tmp[330]:0x0
tmp[331]:0xc
tmp[332]:0x11
tmp[333]:0x11
tmp[334]:0x0
tmp[335]:0xd
tmp[336]:0x11
tmp[337]:0x11
tmp[338]:0x0
tmp[339]:0xe
tmp[340]:0x11
tmp[341]:0x11
tmp[342]:0x0
tmp[343]:0xf
tmp[344]:0x11
tmp[345]:0x11
tmp[346]:0x0
tmp[347]:0x10
tmp[348]:0x11
tmp[349]:0x11
tmp[350]:0x0
tmp[351]:0x11
tmp[352]:0x11
tmp[353]:0x11
tmp[354]:0x0
tmp[355]:0x12
tmp[356]:0x11
tmp[357]:0x11
tmp[358]:0x0
tmp[359]:0x13
tmp[360]:0x11
tmp[361]:0x11
tmp[362]:0x0
tmp[363]:0x14
tmp[364]:0xff
tmp[365]:0xff
tmp[366]:0xff
tmp[367]:0xff
tmp[368]:0x0
tmp[369]:0x2
tmp[370]:0xd3
tmp[371]:0x10
tmp[372]:0x11
tmp[373]:0x11
tmp[374]:0x0
tmp[375]:0x10
tmp[376]:0x11
tmp[377]:0x11
tmp[378]:0x0
tmp[379]:0x12
tmp[380]:0x11
tmp[381]:0x11
tmp[382]:0x0
tmp[383]:0x14
tmp[384]:0x11
tmp[385]:0x11
tmp[386]:0x0
tmp[387]:0x16
tmp[388]:0x0
tmp[389]:0x2
tmp[390]:0xf3
tmp[391]:0xa4
tmp[392]:0x0
tmp[393]:0x2
tmp[394]:0x53
tmp[395]:0xc8
tmp[396]:0x0
tmp[397]:0x2
tmp[398]:0x9b
tmp[399]:0x52
tmp[400]:0x0
tmp[401]:0x0
tmp[402]:0x0
tmp[403]:0x0
tmp[404]:0x0
tmp[405]:0x2
tmp[406]:0xf3
tmp[407]:0xb0
tmp[408]:0x0
tmp[409]:0x1
tmp[410]:0x8e
tmp[411]:0x24
tmp[412]:0x11
tmp[413]:0x11
tmp[414]:0x0
tmp[415]:0xe
tmp[416]:0x0
tmp[417]:0x2
tmp[418]:0xf3
tmp[419]:0xb0
tmp[420]:0x0
tmp[421]:0x1
tmp[422]:0x8d
tmp[423]:0xf0
tmp[424]:0x0
tmp[425]:0x1
tmp[426]:0x8d
tmp[427]:0xf0
tmp[428]:0x0
tmp[429]:0x0
tmp[430]:0x0
tmp[431]:0x0
tmp[432]:0x0
tmp[433]:0x2
tmp[434]:0xf3
tmp[435]:0xbc
tmp[436]:0x0
tmp[437]:0x0
tmp[438]:0x0
tmp[439]:0x0
tmp[440]:0x0
tmp[441]:0x0
tmp[442]:0x0
tmp[443]:0x0
tmp[444]:0x0
tmp[445]:0x0
tmp[446]:0x0
tmp[447]:0x0
tmp[448]:0x0
tmp[449]:0x0
tmp[450]:0x0
tmp[451]:0x0
tmp[452]:0x0
tmp[453]:0x0
tmp[454]:0x0
tmp[455]:0x1
tmp[456]:0x0
tmp[457]:0x0
tmp[458]:0x0
tmp[459]:0x0
tmp[460]:0x0
tmp[461]:0x0
tmp[462]:0x0
tmp[463]:0x0
tmp[464]:0x0
tmp[465]:0x0
tmp[466]:0x0
tmp[467]:0x0
tmp[468]:0x0
tmp[469]:0x0
tmp[470]:0x0
tmp[471]:0x0
tmp[472]:0x0
tmp[473]:0x0
tmp[474]:0x0
tmp[475]:0x0
tmp[476]:0x0
tmp[477]:0x0
tmp[478]:0x0
tmp[479]:0x0
tmp[480]:0x0
tmp[481]:0x0
tmp[482]:0x0
tmp[483]:0x0
tmp[484]:0x0
tmp[485]:0x0
tmp[486]:0x0
tmp[487]:0x0
tmp[488]:0x0
tmp[489]:0x0
tmp[490]:0x0
tmp[491]:0x1
tmp[492]:0x0
tmp[493]:0x0
tmp[494]:0x0
tmp[495]:0x0
tmp[496]:0x0
tmp[497]:0x0
tmp[498]:0x0
tmp[499]:0x0
tmp[500]:0x0
tmp[501]:0x0
tmp[502]:0x0
tmp[503]:0x0
tmp[504]:0x0
tmp[505]:0x0
tmp[506]:0x0
tmp[507]:0x0
tmp[508]:0x0
tmp[509]:0x0
tmp[510]:0x0
tmp[511]:0x1
data:0x2f208
fatfs boot record:--->
sig:0x29
marker0:0x55
marker1:0xaa
jump:0x58eb
oem_name[0]:M
oem_name[1]:S
oem_name[2]:D
oem_name[3]:O
oem_name[4]:S
oem_name[5]:5
oem_name[6]:.
oem_name[7]:0
bytes_per_sec:0x200
sec_per_clu:0x1
res_sec_num:0x187e
fat_tbls_num:0x2
max_root_dents:0x0
sec_num_32:0x0
media_desc:0xf8
sec_per_fat:0x0
sec_per_track:0x3f
heads_num:0xff
hsec_num:0x87
sec_num:0x3ae039
sec_num_32:0x0
sec_num_32:0x0
marker0:0x55
marker1:0xaa
sec_per_fat_32:0x73c1
ext_flags:0x0
fs_ver:0x0
root_cluster:0x2
fs_info_sec:0x1
bk_boot_sec:0x6
drv_num:0x80
ext_sig:0x29
ser_num:0x765a8d36
vol_name[0]:N
vol_name[1]:O
vol_name[2]: 
vol_name[3]:N
vol_name[4]:A
vol_name[5]:M
vol_name[6]:E
vol_name[7]: 
vol_name[8]: 
vol_name[9]: 
vol_name[10]: 
fat_name[0]:F
fat_name[1]:A
fat_name[2]:T
fat_name[3]:3
fat_name[4]:2
fat_name[5]: 
fat_name[6]: 
fat_name[7]: 
mount done!
# ls
We Are The Champions.mp3  fatfs1.c
boot_record.txt           presentation.pdf
# cp presentation.pdf presentation_back.pdf
# ls
We Are The Champions.mp3  fatfs1.c                  presentation_back.pdf
boot_record.txt           presentation.pdf
# cat fatfs1.c 
//==========================================================================
//
//      fatfs1.c
//
//      Test fileio system
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc.
//
// eCos 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 or (at your option) any later      
// version.                                                                 
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):           nickg
// Contributors:        nickg
// Date:                2000-05-25
// Purpose:             Test fileio system
// Description:         This test uses the testfs to check out the initialization
//                      and basic operation of the fileio system
//                      
//                      
//                      
//                      
//                      
//              
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/hal.h>
#include <pkgconf/io_fileio.h>
#include <pkgconf/fs_fat.h>

#include <cyg/infra/cyg_trac.h>        // tracing macros
#include <cyg/infra/cyg_ass.h>         // assertion macros

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <stdio.h>

#include <cyg/fileio/fileio.h>

#include <cyg/infra/testcase.h>
#include <cyg/infra/diag.h>            // HAL polled output
#include <cyg/fs/fatfs.h>



//==========================================================================

#define SHOW_RESULT( _fn, _res ) \
diag_printf("<FAIL>: " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):"");

//==========================================================================

#define IOSIZE  100

#define LONGNAME1       "long_file_name_that_should_take_up_more_than_one_directory_entry_1"
#define LONGNAME2       "long_file_name_that_should_take_up_more_than_one_directory_entry_2"


//==========================================================================

#ifndef CYGPKG_LIBC_STRING

char *strcat( char *s1, const char *s2 )
{
    char *s = s1;
    while( *s1 ) s1++;
    while( (*s1++ = *s2++) != 0);
    return s;
}

#endif

//==========================================================================

static void listdir( char *name, int statp, int numexpected, int *numgot )
{
    int err;
    DIR *dirp;
    int num=0;
    
    diag_printf("<INFO>: reading directory %s\n",name);
    
    dirp = opendir( name );
    if( dirp == NULL ) SHOW_RESULT( opendir, -1 );

    for(;;)
    {
        struct dirent *entry = readdir( dirp );
        
        if( entry == NULL )
            break;
        num++;
        diag_printf("<INFO>: entry %14s",entry->d_name);
#ifdef CYGPKG_FS_FAT_RET_DIRENT_DTYPE
        diag_printf(" d_type %2x", entry->d_type);
#endif
        if( statp )
        {
            char fullname[PATH_MAX];
            struct stat sbuf;

            if( name[0] )
            {
                strcpy(fullname, name );
                if( !(name[0] == '/' && name[1] == 0 ) )
                    strcat(fullname, "/" );
            }
            else fullname[0] = 0;
            
            strcat(fullname, entry->d_name );
            
            err = stat( fullname, &sbuf );
            if( err < 0 )
            {
                if( errno == ENOSYS )
                    diag_printf(" <no status available>");
                else SHOW_RESULT( stat, err );
            }
            else
            {
                diag_printf(" [mode %08x ino %08x nlink %d size %ld]",
                            sbuf.st_mode,sbuf.st_ino,sbuf.st_nlink,(long)sbuf.st_size);
            }
#ifdef CYGPKG_FS_FAT_RET_DIRENT_DTYPE
            if ((entry->d_type & S_IFMT) != (sbuf.st_mode & S_IFMT))
              CYG_TEST_FAIL("File mode's don't match between dirent and stat");
#endif
        }

        diag_printf("\n");
    }

    err = closedir( dirp );
    if( err < 0 ) SHOW_RESULT( stat, err );
    if (numexpected >= 0 && num != numexpected)
        CYG_TEST_FAIL("Wrong number of dir entries\n");
    if ( numgot != NULL )
        *numgot = num;
}

//==========================================================================

static void createfile( char *name, size_t size )
{
    char buf[IOSIZE];
    int fd;
    ssize_t wrote;
    int i;
    int err;

    diag_printf("<INFO>: create file %s size %zd \n",name,size);

    err = access( name, F_OK );
    if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err );
    
    for( i = 0; i < IOSIZE; i++ ) buf[i] = i%256;
 
    fd = open( name, O_WRONLY|O_CREAT );
    if( fd < 0 ) SHOW_RESULT( open, fd );

    while( size > 0 )
    {
        ssize_t len = size;
        if ( len > IOSIZE ) len = IOSIZE;
        
        wrote = write( fd, buf, len );
        if( wrote != len ) SHOW_RESULT( write, (int)wrote );        

        size -= wrote;
    }

    err = close( fd );
    if( err < 0 ) SHOW_RESULT( close, err );
}

//==========================================================================

static void maxfile( char *name )
{
    char buf[IOSIZE];
    int fd;
    ssize_t wrote;
    int i;
    int err;
    size_t size = 0;
    size_t prevsize = 0;
    
    diag_printf("<INFO>: create maximal file %s\n",name);
    diag_printf("<INFO>: This may take a few minutes\n");

    err = access( name, F_OK );
    if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err );
    
    for( i = 0; i < IOSIZE; i++ ) buf[i] = i%256;
 
    fd = open( name, O_WRONLY|O_CREAT );
    if( fd < 0 ) SHOW_RESULT( open, fd );

    do
    {
        wrote = write( fd, buf, IOSIZE );
        //if( wrote < 0 ) SHOW_RESULT( write, wrote );        

        if( wrote >= 0 )
            size += wrote;

        if( (size-prevsize) > 100000 )
        {
            diag_printf("<INFO>: size = %zd \n", size);
            prevsize = size;
        }
        
    } while( wrote == IOSIZE );

    diag_printf("<INFO>: file size == %zd\n",size);

    err = close( fd );
    if( err < 0 ) SHOW_RESULT( close, err );
}

//==========================================================================

static void checkfile( char *name )
{
    char buf[IOSIZE];
    int fd;
    ssize_t done;
    int i;
    int err;
    off_t pos = 0;

    diag_printf("<INFO>: check file %s\n",name);
    
    err = access( name, F_OK );
    if( err != 0 ) SHOW_RESULT( access, err );

    fd = open( name, O_RDONLY );
    if( fd < 0 ) SHOW_RESULT( open, fd );

    for(;;)
    {
        done = read( fd, buf, IOSIZE );
        if( done < 0 ) SHOW_RESULT( read, (int)done );

        if( done == 0 ) break;

        for( i = 0; i < done; i++ )
            if( buf[i] != i%256 )
            {
                diag_printf("buf[%ld+%d](%02x) != %02x\n",pos,i,buf[i],i%256);
                CYG_TEST_FAIL("Data read not equal to data written\n");
            }
        
        pos += done;
    }

    err = close( fd );
    if( err < 0 ) SHOW_RESULT( close, err );
}

#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES
//==========================================================================

static void checkattrib(const char *name, 
                        const cyg_fs_attrib_t test_attrib )
{
    int err;
    cyg_fs_attrib_t file_attrib;

    diag_printf("<INFO>: check attrib %s\n",name);

    err = cyg_fs_get_attrib(name, &file_attrib);
    if( err != 0 ) SHOW_RESULT( stat, err );

    if ( (file_attrib & S_FATFS_ATTRIB) != test_attrib )
        diag_printf("<FAIL>: attrib %s incorrect\n\tExpected %x Was %x\n",
                    name,test_attrib,(file_attrib & S_FATFS_ATTRIB));
}
#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES

//==========================================================================

static void copyfile( char *name2, char *name1 )
{

    int err;
    char buf[IOSIZE];
    int fd1, fd2;
    ssize_t done, wrote;

    diag_printf("<INFO>: copy file %s -> %s\n",name2,name1);

    err = access( name1, F_OK );
    if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err );

    err = access( name2, F_OK );
    if( err != 0 ) SHOW_RESULT( access, err );
    
    fd1 = open( name1, O_WRONLY|O_CREAT );
    if( fd1 < 0 ) SHOW_RESULT( open, fd1 );

    fd2 = open( name2, O_RDONLY );
    if( fd2 < 0 ) SHOW_RESULT( open, fd2 );
    
    for(;;)
    {
        done = read( fd2, buf, IOSIZE );
        if( done < 0 ) SHOW_RESULT( read, (int)done );

        if( done == 0 ) break;

        wrote = write( fd1, buf, done );
        if( wrote != done ) SHOW_RESULT( write, (int) wrote );

        if( wrote != done ) break;
    }

    err = close( fd1 );
    if( err < 0 ) SHOW_RESULT( close, err );

    err = close( fd2 );
    if( err < 0 ) SHOW_RESULT( close, err );
    
}

//==========================================================================

static void comparefiles( char *name2, char *name1 )
{
    int err;
    char buf1[IOSIZE];
    char buf2[IOSIZE];
    int fd1, fd2;
    ssize_t done1, done2;
    int i;

    diag_printf("<INFO>: compare files %s == %s\n",name2,name1);

    err = access( name1, F_OK );
    if( err != 0 ) SHOW_RESULT( access, err );

    err = access( name1, F_OK );
    if( err != 0 ) SHOW_RESULT( access, err );
    
    fd1 = open( name1, O_RDONLY );
    if( fd1 < 0 ) SHOW_RESULT( open, fd1 );

    fd2 = open( name2, O_RDONLY );
    if( fd2 < 0 ) SHOW_RESULT( open, fd2 );
    
    for(;;)
    {
        done1 = read( fd1, buf1, IOSIZE );
        if( done1 < 0 ) SHOW_RESULT( read, (int)done1 );

        done2 = read( fd2, buf2, IOSIZE );
        if( done2 < 0 ) SHOW_RESULT( read, (int)done2 );

        if( done1 != done2 )
            diag_printf("Files different sizes\n");
        
        if( done1 == 0 ) break;

        for( i = 0; i < done1; i++ )
            if( buf1[i] != buf2[i] )
            {
                diag_printf("buf1[%d](%02x) != buf1[%d](%02x)\n",i,buf1[i],i,buf2[i]);
                CYG_TEST_FAIL("Data in files not equal\n");
            }
    }

    err = close( fd1 );
    if( err < 0 ) SHOW_RESULT( close, err );

    err = close( fd2 );
    if( err < 0 ) SHOW_RESULT( close, err );
    
}

//==========================================================================

void checkcwd( const char *cwd )
{
    static char cwdbuf[PATH_MAX];
    char *ret;

    ret = getcwd( cwdbuf, sizeof(cwdbuf));
    if( ret == NULL ) SHOW_RESULT( getcwd, (int)ret );    

    if( strcmp( cwdbuf, cwd ) != 0 )
    {
        diag_printf( "cwdbuf %s cwd %s\n",cwdbuf, cwd );
        CYG_TEST_FAIL( "Current directory mismatch");
    }
}

//==========================================================================
// main

int main( int argc, char **argv )
{
    int err;
    int existingdirents=-1;
#if defined(CYGSEM_FILEIO_BLOCK_USAGE)
    struct cyg_fs_block_usage usage;
#endif

    CYG_TEST_INIT();

    // --------------------------------------------------------------

    err = mount( CYGDAT_DEVS_DISK_TEST_DEVICE, "/", "fatfs" );    
    if( err < 0 ) SHOW_RESULT( mount, err );    

    err = chdir( "/" );
    if( err < 0 ) SHOW_RESULT( chdir, err );

    checkcwd( "/" );
    
    listdir( "/", true, -1, &existingdirents );

    // --------------------------------------------------------------
#if defined(CYGSEM_FILEIO_BLOCK_USAGE)
    err = cyg_fs_getinfo("/", FS_INFO_BLOCK_USAGE, &usage, sizeof(usage));
    if( err < 0 ) SHOW_RESULT( cyg_fs_getinfo, err );
    diag_printf("<INFO>: total size: %6lld blocks, %10lld bytes\n",
		usage.total_blocks, usage.total_blocks * usage.block_size); 
    diag_printf("<INFO>: free size:  %6lld blocks, %10lld bytes\n",
		usage.free_blocks, usage.free_blocks * usage.block_size); 
    diag_printf("<INFO>: block size: %6u bytes\n", usage.block_size);
#endif
    // --------------------------------------------------------------

    createfile( "/foo", 20257 );
    checkfile( "foo" );
    copyfile( "foo", "fee");
    checkfile( "fee" );
    comparefiles( "foo", "/fee" );
    diag_printf("<INFO>: mkdir bar\n");
    err = mkdir( "/bar", 0 );
    if( err < 0 ) SHOW_RESULT( mkdir, err );

    listdir( "/" , true, existingdirents+3, NULL );

    copyfile( "fee", "/bar/fum" );
    checkfile( "bar/fum" );
    comparefiles( "/fee", "bar/fum" );

    diag_printf("<INFO>: cd bar\n");
    err = chdir( "bar" );
    if( err < 0 ) SHOW_RESULT( chdir, err );

    checkcwd( "/bar" );
    
    diag_printf("<INFO>: rename /foo bundy\n");    
    err = rename( "/foo", "bundy" );
    if( err < 0 ) SHOW_RESULT( rename, err );
    
    listdir( "/", true, existingdirents+2, NULL );
    listdir( "" , true, 4, NULL );

    checkfile( "/bar/bundy" );
    comparefiles("/fee", "bundy" );

#if defined(CYGSEM_FILEIO_BLOCK_USAGE)
    err = cyg_fs_getinfo("/", FS_INFO_BLOCK_USAGE, &usage, sizeof(usage));
    if( err < 0 ) SHOW_RESULT( cyg_fs_getinfo, err );
    diag_printf("<INFO>: total size: %6lld blocks, %10lld bytes\n",
		usage.total_blocks, usage.total_blocks * usage.block_size); 
    diag_printf("<INFO>: free size:  %6lld blocks, %10lld bytes\n",
		usage.free_blocks, usage.free_blocks * usage.block_size); 
    diag_printf("<INFO>: block size: %6u bytes\n", usage.block_size);
#endif
    // --------------------------------------------------------------

    diag_printf("<INFO>: unlink fee\n");    
    err = unlink( "/fee" );
    if( err < 0 ) SHOW_RESULT( unlink, err );

    diag_printf("<INFO>: unlink fum\n");        
    err = unlink( "fum" );
    if( err < 0 ) SHOW_RESULT( unlink, err );

    diag_printf("<INFO>: unlink /bar/bundy\n");        
    err = unlink( "/bar/bundy" );
    if( err < 0 ) SHOW_RESULT( unlink, err );

    diag_printf("<INFO>: cd /\n");        
    err = chdir( "/" );
    if( err < 0 ) SHOW_RESULT( chdir, err );

    checkcwd( "/" );
    
    diag_printf("<INFO>: rmdir /bar\n");        
    err = rmdir( "/bar" );
    if( err < 0 ) SHOW_RESULT( rmdir, err );
    
    listdir( "/", false, existingdirents, NULL );

    // --------------------------------------------------------------

#if 0
    diag_printf("<INFO>: mkdir disk2\n");
    err = mkdir( "/disk2", 0 );
    if( err < 0 ) SHOW_RESULT( mkdir, err );
#else
    diag_printf("<INFO>: mount /disk2\n");    
    err = mount( CYGDAT_DEVS_DISK_TEST_DEVICE2, "/disk2", "fatfs" );    
    if( err < 0 ) SHOW_RESULT( mount, err );    
#endif
    
    listdir( "/disk2" , true, -1, &existingdirents);
        
    createfile( "/disk2/tinky", 4567 );
    copyfile( "/disk2/tinky", "/disk2/laalaa" );
    checkfile( "/disk2/tinky");
    checkfile( "/disk2/laalaa");
    comparefiles( "/disk2/tinky", "/disk2/laalaa" );

    diag_printf("<INFO>: cd /disk2\n");    
    err = chdir( "/disk2" );
    if( err < 0 ) SHOW_RESULT( chdir, err );

    checkcwd( "/disk2" );
        
    diag_printf("<INFO>: mkdir noonoo\n");    
    err = mkdir( "noonoo", 0 );
    if( err < 0 ) SHOW_RESULT( mkdir, err );

    listdir( "/disk2" , true, existingdirents+3, NULL);

    diag_printf("<INFO>: cd noonoo\n");
    err = chdir( "noonoo" );
    if( err < 0 ) SHOW_RESULT( chdir, err );

    checkcwd( "/disk2/noonoo" );
    
    createfile( "tinky", 6789 );
    checkfile( "tinky" );

    createfile( "dipsy", 34567 );
    checkfile( "dipsy" );
    copyfile( "dipsy", "po" );
    checkfile( "po" );
    comparefiles( "dipsy", "po" );

    listdir( ".", true, 5, NULL );
    listdir( "", true, 5, NULL );
    listdir( "..", true, existingdirents+3, NULL );

    // --------------------------------------------------------------

    diag_printf("<INFO>: unlink tinky\n");    
    err = unlink( "tinky" );
    if( err < 0 ) SHOW_RESULT( unlink, err );

    diag_printf("<INFO>: unlink dipsy\n");    
    err = unlink( "dipsy" );
    if( err < 0 ) SHOW_RESULT( unlink, err );

    diag_printf("<INFO>: unlink po\n");    
    err = unlink( "po" );
    if( err < 0 ) SHOW_RESULT( unlink, err );

    diag_printf("<INFO>: cd ..\n"); 
    err = chdir( ".." );
    if( err < 0 ) SHOW_RESULT( chdir, err );
    checkcwd( "/disk2" );
    
    diag_printf("<INFO>: rmdir noonoo\n"); 
    err = rmdir( "noonoo" );
    if( err < 0 ) SHOW_RESULT( rmdir, err );

    // --------------------------------------------------------------

    err = mkdir( "x", 0 );
    if( err < 0 ) SHOW_RESULT( mkdir, err );
    
    err = mkdir( "x/y", 0 );
    if( err < 0 ) SHOW_RESULT( mkdir, err );
    
    err = mkdir( "x/y/z", 0 );
    if( err < 0 ) SHOW_RESULT( mkdir, err );

    err = mkdir( "x/y/z/w", 0 );
    if( err < 0 ) SHOW_RESULT( mkdir, err );
    
    diag_printf("<INFO>: cd /disk2/x/y/z/w\n");
    err = chdir( "/disk2/x/y/z/w" );
    if( err < 0 ) SHOW_RESULT( chdir, err );
    checkcwd( "/disk2/x/y/z/w" );

    diag_printf("<INFO>: cd ..\n");
    err = chdir( ".." );
    if( err < 0 ) SHOW_RESULT( chdir, err );
    checkcwd( "/disk2/x/y/z" );
    
    diag_printf("<INFO>: cd .\n");
    err = chdir( "." );
    if( err < 0 ) SHOW_RESULT( chdir, err );
    checkcwd( "/disk2/x/y/z" );

    diag_printf("<INFO>: cd ../../y\n");
    err = chdir( "../../y" );
    if( err < 0 ) SHOW_RESULT( chdir, err );
    checkcwd( "/disk2/x/y" );

    diag_printf("<INFO>: cd ../..\n");
    err = chdir( "../.." );
    if( err < 0 ) SHOW_RESULT( chdir, err );
    checkcwd( "/disk2" );

    diag_printf("<INFO>: rmdir x/y/z/w\n"); 
    err = rmdir( "x/y/z/w" );
    if( err < 0 ) SHOW_RESULT( rmdir, err );

    diag_printf("<INFO>: rmdir x/y/z\n"); 
    err = rmdir( "x/y/z" );
    if( err < 0 ) SHOW_RESULT( rmdir, err );

    diag_printf("<INFO>: rmdir x/y\n"); 
    err = rmdir( "x/y" );
    if( err < 0 ) SHOW_RESULT( rmdir, err );

    diag_printf("<INFO>: rmdir x\n"); 
    err = rmdir( "x" );
    if( err < 0 ) SHOW_RESULT( rmdir, err );
    
    // --------------------------------------------------------------

    checkcwd( "/disk2" );
    
    diag_printf("<INFO>: unlink tinky\n");    
    err = unlink( "tinky" );
    if( err < 0 ) SHOW_RESULT( unlink, err );

    diag_printf("<INFO>: unlink laalaa\n");    
    err = unlink( "laalaa" );
    if( err < 0 ) SHOW_RESULT( unlink, err );

    diag_printf("<INFO>: cd /\n");    
    err = chdir( "/" );
    if( err < 0 ) SHOW_RESULT( chdir, err );
    checkcwd( "/" );

    listdir( "/disk2", true, -1, NULL );
    
#if 0
    diag_printf("<INFO>: rmdir dir\n"); 
    err = rmdir( "disk2" );
    if( err < 0 ) SHOW_RESULT( rmdir, err );
#else
    diag_printf("<INFO>: umount /disk2\n");    
    err = umount( "/disk2" );
    if( err < 0 ) SHOW_RESULT( umount, err );    
#endif
    
#ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES
    // Create file
    diag_printf("<INFO>: create /foo\n");
    createfile( "/foo", 20257 );

    // Verify it is created with archive bit set
    checkattrib( "/foo", S_FATFS_ARCHIVE );

    // Make it System
    diag_printf("<INFO>: attrib -A+S /foo\n");
    err = cyg_fs_set_attrib( "/foo", S_FATFS_SYSTEM );
    if( err < 0 ) SHOW_RESULT( chmod system , err );

    // Verify it is now System
    checkattrib( "/foo", S_FATFS_SYSTEM );

    // Make it Hidden
    diag_printf("<INFO>: attrib -S+H /foo\n");
    err = cyg_fs_set_attrib( "/foo", S_FATFS_HIDDEN );
    if( err < 0 ) SHOW_RESULT( chmod system , err );

    // Verify it is now Hidden
    checkattrib( "/foo", S_FATFS_HIDDEN );

    // Make it Read-only
    diag_printf("<INFO>: attrib -H+R /foo\n");
    err = cyg_fs_set_attrib( "/foo", S_FATFS_RDONLY );
    if( err < 0 ) SHOW_RESULT( chmod system , err );

    // Verify it is now Read-only
    checkattrib( "/foo", S_FATFS_RDONLY );

    // Verify we cannot unlink a read-only file
    diag_printf("<INFO>: unlink /foo\n");
    err = unlink( "/foo" );
    if( (err != -1) || (errno != EPERM) ) SHOW_RESULT( unlink, err );

    // Verify we cannot rename a read-only file
    diag_printf("<INFO>: rename /foo bundy\n");
    err = rename( "/foo", "bundy" );
    if( (err != -1) || (errno != EPERM) ) SHOW_RESULT( rename, err );

    // Verify we cannot open read-only file for writing
    int fd;
    diag_printf("<INFO>: create file /foo\n");
    fd = open( "/foo", O_WRONLY );
    if( (err != -1) || (errno != EACCES) ) SHOW_RESULT( open, err );
    if( err > 0 ) close(fd);

    // Make it Normal
    diag_printf("<INFO>: attrib -H /foo\n");
    err = cyg_fs_set_attrib( "/foo", 0 );
    if( err < 0 ) SHOW_RESULT( chmod none , err );

    // Verify it is now nothing
    checkattrib( "/foo", 0 );

    // Now delete our test file
    diag_printf("<INFO>: unlink /foo\n");
    err = unlink( "/foo" );
    if( err < 0 ) SHOW_RESULT( unlink, err );

#endif // CYGCFG_FS_FAT_USE_ATTRIBUTES

    maxfile("file.max");

    listdir( "/", true, -1, NULL );    
        
    diag_printf("<INFO>: unlink file.max\n");    
    err = unlink( "file.max" );
    if( err < 0 ) SHOW_RESULT( unlink, err );    
    diag_printf("<INFO>: umount /\n");    
    err = umount( "/" );
    if( err < 0 ) SHOW_RESULT( umount, err );    
    
    CYG_TEST_PASS_FINISH("fatfs1");
}

// -------------------------------------------------------------------------
// EOF fatfs1.c
# ls
We Are The Champions.mp3  fatfs1.c                  presentation_back.pdf
boot_record.txt           presentation.pdf
# cp We\ Are\ The\ Champions.mp3 We_gss.mp3
# 
# ls
We Are The Champions.mp3  boot_record.txt           presentation.pdf
We_gss.mp3                fatfs1.c                  presentation_back.pdf
# ls -l
total 9332
-rwxr-xr-x    1 root     0          3642233 Oct  9  2012 We Are The Champions.mp3
-rwxr-xr-x    1 root     0          3642233 Jan  1 00:03 We_gss.mp3
-rwxr-xr-x    1 root     0             8714 Jul 17  2013 boot_record.txt
-rwxr-xr-x    1 root     0            22843 Jun 24  2013 fatfs1.c
-rwxr-xr-x    1 root     0          1111309 Jun  2  2013 presentation.pdf
-rwxr-xr-x    1 root     0          1111309 Jan  1 00:01 presentation_back.pdf
# 


5,小结

通过这次努力,我们终于实现了ORPSoC中的SD控制器的linux驱动程序。至此,我们就可以在eCos和linux两个OS下自由的使用SD卡了。

enjoy!



6,附录 linux的配置

要想本驱动能work,首先要使用make menuconfig命令来配置linux的一些选项。

1>让linux支持块设备驱动

OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序_第1张图片


2>其次是配置linux的文件系统选项,支持VFAT文件系统。file system -> DOS/FAT/NT Filesystems,选择MSDOS fs support 和VFAT fs support。

OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序_第2张图片

OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序_第3张图片 


这里需要注意的是,在上图中提示VFAT需要437和iso8859-1两个字符集的支持,所以还要选择这两个字符集。


3>支持437和iso8859-1字符集

OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序_第4张图片

OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序_第5张图片

OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序_第6张图片


4>配置完成后,退出保存,直接make,编译内核。


OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序_第7张图片

OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序_第8张图片


5>完成以上内核配置工作,再编译驱动程序,就可以进行测试了。



你可能感兴趣的:(linux,sd,openrisc)