linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michael Fortson <mfortson@permabit.com>
To: linux-ide@vger.kernel.org
Subject: Re: libata passthru  (S.M.A.R.T.) support for 2.4.31?
Date: Tue, 23 Aug 2005 15:53:45 -0400	[thread overview]
Message-ID: <430B7EC9.3050802@permabit.com> (raw)
In-Reply-To: <20050823132235.GA21267@tuxdriver.com>

John W. Linville wrote:
> On Tue, Aug 23, 2005 at 09:02:50AM -0400, Michael Fortson wrote:
> 
>>Apologies if this is the wrong list for this question.
>>
>>Is there a patch which provides libata passthru support for the 2.4.31 
>>kernel?  I've seen that Jeff Garzik has a patch for 2.6.x which provides 
>>S.M.A.R.T. support for SATA disks using the libata passthru stuff and 
>>I'd like to apply a similar patch to 2.4.31, if one exists.
> 
> 
> Have you tried applying that passthru patch to the 2.4.31 sources?
> I do not know if that will work or not, but in the past the 2.4 libata
> code was very close to the 2.6 libata code.  So, it might just apply
> directly...?
> 
> Give it a try.  If it doesn't work-out, I'll be happy to try to
> assist you.
> 
> John

I attempted to use 
http://www.kernel.org/pub/linux/kernel/people/jgarzik/libata/2.6.13-rc6-git10-libata1.patch.bz2 
against 2.4.31 but several portions of the patch were rejected and one 
file (drivers/scsi/Kconfig) doesn't exist at all.

Here are the rejects and the actual rejected chunks:

     ./include/linux/libata.h.rej
     ./include/linux/pci_ids.h.rej
     ./drivers/scsi/Makefile.rej
     ./drivers/scsi/ahci.c.rej
     ./drivers/scsi/ata_piix.c.rej
     ./drivers/scsi/libata-core.c.rej
     ./drivers/scsi/libata-scsi.c.rej

-------------------------------------------------------
---- ./include/linux/libata.h.rej
-------------------------------------------------------

***************
*** 645,651 ****
   	ap->ops->scr_write(ap, reg, val);
   }

- static inline void scr_write_flush(struct ata_port *ap, unsigned int reg,
   				   u32 val)
   {
   	ap->ops->scr_write(ap, reg, val);
--- 652,658 ----
   	ap->ops->scr_write(ap, reg, val);
   }

