📄 ide-io.c
字号:
/* * 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 + -