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