linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] libata test-unit-ready for ATAPI devices
@ 2005-11-16  4:48 Jeff Garzik
  2005-11-16 10:01 ` Tejun Heo
  0 siblings, 1 reply; 3+ messages in thread
From: Jeff Garzik @ 2005-11-16  4:48 UTC (permalink / raw)
  To: linux-ide; +Cc: linux-scsi


The following patch clears the "I was just reset" condition from an
ATAPI device, and waits for it to come online, before continuing with
the probe.

Not checking this into any upstream-bound branch, as I'm not yet
convinced of its value.


 drivers/scsi/libata-core.c |  135 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 135 insertions(+)

diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index ba1eb8b..89fa5c7 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -68,6 +68,7 @@ static void ata_dev_reread_id(struct ata
 static void ata_dev_init_params(struct ata_port *ap, struct ata_device *dev);
 static void ata_set_mode(struct ata_port *ap);
 static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev);
+static void atapi_dev_tur(struct ata_port *ap, struct ata_device *dev);
 static unsigned int ata_get_mode_mask(const struct ata_port *ap, int shift);
 static int fgb(u32 bitmap);
 static int ata_choose_xfer_mode(const struct ata_port *ap,
@@ -1361,6 +1362,10 @@ static int ata_bus_probe(struct ata_port
 	if (ap->flags & ATA_FLAG_PORT_DISABLED)
 		goto err_out_disable;
 
+	for (i = 0; i < ATA_MAX_DEVICES; i++)
+		if (ap->device[i].class == ATA_DEV_ATAPI)
+			atapi_dev_tur(ap, &ap->device[i]);
+
 	return 0;
 
 err_out_disable:
@@ -2376,6 +2381,136 @@ static void ata_dev_init_params(struct a
 	DPRINTK("EXIT\n");
 }
 
+static int atapi_tur_request_sense(struct ata_port *ap, struct ata_device *dev)
+{
+	DECLARE_COMPLETION(wait);
+	struct ata_queued_cmd *qc;
+	unsigned long flags;
+	u8 sense_buffer[SCSI_SENSE_BUFFERSIZE];
+	int rc, do_poll = 0;
+
+	DPRINTK("ATAPI TUR request sense\n");
+
+	memset(&sense_buffer, 0xff, sizeof(sense_buffer));
+
+	qc = ata_qc_new_init(ap, dev);
+	BUG_ON(qc == NULL);
+
+	ata_sg_init_one(qc, sense_buffer, sizeof(sense_buffer));
+	qc->dma_dir = DMA_FROM_DEVICE;
+
+	memset(&qc->cdb, 0, ap->cdb_len);
+	qc->cdb[0] = REQUEST_SENSE;
+	qc->cdb[4] = SCSI_SENSE_BUFFERSIZE;
+
+	qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+	qc->tf.command = ATA_CMD_PACKET;
+
+	qc->tf.protocol = ATA_PROT_ATAPI;
+	qc->tf.lbam = (8 * 1024) & 0xff;
+	qc->tf.lbah = (8 * 1024) >> 8;
+
+	qc->nbytes = SCSI_SENSE_BUFFERSIZE;
+
+	qc->waiting = &wait;
+	qc->complete_fn = ata_qc_complete_noop;
+
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	rc = ata_qc_issue(qc);
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+	if (!rc)
+		wait_for_completion(&wait);
+
+	switch (sense_buffer[12]) {	/* byte 12 == ASC */
+	case 0x29:
+	case 0x4:
+		do_poll = 1;
+		break;
+	}
+
+	DPRINTK("EXIT\n");
+
+	return do_poll;
+}
+
+static int ata_qc_complete_sense(struct ata_queued_cmd *qc, unsigned int err_mask)
+{
+	unsigned int *sense = qc->private_data;
+
+	qc->ap->ops->tf_read(qc->ap, &qc->tf);
+	if (qc->tf.command & ATA_ERR)
+		*sense = qc->tf.feature >> 4;
+	else
+		*sense = 0;
+	return 0;
+}
+
+static void atapi_dev_tur(struct ata_port *ap, struct ata_device *dev)
+{
+	DECLARE_COMPLETION(wait);
+	struct ata_queued_cmd *qc;
+	int rc, do_poll;
+	unsigned long flags;
+	unsigned int sense;
+	unsigned int retries = 15;
+
+	/* set up TUR taskfile */
+	DPRINTK("TUR\n");
+
+tur_retry:
+	qc = ata_qc_new_init(ap, dev);
+	BUG_ON(qc == NULL);
+
+	memset(&qc->cdb, 0, ap->cdb_len);
+	qc->cdb[0] = TEST_UNIT_READY;
+
+	qc->dma_dir = DMA_NONE;
+	qc->tf.command = ATA_CMD_PACKET;
+	qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+	qc->tf.protocol = ATA_PROT_ATAPI;
+	qc->tf.lbam = (8 * 1024) & 0xff;
+	qc->tf.lbah = (8 * 1024) >> 8;
+
+	sense = 0xffffffffU;
+	qc->private_data = &sense;
+
+	qc->waiting = &wait;
+	qc->complete_fn = ata_qc_complete_sense;
+
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	rc = ata_qc_issue(qc);
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+	if (rc) {
+tur_error:
+		printk(KERN_WARNING "ata%u: TUR failure, port disabled\n",
+			ap->id);
+		ata_port_disable(ap);
+		return;
+	}
+
+	wait_for_completion(&wait);
+
+	if (sense == 0)
+		return;
+	if (sense > 0xf)
+		goto tur_error;
+
+	do_poll = atapi_tur_request_sense(ap, dev);
+	if (do_poll == 0)
+		return;
+	if (do_poll < 0)
+		goto tur_error;
+
+	msleep(500);
+
+	retries--;
+	goto tur_retry;
+
+	DPRINTK("EXIT\n");
+}
+
 /**
  *	ata_sg_clean - Unmap DMA memory associated with command
  *	@qc: Command containing DMA memory to be released

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

* Re: [PATCH] libata test-unit-ready for ATAPI devices
  2005-11-16  4:48 [PATCH] libata test-unit-ready for ATAPI devices Jeff Garzik
@ 2005-11-16 10:01 ` Tejun Heo
  2005-11-16 12:46   ` Jeff Garzik
  0 siblings, 1 reply; 3+ messages in thread
From: Tejun Heo @ 2005-11-16 10:01 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: linux-ide, linux-scsi

On Tue, Nov 15, 2005 at 11:48:41PM -0500, Jeff Garzik wrote:
> 
> The following patch clears the "I was just reset" condition from an
> ATAPI device, and waits for it to come online, before continuing with
> the probe.
> 
> Not checking this into any upstream-bound branch, as I'm not yet
> convinced of its value.
> 

How about the following one?  It doesn't make libata wait for the
device to become ready before trying to configure ATA stuff.  It just
makes sr wait for the device to become ready before performing SCSI
configuration.  Maybe both are needed?

diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index d68cea7..9656421 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -44,6 +44,7 @@
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/blkdev.h>
+#include <linux/delay.h>
 #include <asm/uaccess.h>
 
 #include <scsi/scsi.h>
@@ -721,14 +722,53 @@ Enomem:
 	goto out;
 }
 
-static void get_capabilities(struct scsi_cd *cd)
+static int initial_test_unit_ready(struct scsi_cd *cd)
 {
-	unsigned char *buffer;
-	struct scsi_mode_data data;
 	unsigned char cmd[MAX_COMMAND_SIZE];
 	struct scsi_sense_hdr sshdr;
 	unsigned int the_result;
-	int retries, rc, n;
+	int retries = 5, becoming_ready = 0, first_becoming_ready = 1;
+
+	memset((void *)cmd, 0, MAX_COMMAND_SIZE);
+	cmd[0] = TEST_UNIT_READY;
+
+	while (--retries) {
+		the_result = scsi_execute_req (cd->device, cmd, DMA_NONE, NULL,
+					       0, &sshdr, SR_TIMEOUT,
+					       MAX_RETRIES);
+
+		if (scsi_status_is_good(the_result))
+			return 0;
+
+		if (!scsi_sense_valid(&sshdr))
+			continue;
+
+		becoming_ready = sshdr.sense_key == NOT_READY &&
+			sshdr.asc == 0x04 && sshdr.ascq == 0x01;
+
+		if (becoming_ready) {
+			if (first_becoming_ready) {
+				printk("%s: device is becoming ready, please "
+				       "be patient\n", cd->cdi.name);
+				retries = 30;	/* 15secs */
+				first_becoming_ready = 0;
+			}
+			msleep(500);
+		} else if (sshdr.sense_key != UNIT_ATTENTION)
+			break;
+	}
+
+	if (becoming_ready)
+		printk(KERN_WARNING "%s: device failed to become ready\n",
+		       cd->cdi.name);
+	return -1;
+}
+
+static void get_capabilities(struct scsi_cd *cd)
+{
+	unsigned char *buffer;
+	struct scsi_mode_data data;
+	int rc, n;
 
 	static char *loadmech[] =
 	{
@@ -750,23 +790,8 @@ static void get_capabilities(struct scsi
 		return;
 	}
 
-	/* issue TEST_UNIT_READY until the initial startup UNIT_ATTENTION
-	 * conditions are gone, or a timeout happens
-	 */
-	retries = 0;
-	do {
-		memset((void *)cmd, 0, MAX_COMMAND_SIZE);
-		cmd[0] = TEST_UNIT_READY;
-
-		the_result = scsi_execute_req (cd->device, cmd, DMA_NONE, NULL,
-					       0, &sshdr, SR_TIMEOUT,
-					       MAX_RETRIES);
-
-		retries++;
-	} while (retries < 5 && 
-		 (!scsi_status_is_good(the_result) ||
-		  (scsi_sense_valid(&sshdr) &&
-		   sshdr.sense_key == UNIT_ATTENTION)));
+	/* test for readiness */
+	initial_test_unit_ready(cd);
 
 	/* ask for mode page 0x2a */
 	rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,
diff --git a/include/linux/libata.h b/include/linux/libata.h


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

* Re: [PATCH] libata test-unit-ready for ATAPI devices
  2005-11-16 10:01 ` Tejun Heo