+ static inline void scr_write_flush(struct ata_port *ap, unsigned int reg,
   				   u32 val)
   {
   	ap->ops->scr_write(ap, reg, val);

-------------------------------------------------------
---- ./include/linux/pci_ids.h.rej
-------------------------------------------------------

***************
*** 1249,1254 ****
   #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA	0x0266
   #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2	0x0267
   #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE	0x036E
   #define PCI_DEVICE_ID_NVIDIA_NVENET_12		0x0268
   #define PCI_DEVICE_ID_NVIDIA_NVENET_13		0x0269
   #define PCI_DEVICE_ID_NVIDIA_MCP51_AUDIO	0x026B
--- 1249,1255 ----
   #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA	0x0266
   #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2	0x0267
   #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE	0x036E
+ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA	0x036F
   #define PCI_DEVICE_ID_NVIDIA_NVENET_12		0x0268
   #define PCI_DEVICE_ID_NVIDIA_NVENET_13		0x0269
   #define PCI_DEVICE_ID_NVIDIA_MCP51_AUDIO	0x026B

-------------------------------------------------------
---- ./drivers/scsi/Makefile.rej
-------------------------------------------------------

***************
*** 120,131 ****
   obj-$(CONFIG_SCSI_NSP32)	+= nsp32.o
   obj-$(CONFIG_SCSI_IPR)		+= ipr.o
   obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi/
   obj-$(CONFIG_SCSI_SATA_AHCI)	+= libata.o ahci.o
   obj-$(CONFIG_SCSI_SATA_SVW)	+= libata.o sata_svw.o
   obj-$(CONFIG_SCSI_ATA_PIIX)	+= libata.o ata_piix.o
   obj-$(CONFIG_SCSI_SATA_PROMISE)	+= libata.o sata_promise.o
   obj-$(CONFIG_SCSI_SATA_QSTOR)	+= libata.o sata_qstor.o
   obj-$(CONFIG_SCSI_SATA_SIL)	+= libata.o sata_sil.o
   obj-$(CONFIG_SCSI_SATA_VIA)	+= libata.o sata_via.o
   obj-$(CONFIG_SCSI_SATA_VITESSE)	+= libata.o sata_vsc.o
   obj-$(CONFIG_SCSI_SATA_SIS)	+= libata.o sata_sis.o
--- 120,134 ----
   obj-$(CONFIG_SCSI_NSP32)	+= nsp32.o
   obj-$(CONFIG_SCSI_IPR)		+= ipr.o
   obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi/
+ obj-$(CONFIG_SCSI_ATA_ADMA)	+= libata.o ata_adma.o
   obj-$(CONFIG_SCSI_SATA_AHCI)	+= libata.o ahci.o
   obj-$(CONFIG_SCSI_SATA_SVW)	+= libata.o sata_svw.o
   obj-$(CONFIG_SCSI_ATA_PIIX)	+= libata.o ata_piix.o
+ obj-$(CONFIG_SCSI_PATA_PDC2027X)+= libata.o pata_pdc2027x.o
   obj-$(CONFIG_SCSI_SATA_PROMISE)	+= libata.o sata_promise.o
   obj-$(CONFIG_SCSI_SATA_QSTOR)	+= libata.o sata_qstor.o
   obj-$(CONFIG_SCSI_SATA_SIL)	+= libata.o sata_sil.o
+ obj-$(CONFIG_SCSI_SATA_SIL24)	+= libata.o sata_sil24.o
   obj-$(CONFIG_SCSI_SATA_VIA)	+= libata.o sata_via.o
   obj-$(CONFIG_SCSI_SATA_VITESSE)	+= libata.o sata_vsc.o
   obj-$(CONFIG_SCSI_SATA_SIS)	+= libata.o sata_sis.o

-------------------------------------------------------
---- ./drivers/scsi/ahci.c.rej
-------------------------------------------------------

***************
*** 269,274 ****
   	  board_ahci }, /* ESB2 */
   	{ PCI_VENDOR_ID_INTEL, 0x2683, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
   	  board_ahci }, /* ESB2 */
   	{ }	/* terminate list */
   };

--- 269,276 ----
   	  board_ahci }, /* ESB2 */
   	{ PCI_VENDOR_ID_INTEL, 0x2683, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
   	  board_ahci }, /* ESB2 */
+ 	{ PCI_VENDOR_ID_INTEL, 0x27c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ 	  board_ahci }, /* ICH7-M DH */
   	{ }	/* terminate list */
   };


-------------------------------------------------------
---- ./drivers/scsi/ata_piix.c.rej
-------------------------------------------------------

