All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [RFC] mass storage : emulation of sat scsi_pass_thru with ATACB
       [not found] ` <47C332E3.2070705-GANU6spQydw@public.gmane.org>
@ 2008-02-29 21:30   ` matthieu castet
  0 siblings, 0 replies; only message in thread
From: matthieu castet @ 2008-02-29 21:30 UTC (permalink / raw)
  To: linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-scsi-u79uwXL29TY76Z2rM5mHXA, Alan Stern

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

matthieu castet wrote:
> Hi,
> 
> I have got a cypress usb-ide bridge and I would like to tune or monitor
> my disk with tools like hdparm, hddtemp or smartctl.
> 
> My controller support a way to send raw ATA command to the disk with
> something call atacb (see
> http://download.cypress.com.edgesuite.net/design_resources/datasheets/contents/cy7c68300c_8.pdf). 
> 
> 
> First I start to add support for atacb in applications like smartctl.
> But there is some disadvantages :
> - I need to patch all application
> - A race is possible if there other accesses, because the emulation can
> be split in 2 atacb scsi transactions. One for sending the command, one
> for reading the register (if ck_cond is set).
> 
> So the idea to implement a SAT emulation for such device came to my mind.
> 
> First I want to ask, are there usb mass storage device that support SAT ?
> After some reading it seems impossible. Mass storage transparent scsi
> requires spc2 that only support fixed sense (starting with 0x70 or 0x71).
> But SAT need a sense with descriptor defined in spc3 (starting with 0x72
> or 0x73).
> 
> So SAT is not compatible with the sense format of mass storage ?
> 
> 
> 
> For doing the emulation, I want to provide a special proto_handler.
> 
> But I don't know what the best way to send command in my emulation layer :
> should I use usb_stor_invoke_transport or directly use us->transport.
> using usb_stor_invoke_transport save me lot's trouble of implementing
> error handling, but I can't control what I do.
> 
> What should I do if I got an error after sending the first command and
> ck_cond is set ? Should I still try to read the register ?
> 
> Is it safe to try to read the register if the first command did
> autosense ? Or should I only send the command, and read the register and
> compute the sense key, asc and ascq from registers ?
> 
> For the moment, my "draft" code look something like [1].
> 
> Any comments ?
> 
I attach a updated version of the driver.

I register the handler with an unsual entry. I don't know if it is the 
best way to do that (we need an entry for each device supporting atacb).

Another solution could have been to put the emulation in 
usb_stor_transparent_scsi_command, and if the device report invalidCDB 
don't try to use it again.

Any comments ?


Matthieu

[-- Attachment #2: mass_storage_atacb_emulate_sat --]
[-- Type: text/plain, Size: 8240 bytes --]

Index: linux-2.6.24.2/drivers/usb/storage/protocol.c
===================================================================
--- linux-2.6.24.2.orig/drivers/usb/storage/protocol.c	2008-02-29 22:14:09.000000000 +0100
+++ linux-2.6.24.2/drivers/usb/storage/protocol.c	2008-02-29 22:18:02.000000000 +0100
@@ -47,6 +47,8 @@
 #include <linux/highmem.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_eh.h>
+#include <linux/ata.h>
 
 #include "usb.h"
 #include "protocol.h"
@@ -144,6 +146,179 @@
 	usb_stor_invoke_transport(srb, us);
 }
 
+static void emulate_pass_thru_with_atacb(struct scsi_cmnd *srb,
+				       struct us_data *us)
+{
+	unsigned char save_cmnd[MAX_COMMAND_SIZE];
+	memcpy(save_cmnd, srb->cmnd, sizeof(save_cmnd));
+	memset(srb->cmnd, 0, sizeof(srb->cmnd));
+
+	/* check if we support the command */
+	if (save_cmnd[1] >> 5) /* MULTIPLE_COUNT */
+		goto invalid_fld;
+	/* check protocol */
+	switch((save_cmnd[1] >> 1) & 0xf) {
+		case 3:
+		case 4:
+		case 5:
+			break;
+		default:
+			goto invalid_fld;
+	}
+
+	/* first build the ATACB command */
+	srb->cmd_len = 16;
+
+	srb->cmnd[0] = 0x24; /* XXX this value can change */
+	srb->cmnd[1] = 0x24;
+	srb->cmnd[2] = 0;
+
+	srb->cmnd[3] = 0xff - 1;
+	srb->cmnd[4] = 1;
+
+	if (save_cmnd[0] == ATA_16) {
+		srb->cmnd[6] = save_cmnd[4]; /* features */
+		srb->cmnd[7] = save_cmnd[6]; /* sector count */
+		srb->cmnd[8] = save_cmnd[8]; /* lba low */
+		srb->cmnd[9] = save_cmnd[10]; /* lba med */
+		srb->cmnd[10] = save_cmnd[12]; /* lba high */
+		srb->cmnd[11] = save_cmnd[13]; /* device */
+		srb->cmnd[12] = save_cmnd[14]; /* command */
+
+		if (save_cmnd[1] & 0x01) {/* extended bit set */
+			if (save_cmnd[3] || save_cmnd[5] || save_cmnd[7] || save_cmnd[9]
+					|| save_cmnd[11])
+				goto invalid_fld;
+		}
+	}
+	else { /* ATA12 */
+		srb->cmnd[6] = save_cmnd[3]; /* features */
+		srb->cmnd[7] = save_cmnd[4]; /* sector count */
+		srb->cmnd[8] = save_cmnd[5]; /* lba low */
+		srb->cmnd[9] = save_cmnd[6]; /* lba med */
+		srb->cmnd[10] = save_cmnd[7]; /* lba high */
+		srb->cmnd[11] = save_cmnd[8]; /* device */
+		srb->cmnd[12] = save_cmnd[9]; /* command */
+
+	}
+	/* Filter SET_FEATURES - XFER MODE command */
+	if ((srb->cmnd[12] == ATA_CMD_SET_FEATURES)
+			&& (srb->cmnd[6] == SETFEATURES_XFER))
+		goto invalid_fld;
+
+	/* realy needed ? isd200 don't do that */
+	if (srb->cmnd[12] == ATA_CMD_ID_ATA || srb->cmnd[12] == ATA_CMD_ID_ATAPI)
+		srb->cmnd[2] |= (1<<7);
+
+
+	usb_stor_invoke_transport(srb, us);
+
+	/* if the device doesn't support ATACB
+	 * abort and register usb_stor_transparent_scsi_command
+	 * callback
+	 */
+	if (srb->result == SAM_STAT_CHECK_CONDITION &&
+			memcmp(srb->sense_buffer, usb_stor_sense_invalidCDB,
+				sizeof(usb_stor_sense_invalidCDB)) == 0) {
+		us->proto_handler = usb_stor_transparent_scsi_command;
+		goto end;
+	}
+
+	if ((srb->result != (DID_ERROR << 16) &&
+				srb->result != (DID_ABORT << 16)) &&
+			save_cmnd[2] & 0x20) { /* ck_cond */
+		/* read regs and put them in sense */
+		struct scsi_eh_save ses;
+		unsigned char sk, asc, ascq;
+		unsigned char regs[8];
+		unsigned char *sb = srb->sense_buffer;
+		unsigned char *desc = sb + 8;
+		int tmp_result;
+
+		if (srb->result == SAM_STAT_GOOD) {
+			/* OK */
+			sk = RECOVERED_ERROR;
+			asc = 0; /* ATA PASS THROUGH INFORMATION AVAILABLE */
+			ascq = 0x1D;
+		}
+		else {
+			/* save sense info */
+			/* XXX we get only 0, we should use regs status and error
+			 */
+#if 0
+			sk = sb[2];
+			asc = sb[12];
+			ascq = sb[13];
+#else
+			sk = ILLEGAL_REQUEST;
+			asc = 0x24;
+			ascq = 0;
+#endif
+		}
+
+
+		/* read registers */
+		srb->cmnd[2] = 1;
+		scsi_eh_prep_cmnd(srb, &ses, NULL, 0, 0);
+		srb->request_buffer = regs;
+		srb->request_bufflen = sizeof(regs);
+		srb->sc_data_direction = DMA_FROM_DEVICE;
+
+		usb_stor_invoke_transport(srb, us);
+		tmp_result = srb->result;
+		scsi_eh_restore_cmnd(srb, &ses);
+		/* we fail to get registers, report invalid command */
+		if (tmp_result != SAM_STAT_GOOD)
+			goto invalid_fld;
+
+		/* build the sense */
+		memset(sb, 0, sizeof(srb->sense_buffer));
+		/* XXX we should generate sk, asc, ascq from status and error
+		 * regs
+		 * (see 11.1 Error translation ­ ATA device error to SCSI error map)
+		 */
+		sb[1] = sk;
+		sb[2] = asc;
+		sb[3] = ascq;
+		sb[0] = 0x72;
+		desc[0] = 0x09; /* ATA_RETURN_DESCRIPTOR */
+
+		sb[7] = 14;
+		desc[1] = 12;
+
+		desc[2] = 0x00;
+		desc[3] = regs[1];
+		desc[5] = regs[2];
+		desc[7] = regs[3];
+		desc[9] = regs[4];
+		desc[11] = regs[5];
+		desc[12] = regs[6];
+		desc[13] = regs[7];
+
+		srb->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
+	}
+	goto end;
+invalid_fld:
+	srb->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
+
+	memcpy(srb->sense_buffer,
+			usb_stor_sense_invalidCDB,
+			sizeof(usb_stor_sense_invalidCDB));
+end:
+	memcpy(srb->cmnd, save_cmnd, sizeof(save_cmnd));
+	if (srb->cmnd[0] == ATA_12)
+		srb->cmd_len = 12;
+}
+
+void usb_stor_transparent_scsi_command_atacb(struct scsi_cmnd *srb,
+				       struct us_data *us)
+{
+	if (srb->cmnd[0] != ATA_16 && srb->cmnd[0] != ATA_12)
+		usb_stor_invoke_transport(srb, us);
+	else
+		emulate_pass_thru_with_atacb(srb, us);
+}
+
 /***********************************************************************
  * Scatter-gather transfer buffer access routines
  ***********************************************************************/
Index: linux-2.6.24.2/drivers/usb/storage/initializers.c
===================================================================
--- linux-2.6.24.2.orig/drivers/usb/storage/initializers.c	2008-02-29 22:14:09.000000000 +0100
+++ linux-2.6.24.2/drivers/usb/storage/initializers.c	2008-02-29 22:16:03.000000000 +0100
@@ -43,6 +43,7 @@
 #include "initializers.h"
 #include "debug.h"
 #include "transport.h"
+#include "protocol.h"
 
 /* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
  * mode */
@@ -104,3 +105,13 @@
 	US_DEBUGP("usb_control_msg performing result is %d\n", result);
 	return (result ? 0 : -1);
 }
