linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFR] a new SCSI driver
@ 2003-05-24 19:51 Jeff Garzik
  2003-05-26  2:59 ` Douglas Gilbert
                   ` (2 more replies)
  0 siblings, 3 replies; 18+ messages in thread
From: Jeff Garzik @ 2003-05-24 19:51 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-scsi



Hi all.

My little project is now ready to face the masses.  Put simply, this
driver allows one to present ATA (and soon ATAPI) devices through
the Linux SCSI layer.

Since we already have an ATA (a.k.a. IDE) driver, I suppose the main
question on a lot of people's minds is "why?"  So I present some quick
notes, in bullet form, that can form the basis of discussion.

Review is requested by all interested:  driver authors, SCSI mavens,
ATA mavens, whatever.

I request that people try to avoid this posting devolving into an
ata-versus-scsi flamewar.  Maybe I can defuse that before the fact, by
saying:  "when in doubt, use drivers/ide."

So let's begin...  comments and questions welcome!


Why SCSI?
---------

* Many of the advantages are derived the existence of the scsi
  mid-layer.  It does a lot of work on our behalf, allowing me
  to focus on the ATA command protocols (PIO-in, PIO-out, DMA,
  etc.) almost exclusively.

* The SCSI mid-layer has shrunk and benefitted a lot from Axboe's
  block layer work.  It's much more lightweight in 2.5.x.

* Serial ATA is looming quickly on the horizon.  Both device and host
  controller SATA implementations really lend themselves to behaviors
  that have existed in SCSI for a while.  SATA even defines use of SCSI
  Enclosure Services.

* The Linux SCSI layer handles hotplugging, and is more modular.
  It already has refcounted devices and sysfs and such.  Creating a
  new block device driver from scratch means handling all those
  little details.

* SCSI has been doing basic error recovery and queue control for a
  while now.  Upcoming SATA2 will benefit greatly from this, as well
  ATA TCQ if I ever get around to implementing the latter.

* ATAPI is SCSI-like.

* Note that PATA in my driver is only an afterthought.  The main
  area of focus, now and in the future, is SATA.  It has strict
  PATA device and host controller restrictions: must do UDMA, LBA,
  be at least ATA-3, etc.  See "when in doubt..." statement above :)


Build notes
-----------

* Patch below requires you move drivers/scsi/{scsi,hosts,scsi_obsolete}.h
  to include/linux/scsi_{defs,hosts,obsolete}.h in order to build.
  These changes are present in the FTP site patches and BK repos, but
  are not included below to save space.  They are obvious changes that
  can even be recreated by hand.

* You should disable CONFIG_IDE.  Both drivers should request_region
  properly, but if you're not careful the IDE driver will grab the
  hardware before my driver does.


Testing status
--------------

* SATA stressed with iozone, bonnie, dbench, and some proprietary stress
  tools, on multiple boxes.

* PATA stressed similarly, on one box.


Near-future directions
----------------------

* ATAPI (see below)

* libata.c DMA and taskfile handling is still host-controller specific.
  It's the most widely used host controller standard, sure.  But that
  mainly applies to PATA devices.

  Future host controllers, with a tiny additional bit of
  abstracting-out, will simply ignore these functions (provided as
  defaults for most host controllers) and use their own.

* Better error handling (see below), and more command queueing work


ATA notes
---------

* Supports max UDMA/33 for PATA right now.  Temporary limitation because
  I'm too slack to worry about cable detection.

* No packet command -- yet.  And thus, no ATAPI cdroms/burners/etc.
  Coming soon!

* Does polling PIO in a kthread.  Watch katad chew up CPU.

ATA hardware notes
------------------

* Intentionally concentrated on modern hardware:  mainly SATA,
  with a little bit of PATA thrown in when convenient.

* Only supports Intel PATA and SATA right now

* Code is structured such that DMA engine and command queueing engine
  can be replaced by different hardware.  Next hardware driver will
  demonstrate this.


SCSI protocol notes
-------------------

* claims compliance with latest scsi3 standards (sam3, spc3, sbc2).
  bug reports in spec compliance are welcome.


Resources
---------

* BitKeeper for 2.4.x and 2.5.x:

	bk://kernel.bkbits.net/jgarzik/atascsi-2.4
	bk://kernel.bkbits.net/jgarzik/atascsi-2.5

* Patchkits for 2.4.x and 2.5.x:

ftp://ftp.kernel.org/pub/linux/kernel/people/jgarzik/patchkits/2.4/2.4.21-rc3-atascsi1.patch.bz2
ftp://ftp.kernel.org/pub/linux/kernel/people/jgarzik/patchkits/2.5/2.5.69-bk17-atascsi1.patch.bz2


Open issues (currently being addressed)
---------------------------------------

* ATAPI.  FWIW, yes, I do know that ATAPI is "SCSI-like" and not 100%
  conformant SCSI.

* Error handling.  VERY primitive right now.  Handling of errors is
  often "we'll stop talking to the device forever".  Hey, it's better than
  data corruption.  :)


Kudos (in alpha order for fairness :))
--------------------------------------

* Jens Axboe, for tons of block layer knowledge and general reality
  checks.

* James Bottomley, for patiently answering a lot of my SCSI questions.

* Alan Cox, for key inspirational ideas.

* Andre Hedrick, for tons of ATA knowledge.  I've soaked up a ton of
  time talking ATA with him, for which I'll be forever grateful.

* Lots of people on IRC and linux-scsi for helpful answers and
  suggestions.

* myself, for progressing quite a bit on one of my Big Projects To Do
  Before I Die.





drivers/scsi/Kconfig    |   27 
drivers/scsi/Makefile   |    1 
drivers/scsi/ata_piix.c |  312 ++++++
drivers/scsi/libata.c   | 2239 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/ata.h     |  483 ++++++++++
5 files changed, 3062 insertions(+)




diff -Nru a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
--- a/drivers/scsi/Kconfig	Sat May 24 14:36:45 2003
+++ b/drivers/scsi/Kconfig	Sat May 24 14:36:45 2003
@@ -400,6 +400,33 @@
 	  say M here and read <file:Documentation/modules.txt>.  The module
 	  will be called megaraid.
 
+config SCSI_ATA
+	bool "ATA support (EXPERIMENTAL)"
+	depends on SCSI && EXPERIMENTAL
+	help
+	  This driver family supports ATA host controllers and devices
+	  (a.k.a. IDE drives).
+
+	  If unsure, say N.
+
+config SCSI_ATA_PATA
+	bool "Parallel ATA support (EXPERIMENTAL)"
+	depends on SCSI_ATA && EXPERIMENTAL
+	help
+	  This option enables support for parallel ATA host controllers.
+
+	  If unsure, say N.
+
+config SCSI_ATA_PIIX
+	tristate "Intel PIIX/ICH PATA/SATA support"
+	depends on SCSI_ATA && PCI
+	help
+	  This option enables support for ICH5 Serial ATA.
+	  If PATA support was enabled previously, this enables
+	  support for select Intel PIIX/ICH PATA host controllers.
+
+	  If unsure, say N.
+
 config SCSI_BUSLOGIC
 	tristate "BusLogic SCSI support"
 	depends on SCSI
diff -Nru a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile	Sat May 24 14:36:45 2003
+++ b/drivers/scsi/Makefile	Sat May 24 14:36:45 2003
@@ -113,6 +113,7 @@
 obj-$(CONFIG_SCSI_CPQFCTS)	+= cpqfc.o
 obj-$(CONFIG_SCSI_LASI700)	+= lasi700.o 53c700.o
 obj-$(CONFIG_SCSI_NSP32)	+= nsp32.o
+obj-$(CONFIG_SCSI_ATA_PIIX)	+= ata_piix.o libata.o
 
 obj-$(CONFIG_ARM)		+= arm/
 
