ide-io.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,604 行 · 第 1/4 页

C
1,604
字号
/* *	IDE I/O functions * *	Basic PIO and command management functionality. * * This code was split off from ide.c. See ide.c for history and original * copyrights. * * 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, 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. * * For the avoidance of doubt the "preferred form" of this code is one which * is in an open non patent encumbered format. Where cryptographic key signing * forms part of the process of creating an executable the information * including keys needed to generate an equivalently functional executable * are deemed to be part of the source code. */  #include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/major.h>#include <linux/errno.h>#include <linux/genhd.h>#include <linux/blkpg.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/ide.h>#include <linux/completion.h>#include <linux/reboot.h>#include <linux/cdrom.h>#include <linux/seq_file.h>#include <linux/device.h>#include <linux/kmod.h>#include <asm/byteorder.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/bitops.h>static void ide_fill_flush_cmd(ide_drive_t *drive, struct request *rq){	char *buf = rq->cmd;	/*	 * reuse cdb space for ata command	 */	memset(buf, 0, sizeof(rq->cmd));	rq->flags |= REQ_DRIVE_TASK | REQ_STARTED;	rq->buffer = buf;	rq->buffer[0] = WIN_FLUSH_CACHE;	if (ide_id_has_flush_cache_ext(drive->id) &&	    (drive->capacity64 >= (1UL << 28)))		rq->buffer[0] = WIN_FLUSH_CACHE_EXT;}/* * preempt pending requests, and store this cache flush for immediate * execution */static struct request *ide_queue_flush_cmd(ide_drive_t *drive,					   struct request *rq, int post){	struct request *flush_rq = &HWGROUP(drive)->wrq;	/*	 * write cache disabled, clear the barrier bit and treat it like	 * an ordinary write	 */	if (!drive->wcache) {		rq->flags |= REQ_BAR_PREFLUSH;		return rq;	}	ide_init_drive_cmd(flush_rq);	ide_fill_flush_cmd(drive, flush_rq);	flush_rq->special = rq;	flush_rq->nr_sectors = rq->nr_sectors;	if (!post) {		drive->doing_barrier = 1;		flush_rq->flags |= REQ_BAR_PREFLUSH;		blkdev_dequeue_request(rq);	} else		flush_rq->flags |= REQ_BAR_POSTFLUSH;	__elv_add_request(drive->queue, flush_rq, ELEVATOR_INSERT_FRONT, 0);	HWGROUP(drive)->rq = NULL;	return flush_rq;}static int __ide_end_request(ide_drive_t *drive, struct request *rq,			     int uptodate, int nr_sectors){	int ret = 1;	BUG_ON(!(rq->flags & REQ_STARTED));	/*	 * if failfast is set on a request, override number of sectors and	 * complete the whole request right now	 */	if (blk_noretry_request(rq) && end_io_error(uptodate))		nr_sectors = rq->hard_nr_sectors;	if (!blk_fs_request(rq) && end_io_error(uptodate) && !rq->errors)		rq->errors = -EIO;	/*	 * decide whether to reenable DMA -- 3 is a random magic for now,	 * if we DMA timeout more than 3 times, just stay in PIO	 */	if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) {		drive->state = 0;		HWGROUP(drive)->hwif->ide_dma_on(drive);	}	if (!end_that_request_first(rq, uptodate, nr_sectors)) {		add_disk_randomness(rq->rq_disk);		if (blk_rq_tagged(rq))			blk_queue_end_tag(drive->queue, rq);		blkdev_dequeue_request(rq);		HWGROUP(drive)->rq = NULL;		end_that_request_last(rq);		ret = 0;	}	return ret;}/** *	ide_end_request		-	complete an IDE I/O *	@drive: IDE device for the I/O *	@uptodate: *	@nr_sectors: number of sectors completed * *	This is our end_request wrapper function. We complete the I/O *	update random number input and dequeue the request, which if *	it was tagged may be out of order. */int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors){	struct request *rq;	unsigned long flags;	int ret = 1;	spin_lock_irqsave(&ide_lock, flags);	rq = HWGROUP(drive)->rq;	if (!nr_sectors)		nr_sectors = rq->hard_cur_sectors;	if (!blk_barrier_rq(rq) || !drive->wcache)		ret = __ide_end_request(drive, rq, uptodate, nr_sectors);	else {		struct request *flush_rq = &HWGROUP(drive)->wrq;		flush_rq->nr_sectors -= nr_sectors;		if (!flush_rq->nr_sectors) {			ide_queue_flush_cmd(drive, rq, 1);			ret = 0;		}	}	spin_unlock_irqrestore(&ide_lock, flags);	return ret;}EXPORT_SYMBOL(ide_end_request);/** *	ide_complete_pm_request - end the current Power Management request *	@drive: target drive *	@rq: request * *	This function cleans up the current PM request and stops the queue *	if necessary. */static void ide_complete_pm_request (ide_drive_t *drive, struct request *rq){	unsigned long flags;#ifdef DEBUG_PM	printk("%s: completing PM request, %s\n", drive->name,	       blk_pm_suspend_request(rq) ? "suspend" : "resume");#endif	spin_lock_irqsave(&ide_lock, flags);	if (blk_pm_suspend_request(rq)) {		blk_stop_queue(drive->queue);	} else {		drive->blocked = 0;		blk_start_queue(drive->queue);	}	blkdev_dequeue_request(rq);	HWGROUP(drive)->rq = NULL;	end_that_request_last(rq);	spin_unlock_irqrestore(&ide_lock, flags);}/* * FIXME: probably move this somewhere else, name is bad too :) */u64 ide_get_error_location(ide_drive_t *drive, char *args){	u32 high, low;	u8 hcyl, lcyl, sect;	u64 sector;	high = 0;	hcyl = args[5];	lcyl = args[4];	sect = args[3];	if (ide_id_has_flush_cache_ext(drive->id)) {		low = (hcyl << 16) | (lcyl << 8) | sect;		HWIF(drive)->OUTB(drive->ctl|0x80, IDE_CONTROL_REG);		high = ide_read_24(drive);	} else {		u8 cur = HWIF(drive)->INB(IDE_SELECT_REG);		if (cur & 0x40)			low = (hcyl << 16) | (lcyl << 8) | sect;		else {			low = hcyl * drive->head * drive->sect;			low += lcyl * drive->sect;			low += sect - 1;		}	}	sector = ((u64) high << 24) | low;	return sector;}EXPORT_SYMBOL(ide_get_error_location);static void ide_complete_barrier(ide_drive_t *drive, struct request *rq,				 int error){	struct request *real_rq = rq->special;	int good_sectors, bad_sectors;	sector_t sector;	if (!error) {		if (blk_barrier_postflush(rq)) {			/*			 * this completes the barrier write			 */			__ide_end_request(drive, real_rq, 1, real_rq->hard_nr_sectors);			drive->doing_barrier = 0;		} else {			/*			 * just indicate that we did the pre flush			 */			real_rq->flags |= REQ_BAR_PREFLUSH;			elv_requeue_request(drive->queue, real_rq);		}		/*		 * all is fine, return		 */		return;	}	/*	 * we need to end real_rq, but it's not on the queue currently.	 * put it back on the queue, so we don't have to special case	 * anything else for completing it	 */	if (!blk_barrier_postflush(rq))		elv_requeue_request(drive->queue, real_rq);	/*	 * drive aborted flush command, assume FLUSH_CACHE_* doesn't	 * work and disable barrier support	 */	if (error & ABRT_ERR) {		printk(KERN_ERR "%s: barrier support doesn't work\n", drive->name);		__ide_end_request(drive, real_rq, -EOPNOTSUPP, real_rq->hard_nr_sectors);		blk_queue_ordered(drive->queue, 0);		blk_queue_issue_flush_fn(drive->queue, NULL);	} else {		/*		 * find out what part of the request failed		 */		good_sectors = 0;		if (blk_barrier_postflush(rq)) {			sector = ide_get_error_location(drive, rq->buffer);			if ((sector >= real_rq->hard_sector) &&			    (sector < real_rq->hard_sector + real_rq->hard_nr_sectors))				good_sectors = sector - real_rq->hard_sector;		} else			sector = real_rq->hard_sector;		bad_sectors = real_rq->hard_nr_sectors - good_sectors;		if (good_sectors)			__ide_end_request(drive, real_rq, 1, good_sectors);		if (bad_sectors)			__ide_end_request(drive, real_rq, 0, bad_sectors);		printk(KERN_ERR "%s: failed barrier write: "				"sector=%Lx(good=%d/bad=%d)\n",				drive->name, (unsigned long long)sector,				good_sectors, bad_sectors);	}	drive->doing_barrier = 0;}/** *	ide_end_drive_cmd	-	end an explicit drive command *	@drive: command  *	@stat: status bits *	@err: error bits * *	Clean up after success/failure of an explicit drive command. *	These get thrown onto the queue so they are synchronized with *	real I/O operations on the drive. * *	In LBA48 mode we have to read the register set twice to get *	all the extra information out. */ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err){	ide_hwif_t *hwif = HWIF(drive);	unsigned long flags;	struct request *rq;	spin_lock_irqsave(&ide_lock, flags);	rq = HWGROUP(drive)->rq;	spin_unlock_irqrestore(&ide_lock, flags);	if (rq->flags & REQ_DRIVE_CMD) {		u8 *args = (u8 *) rq->buffer;		if (rq->errors == 0)			rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);		if (args) {			args[0] = stat;			args[1] = err;			args[2] = hwif->INB(IDE_NSECTOR_REG);		}	} else if (rq->flags & REQ_DRIVE_TASK) {		u8 *args = (u8 *) rq->buffer;		if (rq->errors == 0)			rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);		if (args) {			args[0] = stat;			args[1] = err;			args[2] = hwif->INB(IDE_NSECTOR_REG);			args[3] = hwif->INB(IDE_SECTOR_REG);			args[4] = hwif->INB(IDE_LCYL_REG);			args[5] = hwif->INB(IDE_HCYL_REG);			args[6] = hwif->INB(IDE_SELECT_REG);		}	} else if (rq->flags & REQ_DRIVE_TASKFILE) {		ide_task_t *args = (ide_task_t *) rq->special;		if (rq->errors == 0)			rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);					if (args) {			if (args->tf_in_flags.b.data) {				u16 data				= hwif->INW(IDE_DATA_REG);				args->tfRegister[IDE_DATA_OFFSET]	= (data) & 0xFF;				args->hobRegister[IDE_DATA_OFFSET]	= (data >> 8) & 0xFF;			}			args->tfRegister[IDE_ERROR_OFFSET]   = err;			/* be sure we're looking at the low order bits */			hwif->OUTB(drive->ctl & ~0x80, IDE_CONTROL_REG);			args->tfRegister[IDE_NSECTOR_OFFSET] = hwif->INB(IDE_NSECTOR_REG);			args->tfRegister[IDE_SECTOR_OFFSET]  = hwif->INB(IDE_SECTOR_REG);			args->tfRegister[IDE_LCYL_OFFSET]    = hwif->INB(IDE_LCYL_REG);			args->tfRegister[IDE_HCYL_OFFSET]    = hwif->INB(IDE_HCYL_REG);			args->tfRegister[IDE_SELECT_OFFSET]  = hwif->INB(IDE_SELECT_REG);			args->tfRegister[IDE_STATUS_OFFSET]  = stat;			if (drive->addressing == 1) {				hwif->OUTB(drive->ctl|0x80, IDE_CONTROL_REG);				args->hobRegister[IDE_FEATURE_OFFSET]	= hwif->INB(IDE_FEATURE_REG);				args->hobRegister[IDE_NSECTOR_OFFSET]	= hwif->INB(IDE_NSECTOR_REG);				args->hobRegister[IDE_SECTOR_OFFSET]	= hwif->INB(IDE_SECTOR_REG);				args->hobRegister[IDE_LCYL_OFFSET]	= hwif->INB(IDE_LCYL_REG);

⌨️ 快捷键说明

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