***************
*** 629,641 ****
   	port_info[1] = NULL;

   	if (port_info[0]->host_flags & PIIX_FLAG_AHCI) {
-                u8 tmp;
-                pci_read_config_byte(pdev, PIIX_SCC, &tmp);
-                if (tmp == PIIX_AHCI_DEVICE) {
-                        int rc = piix_disable_ahci(pdev);
-                        if (rc)
-                            return rc;
-                }
   	}

   	if (port_info[0]->host_flags & PIIX_FLAG_COMBINED) {
--- 629,641 ----
   	port_info[1] = NULL;

   	if (port_info[0]->host_flags & PIIX_FLAG_AHCI) {
+ 		u8 tmp;
+ 		pci_read_config_byte(pdev, PIIX_SCC, &tmp);
+ 		if (tmp == PIIX_AHCI_DEVICE) {
+ 			int rc = piix_disable_ahci(pdev);
+ 			if (rc)
+ 				return rc;
+ 		}
   	}

   	if (port_info[0]->host_flags & PIIX_FLAG_COMBINED) {

-------------------------------------------------------
---- ./drivers/scsi/libata-core.c.rej
-------------------------------------------------------

***************
*** 1194,1205 ****
   /**
    * 	ata_dev_config - Run device specific handlers and check for
    * 			 SATA->PATA bridges
-  * 	@ap: Bus
    * 	@i:  Device
    *
    * 	LOCKING:
    */
-
   void ata_dev_config(struct ata_port *ap, unsigned int i)
   {
   	/* limit bridge transfers to udma5, 200 sectors */
--- 1232,1243 ----
   /**
    * 	ata_dev_config - Run device specific handlers and check for
    * 			 SATA->PATA bridges
+  * 	@ap: Bus
    * 	@i:  Device
    *
    * 	LOCKING:
    */
+
   void ata_dev_config(struct ata_port *ap, unsigned int i)
   {
   	/* limit bridge transfers to udma5, 200 sectors */
***************
*** 2020,2025 ****
   }

   /**
    *	ata_sg_clean - Unmap DMA memory associated with command
    *	@qc: Command containing DMA memory to be released
    *
--- 2058,2111 ----
   }

   /**
+  *	ata_dev_init_params - Issue INIT DEV PARAMS command
+  *	@ap: Port associated with device @dev
+  *	@dev: Device to which command will be sent
+  *
+  *	LOCKING:
+  */
+
+ static void ata_dev_init_params(struct ata_port *ap, struct ata_device 
*dev)
+ {
+ 	DECLARE_COMPLETION(wait);
+ 	struct ata_queued_cmd *qc;
+ 	int rc;
+ 	unsigned long flags;
+ 	u16 sectors = dev->id[6];
+ 	u16 heads   = dev->id[3];
+
+ 	/* Number of sectors per track 1-255. Number of heads 1-16 */
+ 	if (sectors < 1 || sectors > 255 || heads < 1 || heads > 16)
+ 		return;
+
+ 	/* set up init dev params taskfile */
+ 	DPRINTK("init dev params \n");
+
+ 	qc = ata_qc_new_init(ap, dev);
+ 	BUG_ON(qc == NULL);
+
+ 	qc->tf.command = ATA_CMD_INIT_DEV_PARAMS;
+ 	qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ 	qc->tf.protocol = ATA_PROT_NODATA;
+ 	qc->tf.nsect = sectors;
+ 	qc->tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */
+
+ 	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)
+ 		ata_port_disable(ap);
+ 	else
+ 		wait_for_completion(&wait);
+
+ 	DPRINTK("EXIT\n");
+ }
+
+ /**
    *	ata_sg_clean - Unmap DMA memory associated with command
    *	@qc: Command containing DMA memory to be released
    *
***************
*** 2369,2379 ****

   	kunmap(page);

- 	if (bytes) {
   		goto next_sg;
- 	}
   }

   static void atapi_pio_bytes(struct ata_queued_cmd *qc)
   {
   	struct ata_port *ap = qc->ap;
--- 2572,2592 ----

   	kunmap(page);

+ 	if (bytes)
   		goto next_sg;
   }

+ /**
+  *	atapi_pio_bytes - Transfer data from/to the ATAPI device.
+  *	@qc: Command on going
+  *
+  *	Transfer Transfer data from/to the ATAPI device.
+  *
+  *	LOCKING:
+  *	Inherited from caller.
+  *
+  */
+
   static void atapi_pio_bytes(struct ata_queued_cmd *qc)
   {
   	struct ata_port *ap = qc->ap;

-------------------------------------------------------
---- ./drivers/scsi/libata-scsi.c.rej
-------------------------------------------------------

***************
*** 213,317 ****
   		{0x04, 		RECOVERED_ERROR, 0x11, 0x00},	// Recovered ECC error	 
Medium error, recovered
   		{0xFF, 0xFF, 0xFF, 0xFF}, // END mark
   	};
- 	int i = 0;
-
- 	cmd->result = SAM_STAT_CHECK_CONDITION;

   	/*
   	 *	Is this an error we can process/parse
   	 */

- 	if(drv_stat & ATA_ERR)
- 		/* Read the err bits */
- 		err = ata_chk_err(qc->ap);
-
- 	/* Display the ATA level error info */
-
- 	printk(KERN_WARNING "ata%u: status=0x%02x { ", qc->ap->id, drv_stat);
- 	if(drv_stat & 0x80)
- 	{
- 		printk("Busy ");
- 		err = 0;	/* Data is not valid in this case */
   	}
- 	else {
- 		if(drv_stat & 0x40)	printk("DriveReady ");
- 		if(drv_stat & 0x20)	printk("DeviceFault ");
- 		if(drv_stat & 0x10)	printk("SeekComplete ");
- 		if(drv_stat & 0x08)	printk("DataRequest ");
- 		if(drv_stat & 0x04)	printk("CorrectedError ");
- 		if(drv_stat & 0x02)	printk("Index ");
- 		if(drv_stat & 0x01)	printk("Error ");
- 	}
- 	printk("}\n");
-
- 	if(err)
- 	{
- 		printk(KERN_WARNING "ata%u: error=0x%02x { ", qc->ap->id, err);
- 		if(err & 0x04)		printk("DriveStatusError ");
- 		if(err & 0x80)
- 		{
- 			if(err & 0x04)
- 				printk("BadCRC ");
- 			else
- 				printk("Sector ");
   		}
- 		if(err & 0x40)		printk("UncorrectableError ");
- 		if(err & 0x10)		printk("SectorIdNotFound ");
- 		if(err & 0x02)		printk("TrackZeroNotFound ");
- 		if(err & 0x01)		printk("AddrMarkNotFound ");
- 		printk("}\n");

- 		/* Should we dump sector info here too ?? */
   	}


- 	/* Look for err */
- 	while(sense_table[i][0] != 0xFF)
- 	{
- 		/* Look for best matches first */
- 		if((sense_table[i][0] & err) == sense_table[i][0])
- 		{
- 			sb[0] = 0x70;
- 			sb[2] = sense_table[i][1];
- 			sb[7] = 0x0a;
- 			sb[12] = sense_table[i][2];
- 			sb[13] = sense_table[i][3];
- 			return;
- 		}
- 		i++;
   	}
- 	/* No immediate match */
- 	if(err)
- 		printk(KERN_DEBUG "ata%u: no sense translation for 0x%02x\n", 
qc->ap->id, err);

- 	i = 0;
- 	/* Fall back to interpreting status bits */
- 	while(stat_table[i][0] != 0xFF)
- 	{
- 		if(stat_table[i][0] & drv_stat)
- 		{
- 			sb[0] = 0x70;
- 			sb[2] = stat_table[i][1];
- 			sb[7] = 0x0a;
- 			sb[12] = stat_table[i][2];
- 			sb[13] = stat_table[i][3];
- 			return;
- 		}
- 		i++;
   	}
- 	/* No error ?? */
- 	printk(KERN_ERR "ata%u: called with no error (%02X)!\n", qc->ap->id, 
drv_stat);
- 	/* additional-sense-code[-qualifier] */

   	sb[0] = 0x70;
- 	sb[2] = MEDIUM_ERROR;
- 	sb[7] = 0x0A;
- 	if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
- 		sb[12] = 0x11; /* "unrecovered read error" */
- 		sb[13] = 0x04;
- 	} else {
- 		sb[12] = 0x0C; /* "write error -             */
- 		sb[13] = 0x02; /*  auto-reallocation failed" */
   	}
   }

