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

📄 ide-io.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *	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/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 <linux/scatterlist.h>#include <linux/bitops.h>#include <asm/byteorder.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/io.h>static int __ide_end_request(ide_drive_t *drive, struct request *rq,			     int uptodate, unsigned int nr_bytes, int dequeue){	int ret = 1;	/*	 * 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_bytes = rq->hard_nr_sectors << 9;	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_chunk(rq, uptodate, nr_bytes)) {		add_disk_randomness(rq->rq_disk);		if (dequeue) {			if (!list_empty(&rq->queuelist))				blkdev_dequeue_request(rq);			HWGROUP(drive)->rq = NULL;		}		end_that_request_last(rq, uptodate);		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){	unsigned int nr_bytes = nr_sectors << 9;	struct request *rq;	unsigned long flags;	int ret = 1;	/*	 * room for locking improvements here, the calls below don't	 * need the queue lock held at all	 */	spin_lock_irqsave(&ide_lock, flags);	rq = HWGROUP(drive)->rq;	if (!nr_bytes) {		if (blk_pc_request(rq))			nr_bytes = rq->data_len;		else			nr_bytes = rq->hard_cur_sectors << 9;	}	ret = __ide_end_request(drive, rq, uptodate, nr_bytes, 1);	spin_unlock_irqrestore(&ide_lock, flags);	return ret;}EXPORT_SYMBOL(ide_end_request);/* * Power Management state machine. This one is rather trivial for now, * we should probably add more, like switching back to PIO on suspend * to help some BIOSes, re-do the door locking on resume, etc... */enum {	ide_pm_flush_cache	= ide_pm_state_start_suspend,	idedisk_pm_standby,	idedisk_pm_restore_pio	= ide_pm_state_start_resume,	idedisk_pm_idle,	ide_pm_restore_dma,};static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 stat, u8 error){	struct request_pm_state *pm = rq->data;	if (drive->media != ide_disk)		return;	switch (pm->pm_step) {	case ide_pm_flush_cache:	/* Suspend step 1 (flush cache) complete */		if (pm->pm_state == PM_EVENT_FREEZE)			pm->pm_step = ide_pm_state_completed;		else			pm->pm_step = idedisk_pm_standby;		break;	case idedisk_pm_standby:	/* Suspend step 2 (standby) complete */		pm->pm_step = ide_pm_state_completed;		break;	case idedisk_pm_restore_pio:	/* Resume step 1 complete */		pm->pm_step = idedisk_pm_idle;		break;	case idedisk_pm_idle:		/* Resume step 2 (idle) complete */		pm->pm_step = ide_pm_restore_dma;		break;	}}static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq){	struct request_pm_state *pm = rq->data;	ide_task_t *args = rq->special;	memset(args, 0, sizeof(*args));	switch (pm->pm_step) {	case ide_pm_flush_cache:	/* Suspend step 1 (flush cache) */		if (drive->media != ide_disk)			break;		/* Not supported? Switch to next step now. */		if (!drive->wcache || !ide_id_has_flush_cache(drive->id)) {			ide_complete_power_step(drive, rq, 0, 0);			return ide_stopped;		}		if (ide_id_has_flush_cache_ext(drive->id))			args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE_EXT;		else			args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE;		args->command_type = IDE_DRIVE_TASK_NO_DATA;		args->handler	   = &task_no_data_intr;		return do_rw_taskfile(drive, args);	case idedisk_pm_standby:	/* Suspend step 2 (standby) */		args->tfRegister[IDE_COMMAND_OFFSET] = WIN_STANDBYNOW1;		args->command_type = IDE_DRIVE_TASK_NO_DATA;		args->handler	   = &task_no_data_intr;		return do_rw_taskfile(drive, args);	case idedisk_pm_restore_pio:	/* Resume step 1 (restore PIO) */		ide_set_max_pio(drive);		/*		 * skip idedisk_pm_idle for ATAPI devices		 */		if (drive->media != ide_disk)			pm->pm_step = ide_pm_restore_dma;		else			ide_complete_power_step(drive, rq, 0, 0);		return ide_stopped;	case idedisk_pm_idle:		/* Resume step 2 (idle) */		args->tfRegister[IDE_COMMAND_OFFSET] = WIN_IDLEIMMEDIATE;		args->command_type = IDE_DRIVE_TASK_NO_DATA;		args->handler = task_no_data_intr;		return do_rw_taskfile(drive, args);	case ide_pm_restore_dma:	/* Resume step 3 (restore DMA) */		/*		 * Right now, all we do is call ide_set_dma(drive),		 * we could be smarter and check for current xfer_speed		 * in struct drive etc...		 */		if (drive->hwif->ide_dma_on == NULL)			break;		drive->hwif->dma_off_quietly(drive);		/*		 * TODO: respect ->using_dma setting		 */		ide_set_dma(drive);		break;	}	pm->pm_step = ide_pm_state_completed;	return ide_stopped;}/** *	ide_end_dequeued_request	-	complete an IDE I/O *	@drive: IDE device for the I/O *	@uptodate: *	@nr_sectors: number of sectors completed * *	Complete an I/O that is no longer on the request queue. This *	typically occurs when we pull the request and issue a REQUEST_SENSE. *	We must still finish the old request but we must not tamper with the *	queue in the meantime. * *	NOTE: This path does not handle barrier, but barrier is not supported *	on ide-cd anyway. */int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq,			     int uptodate, int nr_sectors){	unsigned long flags;	int ret;	spin_lock_irqsave(&ide_lock, flags);	BUG_ON(!blk_rq_started(rq));	ret = __ide_end_request(drive, rq, uptodate, nr_sectors << 9, 0);	spin_unlock_irqrestore(&ide_lock, flags);	return ret;}EXPORT_SYMBOL_GPL(ide_end_dequeued_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, 1);	spin_unlock_irqrestore(&ide_lock, flags);}/** *	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->cmd_type == REQ_TYPE_ATA_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->cmd_type == REQ_TYPE_ATA_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;			/* be sure we're looking at the low order bits */			hwif->OUTB(drive->ctl & ~0x80, IDE_CONTROL_REG);			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->cmd_type == REQ_TYPE_ATA_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);				args->hobRegister[IDE_HCYL_OFFSET]	= hwif->INB(IDE_HCYL_REG);			}		}	} else if (blk_pm_request(rq)) {		struct request_pm_state *pm = rq->data;#ifdef DEBUG_PM		printk("%s: complete_power_step(step: %d, stat: %x, err: %x)\n",			drive->name, rq->pm->pm_step, stat, err);#endif		ide_complete_power_step(drive, rq, stat, err);		if (pm->pm_step == ide_pm_state_completed)			ide_complete_pm_request(drive, rq);		return;	}	spin_lock_irqsave(&ide_lock, flags);	blkdev_dequeue_request(rq);	HWGROUP(drive)->rq = NULL;	rq->errors = err;	end_that_request_last(rq, !rq->errors);	spin_unlock_irqrestore(&ide_lock, flags);}EXPORT_SYMBOL(ide_end_drive_cmd);/** *	try_to_flush_leftover_data	-	flush junk *	@drive: drive to flush * *	try_to_flush_leftover_data() is invoked in response to a drive *	unexpectedly having its DRQ_STAT bit set.  As an alternative to *	resetting the drive, this routine tries to clear the condition *	by read a sector's worth of data from the drive.  Of course, *	this may not help if the drive is *waiting* for data from *us*. */static void try_to_flush_leftover_data (ide_drive_t *drive){	int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;	if (drive->media != ide_disk)		return;	while (i > 0) {		u32 buffer[16];		u32 wcount = (i > 16) ? 16 : i;		i -= wcount;		HWIF(drive)->ata_input_data(drive, buffer, wcount);	}}static void ide_kill_rq(ide_drive_t *drive, struct request *rq){	if (rq->rq_disk) {		ide_driver_t *drv;		drv = *(ide_driver_t **)rq->rq_disk->private_data;		drv->end_request(drive, 0, 0);	} else		ide_end_request(drive, 0, 0);}static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err){	ide_hwif_t *hwif = drive->hwif;	if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) {		/* other bits are useless when BUSY */		rq->errors |= ERROR_RESET;	} else if (stat & ERR_STAT) {		/* err has different meaning on cdrom and tape */

⌨️ 快捷键说明

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