+
+/* This places register the atacb proto callback */
+int usb_atacb_init(struct us_data *us)
+{
+	/* XXX get atacb command from eeprom
+	 * ATM we assume the default : 0x24
+	 */
+	us->proto_handler = usb_stor_transparent_scsi_command_atacb;
+	return 0;
+}
Index: linux-2.6.24.2/drivers/usb/storage/initializers.h
===================================================================
--- linux-2.6.24.2.orig/drivers/usb/storage/initializers.h	2008-02-29 22:14:09.000000000 +0100
+++ linux-2.6.24.2/drivers/usb/storage/initializers.h	2008-02-29 22:16:03.000000000 +0100
@@ -50,3 +50,6 @@
 
 /* This places the HUAWEI E220 devices in multi-port mode */
 int usb_stor_huawei_e220_init(struct us_data *us);
+
+/* This places register the atacb proto callback */
+int usb_atacb_init(struct us_data *us);
Index: linux-2.6.24.2/drivers/usb/storage/protocol.h
===================================================================
--- linux-2.6.24.2.orig/drivers/usb/storage/protocol.h	2008-02-29 22:14:09.000000000 +0100
+++ linux-2.6.24.2/drivers/usb/storage/protocol.h	2008-02-29 22:16:03.000000000 +0100
@@ -47,6 +47,8 @@
 extern void usb_stor_ufi_command(struct scsi_cmnd*, struct us_data*);
 extern void usb_stor_transparent_scsi_command(struct scsi_cmnd*,
 		struct us_data*);