--- 413,599 ----
   		{0x04, 		RECOVERED_ERROR, 0x11, 0x00},	// Recovered ECC error	 
Medium error, recovered
   		{0xFF, 0xFF, 0xFF, 0xFF}, // END mark
   	};

   	/*
   	 *	Is this an error we can process/parse
   	 */
+ 	if (drv_stat & ATA_BUSY) {
+ 		drv_err = 0;	/* Ignore the err bits, they're invalid */
+ 	}

+ 	if (drv_err) {
+ 		/* Look for drv_err */
+ 		for (i = 0; sense_table[i][0] != 0xFF; i++) {
+ 			/* Look for best matches first */
+ 			if ((sense_table[i][0] & drv_err) ==
+ 			    sense_table[i][0]) {
+ 				*sk = sense_table[i][1];
+ 				*asc = sense_table[i][2];
+ 				*ascq = sense_table[i][3];
+ 				goto translate_done;
+ 			}
+ 		}
+ 		/* No immediate match */
+ 		printk(KERN_WARNING "ata%u: no sense translation for "
+ 		       "error 0x%02x\n", id, drv_err);
   	}
+
+ 	/* Fall back to interpreting status bits */
+ 	for (i = 0; stat_table[i][0] != 0xFF; i++) {
+ 		if (stat_table[i][0] & drv_stat) {
+ 			*sk = stat_table[i][1];
+ 			*asc = stat_table[i][2];
+ 			*ascq = stat_table[i][3];
+ 			goto translate_done;
   		}
+ 	}
+ 	/* No error?  Undecoded? */
+ 	printk(KERN_WARNING "ata%u: no sense translation for status: 0x%02x\n",
+ 	       id, drv_stat);
+
+ 	/* For our last chance pick, use medium read error because
+ 	 * it's much more common than an ATA drive telling you a write
+ 	 * has failed.
+ 	 */
+ 	*sk = MEDIUM_ERROR;
+ 	*asc = 0x11; /* "unrecovered read error" */
+ 	*ascq = 0x04; /*  "auto-reallocation failed" */
+
+  translate_done:
+ 	printk(KERN_ERR "ata%u: translated ATA stat/err 0x%02x/%02x to "
+ 	       "SCSI SK/ASC/ASCQ 0x%x/%02x/%02x\n", id, drv_stat, drv_err,
+ 	       *sk, *asc, *ascq);
+ 	return;
+ }

+ /*
+  *	ata_gen_ata_desc_sense - Generate check condition sense block.
+  *	@qc: Command that completed.
+  *
+  *	This function is specific to the ATA descriptor format sense
+  *	block specified for the ATA pass through commands.  Regardless
+  *	of whether the command errored or not, return a sense
+  *	block. Copy all controller registers into the sense
+  *	block. Clear sense key, ASC & ASCQ if there is no error.
+  *
+  *	LOCKING:
+  *	spin_lock_irqsave(host_set lock)
+  */
+ void ata_gen_ata_desc_sense(struct ata_queued_cmd *qc)
+ {
+ 	struct scsi_cmnd *cmd = qc->scsicmd;
+ 	struct ata_taskfile *tf = &qc->tf;
+ 	unsigned char *sb = cmd->sense_buffer;
+ 	unsigned char *desc = sb + 8;
+
+ 	memset(sb, 0, SCSI_SENSE_BUFFERSIZE);
+
+ 	cmd->result = SAM_STAT_CHECK_CONDITION;
+
+ 	/*
+ 	 * Read the controller registers.
+ 	 */
+ 	assert(NULL != qc->ap->ops->tf_read);
+ 	qc->ap->ops->tf_read(qc->ap, tf);
+
+ 	/*
+ 	 * Use ata_to_sense_error() to map status register bits
+ 	 * onto sense key, asc & ascq.
+ 	 */
+ 	if (unlikely(tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ))) {
+ 		ata_to_sense_error(qc->ap->id, tf->command, tf->feature,
+ 				   &sb[1], &sb[2], &sb[3]);
+ 		sb[1] &= 0x0f;
   	}

+ 	/*
+ 	 * Sense data is current and format is descriptor.
+ 	 */
+ 	sb[0] = 0x72;

+ 	desc[0] = 0x09;
+
+ 	/*
+ 	 * Set length of additional sense data.
+ 	 * Since we only populate descriptor 0, the total
+ 	 * length is the same (fixed) length as descriptor 0.
+ 	 */
+ 	desc[1] = sb[7] = 14;
+
+ 	/*
+ 	 * Copy registers into sense buffer.
+ 	 */
+ 	desc[2] = 0x00;
+ 	desc[3] = tf->feature;	/* == error reg */
+ 	desc[5] = tf->nsect;
+ 	desc[7] = tf->lbal;
+ 	desc[9] = tf->lbam;
+ 	desc[11] = tf->lbah;
+ 	desc[12] = tf->device;
+ 	desc[13] = tf->command; /* == status reg */
+
+ 	/*
+ 	 * Fill in Extend bit, and the high order bytes
+ 	 * if applicable.
+ 	 */
+ 	if (tf->flags & ATA_TFLAG_LBA48) {
+ 		desc[2] |= 0x01;
+ 		desc[4] = tf->hob_nsect;
+ 		desc[6] = tf->hob_lbal;
+ 		desc[8] = tf->hob_lbam;
+ 		desc[10] = tf->hob_lbah;
   	}
+ }

+ /**
+  *	ata_gen_fixed_sense - generate a SCSI fixed sense block
+  *	@qc: Command that we are erroring out
+  *
+  *	Leverage ata_to_sense_error() to give us the codes.  Fit our
+  *	LBA in here if there's room.
+  *
+  *	LOCKING:
+  *	inherited from caller
+  */
+ void ata_gen_fixed_sense(struct ata_queued_cmd *qc)
+ {
+ 	struct scsi_cmnd *cmd = qc->scsicmd;
+ 	struct ata_taskfile *tf = &qc->tf;
+ 	unsigned char *sb = cmd->sense_buffer;
+
+ 	memset(sb, 0, SCSI_SENSE_BUFFERSIZE);
+
+ 	cmd->result = SAM_STAT_CHECK_CONDITION;
+
+ 	/*
+ 	 * Read the controller registers.
+ 	 */
+ 	assert(NULL != qc->ap->ops->tf_read);
+ 	qc->ap->ops->tf_read(qc->ap, tf);
+
+ 	/*
+ 	 * Use ata_to_sense_error() to map status register bits
+ 	 * onto sense key, asc & ascq.
+ 	 */
+ 	if (unlikely(tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ))) {
+ 		ata_to_sense_error(qc->ap->id, tf->command, tf->feature,
+ 				   &sb[2], &sb[12], &sb[13]);
+ 		sb[2] &= 0x0f;
   	}

   	sb[0] = 0x70;
+ 	sb[7] = 0x0a;
+
+ #if 0 /* when C/H/S support is merged */
+ 	if (tf->flags & ATA_TFLAG_LBA && !(tf->flags & ATA_TFLAG_LBA48)) {
+ #endif
+ 	if (!(tf->flags & ATA_TFLAG_LBA48)) {
+ 		/* A small (28b) LBA will fit in the 32b info field */
+ 		sb[0] |= 0x80;		/* set valid bit */
+ 		sb[3] = tf->device & 0x0f;
+ 		sb[4] = tf->lbah;
+ 		sb[5] = tf->lbam;
+ 		sb[6] = tf->lbal;
   	}
   }



-Michael Fortson

  reply	other threads:[~2005-08-23 19:53 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-08-23 13:02 libata passthru (S.M.A.R.T.) support for 2.4.31? Michael Fortson
2005-08-23 13:22 ` John W. Linville
2005-08-23 19:53   ` Michael Fortson [this message]
2005-08-24 16:02     ` John W. Linville
2005-08-24 18:26       ` John W. Linville

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=430B7EC9.3050802@permabit.com \
    --to=mfortson@permabit.com \
    --cc=linux-ide@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).