diff options
author | Eran Tromer <eran@tromer.org> | 2006-10-10 17:29:25 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-10-11 04:18:07 -0400 |
commit | bbe1fe7ea3438f8c4447dbcd46a126581ed2ed41 (patch) | |
tree | 5b1eec4a822887a2f1380f6a0aa649322c986a91 /drivers/ata | |
parent | 4ad99f15c6a3cadf36928c399459ea4fdb3d49f9 (diff) |
[PATCH] libata: return sense data in HDIO_DRIVE_CMD ioctl
Make the HDIO_DRIVE_CMD ioctl in libata (ATA command pass through) return a
few ATA registers to userspace, following the same convention as the
drivers/ide implementation of the same ioctl. This is needed to support ATA
commands like CHECK POWER MODE, which return information in nsectors.
This fixes "hdparm -C" on SATA drives.
Forcing the sense data read via the cc flag causes spurious check conditions,
so we filter these out (following the ATA command pass-through specification
T10/04-262r7).
Signed-off-by: Eran Tromer <eran@tromer.org>
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>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/libata-scsi.c | 46 |
1 files changed, 39 insertions, 7 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index b0d0cc41f3e8..7af2a4ba4990 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c | |||
@@ -164,10 +164,10 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) | |||
164 | { | 164 | { |
165 | int rc = 0; | 165 | int rc = 0; |
166 | u8 scsi_cmd[MAX_COMMAND_SIZE]; | 166 | u8 scsi_cmd[MAX_COMMAND_SIZE]; |
167 | u8 args[4], *argbuf = NULL; | 167 | u8 args[4], *argbuf = NULL, *sensebuf = NULL; |
168 | int argsize = 0; | 168 | int argsize = 0; |
169 | struct scsi_sense_hdr sshdr; | ||
170 | enum dma_data_direction data_dir; | 169 | enum dma_data_direction data_dir; |
170 | int cmd_result; | ||
171 | 171 | ||
172 | if (arg == NULL) | 172 | if (arg == NULL) |
173 | return -EINVAL; | 173 | return -EINVAL; |
@@ -175,6 +175,10 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) | |||
175 | if (copy_from_user(args, arg, sizeof(args))) | 175 | if (copy_from_user(args, arg, sizeof(args))) |
176 | return -EFAULT; | 176 | return -EFAULT; |
177 | 177 | ||
178 | sensebuf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO); | ||
179 | if (!sensebuf) | ||
180 | return -ENOMEM; | ||
181 | |||
178 | memset(scsi_cmd, 0, sizeof(scsi_cmd)); | 182 | memset(scsi_cmd, 0, sizeof(scsi_cmd)); |
179 | 183 | ||
180 | if (args[3]) { | 184 | if (args[3]) { |
@@ -191,7 +195,7 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) | |||
191 | data_dir = DMA_FROM_DEVICE; | 195 | data_dir = DMA_FROM_DEVICE; |
192 | } else { | 196 | } else { |
193 | scsi_cmd[1] = (3 << 1); /* Non-data */ | 197 | scsi_cmd[1] = (3 << 1); /* Non-data */ |
194 | /* scsi_cmd[2] is already 0 -- no off.line, cc, or data xfer */ | 198 | scsi_cmd[2] = 0x20; /* cc but no off.line or data xfer */ |
195 | data_dir = DMA_NONE; | 199 | data_dir = DMA_NONE; |
196 | } | 200 | } |
197 | 201 | ||
@@ -210,18 +214,46 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) | |||
210 | 214 | ||
211 | /* Good values for timeout and retries? Values below | 215 | /* Good values for timeout and retries? Values below |
212 | from scsi_ioctl_send_command() for default case... */ | 216 | from scsi_ioctl_send_command() for default case... */ |
213 | if (scsi_execute_req(scsidev, scsi_cmd, data_dir, argbuf, argsize, | 217 | cmd_result = scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, argsize, |
214 | &sshdr, (10*HZ), 5)) { | 218 | sensebuf, (10*HZ), 5, 0); |
219 | |||
220 | if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */ | ||
221 | u8 *desc = sensebuf + 8; | ||
222 | cmd_result &= ~(0xFF<<24); /* DRIVER_SENSE is not an error */ | ||
223 | |||
224 | /* If we set cc then ATA pass-through will cause a | ||
225 | * check condition even if no error. Filter that. */ | ||
226 | if (cmd_result & SAM_STAT_CHECK_CONDITION) { | ||
227 | struct scsi_sense_hdr sshdr; | ||
228 | scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, | ||
229 | &sshdr); | ||
230 | if (sshdr.sense_key==0 && | ||
231 | sshdr.asc==0 && sshdr.ascq==0) | ||
232 | cmd_result &= ~SAM_STAT_CHECK_CONDITION; | ||
233 | } | ||
234 | |||
235 | /* Send userspace a few ATA registers (same as drivers/ide) */ | ||
236 | if (sensebuf[0] == 0x72 && /* format is "descriptor" */ | ||
237 | desc[0] == 0x09 ) { /* code is "ATA Descriptor" */ | ||
238 | args[0] = desc[13]; /* status */ | ||
239 | args[1] = desc[3]; /* error */ | ||
240 | args[2] = desc[5]; /* sector count (0:7) */ | ||
241 | if (copy_to_user(arg, args, sizeof(args))) | ||
242 | rc = -EFAULT; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | |||
247 | if (cmd_result) { | ||
215 | rc = -EIO; | 248 | rc = -EIO; |
216 | goto error; | 249 | goto error; |
217 | } | 250 | } |
218 | 251 | ||
219 | /* Need code to retrieve data from check condition? */ | ||
220 | |||
221 | if ((argbuf) | 252 | if ((argbuf) |
222 | && copy_to_user(arg + sizeof(args), argbuf, argsize)) | 253 | && copy_to_user(arg + sizeof(args), argbuf, argsize)) |
223 | rc = -EFAULT; | 254 | rc = -EFAULT; |
224 | error: | 255 | error: |
256 | kfree(sensebuf); | ||
225 | kfree(argbuf); | 257 | kfree(argbuf); |
226 | return rc; | 258 | return rc; |
227 | } | 259 | } |