+extern void usb_stor_transparent_scsi_command_atacb(struct scsi_cmnd*,
+		struct us_data*);
 
 /* struct scsi_cmnd transfer buffer access utilities */
 enum xfer_buf_dir	{TO_XFER_BUF, FROM_XFER_BUF};
Index: linux-2.6.24.2/drivers/usb/storage/unusual_devs.h
===================================================================
--- linux-2.6.24.2.orig/drivers/usb/storage/unusual_devs.h	2008-02-29 22:14:09.000000000 +0100
+++ linux-2.6.24.2/drivers/usb/storage/unusual_devs.h	2008-02-29 22:16:03.000000000 +0100
@@ -1588,6 +1588,12 @@
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_CAPACITY_HEURISTICS),
 
+UNUSUAL_DEV(  0x04b4, 0x6830, 0x0000, 0x9999,
+		"Cypress",
+		"Cypress AT2LP",
+		US_SC_SCSI, US_PR_BULK, usb_atacb_init,
+		US_FL_NEED_OVERRIDE),
+
 /* Control/Bulk transport for all SubClass values */
 USUAL_DEV(US_SC_RBC, US_PR_CB, USB_US_TYPE_STOR),
 USUAL_DEV(US_SC_8020, US_PR_CB, USB_US_TYPE_STOR),

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2008-02-29 21:30 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <47C332E3.2070705@free.fr>
     [not found] ` <47C332E3.2070705-GANU6spQydw@public.gmane.org>
2008-02-29 21:30   ` [RFC] mass storage : emulation of sat scsi_pass_thru with ATACB matthieu castet

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.