diff options
| author | David Milburn <dmilburn@redhat.com> | 2007-01-30 03:59:15 -0500 | 
|---|---|---|
| committer | Jeff Garzik <jeff@garzik.org> | 2007-01-30 09:20:39 -0500 | 
| commit | af068bd1debcc76c1bc265aa01401901bf0067ed (patch) | |
| tree | 88d360e0974105ba87c6e05ddb4a06612406913d | |
| parent | 2ca6611b1fc1f913f5370b3c50a0a35d817491b3 (diff) | |
libata-scsi: ata_task_ioctl should return ATA registers from sense data
User applications using the HDIO_DRIVE_TASK ioctl through libata expect
specific ATA registers to be returned to userspace.  Verified that
ata_task_ioctl correctly returns register values to the smartctl
application.
Signed-off-by: David Milburn <dmilburn@redhat.com>
Acked-by: Tejun Heo <htejun@gmail.com>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
| -rw-r--r-- | drivers/ata/libata-scsi.c | 51 | 
1 files changed, 45 insertions, 6 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 7cc5a4a910a4..d151cf057ef2 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c  | |||
| @@ -273,8 +273,8 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) | |||
| 273 | { | 273 | { | 
| 274 | int rc = 0; | 274 | int rc = 0; | 
| 275 | u8 scsi_cmd[MAX_COMMAND_SIZE]; | 275 | u8 scsi_cmd[MAX_COMMAND_SIZE]; | 
| 276 | u8 args[7]; | 276 | u8 args[7], *sensebuf = NULL; | 
| 277 | struct scsi_sense_hdr sshdr; | 277 | int cmd_result; | 
| 278 | 278 | ||
| 279 | if (arg == NULL) | 279 | if (arg == NULL) | 
| 280 | return -EINVAL; | 280 | return -EINVAL; | 
| @@ -282,10 +282,14 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) | |||
| 282 | if (copy_from_user(args, arg, sizeof(args))) | 282 | if (copy_from_user(args, arg, sizeof(args))) | 
| 283 | return -EFAULT; | 283 | return -EFAULT; | 
| 284 | 284 | ||
| 285 | sensebuf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO); | ||
| 286 | if (!sensebuf) | ||
| 287 | return -ENOMEM; | ||
| 288 | |||
| 285 | memset(scsi_cmd, 0, sizeof(scsi_cmd)); | 289 | memset(scsi_cmd, 0, sizeof(scsi_cmd)); | 
| 286 | scsi_cmd[0] = ATA_16; | 290 | scsi_cmd[0] = ATA_16; | 
| 287 | scsi_cmd[1] = (3 << 1); /* Non-data */ | 291 | scsi_cmd[1] = (3 << 1); /* Non-data */ | 
| 288 | /* scsi_cmd[2] is already 0 -- no off.line, cc, or data xfer */ | 292 | scsi_cmd[2] = 0x20; /* cc but no off.line or data xfer */ | 
| 289 | scsi_cmd[4] = args[1]; | 293 | scsi_cmd[4] = args[1]; | 
| 290 | scsi_cmd[6] = args[2]; | 294 | scsi_cmd[6] = args[2]; | 
| 291 | scsi_cmd[8] = args[3]; | 295 | scsi_cmd[8] = args[3]; | 
| @@ -295,11 +299,46 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) | |||
| 295 | 299 | ||
| 296 | /* Good values for timeout and retries? Values below | 300 | /* Good values for timeout and retries? Values below | 
| 297 | from scsi_ioctl_send_command() for default case... */ | 301 | from scsi_ioctl_send_command() for default case... */ | 
| 298 | if (scsi_execute_req(scsidev, scsi_cmd, DMA_NONE, NULL, 0, &sshdr, | 302 | cmd_result = scsi_execute(scsidev, scsi_cmd, DMA_NONE, NULL, 0, | 
| 299 | (10*HZ), 5)) | 303 | sensebuf, (10*HZ), 5, 0); | 
| 304 | |||
| 305 | if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */ | ||
| 306 | u8 *desc = sensebuf + 8; | ||
| 307 | cmd_result &= ~(0xFF<<24); /* DRIVER_SENSE is not an error */ | ||
| 308 | |||
| 309 | /* If we set cc then ATA pass-through will cause a | ||
| 310 | * check condition even if no error. Filter that. */ | ||
| 311 | if (cmd_result & SAM_STAT_CHECK_CONDITION) { | ||
| 312 | struct scsi_sense_hdr sshdr; | ||
| 313 | scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, | ||
| 314 | &sshdr); | ||
| 315 | if (sshdr.sense_key==0 && | ||
| 316 | sshdr.asc==0 && sshdr.ascq==0) | ||
| 317 | cmd_result &= ~SAM_STAT_CHECK_CONDITION; | ||
| 318 | } | ||
| 319 | |||
| 320 | /* Send userspace ATA registers */ | ||
| 321 | if (sensebuf[0] == 0x72 && /* format is "descriptor" */ | ||
| 322 | desc[0] == 0x09) {/* code is "ATA Descriptor" */ | ||
| 323 | args[0] = desc[13]; /* status */ | ||
| 324 | args[1] = desc[3]; /* error */ | ||
| 325 | args[2] = desc[5]; /* sector count (0:7) */ | ||
| 326 | args[3] = desc[7]; /* lbal */ | ||
| 327 | args[4] = desc[9]; /* lbam */ | ||
| 328 | args[5] = desc[11]; /* lbah */ | ||
| 329 | args[6] = desc[12]; /* select */ | ||
| 330 | if (copy_to_user(arg, args, sizeof(args))) | ||
| 331 | rc = -EFAULT; | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | if (cmd_result) { | ||
| 300 | rc = -EIO; | 336 | rc = -EIO; | 
| 337 | goto error; | ||
| 338 | } | ||
| 301 | 339 | ||
| 302 | /* Need code to retrieve data from check condition? */ | 340 | error: | 
| 341 | kfree(sensebuf); | ||
| 303 | return rc; | 342 | return rc; | 
| 304 | } | 343 | } | 
| 305 | 344 | ||
