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 + -
显示快捷键?