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

📄 libata-core.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  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 + -