@ 2005-11-16 12:46   ` Jeff Garzik
  0 siblings, 0 replies; 3+ messages in thread
From: Jeff Garzik @ 2005-11-16 12:46 UTC (permalink / raw)
  To: Tejun Heo; +Cc: linux-ide, linux-scsi

Tejun Heo wrote:
> On Tue, Nov 15, 2005 at 11:48:41PM -0500, Jeff Garzik wrote:
> 
>>The following patch clears the "I was just reset" condition from an
>>ATAPI device, and waits for it to come online, before continuing with
>>the probe.
>>
>>Not checking this into any upstream-bound branch, as I'm not yet
>>convinced of its value.
>>
> 
> 
> How about the following one?  It doesn't make libata wait for the
> device to become ready before trying to configure ATA stuff.  It just
> makes sr wait for the device to become ready before performing SCSI
> configuration.  Maybe both are needed?

[thinking out loud]  My rationale for doing TUR in libata was to clear 
the UNIT ATTENTION that libata caused through software/hardware reset. 
Once the UNIT ATTENTION was cleared, my rationale for waiting for the 
NOT READY(BECOMING READY) condition was simply symmetry with other 
libata probe tasks, which similarly wait for device readiness.

However, given that sr is already coded to clear "the initial startup 
UNIT_ATTENTION", I tend to think your patch is more appropriate, and my 
patch should not be applied at all.

	Jeff



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

end of thread, other threads:[~2005-11-16 12:46 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-11-16  4:48 [PATCH] libata test-unit-ready for ATAPI devices Jeff Garzik
2005-11-16 10:01 ` Tejun Heo
2005-11-16 12:46   ` Jeff Garzik

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).