📄 libata-core.c
字号:
/* * libata-core.c - helper library for ATA * * Maintained by: Jeff Garzik <jgarzik@pobox.com> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * * Copyright 2003-2004 Red Hat, Inc. All rights reserved. * Copyright 2003-2004 Jeff Garzik * * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * * libata documentation is available via 'make {ps|pdf}docs', * as Documentation/DocBook/libata.* * * Hardware documentation available from http://www.t13.org/ and * http://www.sata-io.org/ * * Standards documents from: * http://www.t13.org (ATA standards, PCI DMA IDE spec) * http://www.t10.org (SCSI MMC - for ATAPI MMC) * http://www.sata-io.org (SATA) * http://www.compactflash.org (CF) * http://www.qic.org (QIC157 - Tape and DSC) * http://www.ce-ata.org (CE-ATA: not supported) * */#include <linux/kernel.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/list.h>#include <linux/mm.h>#include <linux/highmem.h>#include <linux/spinlock.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/completion.h>#include <linux/suspend.h>#include <linux/workqueue.h>#include <linux/jiffies.h>#include <linux/scatterlist.h>#include <linux/io.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_host.h>#include <linux/libata.h>#include <asm/semaphore.h>#include <asm/byteorder.h>#include <linux/cdrom.h>#include "libata.h"/* debounce timing parameters in msecs { interval, duration, timeout } */const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 };const unsigned long sata_deb_timing_hotplug[] = { 25, 500, 2000 };const unsigned long sata_deb_timing_long[] = { 100, 2000, 5000 };static unsigned int ata_dev_init_params(struct ata_device *dev, u16 heads, u16 sectors);static unsigned int ata_dev_set_xfermode(struct ata_device *dev);static unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature);static void ata_dev_xfermask(struct ata_device *dev);static unsigned long ata_dev_blacklisted(const struct ata_device *dev);unsigned int ata_print_id = 1;static struct workqueue_struct *ata_wq;struct workqueue_struct *ata_aux_wq;int atapi_enabled = 1;module_param(atapi_enabled, int, 0444);MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on)");int atapi_dmadir = 0;module_param(atapi_dmadir, int, 0444);MODULE_PARM_DESC(atapi_dmadir, "Enable ATAPI DMADIR bridge support (0=off, 1=on)");int atapi_passthru16 = 1;module_param(atapi_passthru16, int, 0444);MODULE_PARM_DESC(atapi_passthru16, "Enable ATA_16 passthru for ATAPI devices; on by default (0=off, 1=on)");int libata_fua = 0;module_param_named(fua, libata_fua, int, 0444);MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)");static int ata_ignore_hpa;module_param_named(ignore_hpa, ata_ignore_hpa, int, 0644);MODULE_PARM_DESC(ignore_hpa, "Ignore HPA limit (0=keep BIOS limits, 1=ignore limits, using full disk)");static int libata_dma_mask = ATA_DMA_MASK_ATA|ATA_DMA_MASK_ATAPI|ATA_DMA_MASK_CFA;module_param_named(dma, libata_dma_mask, int, 0444);MODULE_PARM_DESC(dma, "DMA enable/disable (0x1==ATA, 0x2==ATAPI, 0x4==CF)");static int ata_probe_timeout = ATA_TMOUT_INTERNAL / HZ;module_param(ata_probe_timeout, int, 0444);MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)");int libata_noacpi = 0;module_param_named(noacpi, libata_noacpi, int, 0444);MODULE_PARM_DESC(noacpi, "Disables the use of ACPI in probe/suspend/resume when set");MODULE_AUTHOR("Jeff Garzik");MODULE_DESCRIPTION("Library module for ATA devices");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_VERSION);/** * ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure * @tf: Taskfile to convert * @pmp: Port multiplier port * @is_cmd: This FIS is for command * @fis: Buffer into which data will output * * Converts a standard ATA taskfile to a Serial ATA * FIS structure (Register - Host to Device). * * LOCKING: * Inherited from caller. */void ata_tf_to_fis(const struct ata_taskfile *tf, u8 pmp, int is_cmd, u8 *fis){ fis[0] = 0x27; /* Register - Host to Device FIS */ fis[1] = pmp & 0xf; /* Port multiplier number*/ if (is_cmd) fis[1] |= (1 << 7); /* bit 7 indicates Command FIS */ fis[2] = tf->command; fis[3] = tf->feature; fis[4] = tf->lbal; fis[5] = tf->lbam; fis[6] = tf->lbah; fis[7] = tf->device; fis[8] = tf->hob_lbal; fis[9] = tf->hob_lbam; fis[10] = tf->hob_lbah; fis[11] = tf->hob_feature; fis[12] = tf->nsect; fis[13] = tf->hob_nsect; fis[14] = 0; fis[15] = tf->ctl; fis[16] = 0; fis[17] = 0; fis[18] = 0; fis[19] = 0;}/** * ata_tf_from_fis - Convert SATA FIS to ATA taskfile * @fis: Buffer from which data will be input * @tf: Taskfile to output * * Converts a serial ATA FIS structure to a standard ATA taskfile. * * LOCKING: * Inherited from caller. */void ata_tf_from_fis(const u8 *fis, struct ata_taskfile *tf){ tf->command = fis[2]; /* status */ tf->feature = fis[3]; /* error */ tf->lbal = fis[4]; tf->lbam = fis[5]; tf->lbah = fis[6]; tf->device = fis[7]; tf->hob_lbal = fis[8]; tf->hob_lbam = fis[9]; tf->hob_lbah = fis[10]; tf->nsect = fis[12]; tf->hob_nsect = fis[13];}static const u8 ata_rw_cmds[] = { /* pio multi */ ATA_CMD_READ_MULTI, ATA_CMD_WRITE_MULTI, ATA_CMD_READ_MULTI_EXT, ATA_CMD_WRITE_MULTI_EXT, 0, 0, 0, ATA_CMD_WRITE_MULTI_FUA_EXT, /* pio */ ATA_CMD_PIO_READ, ATA_CMD_PIO_WRITE, ATA_CMD_PIO_READ_EXT, ATA_CMD_PIO_WRITE_EXT, 0, 0, 0, 0, /* dma */ ATA_CMD_READ, ATA_CMD_WRITE, ATA_CMD_READ_EXT, ATA_CMD_WRITE_EXT, 0, 0, 0, ATA_CMD_WRITE_FUA_EXT};/** * ata_rwcmd_protocol - set taskfile r/w commands and protocol * @tf: command to examine and configure * @dev: device tf belongs to * * Examine the device configuration and tf->flags to calculate * the proper read/write commands and protocol to use. * * LOCKING: * caller. */static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev){ u8 cmd; int index, fua, lba48, write; fua = (tf->flags & ATA_TFLAG_FUA) ? 4 : 0; lba48 = (tf->flags & ATA_TFLAG_LBA48) ? 2 : 0; write = (tf->flags & ATA_TFLAG_WRITE) ? 1 : 0; if (dev->flags & ATA_DFLAG_PIO) { tf->protocol = ATA_PROT_PIO; index = dev->multi_count ? 0 : 8; } else if (lba48 && (dev->link->ap->flags & ATA_FLAG_PIO_LBA48)) { /* Unable to use DMA due to host limitation */ tf->protocol = ATA_PROT_PIO; index = dev->multi_count ? 0 : 8; } else { tf->protocol = ATA_PROT_DMA; index = 16; } cmd = ata_rw_cmds[index + fua + lba48 + write]; if (cmd) { tf->command = cmd; return 0; } return -1;}/** * ata_tf_read_block - Read block address from ATA taskfile * @tf: ATA taskfile of interest * @dev: ATA device @tf belongs to * * LOCKING: * None. * * Read block address from @tf. This function can handle all * three address formats - LBA, LBA48 and CHS. tf->protocol and * flags select the address format to use. * * RETURNS: * Block address read from @tf. */u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev){ u64 block = 0; if (tf->flags & ATA_TFLAG_LBA) { if (tf->flags & ATA_TFLAG_LBA48) { block |= (u64)tf->hob_lbah << 40; block |= (u64)tf->hob_lbam << 32; block |= tf->hob_lbal << 24; } else block |= (tf->device & 0xf) << 24; block |= tf->lbah << 16; block |= tf->lbam << 8; block |= tf->lbal; } else { u32 cyl, head, sect; cyl = tf->lbam | (tf->lbah << 8); head = tf->device & 0xf; sect = tf->lbal; block = (cyl * dev->heads + head) * dev->sectors + sect; } return block;}/** * ata_build_rw_tf - Build ATA taskfile for given read/write request * @tf: Target ATA taskfile * @dev: ATA device @tf belongs to * @block: Block address * @n_block: Number of blocks * @tf_flags: RW/FUA etc... * @tag: tag * * LOCKING: * None. * * Build ATA taskfile @tf for read/write request described by * @block, @n_block, @tf_flags and @tag on @dev. * * RETURNS: * * 0 on success, -ERANGE if the request is too large for @dev, * -EINVAL if the request is invalid. */int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, u64 block, u32 n_block, unsigned int tf_flags, unsigned int tag){ tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf->flags |= tf_flags; if (ata_ncq_enabled(dev) && likely(tag != ATA_TAG_INTERNAL)) { /* yay, NCQ */ if (!lba_48_ok(block, n_block)) return -ERANGE; tf->protocol = ATA_PROT_NCQ; tf->flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48; if (tf->flags & ATA_TFLAG_WRITE) tf->command = ATA_CMD_FPDMA_WRITE; else tf->command = ATA_CMD_FPDMA_READ; tf->nsect = tag << 3; tf->hob_feature = (n_block >> 8) & 0xff; tf->feature = n_block & 0xff; tf->hob_lbah = (block >> 40) & 0xff; tf->hob_lbam = (block >> 32) & 0xff; tf->hob_lbal = (block >> 24) & 0xff; tf->lbah = (block >> 16) & 0xff; tf->lbam = (block >> 8) & 0xff; tf->lbal = block & 0xff; tf->device = 1 << 6; if (tf->flags & ATA_TFLAG_FUA) tf->device |= 1 << 7; } else if (dev->flags & ATA_DFLAG_LBA) { tf->flags |= ATA_TFLAG_LBA; if (lba_28_ok(block, n_block)) { /* use LBA28 */ tf->device |= (block >> 24) & 0xf; } else if (lba_48_ok(block, n_block)) { if (!(dev->flags & ATA_DFLAG_LBA48)) return -ERANGE; /* use LBA48 */ tf->flags |= ATA_TFLAG_LBA48; tf->hob_nsect = (n_block >> 8) & 0xff; tf->hob_lbah = (block >> 40) & 0xff; tf->hob_lbam = (block >> 32) & 0xff; tf->hob_lbal = (block >> 24) & 0xff; } else /* request too large even for LBA48 */ return -ERANGE; if (unlikely(ata_rwcmd_protocol(tf, dev) < 0)) return -EINVAL; tf->nsect = n_block & 0xff; tf->lbah = (block >> 16) & 0xff; tf->lbam = (block >> 8) & 0xff; tf->lbal = block & 0xff; tf->device |= ATA_LBA; } else { /* CHS */ u32 sect, head, cyl, track; /* The request -may- be too large for CHS addressing. */ if (!lba_28_ok(block, n_block)) return -ERANGE; if (unlikely(ata_rwcmd_protocol(tf, dev) < 0)) return -EINVAL; /* Convert LBA to CHS */ track = (u32)block / dev->sectors; cyl = track / dev->heads; head = track % dev->heads; sect = (u32)block % dev->sectors + 1; DPRINTK("block %u track %u cyl %u head %u sect %u\n", (u32)block, track, cyl, head, sect); /* Check whether the converted CHS can fit. Cylinder: 0-65535 Head: 0-15 Sector: 1-255*/ if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect)) return -ERANGE; tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */ tf->lbal = sect; tf->lbam = cyl; tf->lbah = cyl >> 8; tf->device |= head; } return 0;}/** * ata_pack_xfermask - Pack pio, mwdma and udma masks into xfer_mask * @pio_mask: pio_mask * @mwdma_mask: mwdma_mask * @udma_mask: udma_mask * * Pack @pio_mask, @mwdma_mask and @udma_mask into a single * unsigned int xfer_mask. * * LOCKING: * None. * * RETURNS: * Packed xfer_mask. */static unsigned int ata_pack_xfermask(unsigned int pio_mask, unsigned int mwdma_mask, unsigned int udma_mask){ return ((pio_mask << ATA_SHIFT_PIO) & ATA_MASK_PIO) | ((mwdma_mask << ATA_SHIFT_MWDMA) & ATA_MASK_MWDMA) | ((udma_mask << ATA_SHIFT_UDMA) & ATA_MASK_UDMA);}/** * ata_unpack_xfermask - Unpack xfer_mask into pio, mwdma and udma masks * @xfer_mask: xfer_mask to unpack * @pio_mask: resulting pio_mask * @mwdma_mask: resulting mwdma_mask * @udma_mask: resulting udma_mask * * Unpack @xfer_mask into @pio_mask, @mwdma_mask and @udma_mask. * Any NULL distination masks will be ignored. */static void ata_unpack_xfermask(unsigned int xfer_mask, unsigned int *pio_mask, unsigned int *mwdma_mask, unsigned int *udma_mask){ if (pio_mask) *pio_mask = (xfer_mask & ATA_MASK_PIO) >> ATA_SHIFT_PIO; if (mwdma_mask) *mwdma_mask = (xfer_mask & ATA_MASK_MWDMA) >> ATA_SHIFT_MWDMA; if (udma_mask) *udma_mask = (xfer_mask & ATA_MASK_UDMA) >> ATA_SHIFT_UDMA;}static const struct ata_xfer_ent { int shift, bits; u8 base;} ata_xfer_tbl[] = { { ATA_SHIFT_PIO, ATA_BITS_PIO, XFER_PIO_0 }, { ATA_SHIFT_MWDMA, ATA_BITS_MWDMA, XFER_MW_DMA_0 }, { ATA_SHIFT_UDMA, ATA_BITS_UDMA, XFER_UDMA_0 }, { -1, },};/** * ata_xfer_mask2mode - Find matching XFER_* for the given xfer_mask * @xfer_mask: xfer_mask of interest * * Return matching XFER_* value for @xfer_mask. Only the highest * bit of @xfer_mask is considered. * * LOCKING: * None. * * RETURNS: * Matching XFER_* value, 0 if no match found. */static u8 ata_xfer_mask2mode(unsigned int xfer_mask){ int highbit = fls(xfer_mask) - 1; const struct ata_xfer_ent *ent; for (ent = ata_xfer_tbl; ent->shift >= 0; ent++) if (highbit >= ent->shift && highbit < ent->shift + ent->bits) return ent->base + highbit - ent->shift; return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -