linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2.6.20] libata: Support HDIO_DRIVE_TASKFILE ioctl
@ 2007-02-22  2:13 Anand Kulkarni
  2007-02-24  4:29 ` Tejun Heo
  0 siblings, 1 reply; 3+ messages in thread
From: Anand Kulkarni @ 2007-02-22  2:13 UTC (permalink / raw)
  To: linux-ide; +Cc: Anand.Kulkarni

Hi,

Attached is a patch to allow HDIO_DRIVE_TASKFILE ioctl calls to libata
devices so that arbitrary ATA commands that need PIO data transfers in
either direction can be issued. I have tested it with linux kernel
2.6.20. The format of the input arguments is the same as for the
regular ATA HDIO_DRIVE_TASKFILE ioctl : Code that makes ioctl calls to
ATA devices on /dev/hda can be sent as is to SATA devices on /dev/sda.

Since this is the first time I am sending a patch, please let me know
if I should do anything differently.

Regards,
Anand.

Anand Kulkarni
SJRC 3403 Yerba Buena Road
San Jose, CA 95135
Ph : 408 717 5128
Fax : 408 717 9065

From: Anand Kulkarni <Anand.Kulkarni@hitachigst.com>

Add support for the HDIO_DRIVE_TASKFILE ioctl to libata devices. This
permits sending
arbitrary ATA commands to the device. Currently only non-data and PIO
commands
are supported. Based on the ide_taskfile_ioctl function in
drivers/ide/ide-taskfile.c

Signed-off: Anand Kulkarni <Anand.Kulkarni@hitachigst.com>
---

--- linux-2.6.20_taskfile10/drivers/ata/libata-scsi.c.orig      2007-02-12
17:29:21.000000000 -0800
+++ linux-2.6.20_taskfile10/drivers/ata/libata-scsi.c   2007-02-15
14:32:46.000000000 -0800
@@ -45,6 +45,7 @@
 #include <scsi/scsi_transport.h>
 #include <linux/libata.h>
 #include <linux/hdreg.h>
+#include <linux/ide.h>
 #include <asm/uaccess.h>

 #include "libata.h"
@@ -259,6 +260,235 @@ error:
 }

 /**
+ *     ata_taskfile_ioctl - Handler for HDIO_DRIVE_TASKFILE ioctl
+ *     @scsidev: Device to which we are issuing command
+ *     @arg: User provided data for issuing command
+ *
+ *     LOCKING:
+ *     Defined by the SCSI layer.  We don't really care.
+ *
+ *     @arg is pointer to type ide_task_request_t
+ *             Following fields are mandotory
+ *             arg->io_ports[8];
+ *                     task_ioreg_t feature;           Feature
+ *                     task_ioreg_t sector_count;      NumSectors
+ *                     task_ioreg_t sector_number;     Sector / LBA_L
+ *                     task_ioreg_t low_cylinder;      LCyl /  LBA_M
+ *                     task_ioreg_t high_cylinder;     HCyl /  LBA_H
+ *                     task_ioreg_t device_head;       Select Device/Head
+ *                     task_ioreg_t command;           Command
+ *             arg->hob_ports[8];
+ *             arg->data_phase;  --- TASKFILE_NO_DATA | TASKFILE_IN |
TASKFILE_OUT
+ *             arg->req_cmd;     --- not used
+ *             arg->out_size;
+ *             arg->in_size;
+ *
+ *     RETURNS:
+ *     Zero on success, negative errno on error.
+ */
+
+int ata_taskfile_ioctl(struct scsi_device *scsidev, void __user *arg)
+{
+       ide_task_request_t      * req_task;
+       task_struct_t           * iop_ptr;
+       task_struct_t           * hob_ptr;
+
+       u8 *outbuf      = NULL;
+       u8 *inbuf       = NULL;
+       u8 *argbuf      = NULL;
+       int argsize     = 0;
+
+       int tasksize    = sizeof(struct ide_task_request_s);
+       int taskin      = 0;
+       int taskout     = 0;
+
+       int err         = 0;
+
+       u8 scsi_cmd[MAX_COMMAND_SIZE];
+       struct scsi_sense_hdr sshdr;
+       enum dma_data_direction data_dir = DMA_NONE;
+
+       if (NULL == (void *)arg)
+               return -EINVAL;
+
+       req_task = kzalloc(tasksize, GFP_KERNEL);
+       if (req_task == NULL) return -ENOMEM;
+
+       if (copy_from_user(req_task, arg, tasksize)) {
+               kfree(req_task);
+               return -EFAULT;
+       }
+
+       iop_ptr = (task_struct_t *)(req_task->io_ports);
+       hob_ptr = (task_struct_t *)(req_task->hob_ports);
+
+       taskout = (int) req_task->out_size;
+       taskin  = (int) req_task->in_size;
+
+       if (taskout) {
+               int outtotal = tasksize;
+               outbuf = kzalloc(taskout, GFP_KERNEL);
+               if (outbuf == NULL) {
+                       err = -ENOMEM;
+                       goto abort;
+               }
+               if (copy_from_user(outbuf, arg + outtotal, taskout)) {
+                       err = -EFAULT;
+                       goto abort;
+               }
+       }
+
+       if (taskin) {
+               int intotal = tasksize + taskout;
+               inbuf = kzalloc(taskin, GFP_KERNEL);
+               if (inbuf == NULL) {
+                       err = -ENOMEM;
+                       goto abort;
+               }
+               if (copy_from_user(inbuf, arg + intotal, taskin)) {
+                       err = -EFAULT;
+                       goto abort;
+               }
+       }
+
+       /* Setup scsi_cmd for ATA passthrough command */
+       memset(scsi_cmd, 0, sizeof(scsi_cmd));
+       scsi_cmd[0] = ATA_16;
+
+       // Format for Byte 1 and Byte 2 of scsi_cmd for ATA_16/ ATA_12 (as
per T10/04-262r7)
+       // Byte 1 :
+       //      Bit 7-5 : For PIO transfers, log(number of sectors
transferred per interrupt) base2
+       //      Bit 4-1 : Protocol : HardReset, SRST, Idle. Non-data,
+       //                           PIO DataIn, PIO Dataout, DMA, DMAQ,
+       //                           DevDiag, DevReset, UDMA_DataIn,
UDMA_DataOut,
+       //                           FPDMA, rsvd, rsvd, rsvd
+       //      Bit 0   :  0 -->28bit, 1 => 48bit command
+       // Byte 2 :
+       //      Bit 7-6 : OffLine :Indicate if Device expected to tristate
bus for this command
+       //      Bit 5   : Force Check condition and read task registers at
command completion.
+       //      Bit 4   : Reserved
+       //      Bit 3   : T_Dir : 0=>Host to Device, 1 => Device to host
+       //      Bit 2   : BB : 0=> TransferLength is in bytes, 1 =>
TransferLength is in blocks
+       //      Bit 1-0 : source for Transfer_Length : 0=non-data, 1
Feature reg, 2 SectorCount, 3 Transport Specific IU
+       //
+       // scsi_cmd[2]: Transferlength avail in SectorCount register, in
block
+
+       switch (req_task->data_phase) {
+
+       case TASKFILE_NO_DATA:
+               scsi_cmd[1] = 3 << 1;
+               scsi_cmd[2]  = 0;
+               data_dir = DMA_NONE;
+               argsize = 0;
+               argbuf = NULL;
+               break;
+       case TASKFILE_IN:
+               scsi_cmd[1] = 4 << 1;
+               scsi_cmd[2]  = 0x0e;
+               data_dir = DMA_FROM_DEVICE;
+               argsize = taskin;
+               argbuf = inbuf;
+               break;
+       case TASKFILE_OUT:
+               scsi_cmd[1] = 5 << 1;
+               scsi_cmd[2]  = 0x06;
+               data_dir = DMA_TO_DEVICE;
+               argsize = taskout;
+               argbuf = outbuf;
+               break;
+
+       /* currently unsupported transfer modes */
+       case TASKFILE_MULTI_IN:
+       case TASKFILE_MULTI_OUT:
+       case TASKFILE_IN_OUT:
+       case TASKFILE_IN_DMA:
+       case TASKFILE_OUT_DMA:
+       case TASKFILE_IN_DMAQ:
+       case TASKFILE_OUT_DMAQ:
+               err = -EINVAL;
+               goto abort;
+       }
+
+       scsi_cmd[2]  |= 0x20;  // Generate a check condition after every
command
+
+       scsi_cmd[4]  = iop_ptr->feature;   // Feature
+       scsi_cmd[6]  = iop_ptr->sector_count;   // NSector SectorCount
+       scsi_cmd[8]  = iop_ptr->sector_number;   // LBA_L
+       scsi_cmd[10] = iop_ptr->low_cylinder;   // LBA_M
+       scsi_cmd[12] = iop_ptr->high_cylinder;   // LBA_H
+       scsi_cmd[13] = iop_ptr->device_head;   // Device/Head : Device is
overwritten by actual device (Master/Slave)
+
+       scsi_cmd[3]  = hob_ptr->feature;  // Feature_ext
+       scsi_cmd[5]  = hob_ptr->sector_count;  // NSector_ext
+       scsi_cmd[7]  = hob_ptr->sector_number;  // LBA_L_ext
+       scsi_cmd[9]  = hob_ptr->low_cylinder;  // LBA_M_ext
+       scsi_cmd[11] = hob_ptr->high_cylinder;  // LBA_H_ext
+
+       scsi_cmd[14] = iop_ptr->command;   // Command
+
+       if (taskout) {
+               printk(KERN_INFO "DMA_TO_DEVICE %d bytes fr outbuf 0x%.8X
Cmd %2x %2x %2x %2x %2x %2x %2x  \n",
+                               taskout, (int) outbuf,
+                               iop_ptr->command, iop_ptr->feature,
+                               iop_ptr->sector_count,
iop_ptr->sector_number,
+                               iop_ptr->low_cylinder,
iop_ptr->high_cylinder,
+                               iop_ptr->device_head);
+       } else if (taskin) {
+               printk(KERN_INFO "DMA_FROM_DEVICE %d bytes to inbuf 0x%.8X
  Cmd %2x %2x %2x %2x %2x %2x %2x  \n",
+                               taskin, (int) inbuf,
+                               iop_ptr->command, iop_ptr->feature,
+                               iop_ptr->sector_count,
iop_ptr->sector_number,
+                               iop_ptr->low_cylinder,
iop_ptr->high_cylinder,
+                               iop_ptr->device_head);
+       } else {
+               printk(KERN_INFO "NON_DATA    Cmd %2x %2x %2x %2x %2x %2x
%2x  \n",
+                               iop_ptr->command, iop_ptr->feature,
+                               iop_ptr->sector_count,
iop_ptr->sector_number,
+                               iop_ptr->low_cylinder,
iop_ptr->high_cylinder,
+                               iop_ptr->device_head);
+       }
+
+       /* Good values for timeout and retries?  Values below
+          from scsi_ioctl_send_command() for default case... */
+       if (scsi_execute_req(scsidev, scsi_cmd, data_dir, argbuf, argsize,
+                            &sshdr, (10*HZ), 0)) {
+               if (sshdr.sense_key != 0) {
+                   printk(KERN_INFO "Err Sense  %2x %2x %2x %2x\n",
sshdr.response_code, sshdr.sense_key, sshdr.asc, sshdr.ascq);
+                   err = -EIO;
+                   goto abort;
+               }
+       }
+
+       /* Need code to retrieve data from check condition? */
+       if (copy_to_user(arg, req_task, tasksize)) {
+               err = -EFAULT;
+               goto abort;
+       }
+       if (taskout) {
+               int outtotal = tasksize;
+               if (copy_to_user(arg + outtotal, outbuf, taskout)) {
+                       err = -EFAULT;
+                       goto abort;
+               }
+       }
+
+       if (taskin) {
+               int intotal = tasksize + taskout;
+               if (copy_to_user(arg + intotal, inbuf, taskin)) {
+                       err = -EFAULT;
+                       goto abort;
+               }
+       }
+
+abort:
+       if(req_task) kfree(req_task);
+       if(outbuf)   kfree(outbuf);
+       if(inbuf)    kfree(inbuf);
+
+       return err;
+}
+
+/**
  *     ata_task_ioctl - Handler for HDIO_DRIVE_TASK ioctl
  *     @scsidev: Device to which we are issuing command
  *     @arg: User provided data for issuing command
@@ -369,6 +599,11 @@ int ata_scsi_ioctl(struct scsi_device *s
                        return -EACCES;
                return ata_task_ioctl(scsidev, arg);

+       case HDIO_DRIVE_TASKFILE:
+               if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+                       return -EACCES;
+               return ata_taskfile_ioctl(scsidev, arg);
+
        default:
                rc = -ENOTTY;
                break;

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

* Re: [PATCH 2.6.20] libata: Support HDIO_DRIVE_TASKFILE ioctl
  2007-02-22  2:13 [PATCH 2.6.20] libata: Support HDIO_DRIVE_TASKFILE ioctl Anand Kulkarni
@ 2007-02-24  4:29 ` Tejun Heo
  2007-03-03  0:32   ` Jeff Garzik
  0 siblings, 1 reply; 3+ messages in thread
From: Tejun Heo @ 2007-02-24  4:29 UTC (permalink / raw)
  To: anand.kulkarni.remove.this; +Cc: linux-ide, Anand.Kulkarni

Hello,

Anand Kulkarni wrote:
> Hi,
> 
> Attached is a patch to allow HDIO_DRIVE_TASKFILE ioctl calls to libata
> devices so that arbitrary ATA commands that need PIO data transfers in
> either direction can be issued. I have tested it with linux kernel
> 2.6.20. The format of the input arguments is the same as for the
> regular ATA HDIO_DRIVE_TASKFILE ioctl : Code that makes ioctl calls to
> ATA devices on /dev/hda can be sent as is to SATA devices on /dev/sda.
> 
> Since this is the first time I am sending a patch, please let me know
> if I should do anything differently.

I was kind of hoping we could drop HDIO_DRIVE_TASKFILE.  Please take a
look at the HDIO_DRIVE_TASKFILE section of Documentation/ioctl/hdio.txt.
 That's one scary scary ioctl and we still haven't sorted out which are
features and which are bugs.

For backward compatibility HDIO_DRIVE_CMD and HDIO_DRIVE_TASK are
implemented and most applications seem to be happy with it.  If you need
real fine control over command execution, the recommended way is to use
SAT via SG_IO.

-- 
tejun

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

* Re: [PATCH 2.6.20] libata: Support HDIO_DRIVE_TASKFILE ioctl
  2007-02-24  4:29 ` Tejun Heo
@ 2007-03-03  0:32   ` Jeff Garzik
  0 siblings, 0 replies; 3+ messages in thread
From: Jeff Garzik @ 2007-03-03  0:32 UTC (permalink / raw)
  To: Tejun Heo; +Cc: anand.kulkarni.remove.this, linux-ide, Anand.Kulkarni

Tejun Heo wrote:
> Hello,
> 
> Anand Kulkarni wrote:
>> Hi,
>>
>> Attached is a patch to allow HDIO_DRIVE_TASKFILE ioctl calls to libata
>> devices so that arbitrary ATA commands that need PIO data transfers in
>> either direction can be issued. I have tested it with linux kernel
>> 2.6.20. The format of the input arguments is the same as for the
>> regular ATA HDIO_DRIVE_TASKFILE ioctl : Code that makes ioctl calls to
>> ATA devices on /dev/hda can be sent as is to SATA devices on /dev/sda.
>>
>> Since this is the first time I am sending a patch, please let me know
>> if I should do anything differently.
> 
> I was kind of hoping we could drop HDIO_DRIVE_TASKFILE.  Please take a

I tend to agree



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

end of thread, other threads:[~2007-03-03  0:33 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-02-22  2:13 [PATCH 2.6.20] libata: Support HDIO_DRIVE_TASKFILE ioctl Anand Kulkarni
2007-02-24  4:29 ` Tejun Heo
2007-03-03  0:32   ` 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).