diff -Nru a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/ata_piix.c	Sat May 24 14:36:45 2003
@@ -0,0 +1,312 @@
+/*
+	Copyright 2003 Red Hat Inc
+	Copyright 2003 Jeff Garzik
+
+
+	Copyright header from piix.c:
+
+    Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
+    Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
+    Copyright (C) 2003 Red Hat Inc <alan@redhat.com>
+
+    May be copied or modified under the terms of the GNU General Public License
+
+
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ata.h>
+
+#define DRV_NAME	"ata_piix"
+#define DRV_VERSION	"0.9 "	/* must be exactly four chars */
+
+enum {
+	ICH5_PCS		= 0x92,	/* port control and status */
+
+	ich5_pata		= 0,
+	ich5_sata		= 1,
+	piix4_pata		= 2,
+};
+
+static int __devinit piix_init_one (struct pci_dev *pdev,
+				    const struct pci_device_id *ent);
+static void piix_port_probe(struct ata_host *ah);
+static void piix_port_disable(struct ata_host *ah);
+static void piix_set_piomode (struct ata_host *ah, struct ata_device *adev,
+			      unsigned int pio);
+static void piix_set_udmamode (struct ata_host *ah, struct ata_device *adev,
+			       unsigned int udma);
+
+static unsigned int in_module_init = 1;
+
+static struct pci_device_id piix_pci_tbl[] __devinitdata = {
+	{ 0x8086, 0x7111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix4_pata },
+	{ 0x8086, 0x24db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata },
+
+	{ 0x8086, 0x24d1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata },
+
+	{ }	/* terminate list */
+};
+
+struct pci_driver piix_pci_driver = {
+	.name			= DRV_NAME,
+	.id_table		= piix_pci_tbl,
+	.probe			= piix_init_one,
+	.remove			= ata_pci_remove_one,
+};
+
+static Scsi_Host_Template piix_sht = {
+	.module			= THIS_MODULE,
+	.name			= DRV_NAME,
+	.detect			= ata_scsi_detect,
+	.release		= ata_scsi_release,
+	.queuecommand		= ata_scsi_queuecmd,
+	.eh_strategy_handler	= ata_scsi_error,
+	.can_queue		= ATA_DEF_QUEUE,
+	.this_id		= ATA_SHT_THIS_ID,
+	.sg_tablesize		= ATA_MAX_PRD,
+	.max_sectors		= ATA_MAX_SECTORS,
+	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
+	.emulated		= ATA_SHT_EMULATED,
+	.use_clustering		= ATA_SHT_USE_CLUSTERING,
+	.proc_name		= DRV_NAME,
+};
+
+struct ata_board ata_board_tbl[] __devinitdata = {
+	/* ich5_pata */
+	{
+		.sht		= &piix_sht,
+		.pio_mask	= 0x03,	/* pio3-4 */
+		.udma_mask	= 0x07,	/* udma0-2 ; FIXME */
+	},
+
+	/* ich5_sata */
+	{
+		.sht		= &piix_sht,
+		.host_flags	= ATA_FLAG_SATA,
+		.pio_mask	= 0x03,	/* pio3-4 */
+		.udma_mask	= 0x3f,	/* udma0-5 ; FIXME */
+	},
+
+	/* piix4_pata */
+	{
+		.sht		= &piix_sht,
+		.pio_mask	= 0x03, /* pio3-4 */
+		.udma_mask	= 0x07,	/* udma0-2 ; FIXME */
+	},
+};
+
+struct ata_host_ops piix_pata_ops = {
+	.port_probe		= ata_port_probe,
+	.port_disable		= ata_port_disable,
+	.set_piomode		= piix_set_piomode,
+	.set_udmamode		= piix_set_udmamode,
+};
+
+struct ata_host_ops piix_sata_ops = {
+	.port_probe		= piix_port_probe,
+	.port_disable		= piix_port_disable,
+	.set_piomode		= piix_set_piomode,
+	.set_udmamode		= piix_set_udmamode,
+};
+
+MODULE_AUTHOR("Andre Hedrick, Alan Cox, Andrzej Krzysztofowicz, Jeff Garzik");
+MODULE_DESCRIPTION("SCSI low-level driver for Intel PIIX/ICH ATA controllers");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, piix_pci_tbl);
+
+static void piix_port_probe(struct ata_host *ah)
+{
+	u16 pcs;
+	struct pci_dev *pdev = ah->host_set->pdev;
+
+	pci_read_config_word(pdev, ICH5_PCS, &pcs);
+
+	/* if port not enabled, exit */
+	if ((pcs & (1 << ah->port_no)) == 0) {
+		ata_port_disable(ah);
+		DPRINTK("port %d disabled, ignoring\n", ah->port_no);
+		return;
+	}
+
+	/* if port enabled but no device, disable port and exit */
+	if ((pcs & (1 << (ah->port_no + 4))) == 0) {
+		pcs &= ~(1 << ah->port_no);
+		pci_write_config_word(pdev, ICH5_PCS, pcs);
+
+		ata_port_disable(ah);
+		DPRINTK("port %d has no dev, disabling\n", ah->port_no);
+		return;
+	}
+
+	ata_port_probe(ah);
+}
+
+static void piix_port_disable(struct ata_host *ah)
+{
+	struct pci_dev *pdev = ah->host_set->pdev;
+	u16 pcs;
+
+	ata_port_disable(ah);
+
+	pci_read_config_word(pdev, ICH5_PCS, &pcs);
+
+	if (pcs & (1 << ah->port_no)) {
+		pcs &= ~(1 << ah->port_no);
+		pci_write_config_word(pdev, ICH5_PCS, pcs);
+	}
+}
+
+static void piix_set_piomode (struct ata_host *ah, struct ata_device *adev,
+			      unsigned int pio)
+{
+	struct pci_dev *dev	= ah->host_set->pdev;
+	unsigned int is_slave	= (adev->flags & ATA_DFLAG_MASTER) ? 0 : 1;
+	unsigned int master_port= ah->port_no ? 0x42 : 0x40;
+	unsigned int slave_port	= 0x44;
+	u16 master_data;
+	u8 slave_data;
+			 /* ISP  RTC */
+	u8 timings[][2]	= { { 0, 0 },
+			    { 0, 0 },
+			    { 1, 0 },
+			    { 2, 1 },
+			    { 2, 3 }, };
+
+	pci_read_config_word(dev, master_port, &master_data);
+	if (is_slave) {
+		master_data |= 0x4000;
+		/* enable PPE, IE and TIME */
+		master_data |= 0x0070;
+		pci_read_config_byte(dev, slave_port, &slave_data);
+		slave_data &= (ah->port_no ? 0x0f : 0xf0);
+		slave_data |=
+			(timings[pio][0] << 2) |
+			(timings[pio][1] << (ah->port_no ? 4 : 0));
+	} else {
+		master_data &= 0xccf8;
+		/* enable PPE, IE and TIME */
+		master_data |= 0x0007;
+		master_data |=
+			(timings[pio][0] << 12) |
+			(timings[pio][1] << 8);
+	}
+	pci_write_config_word(dev, master_port, master_data);
+	if (is_slave)
+		pci_write_config_byte(dev, slave_port, slave_data);
+}
+
+static void piix_set_udmamode (struct ata_host *ah, struct ata_device *adev,
+			      unsigned int udma)
+{
+	struct pci_dev *dev	= ah->host_set->pdev;
+	u8 maslave		= ah->port_no ? 0x42 : 0x40;
+	u8 speed		= udma;
+	unsigned int drive_dn	= (ah->port_no ? 2 : 0) + adev->devno;
+	int a_speed		= 3 << (drive_dn * 4);
+	int u_flag		= 1 << drive_dn;
+	int v_flag		= 0x01 << drive_dn;
+	int w_flag		= 0x10 << drive_dn;
+	int u_speed		= 0;
+	int			sitre;
+	u16			reg4042, reg44, reg48, reg4a, reg54;
+	u8			reg55;
+
+	pci_read_config_word(dev, maslave, &reg4042);
+	DPRINTK("reg4042 = 0x%04x\n", reg4042);
+	sitre = (reg4042 & 0x4000) ? 1 : 0;
+	pci_read_config_word(dev, 0x44, &reg44);
+	pci_read_config_word(dev, 0x48, &reg48);
+	pci_read_config_word(dev, 0x4a, &reg4a);
+	pci_read_config_word(dev, 0x54, &reg54);
+	pci_read_config_byte(dev, 0x55, &reg55);
+
+	switch(speed) {
+		case XFER_UDMA_4:
+		case XFER_UDMA_2:	u_speed = 2 << (drive_dn * 4); break;
+		case XFER_UDMA_6:
+		case XFER_UDMA_5:
+		case XFER_UDMA_3:
+		case XFER_UDMA_1:	u_speed = 1 << (drive_dn * 4); break;
+		case XFER_UDMA_0:	u_speed = 0 << (drive_dn * 4); break;
+		default:
+			BUG();
+			return;
+	}
+
+	if (!(reg48 & u_flag))
+		pci_write_config_word(dev, 0x48, reg48|u_flag);
+	if (speed == XFER_UDMA_5) {
+		pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag);
+	} else {
+		pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
+	}
+	if (!(reg4a & u_speed)) {
+		pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+		pci_write_config_word(dev, 0x4a, reg4a|u_speed);
+	}
+	if (speed > XFER_UDMA_2) {
+		if (!(reg54 & v_flag)) {
+			pci_write_config_word(dev, 0x54, reg54|v_flag);
+		}
+	} else {
+		pci_write_config_word(dev, 0x54, reg54 & ~v_flag);
+	}
+}
+
+static int __devinit piix_init_one (struct pci_dev *pdev,
+				    const struct pci_device_id *ent)
+{
+	struct ata_board *board_info = &ata_board_tbl[ent->driver_data];
+	unsigned is_sata = board_info->host_flags & ATA_FLAG_SATA;
+	static int printed_version;
+
+	if (!printed_version++)
+		printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+	/* no hotplugging support (FIXME) */
+	if (!in_module_init)
+		return -ENODEV;
+
+	return ata_pci_init_one(pdev, board_info,
+				is_sata ? &piix_sata_ops : &piix_pata_ops);
+}
+
+static int __init piix_init(void)
+{
+	int rc;
+
+	DPRINTK("pci_module_init\n");
+	rc = pci_module_init(&piix_pci_driver);
+	if (rc)
+		return rc;
+
+	in_module_init = 0;
+
+	DPRINTK("scsi_register_host\n");
+	rc = scsi_register_host(&piix_sht);
+	if (rc) {
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	DPRINTK("done\n");
+	return 0;
+
+err_out:
+	pci_unregister_driver(&piix_pci_driver);
+	return rc;
+}
+
+static void __exit piix_exit(void)
+{
+	scsi_unregister_host(&piix_sht);
+	pci_unregister_driver(&piix_pci_driver);
+}
+
+module_init(piix_init);
+module_exit(piix_exit);
+
diff -Nru a/drivers/scsi/libata.c b/drivers/scsi/libata.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/libata.c	Sat May 24 14:36:45 2003
@@ -0,0 +1,2239 @@
+/*
+   Copyright 2003 Red Hat, Inc.  All rights reserved.
+   Copyright 2003 Jeff Garzik
+
+   The contents of this file are subject to the Open
+   Software License version 1.1 that can be found at
+   http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+   by reference.
+
+   Alternatively, the contents of this file may be used under the terms
+   of the GNU General Public License version 2 (the "GPL") as distributed
+   in the kernel source COPYING file, in which case the provisions of
+   the GPL are applicable instead of the above.  If you wish to allow
+   the use of your version of this file only under the terms of the
+   GPL and not to allow others to use your version of this file under
+   the OSL, indicate your decision by deleting the provisions above and
+   replace them with the notice and other provisions required by the GPL.
+   If you do not delete the provisions above, a recipient may use your
+   version of this file under either the OSL or the GPL.
+
+
+   TODO:
+	ATAPI.
+	ATAPI error handling.
+	Better ATA error handling.
+	request_region for legacy ATA ISA addresses.
+	scsi SYNCHRONIZE CACHE and associated mode page
+	other minor items, too many to list
+	replace THR_*_POLL states with calls to a poll function
+	use set-mult-mode/rw mult for faster PIO
+
+
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/ata.h>
+#include <scsi/scsi.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+
+#include <linux/scsi_defs.h>
+#include <linux/scsi_hosts.h>
+
+#define DRV_NAME	"libata"
+#define DRV_VERSION	"0.51"	/* must be exactly four chars */
+
+struct ata_scsi_args {
+	struct ata_host		*ah;
+	struct ata_device	*dev;
+	Scsi_Cmnd		*cmd;
+	void			(*done)(Scsi_Cmnd *);
+};
+
+static void ata_to_sense_error(struct ata_queued_cmd *qc, Scsi_Cmnd *cmd);
+static void ata_scsi_badcmd(Scsi_Cmnd *cmd,
+			    void (*done)(Scsi_Cmnd *),
+			    u8 asc, u8 ascq);
+
+static unsigned int ata_unique_id = 1;
+static LIST_HEAD(ata_probe_list);
+static LIST_HEAD(ata_scsihost_list);
+static spinlock_t ata_module_lock = SPIN_LOCK_UNLOCKED;
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("Library module for running ATA devices underneath SCSI");
+MODULE_LICENSE("GPL");
+
+static const char * thr_state_name[] = {
+	"THR_UNKNOWN",
+	"THR_CHECKPORT",
+	"THR_EDD",
+	"THR_AWAIT_DEATH",
+	"THR_IDENTIFY",
+	"THR_CONFIG_PIO",
+	"THR_CONFIG_DMA",
+	"THR_PROBE_FAILED",
+	"THR_IDLE",
+	"THR_PROBE_SUCCESS",
+	"THR_PROBE_START",
+	"THR_CONFIG_FORCE_PIO",
+	"THR_PIO_POLL",
+	"THR_PIO_TMOUT",
+	"THR_PIO",
+	"THR_PIO_LAST",
+	"THR_PIO_LAST_POLL",
+	"THR_PIO_ERR",
+};
+
+static const char *ata_thr_state_name(unsigned int thr_state)
+{
+	if (thr_state < ARRAY_SIZE(thr_state_name))
+		return thr_state_name[thr_state];
+	return "<invalid THR_xxx state>";
+}
+
+static void msleep(unsigned long msecs)
+{
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(msecs_to_jiffies(msecs));
+}
+
+static void __ata_tf_to_host(struct ata_ioports *ioaddr,
+			     struct ata_taskfile *tf)
+{
+	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+	outb(tf->ctl, ioaddr->ctl_addr);
+
+	if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+		outb(tf->hob_feature, ioaddr->cmd_addr + ATA_REG_FEATURE);
+		outb(tf->hob_nsect, ioaddr->cmd_addr + ATA_REG_NSECT);
+		outb(tf->hob_lbal, ioaddr->cmd_addr + ATA_REG_LBAL);
+		outb(tf->hob_lbam, ioaddr->cmd_addr + ATA_REG_LBAM);
+		outb(tf->hob_lbah, ioaddr->cmd_addr + ATA_REG_LBAH);
+		DPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
+			tf->hob_feature,
+			tf->hob_nsect,
+			tf->hob_lbal,
+			tf->hob_lbam,
+			tf->hob_lbah);
+	}
+
+	if (is_addr) {
+		outb(tf->feature, ioaddr->cmd_addr + ATA_REG_FEATURE);
+		outb(tf->nsect, ioaddr->cmd_addr + ATA_REG_NSECT);
+		outb(tf->lbal, ioaddr->cmd_addr + ATA_REG_LBAL);
+		outb(tf->lbam, ioaddr->cmd_addr + ATA_REG_LBAM);
+		outb(tf->lbah, ioaddr->cmd_addr + ATA_REG_LBAH);
+		DPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
+			tf->feature,
+			tf->nsect,
+			tf->lbal,
+			tf->lbam,
+			tf->lbah);
+	}
+
+	if (tf->flags & ATA_TFLAG_DEVICE) {
+		outb(tf->device, ioaddr->cmd_addr + ATA_REG_DEVICE);
+		DPRINTK("device 0x%X\n", tf->device);
+	}
+
+	ata_wait_idle(ioaddr);
+}
+
+static inline void ata_exec(struct ata_host *ah, struct ata_taskfile *tf,
+			   unsigned int bus_state)
+{
+	unsigned long flags;
+
+	DPRINTK("command 0x%X\n", tf->command);
+	spin_lock_irqsave(&ah->host_set->lock, flags);
+	ah->bus_state = bus_state;
+	outb(tf->command, ah->ioaddr.cmd_addr + ATA_REG_CMD);
+	ndelay(400);
+	spin_unlock_irqrestore(&ah->host_set->lock, flags);
+}
+
+static void ata_tf_to_host(struct ata_host *ah, struct ata_taskfile *tf,
+			   unsigned int bus_state)
+{
+	init_MUTEX_LOCKED(&ah->sem);
+
+	__ata_tf_to_host(&ah->ioaddr, tf);
+
+	ata_exec(ah, tf, bus_state);
+}
+
+static void ata_tf_from_host(struct ata_ioports *ioaddr,
+			     struct ata_taskfile *tf)
+{
+	if (tf->flags & ATA_TFLAG_DATAREG) {
+		u16 data = inw(ioaddr->cmd_addr + ATA_REG_DATA);
+		tf->data = data & 0xff;
+		tf->hob_data = data >> 8;
+	}
+
+	tf->nsect = inb(ioaddr->cmd_addr + ATA_REG_NSECT);
+	tf->lbal = inb(ioaddr->cmd_addr + ATA_REG_LBAL);
+	tf->lbam = inb(ioaddr->cmd_addr + ATA_REG_LBAM);
+	tf->lbah = inb(ioaddr->cmd_addr + ATA_REG_LBAH);
+	tf->device = inb(ioaddr->cmd_addr + ATA_REG_DEVICE);
+
+	if (tf->flags & ATA_TFLAG_LBA48) {
+		outb(tf->ctl | ATA_HOB, ioaddr->ctl_addr);
+		tf->hob_feature = inb(ioaddr->cmd_addr + ATA_REG_FEATURE);
+		tf->hob_nsect = inb(ioaddr->cmd_addr + ATA_REG_NSECT);
+		tf->hob_lbal = inb(ioaddr->cmd_addr + ATA_REG_LBAL);
+		tf->hob_lbam = inb(ioaddr->cmd_addr + ATA_REG_LBAM);
+		tf->hob_lbah = inb(ioaddr->cmd_addr + ATA_REG_LBAH);
+	}
+}
+
+static const char * udma_str[] = {
+	"UDMA/16",
+	"UDMA/25",
+	"UDMA/33",
+	"UDMA/44",
+	"UDMA/66",
+	"UDMA/100",
+	"UDMA/133",
+	"UDMA7",
+};
+
+static const char *ata_udma_string(unsigned int udma_mask)
+{
+	unsigned int i;
+
+	for (i = 7; i >= 0; i--) {
+		if (udma_mask & (1 << i))
+			return udma_str[i];
+	}
+
+	return "<n/a>";
+}
+
+static unsigned int ata_dev_classify(struct ata_taskfile *tf)
+{
+	/* Apple's open source Darwin code hints that some devices only
+	 * put a proper signature into the LBA mid/high registers,
+	 * So, we only check those.  It's sufficient for uniqueness.
+	 */
+
+	if (((tf->lbam == 0) && (tf->lbah == 0)) ||
+	    ((tf->lbam == 0x3c) && (tf->lbah == 0xc3))) {
+		DPRINTK("found ATA device by sig\n");
+		return ATA_DEV_ATA;
+	}
+
+	if (((tf->lbam == 0x14) && (tf->lbah == 0xeb)) ||
+	    ((tf->lbam == 0x69) && (tf->lbah == 0x96))) {
+		DPRINTK("found ATAPI device by sig\n");
+		return ATA_DEV_ATAPI;
+	}
+
+	DPRINTK("unknown device\n");
+	return ATA_DEV_UNKNOWN;
+}
+
+static unsigned int ata_dev_id_string(struct ata_device *dev, unsigned char *s,
+				      unsigned int ofs, unsigned int len)
+{
+	unsigned int c, ret = 0;
+
+	while (len > 0) {
+		c = dev->id[ofs] >> 8;
+		*s = c;
+		s++;
+
+		ret = c = dev->id[ofs] & 0xff;
+		*s = c;
+		s++;
+
+		ofs++;
+		len -= 2;
+	}
+
+	return ret;
+}
+
+static void ata_dev_parse_strings(struct ata_device *dev)
+{
+	if (dev->class == ATA_DEV_ATA)
+		memcpy(dev->vendor, "ATA     ", 8);
+	else if (dev->class == ATA_DEV_ATAPI)
+		memcpy(dev->vendor, "ATAPI   ", 8);
+	else
+		memcpy(dev->vendor, "Unknown ", 8);
+
+	ata_dev_id_string(dev, dev->product, ATA_ID_PROD_OFS,
+			  sizeof(dev->product));
+}
+
+static u8 ata_dev_select(struct ata_host *ah, unsigned int device,
+			   unsigned int unconditional, unsigned int islba)
+{
+	struct ata_ioports *ioaddr = &ah->ioaddr;
+	u8 tmp, newtmp;
+
+	newtmp = tmp = inb(ioaddr->cmd_addr + ATA_REG_DEVICE);
+
+	if (device == 0) {
+		newtmp = tmp & ~ATA_DEV1;
+	} else {
+		newtmp = tmp | ATA_DEV1;
+	}
+	newtmp |= ATA_DEVICE_OBS;
+	if (islba)
+		newtmp |= ATA_LBA;
+
+	if (unconditional || (tmp != newtmp)) {
+		outb(newtmp, ioaddr->cmd_addr + ATA_REG_DEVICE);
+		ah->devsel = newtmp;
+		tmp = ata_wait_idle(&ah->ioaddr);
+		DPRINTK("selected dev using Device 0x%X, drv_stat 0x%X\n",
+			newtmp, tmp);
+	} else {
+		DPRINTK("Using cached Device reg, 0x%X\n", (u32) tmp);
+	}
+
+	assert(ah->bus_state == BUS_IDLE);
+
+	return tmp;
+}
+
+static inline void ata_dump_id(struct ata_device *dev)
+{
+	DPRINTK("49==0x%04x  "
+		"53==0x%04x  "
+		"63==0x%04x  "
+		"64==0x%04x  "
+		"75==0x%04x  \n",
+		dev->id[49],
+		dev->id[53],
+		dev->id[63],
+		dev->id[64],
+		dev->id[75]);
+	DPRINTK("80==0x%04x  "
+		"81==0x%04x  "
+		"82==0x%04x  "
+		"83==0x%04x  "
+		"84==0x%04x  \n",
+		dev->id[80],
+		dev->id[81],
+		dev->id[82],
+		dev->id[83],
+		dev->id[84]);
+	DPRINTK("88==0x%04x  "
+		"93==0x%04x\n",
+		dev->id[88],
+		dev->id[93]);
+}
+
+static void ata_dev_identify(struct ata_host *ah, unsigned int device)
+{
+	struct ata_device *dev = &ah->device[device];
+	unsigned int i;
+	u16 tmp;
+	u8 status;
+	struct ata_taskfile tf;
+
+	if (!ata_dev_present(dev)) {
+		DPRINTK("ENTER/EXIT (host %u, dev %u) -- nodev\n",
+			ah->id, device);
+		return;
+	}
+
+	DPRINTK("ENTER, host %u, dev %u\n", ah->id, device);
+
+	if (dev->class == ATA_DEV_UNKNOWN)
+		BUG();
+	if (device > ATA_MAX_DEVICES)
+		BUG();
+	if (device > 1)
+		BUG();
+
+	ata_dev_select(ah, device, 0, 0); /* select device 0/1 */
+
+	ata_tf_init(ah, &tf);
+
+	if (dev->class == ATA_DEV_ATA)
+		tf.command = ATA_CMD_ID_ATA;
+	else
+		tf.command = ATA_CMD_ID_ATAPI;
+
+	DPRINTK("do identify\n");
+	ata_tf_to_host(ah, &tf, BUS_IDENTIFY);
+
+	/* wait for completion or timeout */
+	DPRINTK("completion wait\n");
+	down(&ah->sem);
+	DPRINTK("end wait\n");
+
+	assert(ah->bus_state == BUS_PIO);
+
+	/* make sure we have BSY=0, DRQ=1 */
+	status = ata_busy_wait(&ah->ioaddr, ATA_BUSY, 1000);
+	assert (((status & ATA_BUSY) == 0) && (status & ATA_DRQ));
+
+	/* read IDENTIFY [X] DEVICE page */
+	for (i = 0; i < ATA_ID_WORDS; i++)
+		dev->id[i] = inw(ah->ioaddr.cmd_addr + ATA_REG_DATA);
+
+	/* wait for host_idle */
+	status = ata_wait_idle(&ah->ioaddr);
+	if (status & (ATA_BUSY | ATA_DRQ)) {
+		DPRINTK("abormal status 2, 0x%X\n", status);
+		goto err_out;
+	}
+
+	ah->bus_state = BUS_IDLE;
+
+	if (dev->class == ATA_DEV_ATA) {
+		ata_dump_id(dev);
+
+		if (!ata_id_is_ata(dev))	/* sanity check */
+			goto err_out_nosup;
+
+		if (ata_id_has_lba48(dev)) {
+			dev->flags |= ATA_DFLAG_LBA48;
+			dev->n_sectors = ata_id_u64(dev, 100);
+		} else {
+			dev->n_sectors = ata_id_u32(dev, 60);
+		}
+
+		/* we require LBA and DMA support (bits 8 & 9 of word 49) */
+		if (!ata_id_has_dma(dev) || !ata_id_has_lba(dev))
+			goto err_out_nosup;
+
+		ata_dev_parse_strings(dev);
+
+		tmp = dev->id[ATA_ID_MAJOR_VER];
+		for (i = 14; i >= 1; i--)
+			if (tmp & (1 << i))
+				break;
+
+		/* we require at least ATA-3 */
+		if (i < 3)
+			goto err_out_nosup;
+
+		/* we require UDMA support */
+		tmp = dev->id[ATA_ID_UDMA_MODES];
+		if ((tmp & 0xff) == 0)
+			goto err_out_nosup;
+
+		/* print device info to dmesg */
+		printk(KERN_INFO "ata%u: dev %u ATA, max %s, %Lu sectors\n",
+		       ah->id, device,
+		       ata_udma_string(tmp),
+		       dev->n_sectors);
+	} else {
+		/* we require UDMA support */
+		tmp = dev->id[ATA_ID_UDMA_MODES];
+		if ((tmp & 0xff) == 0)
+			goto err_out_nosup;
+
+		/* print device info to dmesg */
+		printk(KERN_INFO "ata%u: dev %u ATAPI, max %s\n",
+		       ah->id, device,
+		       ata_udma_string(tmp));
+	}
+
+	DPRINTK("EXIT\n");
+	return;
+
+err_out_nosup:
+	printk(KERN_WARNING "ata%u: dev %u not supported, ignoring\n",
+	       ah->id, device);
+err_out:
+	dev->class = ATA_DEV_NONE;
+	DPRINTK("EXIT, err\n");
+}
+
+void ata_port_probe(struct ata_host *ah)
+{
+	ah->flags &= ~ATA_FLAG_PORT_DISABLED;
+}
+
+void ata_port_disable(struct ata_host *ah)
+{
+	ah->device[0].class = ATA_DEV_NONE;
+	ah->device[1].class = ATA_DEV_NONE;
+	ah->flags |= ATA_FLAG_PORT_DISABLED;
+}
+
+static unsigned int ata_bus_reset(struct ata_host *ah)
+{
+	unsigned int slave_possible = ah->flags & ATA_FLAG_SLAVE_POSS;
+	struct ata_taskfile tf;
+	u8 err, devsel, status;
+	unsigned int rc = 0;
+
+	DPRINTK("ENTER, host %u, port %u\n", ah->id, ah->port_no);
+
+	DPRINTK("irq toggle\n");
+	ata_irq_on(ah);		/* make sure interrupts are enabled */
+
+	/* set up execute-device-diag (bus reset) taskfile */
+	DPRINTK("execute-device-diag\n");
+	ata_tf_init(ah, &tf);
+	tf.command = ATA_CMD_EDD;
+
+	/* do bus reset */
+	ata_tf_to_host(ah, &tf, BUS_EDD);
+
+	/* wait for completion or timeout */
+	DPRINTK("EDD completion wait\n");
+	down(&ah->sem);
+	DPRINTK("EDD end wait, bus_idle: %s\n",
+		(ah->bus_state == BUS_IDLE) ? "YES" : "NO");
+
+	/* select device 0 */
+	devsel = ata_dev_select(ah, 0, 1, 0);
+
+	/* read back results of E.D.D. */
+	status = ata_chk_status(&ah->ioaddr);
+	err = inb(ah->ioaddr.cmd_addr + ATA_REG_ERR);
+
+	if ((devsel == 0xff) && (err == 0xff) && (status == 0xff)) {
+		ah->ops->port_disable(ah);
+		rc = 0;
+		goto out;
+	}
+
+	ah->device[1].class = ATA_DEV_NONE;
+
+	/* spec says 01h or 81h indicates device 0 passed diags */
+	DPRINTK("err register == 0x%X\n", err);
+	if ((err == 0x01) || (err == 0x81)) {
+		ata_tf_from_host(&ah->ioaddr, &tf);
+		ah->device[0].class = ata_dev_classify(&tf);
+
+		/* check for invalid signature */
+		if (ah->device[0].class == ATA_DEV_UNKNOWN) {
+			rc = 1;
+			goto out;
+		}
+
+		/* exit now if device 1 is absent/failed as well */
+		if ((!slave_possible) || (err == 0x81))
+			goto out;
+	} else {
+		/* no device 0 exists (or device 0 failed diags) */
+		ah->device[0].class = ATA_DEV_NONE;
+
+		/* exit now if device 1 is absent as well */
+		if ((!slave_possible) || (err == 0x80) ||
+		    ((err >= 0x82) && (err <= 0xff)))
+			goto out;
+	}
+
+	/* if we reach this point, we _might_ have a device 1 */
+	/* FIXME (PATA only case, so we put it off until later) */
+
+out:
+	DPRINTK("EXIT, returning %u\n", rc);
+	return rc;
+}
+
+static void ata_host_set_pio(struct ata_host *ah)
+{
+	struct ata_device *master, *slave;
+	unsigned int pio, i;
+	u16 mask;
+
+	master = &ah->device[0];
+	slave = &ah->device[1];
+
+	assert (ata_dev_present(master) || ata_dev_present(slave));
+
+	mask = ah->pio_mask;
+	if (ata_dev_present(master))
+		mask &= (master->id[ATA_ID_PIO_MODES] & 0x03);
+	if (ata_dev_present(slave))
+		mask &= (slave->id[ATA_ID_PIO_MODES] & 0x03);
+
+	/* require pio mode 3 or 4 support for host and all devices */
+	if (mask == 0) {
+		printk(KERN_WARNING "ata%u: no PIO3/4 support, ignoring\n",
+		       ah->id);
+		goto err_out;
+	}
+
+	pio = (mask & ATA_ID_PIO4) ? 4 : 3;
+	for (i = 0; i < ATA_MAX_DEVICES; i++)
+		if (ata_dev_present(&ah->device[i])) {
+			ah->device[i].pio_mode = (pio == 3) ?
+				XFER_PIO_3 : XFER_PIO_4;
+			ah->ops->set_piomode(ah, &ah->device[i], pio);
+		}
+
+	return;
+
+err_out:
+	ah->ops->port_disable(ah);
+}
+
+static void ata_host_set_udma(struct ata_host *ah)
+{
+	struct ata_device *master, *slave;
+	u16 mask;
+	unsigned int i, j;
+	int udma_mode = -1;
+
+	master = &ah->device[0];
+	slave = &ah->device[1];
+
+	assert (ata_dev_present(master) || ata_dev_present(slave));
+	assert ((ah->flags & ATA_FLAG_PORT_DISABLED) == 0);
+
+	DPRINTK("udma masks: host 0x%X, master 0x%X, slave 0x%X\n",
+		ah->udma_mask,
+		(!ata_dev_present(master)) ? 0xff :
+			(master->id[ATA_ID_UDMA_MODES] & 0xff),
+		(!ata_dev_present(slave)) ? 0xff :
+			(slave->id[ATA_ID_UDMA_MODES] & 0xff));
+
+	mask = ah->udma_mask;
+	if (ata_dev_present(master))
+		mask &= (master->id[ATA_ID_UDMA_MODES] & 0xff);
+	if (ata_dev_present(slave))
+		mask &= (slave->id[ATA_ID_UDMA_MODES] & 0xff);
+
+	i = XFER_UDMA_7;
+	while (i >= XFER_UDMA_0) {
+		j = i - XFER_UDMA_0;
+		DPRINTK("mask 0x%X i 0x%X j %u\n", mask, i, j);
+		if (mask & (1 << j)) {
+			udma_mode = i;
+			break;
+		}
+
+		i--;
+	}
+
+	/* require udma for host and all attached devices */
+	if (udma_mode < 0) {
+		printk(KERN_WARNING "ata%u: no UltraDMA support, ignoring\n",
+		       ah->id);
+		goto err_out;
+	}
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++)
+		if (ata_dev_present(&ah->device[i])) {
+			ah->device[i].udma_mode = udma_mode;
+			ah->ops->set_udmamode(ah, &ah->device[i], udma_mode);
+		}
+
+	return;
+
+err_out:
+	ah->ops->port_disable(ah);
+}
+
+static void ata_dev_set_xfermode(struct ata_host *ah, struct ata_device *dev)
+{
+	struct ata_taskfile tf;
+
+	/* set up set-features taskfile */
+	DPRINTK("set features - xfer mode\n");
+	ata_tf_init(ah, &tf);
+	tf.command = ATA_CMD_SET_FEATURES;
+	tf.feature = SETFEATURES_XFER;
+	tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+	if (dev->flags & ATA_DFLAG_PIO)
+		tf.nsect = dev->pio_mode;
+	else
+		tf.nsect = dev->udma_mode;
+
+	/* do bus reset */
+	ata_tf_to_host(ah, &tf, BUS_NODATA);
+
+	/* wait for completion or timeout */
+	DPRINTK("SETFEATURES completion wait\n");
+	down(&ah->sem);
+	DPRINTK("SETFEATURES end wait\n");
+
+	assert(ah->bus_state == BUS_IDLE);
+
+	ata_wait_idle(&ah->ioaddr);
+
+	DPRINTK("EXIT\n");
+}
+
+static void ata_dev_set_udma(struct ata_host *ah, unsigned int device)
+{
+	struct ata_device *dev = &ah->device[device];
+
+	if (!ata_dev_present(dev) || (ah->flags & ATA_FLAG_PORT_DISABLED))
+		return;
+
+	ata_dev_set_xfermode(ah, dev);
+
+	assert((dev->udma_mode >= XFER_UDMA_0) &&
+	       (dev->udma_mode <= XFER_UDMA_7));
+	printk(KERN_INFO "ata%u: dev %u configured for %s\n",
+	       ah->id, device,
+	       udma_str[dev->udma_mode - XFER_UDMA_0]);
+}
+
+static void ata_dev_set_pio(struct ata_host *ah, unsigned int device)
+{
+	struct ata_device *dev = &ah->device[device];
+
+	if (!ata_dev_present(dev) || (ah->flags & ATA_FLAG_PORT_DISABLED))
+		return;
+
+	/* force PIO mode */
+	dev->flags |= ATA_DFLAG_PIO;
+
+	ata_dev_set_xfermode(ah, dev);
+
+	assert((dev->pio_mode >= XFER_PIO_3) &&
+	       (dev->pio_mode <= XFER_PIO_4));
+	printk(KERN_INFO "ata%u: dev %u configured for PIO%c\n",
+	       ah->id, device,
+	       dev->pio_mode == 3 ? '3' : '4');
+}
+
+static void ata_sg_clean(struct ata_host *ah, struct ata_queued_cmd *qc)
+{
+	Scsi_Cmnd *cmd = qc->scsicmd;
+	struct scatterlist *sg;
+	unsigned int dma = (qc->flags & ATA_QCFLAG_DMA);
+
+#ifndef ATA_DEBUG
+	if (!dma)
+		return;
+#endif
+
+	sg = (struct scatterlist *)qc->scsicmd->request_buffer;
+
+#if defined(ATA_DEBUG) && defined(ATA_READ_POISON)
+	{
+	unsigned int i, j, match;
+	unsigned char *p;
+
+	for (i = 0; i < qc->n_elem; i++) {
+		match = 1;
+		p = sg[i].address;
+		for (j = 0; j < sg[i].length; j++)
+			if (p[j] != 0xfb) {
+				match = 0;
+				break;
+			}
+		if (match)
+			DPRINTK("PRD[%u] STILL POISONED\n", i);
+	}
+
+	}
+#endif
+
+	if (dma) {
+		int dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+		DPRINTK("unmapping %u sg elements\n", qc->n_elem);
+
+		pci_unmap_sg(ah->host_set->pdev, sg, qc->n_elem, dir);
+	}
+}
+
+static int ata_sg_setup(struct ata_host *ah, struct ata_queued_cmd *qc)
+{
+	Scsi_Cmnd *cmd = qc->scsicmd;
+	struct scatterlist *sg;
+	int n_elem;
+	unsigned int i;
+	unsigned int dma = (qc->flags & ATA_QCFLAG_DMA);
+#if defined(ATA_DEBUG) && defined(ATA_READ_POISON)
+	unsigned int rw = (qc->flags & ATA_QCFLAG_WRITE);
+#endif
+
+	VPRINTK("ENTER, ata%u, use_sg %d\n", ah->id, cmd->use_sg);
+	assert(cmd->use_sg > 0);
+
+	sg = (struct scatterlist *)cmd->request_buffer;
+	if (dma) {
+		int dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+		n_elem = pci_map_sg(ah->host_set->pdev, sg, cmd->use_sg, dir);
+		if (n_elem < 1)
+			return -1;
+		DPRINTK("%d sg elements mapped\n", n_elem);
+	} else {
+		n_elem = cmd->use_sg;
+	}
+	qc->n_elem = n_elem;
+
+
+#ifndef ATA_DEBUG
+	if (!dma)
+		return 0;
+#endif
+
+	for (i = 0; i < n_elem; i++) {
+#if defined(ATA_DEBUG) && defined(ATA_READ_POISON)
+		if (!rw)
+			memset(sg[i].address, 0xfb, sg[i].length);
+#endif
+		ah->prd[i].addr = cpu_to_le32(sg[i].dma_address);
+		ah->prd[i].flags_len = cpu_to_le32(sg[i].length);
+		DPRINTK("PRD[%u] = (0x%X, 0x%X)\n",
+			i, ah->prd[i].addr, ah->prd[i].flags_len);
+	}
+	ah->prd[n_elem - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
+
+#ifdef ATA_DEBUG
+	i = n_elem - 1;
+	DPRINTK("PRD[%u] = (0x%X, 0x%X)\n",
+		i, ah->prd[i].addr, ah->prd[i].flags_len);
+
+	for (i = n_elem; i < ATA_MAX_PRD; i++) {
+		ah->prd[i].addr = 0;
+		ah->prd[i].flags_len = cpu_to_le32(ATA_PRD_EOT);
+	}
+#endif
+
+	return 0;
+}
+
+static unsigned long ata_pio_poll(struct ata_host *ah)
+{
+	u8 status;
+	unsigned int poll_state = THR_UNKNOWN;
+	unsigned int reg_state = THR_UNKNOWN;
+	const unsigned int tmout_state = THR_PIO_TMOUT;
+
+	switch (ah->thr_state) {
+	case THR_PIO:
+	case THR_PIO_POLL:
+		poll_state = THR_PIO_POLL;
+		reg_state = THR_PIO;
+		break;
+	case THR_PIO_LAST:
+	case THR_PIO_LAST_POLL:
+		poll_state = THR_PIO_LAST_POLL;
+		reg_state = THR_PIO_LAST;
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	status = ata_chk_status(&ah->ioaddr);
+	if (status & ATA_BUSY) {
+		if (time_after(jiffies, ah->thr_timeout)) {
+			ah->thr_state = tmout_state;
+			return 0;
+		}
+		ah->thr_state = poll_state;
+		return ATA_SHORT_PAUSE;
+	}
+
+	ah->thr_state = reg_state;
+	return 0;
+}
+
+static void ata_pio_start (struct ata_host *ah, struct ata_queued_cmd *qc)
+{
+	/* FIXME: support TCQ */
+	qc->tf.ctl |= ATA_NIEN;	/* disable interrupts */
+	ata_tf_to_host(ah, &qc->tf, BUS_PIO);
+	assert(ah->thr_state == THR_IDLE);
+	ah->thr_state = THR_PIO;
+	up(&ah->thr_sem);
+}
+
+static void ata_pio_complete (struct ata_host *ah)
+{
+	struct ata_queued_cmd *qc;
+	unsigned long flags;
+	u8 drv_stat;
+	
+	/*
+	 * This is purely hueristic.  This is a fast path.
+	 * Sometimes when we enter, BSY will be cleared in
+	 * a chk-status or two.  If not, the drive is probably seeking
+	 * or something.  Snooze for a couple msecs, then
+	 * chk-status again.  If still busy, fall back to
+	 * THR_PIO_POLL state.
+	 */
+	drv_stat = ata_busy_wait(&ah->ioaddr, ATA_BUSY | ATA_DRQ, 10);
+	if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
+		msleep(2);
+		drv_stat = ata_busy_wait(&ah->ioaddr, ATA_BUSY | ATA_DRQ, 10);
+		if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
+			ah->thr_state = THR_PIO_LAST_POLL;
+			ah->thr_timeout = jiffies + ATA_TMOUT_PIO;
+			return;
+		}
+	}
+
+	drv_stat = ata_wait_idle(&ah->ioaddr);
+	if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
+		ah->thr_state = THR_PIO_ERR;
+		return;
+	}
+
+	/* FIXME: support TCQ */
+	qc = &ah->qcmd[0];
+
+	ata_sg_clean(ah, qc);
+
+	qc->flags &= ~ATA_QCFLAG_ACTIVE;
+	ah->thr_state = THR_IDLE;
+
+	spin_lock_irqsave(&ah->host_set->lock, flags);
+	assert(ah->bus_state == BUS_PIO);
+	ah->bus_state = BUS_IDLE;
+	spin_unlock_irqrestore(&ah->host_set->lock, flags);
+
+	ata_irq_on(ah);
+
+	if (drv_stat & ATA_ERR) {
+		ata_to_sense_error(qc, qc->scsicmd);
+	} else {
+		qc->scsicmd->result = SAM_STAT_GOOD;
+		qc->done(qc->scsicmd);
+	}
+}
+
+static void ata_pio_sector(struct ata_host *ah)
+{
+	struct ata_queued_cmd *qc;
+	struct scatterlist *sg;
+	Scsi_Cmnd *cmd;
+	void *kaddr;
+	unsigned char *buf;
+	u8 status;
+
+	assert(ah->bus_state == BUS_PIO);
+
+	/*
+	 * This is purely hueristic.  This is a fast path.
+	 * Sometimes when we enter, BSY will be cleared in
+	 * a chk-status or two.  If not, the drive is probably seeking
+	 * or something.  Snooze for a couple msecs, then
+	 * chk-status again.  If still busy, fall back to
+	 * THR_PIO_POLL state.
+	 */
+	status = ata_busy_wait(&ah->ioaddr, ATA_BUSY, 5);
+	if (status & ATA_BUSY) {
+		msleep(2);
+		status = ata_busy_wait(&ah->ioaddr, ATA_BUSY, 10);
+		if (status & ATA_BUSY) {
+			ah->thr_state = THR_PIO_POLL;
+			ah->thr_timeout = jiffies + ATA_TMOUT_PIO;
+			return;
+		}
+	}
+
+	/* handle BSY=0, DRQ=0 as error */
+	if ((status & ATA_DRQ) == 0) {
+		ah->thr_state = THR_PIO_ERR;
+		return;
+	}
+
+	/* FIXME: support TCQ */
+	qc = &ah->qcmd[0];
+	cmd = qc->scsicmd;
+	sg = (struct scatterlist *)cmd->request_buffer;
+	if (qc->cursect == (qc->nsect - 1))
+		ah->thr_state = THR_PIO_LAST;
+
+	kaddr = kmap(sg[qc->cursg].page);
+	buf = kaddr + sg[qc->cursg].offset + (qc->cursg_ofs * ATA_SECT_SIZE);
+
+	qc->cursect++;
+	qc->cursg_ofs++;
+	
+	if ((qc->cursg_ofs * ATA_SECT_SIZE) == sg[qc->cursg].length) {
+		qc->cursg++;
+		qc->cursg_ofs = 0;
+	}
+
+	DPRINTK("data %s, drv_stat 0x%X\n",
+		qc->flags & ATA_QCFLAG_WRITE ? "write" : "read",
+		status);
+
+	/* do the actual data transfer */
+	if (qc->flags & ATA_QCFLAG_WRITE)
+		outsl(ah->ioaddr.cmd_addr + ATA_REG_DATA, buf, ATA_SECT_DWORDS);
+	else
+		insl(ah->ioaddr.cmd_addr + ATA_REG_DATA, buf, ATA_SECT_DWORDS);
+
+	kunmap(sg[qc->cursg].page);
+}
+
+static void ata_dma_start (struct ata_host *ah, struct ata_queued_cmd *qc)
+{
+	unsigned int rw = (qc->flags & ATA_QCFLAG_WRITE);
+	u8 host_stat, dmactl;
+
+	/* load taskfile registers */
+	__ata_tf_to_host(&ah->ioaddr, &qc->tf);
+
+	/* load PRD table addr. */
+	outl(ah->prd_dma, ah->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS);
+
+	/* specify data direction */
+	/* FIXME: redundant to later start-dma command? */
+	ah->dmactl = rw ? 0 : ATA_DMA_WR;
+	outb(ah->dmactl, ah->ioaddr.bmdma_addr + ATA_DMA_CMD);
+
+	/* clear interrupt, error bits */
+	host_stat = inb(ah->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+	outb(host_stat | ATA_DMA_INTR | ATA_DMA_ERR,
+	     ah->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+
+	/* issue r/w command */
+	ata_exec(ah, &qc->tf, BUS_DMA);
+
+	/* start host DMA transaction */
+	dmactl = inb(ah->ioaddr.bmdma_addr + ATA_DMA_CMD);
+	outb(dmactl | ATA_DMA_START,
+	     ah->ioaddr.bmdma_addr + ATA_DMA_CMD);
+}
+
+static void ata_dma_complete(struct ata_host *ah, u8 host_stat,
+			     unsigned int err)
+{
+	struct ata_queued_cmd *qc;
+	u8 drv_stat;
+
+	VPRINTK("ENTER\n");
+
+	/* clear start/stop bit */
+	outb(0, ah->ioaddr.bmdma_addr + ATA_DMA_CMD);
+
+	/* get controller status; clear intr, err bits */
+	outb(host_stat | ATA_DMA_INTR | ATA_DMA_ERR,
+	     ah->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+
+	/* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
+	inb(ah->ioaddr.ctl_addr);	/* dummy read */
+
+	/* get drive status; clear intr */
+	drv_stat = ata_wait_idle(&ah->ioaddr);
+
+	DPRINTK("host %u, host_stat==0x%X, drv_stat==0x%X\n",
+		ah->id, (u32) host_stat, (u32) drv_stat);
+
+	/* FIXME: support TCQ */
+	qc = &ah->qcmd[0];
+	if (!(qc->flags & ATA_QCFLAG_ACTIVE))
+		printk(KERN_ERR "ata%u: BUG: SCSI cmd not active\n", ah->id);
+	else {
+		ata_sg_clean(ah, qc);
+
+		if (err || (drv_stat & ATA_ERR)) {
+			ata_to_sense_error(qc, qc->scsicmd);
+		} else {
+			qc->scsicmd->result = SAM_STAT_GOOD;
+			qc->done(qc->scsicmd);
+		}
+
+		qc->flags &= ~ATA_QCFLAG_ACTIVE;
+	}
+}
+
+static inline unsigned int ata_host_intr (struct ata_host *ah)
+{
+	u8 status, host_stat;
+	unsigned int handled = 0;
+
+	switch (ah->bus_state) {
+	case BUS_NOINTR:	/* do nothing; (hopefully!) shared irq */
+	case BUS_IDLE:
+	case BUS_PIO:
+		ah->stats.idle_irq++;
+
+#ifdef ATA_IRQ_TRAP
+		if ((ah->stats.idle_irq % 1000) == 0) {
+			handled = 1;
+			ata_irq_ack(ah, 0); /* debug trap */
+			printk(KERN_WARNING "ata%d: irq trap\n", ah->id);
+		}
+#endif
+		break;
+
+	case BUS_DMA:
+		host_stat = inb(ah->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+		DPRINTK("BUS_DMA (host_stat 0x%X)\n", host_stat);
+
+		if (!(host_stat & ATA_DMA_INTR)) {
+			ah->stats.idle_irq++;
+			break;
+		}
+
+		ata_dma_complete(ah, host_stat, 0);
+		ah->bus_state = BUS_IDLE;
+		handled = 1;
+		break;
+
+	case BUS_EDD:
+		status = ata_irq_ack(ah, 0);
+		DPRINTK("BUS_EDD (drv_stat 0x%X)\n", status);
+		if (status & ATA_BUSY)
+			DPRINTK("abnormal status 0x%X host_stat 0x%X\n",
+				status, inb(ah->ioaddr.bmdma_addr +
+					ATA_DMA_STATUS));
+		ah->bus_state = BUS_IDLE;
+		up(&ah->sem);
+		handled = 1;
+		break;
+
+	case BUS_NODATA:	/* command completion, but no data xfer */
+		status = ata_irq_ack(ah, 1);
+		DPRINTK("BUS_NODATA (drv_stat 0x%X)\n", status);
+		ah->bus_state = BUS_IDLE;
+		up(&ah->sem);
+		handled = 1;
+		break;
+
+	case BUS_IDENTIFY:
+		status = ata_irq_ack(ah, 0);
+		DPRINTK("BUS_IDENTIFY (drv_stat 0x%X)\n", status);
+		ah->bus_state = BUS_PIO;
+		up(&ah->sem);
+		handled = 1;
+		break;
+
+	default:
+		printk(KERN_DEBUG "ata%u: unhandled bus state %u\n",
+		       ah->id, ah->bus_state);
+		ah->stats.unhandled_irq++;
+
+#ifdef ATA_IRQ_TRAP
+		if ((ah->stats.unhandled_irq % 1000) == 0) {
+			handled = 1;
+			ata_irq_ack(ah, 0); /* debug trap */
+			printk(KERN_WARNING "ata%d: irq trap\n", ah->id);
+		}
+#endif
+		break;
+	}
+
+	return handled;
+}
+
+static irqreturn_t ata_interrupt (int irq, void *dev_instance,
+				  struct pt_regs *regs)
+{
+	struct ata_host_set *host_set = dev_instance;
+	unsigned int i;
+	unsigned int handled = 0;
+
+	spin_lock(&host_set->lock);
+
+	for (i = 0; i < host_set->n_hosts; i++) {
+		struct ata_host *ah;
+
+		ah = host_set->hosts[i];
+		if (ah && (!(ah->flags & ATA_FLAG_PORT_DISABLED)))
+			handled += ata_host_intr(ah);
+	}
+
+	spin_unlock(&host_set->lock);
+
+	return IRQ_RETVAL(handled);
+}
+
+static void ata_timer(struct ata_host *ah)
+{
+	unsigned long flags;
+	unsigned int bus_state;
+	u8 host_stat;
+
+	DPRINTK("ENTER\n");
+
+	spin_lock_irqsave(&ah->host_set->lock, flags);
+	bus_state = ah->bus_state;
+	ah->bus_state = BUS_TIMER;
+	spin_unlock_irqrestore(&ah->host_set->lock, flags);
+
+	switch (bus_state) {
+	case BUS_PIO:
+	case BUS_EDD:
+	case BUS_NODATA:
+		ata_wait_idle(&ah->ioaddr);
+		up(&ah->sem);
+		break;
+
+	case BUS_DMA:
+		printk(KERN_WARNING "ata%u: DMA timeout\n", ah->id);
+		host_stat = inb(ah->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+		ata_dma_complete(ah, host_stat, 1);
+		break;
+
+	default:
+		DPRINTK("unhandled bus state %u\n", bus_state);
+		break;
+	}
+
+	spin_lock_irqsave(&ah->host_set->lock, flags);
+	ah->bus_state = BUS_IDLE;
+	spin_unlock_irqrestore(&ah->host_set->lock, flags);
+	DPRINTK("EXIT\n");
+}
+
+static void ata_thread_timer(unsigned long opaque)
+{
+	struct ata_host *ah = (struct ata_host *) opaque;
+
+	up(&ah->thr_sem);
+}
+
+static unsigned long ata_thread_iter(struct ata_host *ah)
+{
+	long timeout = 0;
+
+	DPRINTK("ata%u: thr_state %s\n",
+		ah->id, ata_thr_state_name(ah->thr_state));
+
+	switch (ah->thr_state) {
+	case THR_UNKNOWN:
+		ah->thr_state = THR_CHECKPORT;
+		break;
+
+	case THR_PROBE_START:
+		down(&ah->sem);
+		ah->thr_state = THR_CHECKPORT;
+		break;
+
+	case THR_CHECKPORT:
+		ah->ops->port_probe(ah);
+		if (ah->flags & ATA_FLAG_PORT_DISABLED)
+			ah->thr_state = THR_PROBE_FAILED;
+		else
+			ah->thr_state = THR_EDD;
+		break;
+
+	case THR_EDD:
+		if ((ata_bus_reset(ah)) || (ah->flags & ATA_FLAG_PORT_DISABLED))
+			ah->thr_state = THR_PROBE_FAILED;
+		else
+			ah->thr_state = THR_IDENTIFY;
+		break;
+
+	case THR_IDENTIFY:
+		ata_dev_identify(ah, 0);
+		ata_dev_identify(ah, 1);
+
+		if (!ata_dev_present(&ah->device[0]) &&
+		    !ata_dev_present(&ah->device[1])) {
+			ah->ops->port_disable(ah);
+			ah->thr_state = THR_PROBE_FAILED;
+		} else
+			ah->thr_state = THR_CONFIG_PIO;
+		break;
+
+	case THR_CONFIG_PIO:
+		ata_host_set_pio(ah);
+		if (ah->flags & ATA_FLAG_PORT_DISABLED)
+			ah->thr_state = THR_PROBE_FAILED;
+		else
+#ifdef ATA_FORCE_PIO
+			ah->thr_state = THR_CONFIG_FORCE_PIO;
+#else
+			ah->thr_state = THR_CONFIG_DMA;
+#endif
+		break;
+
+	case THR_CONFIG_FORCE_PIO:
+		ata_dev_set_pio(ah, 0);
+		ata_dev_set_pio(ah, 1);
+
+		if (ah->flags & ATA_FLAG_PORT_DISABLED)
+			ah->thr_state = THR_PROBE_FAILED;
+		else
+			ah->thr_state = THR_PROBE_SUCCESS;
+		break;
+
+	case THR_CONFIG_DMA:
+		ata_host_set_udma(ah);
+		ata_dev_set_udma(ah, 0);
+		ata_dev_set_udma(ah, 1);
+
+		if (ah->flags & ATA_FLAG_PORT_DISABLED)
+			ah->thr_state = THR_PROBE_FAILED;
+		else
+			ah->thr_state = THR_PROBE_SUCCESS;
+		break;
+
+	case THR_PROBE_SUCCESS:
+		up(&ah->probe_sem);
+		ah->thr_state = THR_IDLE;
+		break;
+
+	case THR_PROBE_FAILED:
+		up(&ah->probe_sem);
+		ah->thr_state = THR_AWAIT_DEATH;
+		break;
+
+	case THR_AWAIT_DEATH:
+		timeout = -1;
+		break;
+
+	case THR_IDLE:
+		timeout = 30 * HZ;
+		break;
+
+	case THR_PIO:
+		ata_pio_sector(ah);
+		break;
+
+	case THR_PIO_LAST:
+		ata_pio_complete(ah);
+		break;
+
+	case THR_PIO_POLL:
+	case THR_PIO_LAST_POLL:
+		timeout = ata_pio_poll(ah);
+		break;
+
+	case THR_PIO_TMOUT:
+		printk(KERN_ERR "ata%d: FIXME: THR_PIO_TMOUT\n", /* FIXME */
+		       ah->id);
+		timeout = 11 * HZ;
+		break;
+
+	case THR_PIO_ERR:
+		printk(KERN_ERR "ata%d: FIXME: THR_PIO_ERR\n", /* FIXME */
+		       ah->id);
+		timeout = 11 * HZ;
+		break;
+
+	default:
+		DPRINTK("unknown thread state %u!\n", ah->thr_state);
+		break;
+	}
+
+	DPRINTK("ata%u: new thr_state %s, returning %ld\n",
+		ah->id, ata_thr_state_name(ah->thr_state), timeout);
+	return timeout;
+}
+
+static int ata_thread (void *data)
+{
+        struct ata_host *ah = data;
+	long timeout;
+
+	daemonize ("katad-%u", ah->id);
+	allow_signal(SIGTERM);
+
+        while (1) {
+		cond_resched();
+
+		timeout = ata_thread_iter(ah);
+
+                if (signal_pending (current))
+                        flush_signals(current);
+
+                if ((timeout < 0) || (ah->time_to_die))
+                        break;
+
+ 		/* note sleeping for full timeout not guaranteed (that's ok) */
+		if (timeout) {
+			mod_timer(&ah->thr_timer, jiffies + timeout);
+			down_interruptible(&ah->thr_sem);
+
+                	if (signal_pending (current))
+                        	flush_signals(current);
+
+                	if (ah->time_to_die)
+                        	break;
+		}
+        }
+
+	printk(KERN_INFO "ata%u: thread exiting\n", ah->id);
+	ah->thr_pid = -1;
+        complete_and_exit (&ah->thr_exited, 0);
+}
+
+static void ata_to_sense_error(struct ata_queued_cmd *qc, Scsi_Cmnd *cmd)
+{
+	cmd->result = SAM_STAT_CHECK_CONDITION;
+
+	cmd->sense_buffer[0] = 0x70;
+	cmd->sense_buffer[2] = MEDIUM_ERROR;
+	cmd->sense_buffer[7] = 14 - 8;	/* addnl. sense len. FIXME: correct? */
+
+	/* additional-sense-code[-qualifier] */
+	if ((qc->flags & ATA_QCFLAG_WRITE) == 0) {
+		cmd->sense_buffer[12] = 0x11; /* "unrecovered read error" */
+		cmd->sense_buffer[13] = 0x04;
+	} else {
+		cmd->sense_buffer[12] = 0x0C; /* "write error -             */
+		cmd->sense_buffer[13] = 0x02; /*  auto-reallocation failed" */
+	}
+
+	qc->done(cmd);
+}
+
+int ata_scsi_error(struct Scsi_Host *host)
+{
+	struct ata_host *ah;
+	struct ata_queued_cmd *qc;
+	Scsi_Cmnd *cmd;
+
+	DPRINTK("ENTER\n");
+	ah = (struct ata_host *) &host->hostdata[0];
+
+	ata_timer(ah);
+
+	/* FIXME: support TCQ */
+	qc = &ah->qcmd[0];
+	if (qc->flags & ATA_QCFLAG_ACTIVE) {
+		DPRINTK("cancelling command\n");
+
+		ata_sg_clean(ah, qc);
+
+		cmd = qc->scsicmd;
+
+		/* FIXME: slackness!!  we just assume medium error */
+		ata_to_sense_error(qc, cmd);
+
+		qc->flags &= ~ATA_QCFLAG_ACTIVE;
+	}
+
+	DPRINTK("EXIT\n");
+	return 0;
+}
+
+static unsigned int scsi_rw_to_ata(struct ata_queued_cmd *qc, u8 *scsicmd,
+				   unsigned int cmd_size)
+{
+	struct ata_taskfile *tf = &qc->tf;
+	unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48;
+	unsigned int pio = tf->flags & ATA_TFLAG_PIO;
+
+	qc->cursect = qc->cursg = qc->cursg_ofs = 0;
+	tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+	tf->hob_nsect = 0;
+	tf->hob_lbal = 0;
+	tf->hob_lbam = 0;
+	tf->hob_lbah = 0;
+
+	if (scsicmd[0] == READ_10 || scsicmd[0] == READ_6 ||
+	    scsicmd[0] == READ_16) {
+		if (pio) {
+			if (lba48)
+				tf->command = ATA_CMD_PIO_READ_EXT;
+			else
+				tf->command = ATA_CMD_PIO_READ;
+		} else {
+			if (lba48)
+				tf->command = ATA_CMD_READ_EXT;
+			else
+				tf->command = ATA_CMD_READ;
+		}
+		qc->flags &= ~ATA_QCFLAG_WRITE;
+		DPRINTK("reading\n");
+	} else {
+		if (pio) {
+			if (lba48)
+				tf->command = ATA_CMD_PIO_WRITE_EXT;
+			else
+				tf->command = ATA_CMD_PIO_WRITE;
+		} else {
+			if (lba48)
+				tf->command = ATA_CMD_WRITE_EXT;
+			else
+				tf->command = ATA_CMD_WRITE;
+		}
+		qc->flags |= ATA_QCFLAG_WRITE;
+		DPRINTK("writing\n");
+	}
+
+	if (cmd_size == 10) {
+		if (lba48) {
+			tf->hob_nsect = scsicmd[7];
+			tf->hob_lbal = scsicmd[2];
+
+			qc->nsect = ((unsigned int)scsicmd[7] << 8) |
+					scsicmd[8];
+		} else {
+			/* if we don't support LBA48 addressing, the request
+			 * -may- be too large. */
+			if ((scsicmd[2] & 0xf0) || scsicmd[7])
+				return 1;
+
+			/* stores LBA27:24 in lower 4 bits of device reg */
+			tf->device |= scsicmd[2];
+
+			qc->nsect = scsicmd[8];
+		}
+		tf->device |= ATA_LBA;
+
+		tf->nsect = scsicmd[8];
+		tf->lbal = scsicmd[5];
+		tf->lbam = scsicmd[4];
+		tf->lbah = scsicmd[3];
+
+		DPRINTK("ten-byte command\n");
+		return 0;
+	}
+
+	if (cmd_size == 6) {
+		qc->nsect = tf->nsect = scsicmd[4];
+		tf->lbal = scsicmd[3];
+		tf->lbam = scsicmd[2];
+		tf->lbah = scsicmd[1] & 0x1f; /* mask out reserved bits */
+
+		DPRINTK("six-byte command\n");
+		return 0;
+	}
+
+	if (cmd_size == 16) {
+		/* rule out impossible LBAs and sector counts */
+		if (scsicmd[2] || scsicmd[3] || scsicmd[10] || scsicmd[11])
+			return 1;
+
+		if (lba48) {
+			tf->hob_nsect = scsicmd[12];
+			tf->hob_lbal = scsicmd[6];
+			tf->hob_lbam = scsicmd[5];
+			tf->hob_lbah = scsicmd[4];
+
+			qc->nsect = ((unsigned int)scsicmd[12] << 8) |
+					scsicmd[13];
+		} else {
+			/* once again, filter out impossible non-zero values */
+			if (scsicmd[4] || scsicmd[5] || scsicmd[12] ||
+			    (scsicmd[6] & 0xf0))
+				return 1;
+
+			/* stores LBA27:24 in lower 4 bits of device reg */
+			tf->device |= scsicmd[2];
+
+			qc->nsect = scsicmd[13];
+		}
+		tf->device |= ATA_LBA;
+
+		tf->nsect = scsicmd[13];
+		tf->lbal = scsicmd[9];
+		tf->lbam = scsicmd[8];
+		tf->lbah = scsicmd[7];
+
+		DPRINTK("sixteen-byte command\n");
+		return 0;
+	}
+
+	DPRINTK("no-byte command\n");
+	return 1;
+}
+
+static void ata_do_rw(struct ata_host *ah, struct ata_device *dev,
+		      Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *),
+		      unsigned int cmd_size)
+{
+	struct ata_queued_cmd *qc;
+	u8 *scsicmd = cmd->cmnd;
+	unsigned int pio;
+
+	VPRINTK("ENTER\n");
+	/* FIXME: support TCQ */
+	qc = &ah->qcmd[0];
+	qc->flags = ATA_QCFLAG_ACTIVE;
+	qc->scsicmd = cmd;
+	qc->done = done;
+
+	ata_dev_select(ah, dev->devno, 0, 1);
+
+	ata_tf_init(ah, &qc->tf);
+	pio = (dev->flags & ATA_DFLAG_PIO);
+	if (pio)
+		qc->tf.flags |= ATA_TFLAG_PIO;
+	else
+		qc->flags |= ATA_QCFLAG_DMA;
+	if (scsi_rw_to_ata(qc, scsicmd, cmd_size))
+		goto err_out;
+
+	/* set up SG table */
+	if (cmd->use_sg) {
+		if (ata_sg_setup(ah, qc))
+			goto err_out;
+	} else {
+		printk(KERN_ERR "ata%u: BUG: cmd->use_sg == 0\n", ah->id);
+		goto err_out;
+	}
+
+	if (pio)
+		ata_pio_start(ah, qc);
+	else
+		ata_dma_start(ah, qc);
+
+	VPRINTK("EXIT\n");
+	return;
+
+err_out:
+	ata_scsi_badcmd(cmd, done, 0x24, 0x00);
+	DPRINTK("EXIT - badcmd\n");
+}
+
+static unsigned int ata_scsi_get_reqbuf(Scsi_Cmnd *cmd, u8 **buf_out)
+{
+	u8 *buf;
+	unsigned int buflen;
+
+	if (cmd->use_sg) {
+		struct scatterlist *sg;
+
+		sg = (struct scatterlist *) cmd->request_buffer;
+		buf = kmap(sg->page) + sg->offset;
+		buflen = sg->length;
+	} else {
+		buf = cmd->request_buffer;
+		buflen = cmd->request_bufflen;
+	}
+
+	memset(buf, 0, buflen);
+	*buf_out = buf;
+	return buflen;
+}
+
+static inline void ata_scsi_put_reqbuf(Scsi_Cmnd *cmd)
+{
+	if (cmd->use_sg) {
+		struct scatterlist *sg;
+
+		sg = (struct scatterlist *) cmd->request_buffer;
+		kunmap(sg->page);
+	}
+}
+
+static void ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *reqbuf,
+			       unsigned int buflen)
+{
+	VPRINTK("ENTER\n");
+
+	reqbuf[0] = TYPE_DISK;
+	reqbuf[2] = 0x5;	/* claim SPC-3 version compatibility */
+	reqbuf[3] = 2;
+	reqbuf[4] = 96 - 4;
+
+	if (buflen > 36) {
+		memcpy(&reqbuf[8], args->dev->vendor, 8);
+		memcpy(&reqbuf[16], args->dev->product, 16);
+		memcpy(&reqbuf[32], DRV_VERSION, 4);
+	}
+
+	if (buflen > 63) {
+		reqbuf[59] = 0x60;	/* SAM-3 (no version claimed) */
+
+		reqbuf[60] = 0x03;
+		reqbuf[61] = 0x20;	/* SBC-2 (no version claimed) */
+
+		reqbuf[62] = 0x02;
+		reqbuf[63] = 0x60;	/* SPC-3 (no version claimed) */
+	}
+}
+
+static void ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *reqbuf,
+			      unsigned int buflen)
+{
+	reqbuf[3] = 3;		/* number of supported EVPD pages */
+
+	if (buflen > 6) {
+		reqbuf[4] = 0x00;	/* page 0x00, this page */
+		reqbuf[5] = 0x80;	/* page 0x80, unit serial no page */
+		reqbuf[6] = 0x83;	/* page 0x83, device ident page */
+	}
+}
+
+static void ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *reqbuf,
+			      unsigned int buflen)
+{
+	reqbuf[1] = 0x80;			/* this page code */
+	reqbuf[3] = ATA_SERNO_LEN;		/* page len */
+
+	if (buflen > (ATA_SERNO_LEN + 4))
+		ata_dev_id_string(args->dev, (unsigned char *) &reqbuf[4],
+				  ATA_ID_SERNO_OFS, ATA_SERNO_LEN);
+}
+
+static const char *inq_83_str = "Linux ATA-SCSI simulator";
+
+static void ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *reqbuf,
+			      unsigned int buflen)
+{
+	reqbuf[1] = 0x83;			/* this page code */
+	reqbuf[3] = 4 + strlen(inq_83_str);	/* page len */
+
+	/* our one and only identification descriptor (vendor-specific) */
+	if (buflen > (strlen(inq_83_str) + 4 + 4)) {
+		reqbuf[4 + 0] = 2;	/* code set: ASCII */
+		reqbuf[4 + 3] = strlen(inq_83_str);
+		memcpy(reqbuf + 4 + 4, inq_83_str, strlen(inq_83_str));
+	}
+}
+
+static void ata_scsiop_noop(struct ata_scsi_args *args, u8 *reqbuf,
+			    unsigned int buflen)
+{
+	VPRINTK("ENTER\n");
+}
+
+static void ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *reqbuf,
+			        unsigned int buflen)
+{
+	u64 n_sectors = args->dev->n_sectors;
+	u32 tmp;
+
+	VPRINTK("ENTER\n");
+
+	tmp = n_sectors;	/* note: truncates, if lba48 */
+	if (args->cmd->cmnd[0] == READ_CAPACITY) {
+		reqbuf[0] = tmp >> (8 * 3);
+		reqbuf[1] = tmp >> (8 * 2);
+		reqbuf[2] = tmp >> (8 * 1);
+		reqbuf[3] = tmp;
+
+		tmp = ATA_SECT_SIZE;
+		reqbuf[6] = tmp >> 8;
+		reqbuf[7] = tmp;
+
+	} else {
+		reqbuf[2] = n_sectors >> (8 * 7);
+		reqbuf[3] = n_sectors >> (8 * 6);
+		reqbuf[4] = n_sectors >> (8 * 5);
+		reqbuf[5] = n_sectors >> (8 * 4);
+		reqbuf[6] = tmp >> (8 * 3);
+		reqbuf[7] = tmp >> (8 * 2);
+		reqbuf[8] = tmp >> (8 * 1);
+		reqbuf[9] = tmp;
+
+		tmp = ATA_SECT_SIZE;
+		reqbuf[12] = tmp >> 8;
+		reqbuf[13] = tmp;
+	}
+}
+
+static void ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *reqbuf,
+				   unsigned int buflen)
+{
+	VPRINTK("ENTER\n");
+	reqbuf[3] = 8;	/* just one lun, LUN 0, size 8 bytes */
+}
+
+static void ata_scsi_reqfill(struct ata_scsi_args *args,
+			     void (*actor) (struct ata_scsi_args *args,
+			     		    u8 *reqbuf,
+					    unsigned int buflen))
+{
+	u8 *reqbuf;
+	unsigned int buflen;
+	Scsi_Cmnd *cmd = args->cmd;
+
+	buflen = ata_scsi_get_reqbuf(cmd, &reqbuf);
+	actor(args, reqbuf, buflen);
+	ata_scsi_put_reqbuf(cmd);
+
+	cmd->result = SAM_STAT_GOOD;
+	args->done(cmd);
+}
+
+static void ata_scsi_badcmd(Scsi_Cmnd *cmd,
+			    void (*done)(Scsi_Cmnd *),
+			    u8 asc, u8 ascq)
+{
+	DPRINTK("ENTER\n");
+	cmd->result = SAM_STAT_CHECK_CONDITION;
+
+	cmd->sense_buffer[0] = 0x70;
+	cmd->sense_buffer[2] = ILLEGAL_REQUEST;
+	cmd->sense_buffer[7] = 14 - 8;	/* addnl. sense len. FIXME: correct? */
+	cmd->sense_buffer[12] = asc;
+	cmd->sense_buffer[13] = ascq;
+
+	done(cmd);
+}
+
+int ata_scsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+	u8 *scsicmd = cmd->cmnd;
+	struct ata_host *ah;
+	struct ata_device *dev;
+	struct ata_scsi_args args;
+
+	DPRINTK("CDB (%d,%d,%d) %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+		cmd->channel, cmd->target, cmd->lun,
+		scsicmd[0], scsicmd[1], scsicmd[2], scsicmd[3],
+		scsicmd[4], scsicmd[5], scsicmd[6], scsicmd[7],
+		scsicmd[8]);
+
+	/* skip commands not addressed to targets we care about */
+	if ((cmd->device->channel != 0) || (cmd->device->lun != 0) ||
+	    (cmd->device->id >= ATA_MAX_DEVICES)) {
+		cmd->result = (DID_BAD_TARGET << 16); /* FIXME: correct?  */
+		done(cmd);
+		return 0;
+	}
+
+	ah = (struct ata_host *) &cmd->device->host->hostdata[0];
+	dev = &ah->device[cmd->device->id];
+
+	DPRINTK("ata%u: chan %u, target %u, lun %u\n",
+		ah->id, cmd->device->channel, cmd->device->id,
+		cmd->device->lun);
+
+	if (!ata_dev_present(dev)) {
+		DPRINTK("no ATA device\n");
+		cmd->result = (DID_BAD_TARGET << 16); /* FIXME: correct?  */
+		done(cmd);
+		return 0;
+	}
+
+	if (dev->class == ATA_DEV_ATAPI) {
+		DPRINTK("FIXME: support ATAPI\n");
+		cmd->result = (DID_BAD_TARGET << 16); /* FIXME: correct?  */
+		done(cmd);
+		return 0;
+	}
+
+	/* fast path */
+	switch(scsicmd[0]) {
+		case READ_6:
+		case WRITE_6:
+			ata_do_rw(ah, dev, cmd, done, 6);
+			return 0;
+
+		case READ_10:
+		case WRITE_10:
+			ata_do_rw(ah, dev, cmd, done, 10);
+			return 0;
+
+		case READ_16:
+		case WRITE_16:
+			ata_do_rw(ah, dev, cmd, done, 16);
+			return 0;
+
+		default:
+			/* do nothing */
+			break;
+	}
+
+	/*
+	 * slow path
+	 */
+
+	args.ah = ah;
+	args.dev = dev;
+	args.cmd = cmd;
+	args.done = done;
+
+	switch(scsicmd[0]) {
+		case TEST_UNIT_READY:		/* FIXME: correct? */
+		case FORMAT_UNIT:		/* FIXME: correct? */
+		case SEND_DIAGNOSTIC:		/* FIXME: correct? */
+			ata_scsi_reqfill(&args, ata_scsiop_noop);
+			break;
+
+		case INQUIRY:
+			if (scsicmd[1] & 2)	           /* is CmdDt set?  */
+				ata_scsi_badcmd(cmd, done, 0x24, 0x00);
+			else if ((scsicmd[1] & 1) == 0)    /* is EVPD clear? */
+				ata_scsi_reqfill(&args, ata_scsiop_inq_std);
+			else if (scsicmd[2] == 0x00)
+				ata_scsi_reqfill(&args, ata_scsiop_inq_00);
+			else if (scsicmd[2] == 0x80)
+				ata_scsi_reqfill(&args, ata_scsiop_inq_80);
+			else if (scsicmd[2] == 0x83)
+				ata_scsi_reqfill(&args, ata_scsiop_inq_83);
+			else
+				ata_scsi_badcmd(cmd, done, 0x24, 0x00);
+			break;
+
+		case READ_CAPACITY:
+			ata_scsi_reqfill(&args, ata_scsiop_read_cap);
+			break;
+
+		case SERVICE_ACTION_IN:
+			if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16)
+				ata_scsi_reqfill(&args, ata_scsiop_read_cap);
+			else
+				ata_scsi_badcmd(cmd, done, 0x24, 0x00);
+			break;
+
+		case REPORT_LUNS:
+			ata_scsi_reqfill(&args, ata_scsiop_report_luns);
+			break;
+
+		/* mandantory commands we haven't implemented yet */
+		case REQUEST_SENSE:
+
+		/* all other commands */
+		default:
+			ata_scsi_badcmd(cmd, done, 0x20, 0x00);
+			break;
+	}
+
+	return 0;
+}
+
+static void ata_host_remove(struct ata_host *ah, unsigned int do_unregister)
+{
+	struct Scsi_Host *sh = ah->host;
+
+	DPRINTK("ENTER\n");
+	outl(0, ah->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS);
+	pci_free_consistent(ah->host_set->pdev, ATA_PRD_TBL_SZ, ah->prd, ah->prd_dma);
+
+	if (do_unregister)
+		scsi_unregister(sh);
+}
+
+static struct ata_host * __init ata_host_add(struct ata_probe_ent *ent,
+				struct ata_host_set *host_set,
+				unsigned int port_no)
+{
+	struct pci_dev *pdev = ent->pdev;
+	struct Scsi_Host *host;
+	struct ata_host *ah;
+	unsigned int i;
+
+	DPRINTK("ENTER\n");
+	host = scsi_register(ent->sht, sizeof(struct ata_host));
+	if (!host)
+		return NULL;
+
+	host->max_id = 16;
+	host->max_lun = 1;
+	host->max_channel = 1;
+	host->unique_id = ata_unique_id++;
+	host->max_cmd_len = 16;
+
+	ah = (struct ata_host *) &host->hostdata[0];
+	ah->flags = ATA_FLAG_PORT_DISABLED;
+	ah->id = host->unique_id;
+	ah->host = host;
+	ah->ctl = ATA_DEVCTL_OBS;
+	ah->host_set = host_set;
+	ah->port_no = port_no;
+	ah->pio_mask = ent->pio_mask;
+	ah->udma_mask = ent->udma_mask;
+	ah->flags |= ent->host_flags;
+	ah->ops = ent->host_ops;
+	ah->bus_state = BUS_UNKNOWN;
+	ah->thr_state = THR_PROBE_START;
+	ah->device[0].flags = ATA_DFLAG_MASTER;
+	for (i = 0; i < ATA_MAX_DEVICES; i++)
+		ah->device[i].devno = i;
+	init_completion(&ah->thr_exited);
+	init_MUTEX_LOCKED(&ah->probe_sem);
+	init_MUTEX_LOCKED(&ah->sem);
+	init_MUTEX_LOCKED(&ah->thr_sem);
+	init_timer(&ah->thr_timer);
+	ah->thr_timer.function = ata_thread_timer;
+	ah->thr_timer.data = (unsigned long) ah;
+#ifdef ATA_IRQ_TRAP
+	ah->stats.unhandled_irq = 1;
+	ah->stats.idle_irq = 1;
+#endif
+
+	memcpy(&ah->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports));
+
+	ah->prd = pci_alloc_consistent(pdev, ATA_PRD_TBL_SZ, &ah->prd_dma);
+	if (!ah->prd)
+		goto err_out;
+	DPRINTK("prd alloc, virt %p, dma %x\n", ah->prd, ah->prd_dma);
+
+	ah->thr_pid = kernel_thread(ata_thread, ah, CLONE_FS | CLONE_FILES);
+	if (ah->thr_pid < 0) {
+		printk(KERN_ERR "ata%d: unable to start kernel thread\n",
+		       ah->id);
+		goto err_out_free;
+	}
+
+	return ah;
+
+err_out_free:
+	pci_free_consistent(ah->host_set->pdev, ATA_PRD_TBL_SZ, ah->prd, ah->prd_dma);
+err_out:
+	scsi_unregister(host);
+	return NULL;
+}
+
+static int __init ata_device_add(struct ata_probe_ent *ent)
+{
+	unsigned int count = 0, i;
+	struct pci_dev *pdev = ent->pdev;
+	struct ata_host_set *host_set;
+	struct ata_host *host;
+
+	DPRINTK("ENTER\n");
+	/* alloc a container for our list of ATA ports (buses) */
+	host_set = kmalloc(sizeof(struct ata_host_set) +
+			   (ent->n_ports * sizeof(void *)), GFP_KERNEL);
+	if (!host_set)
+		return 0;
+	memset(host_set, 0, sizeof(struct ata_host_set) + (ent->n_ports * sizeof(void *)));
+	INIT_LIST_HEAD(&host_set->driver_list);
+	spin_lock_init(&host_set->lock);
+
+	host_set->pdev = pdev;
+	host_set->n_hosts = ent->n_ports;
+	host_set->irq = ent->irq;
+
+	/* register each port bound to this device */
+	for (i = 0; i < ent->n_ports; i++) {
+		host = ata_host_add(ent, host_set, i);
+		if (!host)
+			goto err_out;
+
+		host_set->hosts[i] = host;
+
+		/* print per-port info to dmesg */
+		printk(KERN_INFO "ata%u: %cATA max %s cmd 0x%lX ctl 0x%lX "
+				 "bmdma 0x%lX irq %lu\n",
+			host->id,
+			host->flags & ATA_FLAG_SATA ? 'S' : 'P',
+			ata_udma_string(ent->udma_mask),
+	       		host->ioaddr.cmd_addr,
+	       		host->ioaddr.ctl_addr,
+	       		host->ioaddr.bmdma_addr,
+	       		ent->irq);
+
+		count++;
+	}
+
+	if (!count) {
+		kfree(host_set);
+		return 0;
+	}
+
+	/* obtain irq, that is shared between channels */
+	if (request_irq(ent->irq, ata_interrupt, ent->irq_flags,
+			DRV_NAME, host_set))
+		goto err_out;
+
+	/* perform each probe synchronously */
+	DPRINTK("probe begin\n");
+	for (i = 0; i < count; i++) {
+		DPRINTK("ata%u: probe begin\n", host_set->hosts[i]->id);
+		up(&host_set->hosts[i]->sem);		/* start probe */
+		DPRINTK("ata%u: probe-wait begin\n", host_set->hosts[i]->id);
+		down(&host_set->hosts[i]->probe_sem);	/* wait for end */
+		DPRINTK("ata%u: probe-wait end\n", host_set->hosts[i]->id);
+	}
+
+	/* add to driver-global list */
+	spin_lock(&ata_module_lock);
+	list_add_tail(&host_set->driver_list, &ata_scsihost_list);
+	spin_unlock(&ata_module_lock);
+
+	pci_set_drvdata(pdev, host_set);
+
+	VPRINTK("EXIT, returning %u\n", ent->n_ports);
+	return ent->n_ports; /* success */
+
+err_out:
+	for (i = 0; i < count; i++)
+		ata_host_remove(host_set->hosts[i], 1);
+	kfree(host_set);
+	VPRINTK("EXIT, returning 0\n");
+	return 0;
+}
+
+int ata_scsi_detect(Scsi_Host_Template *sht)
+{
+	struct list_head *node;
+	struct ata_probe_ent *ent;
+	int count = 0;
+
+	VPRINTK("ENTER\n");
+
+	spin_lock(&ata_module_lock);
+	while (!list_empty(&ata_probe_list)) {
+		node = ata_probe_list.next;
+		ent = list_entry(node, struct ata_probe_ent, node);
+		list_del(node);
+
+		spin_unlock(&ata_module_lock);
+
+		count += ata_device_add(ent);
+		kfree(ent);
+
+		spin_lock(&ata_module_lock);
+	}
+	spin_unlock(&ata_module_lock);
+
+	VPRINTK("EXIT, returning %d\n", count);
+	return count;
+}
+
+int ata_scsi_release(struct Scsi_Host *host)
+{
+	struct ata_host *ah = (struct ata_host *) &host->hostdata[0];
+	int ret = 0;
+
+	DPRINTK("ENTER\n");
+
+	if (ah->thr_pid >= 0) {
+		ah->time_to_die = 1;
+		wmb();
+		ret = kill_proc(ah->thr_pid, SIGTERM, 1);
+		if (ret) {
+			printk(KERN_ERR "ata%d: unable to kill kernel thread\n",
+			       ah->id);
+			return 0;	/* FIXME: correct ret code? */
+		}
+		wait_for_completion(&ah->thr_exited);
+	}
+
+	ah->ops->port_disable(ah);
+	ata_host_remove(ah, 0);
+
+	DPRINTK("EXIT\n");
+	return 1;
+}
+
+int ata_pci_init_one (struct pci_dev *pdev, struct ata_board *board_info,
+		      struct ata_host_ops *host_ops)
+{
+	struct ata_probe_ent *probe_ent, *probe_ent2 = NULL;
+	u8 tmp8, mask;
+	unsigned int legacy_mode = 0;
+	int rc;
+
+	DPRINTK("ENTER\n");
+
+	/* require that firmware put us in native mode.
+	 * FIXME: temporary restriction.
+	 */
+	pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
+	mask = (1 << 2) | (1 << 0);
+	if ((tmp8 & mask) != mask)
+		legacy_mode = 1;
+
+	rc = pci_enable_device(pdev);
+	if (rc)
+		return rc;
+
+	rc = pci_request_regions(pdev, DRV_NAME);
+	if (rc)
+		goto err_out;
+
+	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+	if (rc)
+		goto err_out_regions;
+
+	probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+	if (!probe_ent) {
+		rc = -ENOMEM;
+		goto err_out_regions;
+	}
+
+	memset(probe_ent, 0, sizeof(*probe_ent));
+	probe_ent->pdev = pdev;
+	INIT_LIST_HEAD(&probe_ent->node);
+
+	if (legacy_mode) {
+		probe_ent2 = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+		if (!probe_ent2) {
+			rc = -ENOMEM;
+			goto err_out_free_ent;
+		}
+
+		memset(probe_ent2, 0, sizeof(*probe_ent));
+		probe_ent2->pdev = pdev;
+		INIT_LIST_HEAD(&probe_ent2->node);
+	}
+
+	probe_ent->pio_mask = board_info->pio_mask;
+	probe_ent->udma_mask = board_info->udma_mask;
+	probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4);
+	probe_ent->host_flags = board_info->host_flags;
+	probe_ent->host_ops = host_ops;
+	probe_ent->sht = board_info->sht;
+
+	if (legacy_mode) {
+		probe_ent->port[0].cmd_addr = 0x1f0;
+		probe_ent->port[0].ctl_addr = 0x3f6;
+		probe_ent->n_ports = 1;
+		probe_ent->irq = 14;
+
+		probe_ent2->port[0].cmd_addr = 0x170;
+		probe_ent2->port[0].ctl_addr = 0x376;
+		probe_ent2->port[0].bmdma_addr = pci_resource_start(pdev, 4)+8;
+		probe_ent2->n_ports = 1;
+		probe_ent2->irq = 15;
+
+		probe_ent2->pio_mask = board_info->pio_mask;
+		probe_ent2->udma_mask = board_info->udma_mask;
+		probe_ent2->host_flags = board_info->host_flags;
+		probe_ent2->host_ops = host_ops;
+		probe_ent2->sht = board_info->sht;
+	} else {
+		probe_ent->port[0].cmd_addr = pci_resource_start(pdev, 0);
+		probe_ent->port[0].ctl_addr =
+			pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS;
+
+		probe_ent->port[1].cmd_addr = pci_resource_start(pdev, 2);
+		probe_ent->port[1].ctl_addr =
+			pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS;
+		probe_ent->port[1].bmdma_addr = pci_resource_start(pdev, 4) + 8;
+
+		probe_ent->n_ports = 2;
+		probe_ent->irq = pdev->irq;
+		probe_ent->irq_flags = SA_SHIRQ;
+	}
+
+	pci_set_master(pdev);
+
+	spin_lock(&ata_module_lock);
+	list_add_tail(&probe_ent->node, &ata_probe_list);
+	if (legacy_mode)
+		list_add_tail(&probe_ent2->node, &ata_probe_list);
+	spin_unlock(&ata_module_lock);
+
+	return 0;
+
+err_out_free_ent:
+	kfree(probe_ent);
+err_out_regions:
+	pci_release_regions(pdev);
+err_out:
+	pci_disable_device(pdev);
+	return rc;
+}
+
+void ata_pci_remove_one (struct pci_dev *pdev)
+{
+	struct ata_host_set *host_set = pci_get_drvdata(pdev);
+
+	/* FIXME: remove host_set from module-global list */
+
+	free_irq(host_set->irq, host_set);
+	kfree(host_set);
+
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+}
+
+static int __init ata_init(void)
+{
+	printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
+	return 0;
+}
+
+module_init(ata_init);
+
+EXPORT_SYMBOL(ata_port_probe);
+EXPORT_SYMBOL(ata_port_disable);
+EXPORT_SYMBOL(ata_pci_init_one);
+EXPORT_SYMBOL(ata_pci_remove_one);
+EXPORT_SYMBOL(ata_scsi_detect);
+EXPORT_SYMBOL(ata_scsi_release);
+EXPORT_SYMBOL(ata_scsi_queuecmd);
+EXPORT_SYMBOL(ata_scsi_error);
+
diff -Nru a/include/linux/ata.h b/include/linux/ata.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/linux/ata.h	Sat May 24 14:36:45 2003
@@ -0,0 +1,483 @@
+/*
+   Copyright 2003 Red Hat, Inc.  All rights reserved.
+   Copyright 2003 Jeff Garzik
+
+   The contents of this file are subject to the Open 
+   Software License version 1.1 that can be found at
+   http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+   by reference.
+
+   Alternatively, the contents of this file may be used under the terms
+   of the GNU General Public License version 2 (the "GPL") as distributed
+   in the kernel source COPYING file, in which case the provisions of
+   the GPL are applicable instead of the above.  If you wish to allow
+   the use of your version of this file only under the terms of the
+   GPL and not to allow others to use your version of this file under
+   the OSL, indicate your decision by deleting the provisions above and
+   replace them with the notice and other provisions required by the GPL.
+   If you do not delete the provisions above, a recipient may use your
+   version of this file under either the OSL or the GPL.
+
+ */
+
+#ifndef __LINUX_ATA_H__
+#define __LINUX_ATA_H__
+
+#include <linux/blkdev.h>
+#include <linux/scsi_defs.h>
+#include <linux/scsi_hosts.h>
+#include <linux/delay.h>
+
+/*
+ * compile-time options
+ */
+#undef ATA_FORCE_PIO		/* do not configure or use DMA */
+#undef ATA_READ_POISON		/* poison membufs before data is read in */
+#undef ATA_DEBUG		/* debugging output */
+#undef ATA_VERBOSE_DEBUG	/* yet more debugging output */
+#undef ATA_IRQ_TRAP		/* define to ack screaming irqs */
+#undef ATA_NDEBUG		/* define to disable quick runtime checks */
+
+
+/* note: prints function name for you */
+#ifdef ATA_DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
+#ifdef ATA_VERBOSE_DEBUG
+#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
+#else
+#define VPRINTK(fmt, args...)
+#endif	/* ATA_VERBOSE_DEBUG */
+#else
+#define DPRINTK(fmt, args...)
+#define VPRINTK(fmt, args...)
+#endif	/* ATA_DEBUG */
+
+#ifdef ATA_NDEBUG
+#define assert(expr)
+#else
+#define assert(expr) \
+        if(!(expr)) {                                   \
+        printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
+        #expr,__FILE__,__FUNCTION__,__LINE__);          \
+        }
+#endif
+
+enum {
+	/* various global constants */
+	ATA_MAX_PORTS		= 2,
+	ATA_MAX_DEVICES		= 2,	/* per bus/port */
+	ATA_DEF_QUEUE		= 1,
+	ATA_MAX_QUEUE		= 1,
+	ATA_MAX_PRD		= 256,	/* we could make these 256/256 */
+	ATA_MAX_SECTORS		= 200,	/* FIXME */
+	ATA_MAX_BUS		= 2,
+	ATA_SECT_SIZE		= 512,
+	ATA_SECT_SIZE_MASK	= (ATA_SECT_SIZE - 1),
+	ATA_SECT_DWORDS		= ATA_SECT_SIZE / sizeof(u32),
+	ATA_ID_WORDS		= 256,
+	ATA_ID_PROD_OFS		= 27,
+	ATA_ID_SERNO_OFS	= 10,
+	ATA_ID_MAJOR_VER	= 80,
+	ATA_ID_PIO_MODES	= 64,
+	ATA_ID_UDMA_MODES	= 88,
+	ATA_ID_PIO4		= (1 << 1),
+	ATA_DEF_BUSY_WAIT	= 10000,
+	ATA_PCI_CTL_OFS		= 2,
+	ATA_SHORT_PAUSE		= (HZ >> 6) + 1,
+	ATA_SERNO_LEN		= 20,
+
+	ATA_SHT_EMULATED	= 1,
+	ATA_SHT_CMD_PER_LUN	= 1,
+	ATA_SHT_THIS_ID		= -1,
+	ATA_SHT_USE_CLUSTERING	= 0,	/* FIXME: which is best, 0 or 1?  */
+
+	/* DMA-related */
+	ATA_PRD_SZ		= 8,
+	ATA_PRD_TBL_SZ		= (ATA_MAX_PRD * ATA_PRD_SZ),
+	ATA_PRD_EOT		= (1 << 31),	/* end-of-table flag */
+
+	ATA_DMA_MASK		= 0xffffffff,
+	ATA_DMA_TABLE_OFS	= 4,
+	ATA_DMA_STATUS		= 2,
+	ATA_DMA_CMD		= 0,
+	ATA_DMA_WR		= (1 << 3),
+	ATA_DMA_START		= (1 << 0),
+	ATA_DMA_INTR		= (1 << 2),
+	ATA_DMA_ERR		= (1 << 1),
+	ATA_DMA_ACTIVE		= (1 << 0),
+
+	/* bits in ATA command block registers */
+	ATA_HOB			= (1 << 7),	/* LBA48 selector */
+	ATA_NIEN		= (1 << 1),	/* disable-irq flag */
+	ATA_LBA			= (1 << 6),	/* LBA28 selector */
+	ATA_DEV1		= (1 << 4),	/* Select Device 1 (slave) */
+	ATA_BUSY		= (1 << 7),	/* BSY status bit */
+	ATA_DEVICE_OBS		= (1 << 7) | (1 << 5), /* obs bits in dev reg */
+	ATA_DEVCTL_OBS		= (1 << 3),
+	ATA_DRQ			= (1 << 3),
+	ATA_ERR			= (1 << 0),
+
+	/* ATA command block registers */
+	ATA_REG_DATA		= 0x00,
+	ATA_REG_ERR		= 0x01,
+	ATA_REG_NSECT		= 0x02,
+	ATA_REG_LBAL		= 0x03,
+	ATA_REG_LBAM		= 0x04,
+	ATA_REG_LBAH		= 0x05,
+	ATA_REG_DEVICE		= 0x06,
+	ATA_REG_STATUS		= 0x07,
+
+	ATA_REG_FEATURE		= ATA_REG_ERR, /* and their aliases */
+	ATA_REG_CMD		= ATA_REG_STATUS,
+	ATA_REG_BYTEL		= ATA_REG_LBAM,
+	ATA_REG_BYTEH		= ATA_REG_LBAH,
+	ATA_REG_DEVSEL		= ATA_REG_DEVICE,
+	ATA_REG_IRQ		= ATA_REG_NSECT,
+
+	/* struct ata_device stuff */
+	ATA_DFLAG_LBA48		= (1 << 0), /* device supports LBA48 */
+	ATA_DFLAG_PIO		= (1 << 1), /* device currently in PIO mode */
+	ATA_DFLAG_MASTER	= (1 << 2), /* is device 0? */
+
+	ATA_DEV_UNKNOWN		= 0,	/* unknown device */
+	ATA_DEV_ATA		= 1,	/* ATA device */
+	ATA_DEV_ATAPI		= 2,	/* ATAPI device */
+	ATA_DEV_NONE		= 3,	/* no device */
+
+	/* struct ata_host flags */
+	ATA_FLAG_SLAVE_POSS	= (1 << 1), /* host supports slave dev */
+					    /* (doesn't imply presence) */
+	ATA_FLAG_PORT_DISABLED	= (1 << 2), /* port is disabled, ignore it */
+	ATA_FLAG_SATA		= (1 << 3),
+
+	/* struct ata_taskfile flags */
+	ATA_TFLAG_LBA48		= (1 << 0),
+	ATA_TFLAG_DATAREG	= (1 << 1),
+	ATA_TFLAG_ISADDR	= (1 << 2), /* enable r/w to nsect/lba regs */
+	ATA_TFLAG_DEVICE	= (1 << 4), /* enable r/w to device reg */
+	ATA_TFLAG_PIO		= (1 << 5),
+
+	ATA_QCFLAG_WRITE	= (1 << 0), /* read==0, write==1 */
+	ATA_QCFLAG_ACTIVE	= (1 << 1), /* cmd not yet ack'd to scsi lyer */
+	ATA_QCFLAG_DMA		= (1 << 2), /* data delivered via DMA */
+
+	/* ATA device commands */
+	ATA_CMD_EDD		= 0x90,	/* execute device diagnostic */
+	ATA_CMD_ID_ATA		= 0xEC,
+	ATA_CMD_ID_ATAPI	= 0xA1,
+	ATA_CMD_READ		= 0xC8,
+	ATA_CMD_READ_EXT	= 0x25,
+	ATA_CMD_WRITE		= 0xCA,
+	ATA_CMD_WRITE_EXT	= 0x35,
+	ATA_CMD_PIO_READ	= 0x20,
+	ATA_CMD_PIO_READ_EXT	= 0x24,
+	ATA_CMD_PIO_WRITE	= 0x30,
+	ATA_CMD_PIO_WRITE_EXT	= 0x34,
+	ATA_CMD_SET_FEATURES	= 0xEF,
+
+	/* various lengths of time */
+	ATA_TMOUT_BOOT		= 30,		/* seconds */
+	ATA_TMOUT_EDD		= 5 * HZ,	/* just a guess */
+	ATA_TMOUT_PIO		= 30 * HZ,
+
+	/* ATA bus states */
+	BUS_UNKNOWN		= 0,
+	BUS_DMA			= 1,
+	BUS_IDLE		= 2,
+	BUS_NOINTR		= 3,
+	BUS_NODATA		= 4,
+	BUS_TIMER		= 5,
+	BUS_PIO			= 6,
+	BUS_EDD			= 7,
+	BUS_IDENTIFY		= 8,
+
+	/* thread states */
+	THR_UNKNOWN		= 0,
+	THR_CHECKPORT		= 1,
+	THR_EDD			= 2,
+	THR_AWAIT_DEATH		= 3,
+	THR_IDENTIFY		= 4,
+	THR_CONFIG_PIO		= 5,
+	THR_CONFIG_DMA		= 6,
+	THR_PROBE_FAILED	= 7,
+	THR_IDLE		= 8,
+	THR_PROBE_SUCCESS	= 9,
+	THR_PROBE_START		= 10,
+	THR_CONFIG_FORCE_PIO	= 11,
+	THR_PIO_POLL		= 12,
+	THR_PIO_TMOUT		= 13,
+	THR_PIO			= 14,
+	THR_PIO_LAST		= 15,
+	THR_PIO_LAST_POLL	= 16,
+	THR_PIO_ERR		= 17,
+
+	/* SATA port states */
+	PORT_UNKNOWN		= 0,
+	PORT_ENABLED		= 1,
+	PORT_DISABLED		= 2,
+
+	/* SETFEATURES stuff */
+	SETFEATURES_XFER	= 0x03,
+	XFER_UDMA_7		= 0x47,
+	XFER_UDMA_6		= 0x46,
+	XFER_UDMA_5		= 0x45,
+	XFER_UDMA_4		= 0x44,
+	XFER_UDMA_3		= 0x43,
+	XFER_UDMA_2		= 0x42,
+	XFER_UDMA_1		= 0x41,
+	XFER_UDMA_0		= 0x40,
+	XFER_PIO_4		= 0x0C,
+	XFER_PIO_3		= 0x0B,
+};
+
+struct ata_prd {
+	u32			addr;
+	u32			flags_len;
+} __attribute__((packed));
+
+struct ata_ioports {
+	unsigned long		cmd_addr;
+	unsigned long		ctl_addr;
+	unsigned long		bmdma_addr;
+};
+
+struct ata_probe_ent {
+	struct list_head	node;
+	struct pci_dev		*pdev;
+	struct ata_host_ops	*host_ops;
+	Scsi_Host_Template	*sht;
+	struct ata_ioports	port[ATA_MAX_PORTS];
+	unsigned int		n_ports;
+	unsigned int		pio_mask;
+	unsigned int		udma_mask;
+	unsigned int		legacy_mode;
+	unsigned long		irq;
+	unsigned int		irq_flags;
+	unsigned long		host_flags;
+};
+
+struct ata_host_set {
+	struct list_head	driver_list;
+	spinlock_t		lock;
+	struct pci_dev		*pdev;
+	unsigned long		irq;
+	unsigned int		n_hosts;
+	struct ata_host *	hosts[0];
+};
+
+struct ata_taskfile {
+	unsigned long		flags;		/* ATA_TFLAG_xxx */
+
+	u8			data;		/* command registers */
+	u8			feature;
+	u8			nsect;
+	u8			lbal;
+	u8			lbam;
+	u8			lbah;
+	u8			device;
+
+	u8			command;	/* IO operation */
+
+	u8			hob_feature;	/* additional data */
+	u8			hob_nsect;	/* to support LBA48 */
+	u8			hob_lbal;
+	u8			hob_lbam;
+	u8			hob_lbah;
+	u8			hob_data;
+
+	u8			ctl;		/* control reg/altstatus */
+};
+
+struct ata_queued_cmd {
+	unsigned long		flags;		/* ATA_QCFLAG_xxx */
+	unsigned int		n_elem;
+	unsigned int		nsect;
+	unsigned int		cursect;
+	unsigned int		cursg;
+	unsigned int		cursg_ofs;
+	Scsi_Cmnd		*scsicmd;
+	void			(*done)(Scsi_Cmnd *);
+	struct ata_taskfile	tf;
+	struct scatterlist	sgent;
+};
+
+struct ata_host_stats {
+	unsigned long		unhandled_irq;
+	unsigned long		idle_irq;
+	unsigned long		rw_reqbuf;
+};
+
+struct ata_device {
+	u64			n_sectors;	/* size of device, if ATA */
+	unsigned long		flags;		/* ATA_DFLAG_xxx */
+	unsigned int		class;		/* ATA_DEV_xxx */
+	unsigned int		devno;		/* 0 or 1 */
+	u16			id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
+	unsigned int		pio_mode;
+	unsigned int		udma_mode;
+
+	unsigned char		vendor[8];	/* space-padded, not ASCIIZ */
+	unsigned char		product[16];
+};
+
+struct ata_host_ops;
+struct ata_host {
+	struct Scsi_Host	*host;	/* our co-allocated scsi host */
+	struct ata_host_ops	*ops;
+	unsigned long		flags;	/* ATA_FLAG_xxx */
+	unsigned int		id;	/* unique id req'd by scsi midlyr */
+	unsigned int		port_no; /* unique port #; from zero */
+
+	struct ata_prd		*prd;	 /* our SG list */
+	dma_addr_t		prd_dma; /* and its DMA mapping */
+
+	struct ata_ioports	ioaddr;	/* ATA cmd/ctl/dma register blocks */
+
+	u8			ctl;	/* cache of ATA control register */
+	u8			dmactl; /* cache of DMA control register */
+	u8			devsel;	/* cache of Device Select reg */
+	unsigned int		bus_state;
+	unsigned int		port_state;
+	unsigned int		pio_mask;
+	unsigned int		udma_mask;
+
+	struct ata_device	device[ATA_MAX_DEVICES];
+	struct ata_queued_cmd	qcmd[ATA_MAX_QUEUE];
+	struct ata_host_stats	stats;
+	struct ata_host_set	*host_set;
+
+	struct semaphore	sem;
+	struct semaphore	probe_sem;
+
+	unsigned int		thr_state;
+	int			time_to_die;
+	pid_t			thr_pid;
+	struct completion	thr_exited;
+	struct semaphore	thr_sem;
+	struct timer_list	thr_timer;
+	unsigned long		thr_timeout;
+};
+
+struct ata_host_ops {
+	void (*port_probe) (struct ata_host *);
+	void (*port_disable) (struct ata_host *);
+
+	void (*set_piomode) (struct ata_host *, struct ata_device *,
+			     unsigned int);
+	void (*set_udmamode) (struct ata_host *, struct ata_device *,
+			     unsigned int);
+};
+
+struct ata_board {
+	Scsi_Host_Template	*sht;
+	unsigned long		host_flags;
+	unsigned long		pio_mask;
+	unsigned long		udma_mask;
+};
+
+#define ata_id_is_ata(dev)	(((dev)->id[0] & (1 << 15)) == 0)
+#define ata_id_has_lba48(dev)	((dev)->id[83] & (1 << 10))
+#define ata_id_has_lba(dev)	((dev)->id[49] & (1 << 8))
+#define ata_id_has_dma(dev)	((dev)->id[49] & (1 << 9))
+#define ata_id_u32(dev,n)	\
+	(((u32) (dev)->id[(n) + 1] << 16) | ((u32) (dev)->id[(n)]))
+#define ata_id_u64(dev,n)	\
+	( ((u64) dev->id[(n) + 3] << 48) |	\
+	  ((u64) dev->id[(n) + 2] << 32) |	\
+	  ((u64) dev->id[(n) + 1] << 16) |	\
+	  ((u64) dev->id[(n) + 0]) )  
+
+extern void ata_port_probe(struct ata_host *);
+extern void ata_port_disable(struct ata_host *);
+extern int ata_pci_init_one (struct pci_dev *, struct ata_board *,
+			     struct ata_host_ops *);
+extern void ata_pci_remove_one (struct pci_dev *pdev);
+extern int ata_scsi_detect(Scsi_Host_Template *sht);
+extern int ata_scsi_release(struct Scsi_Host *host);
+extern int ata_scsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+extern int ata_scsi_error(struct Scsi_Host *host);
+
+static inline unsigned long msecs_to_jiffies(unsigned long msecs)
+{
+	return ((HZ * msecs + 999) / 1000);
+}
+
+static inline unsigned int ata_dev_present(struct ata_device *dev)
+{
+	return ((dev->class == ATA_DEV_ATA) ||
+		(dev->class == ATA_DEV_ATAPI));
+}
+
+static inline u8 ata_chk_status(struct ata_ioports *ioaddr)
+{
+	return inb(ioaddr->cmd_addr + ATA_REG_STATUS);
+}
+
+static inline u8 ata_busy_wait(struct ata_ioports *ioaddrs, unsigned int bits,
+			       unsigned int max)
+{
+	u8 status;
+
+	do {
+		udelay(10);
+		status = ata_chk_status(ioaddrs);
+		max--;
+	} while ((status & bits) && (max > 0));
+
+	return status;
+}
+
+static inline u8 ata_wait_idle(struct ata_ioports *ioaddrs)
+{
+	u8 status = ata_busy_wait(ioaddrs, ATA_BUSY | ATA_DRQ, 1000);
+
+	if (status & (ATA_BUSY | ATA_DRQ)) {
+		unsigned long l = ioaddrs->cmd_addr + ATA_REG_STATUS;
+		printk(KERN_WARNING
+		       "ATA: abnormal status 0x%X on port 0x%lX\n",
+		       status, l);
+	}
+
+	return status;
+}
+
+static inline void ata_tf_init(struct ata_host *ah, struct ata_taskfile *tf)
+{
+	memset(tf, 0, sizeof(*tf));
+
+	tf->ctl = ah->ctl;
+	tf->device = ah->devsel;
+}
+
+static inline void ata_irq_on(struct ata_host *ah)
+{
+	struct ata_ioports *ioaddr = &ah->ioaddr;
+
+	ah->ctl &= ~ATA_NIEN;
+	outb(ah->ctl, ioaddr->ctl_addr);
+
+	ata_wait_idle(ioaddr);
+}
+
+static inline u8 ata_irq_ack(struct ata_host *ah, unsigned int chk_drq)
+{
+	unsigned int bits = chk_drq ? ATA_BUSY | ATA_DRQ : ATA_BUSY;
+	u8 host_stat, status;
+
+	status = ata_busy_wait(&ah->ioaddr, bits, 1000);
+	if (status & bits)
+		DPRINTK("abnormal status 0x%X\n", status);
+
+	/* get controller status; clear intr, err bits */
+	host_stat = inb(ah->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+	outb(host_stat | ATA_DMA_INTR | ATA_DMA_ERR,
+	     ah->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+
+	VPRINTK("irq ack: host_stat 0x%X, new host_stat 0x%X, drv_stat 0x%X\n",
+		host_stat, inb(ah->ioaddr.bmdma_addr + ATA_DMA_STATUS),
+		status);
+
+	return status;
+}
+
+#endif /* __LINUX_ATA_H__ */

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-24 19:51 [RFR] a new SCSI driver Jeff Garzik
@ 2003-05-26  2:59 ` Douglas Gilbert
  2003-05-26 20:52   ` Ishikawa
  2003-05-26 15:16 ` Rabeeh Khoury
  2003-05-27 11:38 ` Douglas Gilbert
  2 siblings, 1 reply; 18+ messages in thread
From: Douglas Gilbert @ 2003-05-26  2:59 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-scsi

Jeff Garzik wrote:
> 
<snip/>
> * Serial ATA is looming quickly on the horizon.  Both device and host
>   controller SATA implementations really lend themselves to behaviors
>   that have existed in SCSI for a while.  SATA even defines use of SCSI
>   Enclosure Services.

That last statement is interesting. SATA-1 has a maximum
length of 1 metre and no external connector/cable definitions
(I think). Serial attached SCSI (SAS) uses SATA-1 connector/cable
definitions for internal connections and defines its own
external connector/cable definitions (up to 6 metres). The
SCSI Enclosure Services (SES) command set "is defined for
managing various non-SCSI elements contained within the
enclosure" (according to its draft standard abstract).
So it seems that the 1 metre of SATA may be going to a external
enclosure (e.g. RAID or JBOD). Recent SATA versus SAS
comparisons have noted that even though SATA is a point to
point protocol, fan out devices have been demonstrated.

BTW The SATA people at t13.org signed an agreement with the
SAS folks at t10.org in February this year so hopefully there
will be a convergence in several areas in the future.

This driver is very relevant to a SAS HBA which allows
both SAS disks and SATA disks to be controlled **. The
SAS standard envisages that such SATA disks will be
controlled by a "normal" ATA application layer in the
host machine.

> * The Linux SCSI layer handles hotplugging, and is more modular.
>   It already has refcounted devices and sysfs and such.  Creating a
>   new block device driver from scratch means handling all those
>   little details.
> 
> * SCSI has been doing basic error recovery and queue control for a
>   while now.  Upcoming SATA2 will benefit greatly from this, as well
>   ATA TCQ if I ever get around to implementing the latter.
> 
> * ATAPI is SCSI-like.

The MMC standards that define the command set for CD/DVDs
across ATAPI (as well as other transports, including 1394
and USB) are under the SCSI "umbrella" at t10.org . The MMC
drafts have transport specific appendices that outline some
of the compromises. Is the ATAPI transport used for anything
other than carrying SCSI command sets?

> Build notes
> -----------
<snip>
> 
> * PATA stressed similarly, on one box.

I loaded your patch and built it against lk 2.5.69-bk17.
It compiled clean (without the patch to move the scsi
headers into the ~linux/include/scsi directory), built
and loaded.

However there were no signs of life from my Maxtor
D740X-6L (P)ATA-5 disk. The PATA selection in the .config
file doesn't have a module option. Actually there
seems no specific code for PATA in your patch
(apart from libata.c) and no mention of
CONFIG_SCSI_ATA_PATA in drivers/scsi/Makefile .

> Near-future directions
> ----------------------
> 
> * ATAPI (see below)
> 
> * libata.c DMA and taskfile handling is still host-controller specific.
>   It's the most widely used host controller standard, sure.  But that
>   mainly applies to PATA devices.
> 
>   Future host controllers, with a tiny additional bit of
>   abstracting-out, will simply ignore these functions (provided as
>   defaults for most host controllers) and use their own.
> 
> * Better error handling (see below), and more command queueing work
> 
> 
> ATA notes
> ---------
> 
> * Supports max UDMA/33 for PATA right now.  Temporary limitation because
>   I'm too slack to worry about cable detection.

Perhaps my IDE interface chip isn't supported:

00:04.1 IDE interface: VIA Technologies, Inc. VT82C586/B/686A/B PIPC
    Bus Master IDE (rev 06)

> * No packet command -- yet.  And thus, no ATAPI cdroms/burners/etc.
>   Coming soon!

Look forward to that.

<snip>


Thinking about how existing applications such as
hdparm and smartmontools will cope with an ATA device
(e.g. a disk) with a device node like "/dev/sdb".
Those apps would make the assumption from the device
name that such a device should be sent a SCSI command
set. So perhaps we need a "transport" indicator in
the sysfs directory for that device (dare I mention
another ioctl).


** When a SATA disk is connected to a SAS HBA there needs
to be an expander in between them that does the physical
and link layer translation.

Doug Gilbert


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-24 19:51 [RFR] a new SCSI driver Jeff Garzik
  2003-05-26  2:59 ` Douglas Gilbert
@ 2003-05-26 15:16 ` Rabeeh Khoury
  2003-05-27 11:38 ` Douglas Gilbert
  2 siblings, 0 replies; 18+ messages in thread
From: Rabeeh Khoury @ 2003-05-26 15:16 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-scsi

A small correction to the code -
While translating the GET_CAPACITY to ATA, you are returning the number of sectors the ATA drive but the GET_CAPACITY requests the last addressable sector.
So you should return (n_sectors-1).


static void ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *reqbuf,
			        unsigned int buflen)
{
	u64 n_sectors = args->dev->n_sectors;
	u32 tmp;
+++ n_sectors --;
	VPRINTK("ENTER\n");




^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-26  2:59 ` Douglas Gilbert
@ 2003-05-26 20:52   ` Ishikawa
  0 siblings, 0 replies; 18+ messages in thread
From: Ishikawa @ 2003-05-26 20:52 UTC (permalink / raw)
  To: dougg; +Cc: linux-kernel, linux-scsi

Hi,

> Thinking about how existing applications such as
> hdparm and smartmontools will cope with an ATA device
> (e.g. a disk) with a device node like "/dev/sdb".
> Those apps would make the assumption from the device
> name that such a device should be sent a SCSI command
> set. So perhaps we need a "transport" indicator in
> the sysfs directory for that device (dare I mention
> another ioctl).

As of now, smartmontools uses /etc/smartd.conf in which
we can specify a device type is ata or scsi
if the device name is not clear enough. (-d scsi or
-d ata). I am not sure why this feature is there. Maybe
devfs name thing.

Upon cursor examination, 
I am not entirely sure whether we can
*always* put a meaningful "transport indicator" in the sysfs
directory when there will be multiple
combination of transport layer(s) over the long term/haul.
For this particular situation of SATA and SCSI, yes, though.
(As the technology trend goes, I won't be surprised to
find a home PC that hooks SCSI device via a few different
transport layers such as serial, another different serial,
say, USB, and other transport layer, say, firewire
with some glue gadgets in between. 
Whether such beast will be supported
under linux, I am not sure. But we do support USB 
storage device as a SCSI device, so there may be some demand.
I am sure there will be some cheap interface boxes that probably
work under some other OSs when everything works perfectly.
I am not recommending it,  but some people will be hooked to
such setup.)




-- 
int main(void){int j=2003;/*(c)2003 cishikawa. */
char t[] ="<CI> @abcdefghijklmnopqrstuvwxyz.,\n\"";
char *i ="g>qtCIuqivb,gCwe\np@.ietCIuqi\"tqkvv is>dnamz";
while(*i)((j+=strchr(t,*i++)-(int)t),(j%=sizeof t-1),
(putchar(t[j])));return 0;}/* under GPL */

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-24 19:51 [RFR] a new SCSI driver Jeff Garzik
  2003-05-26  2:59 ` Douglas Gilbert
  2003-05-26 15:16 ` Rabeeh Khoury
@ 2003-05-27 11:38 ` Douglas Gilbert
  2 siblings, 0 replies; 18+ messages in thread
From: Douglas Gilbert @ 2003-05-27 11:38 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-scsi

Jeff Garzik wrote:
> 
<snip>

> * Only supports Intel PATA and SATA right now

Jeff,
Your patch applies to lk 2.5.70 without problems.

I found that my dual Celeron (abit mobo) has this
Intel IDE controller:
   00:07.1 IDE interface: Intel Corp. 82371AB/EB/MB PIIX4 IDE (rev 01)

so I tried a 2.5.70 kernel with this patch. Two disks
connected:
   - SCSI disk to a Tekram 390u3w controller (sym53c8xx_2
     driver). Root partition on this disk
   - a Maxtor ATA disk (D740X-6L) to the Intel IDE
     controller.

Didn't get too far. After finding the SCSI controller and
SCSI disk at boot up, this came out just before it locked up:
  ata1:PATA max UDMA/33 cmd 0x1F0 ctl 0x3F6 bmdma 0xF000 irq 14

So I disabled the IDE controllers in the BIOS and then
this came out:
  ....
  PCI: Enabling device 00:07.1 (0000->0001)
  ata1:PATA max UDMA/33 cmd 0x1F0 ctl 0x3F6 bmdma 0xF000 irq 14
  ATA: abnormal status 0xFF on port 0x1F7
  ATA: abnormal status 0xFF on port 0x1F7

and it locked up again.

Doug Gilbert


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-25 10:18 ` Zwane Mwaikambo
@ 2003-05-28 18:25   ` Jörn Engel
  0 siblings, 0 replies; 18+ messages in thread
From: Jörn Engel @ 2003-05-28 18:25 UTC (permalink / raw)
  To: Zwane Mwaikambo; +Cc: john, rmk, jgarzik, linux-kernel, linux-scsi

On Sun, 25 May 2003 06:18:34 -0400, Zwane Mwaikambo wrote:
> On Sun, 25 May 2003 john@grabjohn.com wrote:
> 
> > Also embedded world != PDAs. 
> 
> I don't mean to troll, but could you elaborate on that.

There are two common definitions for embedded.  One means something
very small, low power consumption etc.  The other is no
user-interface.  PDA fits one definition, but not the other.

Everyone knows what embedded is, but not everyone agrees with the
others definition. ;)

Jörn

-- 
Measure. Don't tune for speed until you've measured, and even then
don't unless one part of the code overwhelms the rest.
-- Rob Pike

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-25  9:35 ` Russell King
@ 2003-05-27  0:52   ` Alan Cox
  0 siblings, 0 replies; 18+ messages in thread
From: Alan Cox @ 2003-05-27  0:52 UTC (permalink / raw)
  To: Russell King; +Cc: john, Linux Kernel Mailing List, jgarzik, linux-scsi

On Sul, 2003-05-25 at 10:35, Russell King wrote:
> Rubbish.  PIO mode ATA will be around for some years to come - there
> is just too much invested there (especially in the embedded world) for
> it to vanish this quickly.  For example, think about compact flash cards,
> many of which are still driven using PIO mode accesses in todays PDAs.

For CF IDE you might as well drop the IDE layer too and write a small
custom CF-IDE driver if you are doing heavily embedded stuff. 


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-25  9:44 john
  2003-05-25  8:52 ` Zwane Mwaikambo
  2003-05-25  9:35 ` Russell King
@ 2003-05-25 13:21 ` Jeff Garzik
  2 siblings, 0 replies; 18+ messages in thread
From: Jeff Garzik @ 2003-05-25 13:21 UTC (permalink / raw)
  To: john; +Cc: linux-kernel, linux-scsi

john@grabjohn.com wrote:
>>Serial ATA is looming quickly on the horizon.  Both device and host
>>controller SATA implementations really lend themselves to behaviors
>>that have existed in SCSI for a while.  SATA even defines use of SCSI
>>Enclosure Services.
> 
> 
> Thinking ahead, by the 2.8 timescale, PATA could well be legacy hardware 
> which could be supported only by an 'old' IDE driver, much like we already
> have at the moment - I.E. we could remove the current 'old' IDE driver
> sometime during the 2.7 timescale, and support SATA only via the SCSI layer.
> 
> This would save having any more than the minimum SATA code going in to the
> existing IDE driver, and consolidate work in the future.        


I'm content to let evolution make these decisions...  predicting into 
the future isn't the best skill a technologist has :)

	Jeff




^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
@ 2003-05-25 12:09 john
  0 siblings, 0 replies; 18+ messages in thread
From: john @ 2003-05-25 12:09 UTC (permalink / raw)
  To: arjanv; +Cc: jgarzik, linux-kernel, linux-scsi, rmk

> on common misunderstanding ; PATA != PIO mode ATA but Parallel ATA, as
> opposed to Serial ATA

Yes, Parallel ATA is what I was suggesting we moved in to a legacy driver
sometime during the 2.7 timescale.

John.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-25 10:45 ` Arjan van de Ven
@ 2003-05-25 11:48   ` Russell King
  0 siblings, 0 replies; 18+ messages in thread
From: Russell King @ 2003-05-25 11:48 UTC (permalink / raw)
  To: Arjan van de Ven; +Cc: john, jgarzik, linux-kernel, linux-scsi

On Sun, May 25, 2003 at 12:45:25PM +0200, Arjan van de Ven wrote:
> On Sun, 2003-05-25 at 13:19, john@grabjohn.com wrote:
> > Also embedded world != PDAs.  I was thinking more of things like PVRs, and blade
> > servers.  What about solid state video cameras?  Are they going to use PIO mode
> > ATA?
> on common misunderstanding ; PATA != PIO mode ATA but Parallel ATA, as
> opposed to Serial ATA

I was aware of that point.  That's why I used the term "PIO mode ATA" to
describe something which isn't "PATA".

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
@ 2003-05-25 11:48 john
  0 siblings, 0 replies; 18+ messages in thread
From: john @ 2003-05-25 11:48 UTC (permalink / raw)
  To: john, zwane; +Cc: jgarzik, linux-kernel, linux-scsi, rmk

> > Also embedded world != PDAs. 

> I don't mean to troll, but could you elaborate on that.

Err, I did.  In the very next sentence to the one you quoted.

I don't see why I've triggered such a negative response to all this.

All I was suggesting was that supporting SATA as SCSI makes sense, (to me).

If we're going to do that, does it make sense to continue to add SATA
functionality to the existing (P)ATA model, or should we draw the line here?

John.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
@ 2003-05-25 11:19 john
  2003-05-25 10:18 ` Zwane Mwaikambo
  2003-05-25 10:45 ` Arjan van de Ven
  0 siblings, 2 replies; 18+ messages in thread
From: john @ 2003-05-25 11:19 UTC (permalink / raw)
  To: john, rmk; +Cc: jgarzik, linux-kernel, linux-scsi

> > Thinking ahead, by the 2.8 timescale, PATA could well be legacy hardware 
> > which could be supported only by an 'old' IDE driver, much like we already
> > have at the moment - I.E. we could remove the current 'old' IDE driver
> > sometime during the 2.7 timescale, and support SATA only via the SCSI layer.

> Rubbish.  PIO mode ATA will be around for some years to come - there
> is just too much invested there (especially in the embedded world) for 
> it to vanish this quickly.  For example, think about compact flash cards,
> many of which are still driven using PIO mode accesses in todays PDAs.

Why couldn't PDAs be served by the 'old' driver then?  Old doesn't mean obsolete.

Also embedded world != PDAs.  I was thinking more of things like PVRs, and blade
servers.  What about solid state video cameras?  Are they going to use PIO mode
ATA?

John.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-25 11:19 john
  2003-05-25 10:18 ` Zwane Mwaikambo
@ 2003-05-25 10:45 ` Arjan van de Ven
  2003-05-25 11:48   ` Russell King
  1 sibling, 1 reply; 18+ messages in thread
From: Arjan van de Ven @ 2003-05-25 10:45 UTC (permalink / raw)
  To: john; +Cc: rmk, jgarzik, linux-kernel, linux-scsi

[-- Attachment #1: Type: text/plain, Size: 1053 bytes --]

On Sun, 2003-05-25 at 13:19, john@grabjohn.com wrote:
> > > Thinking ahead, by the 2.8 timescale, PATA could well be legacy hardware 
> > > which could be supported only by an 'old' IDE driver, much like we already
> > > have at the moment - I.E. we could remove the current 'old' IDE driver
> > > sometime during the 2.7 timescale, and support SATA only via the SCSI layer.
> 
> > Rubbish.  PIO mode ATA will be around for some years to come - there
> > is just too much invested there (especially in the embedded world) for 
> > it to vanish this quickly.  For example, think about compact flash cards,
> > many of which are still driven using PIO mode accesses in todays PDAs.
> 
> Why couldn't PDAs be served by the 'old' driver then?  Old doesn't mean obsolete.
> 
> Also embedded world != PDAs.  I was thinking more of things like PVRs, and blade
> servers.  What about solid state video cameras?  Are they going to use PIO mode
> ATA?
on common misunderstanding ; PATA != PIO mode ATA but Parallel ATA, as
opposed to Serial ATA

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
@ 2003-05-25 10:32 john
  0 siblings, 0 replies; 18+ messages in thread
From: john @ 2003-05-25 10:32 UTC (permalink / raw)
  To: john, zwane; +Cc: jgarzik, linux-kernel, linux-scsi

> > Thinking ahead, by the 2.8 timescale, PATA could well be legacy hardware 
> > which could be supported only by an 'old' IDE driver, much like we already
> > have at the moment - I.E. we could remove the current 'old' IDE driver
> > sometime during the 2.7 timescale, and support SATA only via the SCSI layer.
> >
> > This would save having any more than the minimum SATA code going in to the
> > existing IDE driver, and consolidate work in the future.        

> PATA is in _way_ too many current boxes, those computers will continue to 
> run for a very long time from now.

That would be the reason for the old IDE driver.  PATA will still be in use, but
it won't be mainstream.                   

> In 10 years what is technologically obselete will still be very capable.

Of course it will - the same is true today.  That doesn't mean it will be in      
mainstream use.  The systems that do use PATA will benefit from having it
supported in a smaller footprint driver.  It will be analogous to using the 
current 'old' IDE driver on a 4 MB 386 today.  

> > The bloat of the SCSI layer in embedded machines might be a concern, but
> > then again, maybe it won't - how many embedded machines are going to be
> > using SATA, anyway?  Once we move away from spinning disks towards solid
> > state storage, (which is going to happen first in the embedded market),
> > will we want to use *ATA or SCSI at all?

> You're confusing media and transport.

No, I'm not.       

20-40 GB of RAM will be very cheap in a few years time.  A lot of the devices
using disks today will be using direct memory mapped RAM as their main storage
in the near future.  You don't need *ATA for that...

John.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-25 11:19 john
@ 2003-05-25 10:18 ` Zwane Mwaikambo
  2003-05-28 18:25   ` Jörn Engel
  2003-05-25 10:45 ` Arjan van de Ven
  1 sibling, 1 reply; 18+ messages in thread
From: Zwane Mwaikambo @ 2003-05-25 10:18 UTC (permalink / raw)
  To: john; +Cc: rmk, jgarzik, linux-kernel, linux-scsi

On Sun, 25 May 2003 john@grabjohn.com wrote:

> Also embedded world != PDAs. 

I don't mean to troll, but could you elaborate on that.

Thanks,
	Zwane

-- 
function.linuxpower.ca

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
@ 2003-05-25  9:44 john
  2003-05-25  8:52 ` Zwane Mwaikambo
                   ` (2 more replies)
  0 siblings, 3 replies; 18+ messages in thread
From: john @ 2003-05-25  9:44 UTC (permalink / raw)
  To: linux-kernel; +Cc: jgarzik, linux-scsi

> Serial ATA is looming quickly on the horizon.  Both device and host
> controller SATA implementations really lend themselves to behaviors
> that have existed in SCSI for a while.  SATA even defines use of SCSI
> Enclosure Services.

Thinking ahead, by the 2.8 timescale, PATA could well be legacy hardware 
which could be supported only by an 'old' IDE driver, much like we already
have at the moment - I.E. we could remove the current 'old' IDE driver
sometime during the 2.7 timescale, and support SATA only via the SCSI layer.

This would save having any more than the minimum SATA code going in to the
existing IDE driver, and consolidate work in the future.        

The bloat of the SCSI layer in embedded machines might be a concern, but  
then again, maybe it won't - how many embedded machines are going to be   
using SATA, anyway?  Once we move away from spinning disks towards solid
state storage, (which is going to happen first in the embedded market),
will we want to use *ATA or SCSI at all?

John

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-25  9:44 john
  2003-05-25  8:52 ` Zwane Mwaikambo
@ 2003-05-25  9:35 ` Russell King
  2003-05-27  0:52   ` Alan Cox
  2003-05-25 13:21 ` Jeff Garzik
  2 siblings, 1 reply; 18+ messages in thread
From: Russell King @ 2003-05-25  9:35 UTC (permalink / raw)
  To: john; +Cc: linux-kernel, jgarzik, linux-scsi

On Sun, May 25, 2003 at 10:44:05AM +0100, john@grabjohn.com wrote:
> Thinking ahead, by the 2.8 timescale, PATA could well be legacy hardware 
> which could be supported only by an 'old' IDE driver, much like we already
> have at the moment - I.E. we could remove the current 'old' IDE driver
> sometime during the 2.7 timescale, and support SATA only via the SCSI layer.

Rubbish.  PIO mode ATA will be around for some years to come - there
is just too much invested there (especially in the embedded world) for
it to vanish this quickly.  For example, think about compact flash cards,
many of which are still driven using PIO mode accesses in todays PDAs.

(and in some PDAs, there isn't the hardware facilities to even think
about trying any type of DMA transfer.)

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFR] a new SCSI driver
  2003-05-25  9:44 john
@ 2003-05-25  8:52 ` Zwane Mwaikambo
  2003-05-25  9:35 ` Russell King
  2003-05-25 13:21 ` Jeff Garzik
  2 siblings, 0 replies; 18+ messages in thread
From: Zwane Mwaikambo @ 2003-05-25  8:52 UTC (permalink / raw)
  To: john; +Cc: linux-kernel, jgarzik, linux-scsi

On Sun, 25 May 2003 john@grabjohn.com wrote:

> Thinking ahead, by the 2.8 timescale, PATA could well be legacy hardware 
> which could be supported only by an 'old' IDE driver, much like we already
> have at the moment - I.E. we could remove the current 'old' IDE driver
> sometime during the 2.7 timescale, and support SATA only via the SCSI layer.
> 
> This would save having any more than the minimum SATA code going in to the
> existing IDE driver, and consolidate work in the future.        

PATA is in _way_ too many current boxes, those computers will continue to 
run for a very long time from now. In 10 years what is technologically 
obselete will still be very capable.

> 
> The bloat of the SCSI layer in embedded machines might be a concern, but  
> then again, maybe it won't - how many embedded machines are going to be   
> using SATA, anyway?  Once we move away from spinning disks towards solid
> state storage, (which is going to happen first in the embedded market),
> will we want to use *ATA or SCSI at all?

You're confusing media and transport.

	Zwane
-- 
function.linuxpower.ca

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2003-05-28 18:12 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-05-24 19:51 [RFR] a new SCSI driver Jeff Garzik
2003-05-26  2:59 ` Douglas Gilbert
2003-05-26 20:52   ` Ishikawa
2003-05-26 15:16 ` Rabeeh Khoury
2003-05-27 11:38 ` Douglas Gilbert
2003-05-25  9:44 john
2003-05-25  8:52 ` Zwane Mwaikambo
2003-05-25  9:35 ` Russell King
2003-05-27  0:52   ` Alan Cox
2003-05-25 13:21 ` Jeff Garzik
2003-05-25 10:32 john
2003-05-25 11:19 john
2003-05-25 10:18 ` Zwane Mwaikambo
2003-05-28 18:25   ` Jörn Engel
2003-05-25 10:45 ` Arjan van de Ven
2003-05-25 11:48   ` Russell King
2003-05-25 11:48 john
2003-05-25 12:09 john

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).