📄 libata-scsi.c
字号:
/* * libata-scsi.c - helper library for ATA * * Maintained by: Jeff Garzik <jgarzik@pobox.com> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * * Copyright 2003-2004 Red Hat, Inc. All rights reserved. * Copyright 2003-2004 Jeff Garzik * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * * libata documentation is available via 'make {ps|pdf}docs', * as Documentation/DocBook/libata.* * * Hardware documentation available from * - http://www.t10.org/ * - http://www.t13.org/ * */#include <linux/kernel.h>#include <linux/blkdev.h>#include <linux/spinlock.h>#include <scsi/scsi.h>#include <scsi/scsi_host.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_device.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_transport.h>#include <linux/libata.h>#include <linux/hdreg.h>#include <linux/uaccess.h>#include "libata.h"#define SECTOR_SIZE 512typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc);static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev);static struct ata_device *ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev);static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, unsigned int id, unsigned int lun);#define RW_RECOVERY_MPAGE 0x1#define RW_RECOVERY_MPAGE_LEN 12#define CACHE_MPAGE 0x8#define CACHE_MPAGE_LEN 20#define CONTROL_MPAGE 0xa#define CONTROL_MPAGE_LEN 12#define ALL_MPAGES 0x3f#define ALL_SUB_MPAGES 0xffstatic const u8 def_rw_recovery_mpage[RW_RECOVERY_MPAGE_LEN] = { RW_RECOVERY_MPAGE, RW_RECOVERY_MPAGE_LEN - 2, (1 << 7), /* AWRE */ 0, /* read retry count */ 0, 0, 0, 0, 0, /* write retry count */ 0, 0, 0};static const u8 def_cache_mpage[CACHE_MPAGE_LEN] = { CACHE_MPAGE, CACHE_MPAGE_LEN - 2, 0, /* contains WCE, needs to be 0 for logic */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* contains DRA, needs to be 0 for logic */ 0, 0, 0, 0, 0, 0, 0};static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { CONTROL_MPAGE, CONTROL_MPAGE_LEN - 2, 2, /* DSENSE=0, GLTSD=1 */ 0, /* [QAM+QERR may be 1, see 05-359r1] */ 0, 0, 0, 0, 0xff, 0xff, 0, 30 /* extended self test time, see 05-359r1 */};/* * libata transport template. libata doesn't do real transport stuff. * It just needs the eh_timed_out hook. */static struct scsi_transport_template ata_scsi_transport_template = { .eh_strategy_handler = ata_scsi_error, .eh_timed_out = ata_scsi_timed_out, .user_scan = ata_scsi_user_scan,};static const struct { enum link_pm value; const char *name;} link_pm_policy[] = { { NOT_AVAILABLE, "max_performance" }, { MIN_POWER, "min_power" }, { MAX_PERFORMANCE, "max_performance" }, { MEDIUM_POWER, "medium_power" },};static const char *ata_scsi_lpm_get(enum link_pm policy){ int i; for (i = 0; i < ARRAY_SIZE(link_pm_policy); i++) if (link_pm_policy[i].value == policy) return link_pm_policy[i].name; return NULL;}static ssize_t ata_scsi_lpm_put(struct class_device *class_dev, const char *buf, size_t count){ struct Scsi_Host *shost = class_to_shost(class_dev); struct ata_port *ap = ata_shost_to_port(shost); enum link_pm policy = 0; int i; /* * we are skipping array location 0 on purpose - this * is because a value of NOT_AVAILABLE is displayed * to the user as max_performance, but when the user * writes "max_performance", they actually want the * value to match MAX_PERFORMANCE. */ for (i = 1; i < ARRAY_SIZE(link_pm_policy); i++) { const int len = strlen(link_pm_policy[i].name); if (strncmp(link_pm_policy[i].name, buf, len) == 0 && buf[len] == '\n') { policy = link_pm_policy[i].value; break; } } if (!policy) return -EINVAL; ata_lpm_schedule(ap, policy); return count;}static ssize_tata_scsi_lpm_show(struct class_device *class_dev, char *buf){ struct Scsi_Host *shost = class_to_shost(class_dev); struct ata_port *ap = ata_shost_to_port(shost); const char *policy = ata_scsi_lpm_get(ap->pm_policy); if (!policy) return -EINVAL; return snprintf(buf, 23, "%s\n", policy);}CLASS_DEVICE_ATTR(link_power_management_policy, S_IRUGO | S_IWUSR, ata_scsi_lpm_show, ata_scsi_lpm_put);EXPORT_SYMBOL_GPL(class_device_attr_link_power_management_policy);static void ata_scsi_invalid_field(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)){ ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ done(cmd);}/** * ata_std_bios_param - generic bios head/sector/cylinder calculator used by sd. * @sdev: SCSI device for which BIOS geometry is to be determined * @bdev: block device associated with @sdev * @capacity: capacity of SCSI device * @geom: location to which geometry will be output * * Generic bios head/sector/cylinder calculator * used by sd. Most BIOSes nowadays expect a XXX/255/16 (CHS) * mapping. Some situations may arise where the disk is not * bootable if this is not used. * * LOCKING: * Defined by the SCSI layer. We don't really care. * * RETURNS: * Zero. */int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]){ geom[0] = 255; geom[1] = 63; sector_div(capacity, 255*63); geom[2] = capacity; return 0;}/** * ata_get_identity - Handler for HDIO_GET_IDENTITY ioctl * @sdev: SCSI device to get identify data for * @arg: User buffer area for identify data * * LOCKING: * Defined by the SCSI layer. We don't really care. * * RETURNS: * Zero on success, negative errno on error. */static int ata_get_identity(struct scsi_device *sdev, void __user *arg){ struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *dev = ata_scsi_find_dev(ap, sdev); u16 __user *dst = arg; char buf[40]; if (!dev) return -ENOMSG; if (copy_to_user(dst, dev->id, ATA_ID_WORDS * sizeof(u16))) return -EFAULT; ata_id_string(dev->id, buf, ATA_ID_PROD, ATA_ID_PROD_LEN); if (copy_to_user(dst + ATA_ID_PROD, buf, ATA_ID_PROD_LEN)) return -EFAULT; ata_id_string(dev->id, buf, ATA_ID_FW_REV, ATA_ID_FW_REV_LEN); if (copy_to_user(dst + ATA_ID_FW_REV, buf, ATA_ID_FW_REV_LEN)) return -EFAULT; ata_id_string(dev->id, buf, ATA_ID_SERNO, ATA_ID_SERNO_LEN); if (copy_to_user(dst + ATA_ID_SERNO, buf, ATA_ID_SERNO_LEN)) return -EFAULT; return 0;}/** * ata_cmd_ioctl - Handler for HDIO_DRIVE_CMD 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. * * RETURNS: * Zero on success, negative errno on error. */int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg){ int rc = 0; u8 scsi_cmd[MAX_COMMAND_SIZE]; u8 args[4], *argbuf = NULL, *sensebuf = NULL; int argsize = 0; enum dma_data_direction data_dir; int cmd_result; if (arg == NULL) return -EINVAL; if (copy_from_user(args, arg, sizeof(args))) return -EFAULT; sensebuf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO); if (!sensebuf) return -ENOMEM; memset(scsi_cmd, 0, sizeof(scsi_cmd)); if (args[3]) { argsize = SECTOR_SIZE * args[3]; argbuf = kmalloc(argsize, GFP_KERNEL); if (argbuf == NULL) { rc = -ENOMEM; goto error; } scsi_cmd[1] = (4 << 1); /* PIO Data-in */ scsi_cmd[2] = 0x0e; /* no off.line or cc, read from dev, block count in sector count field */ data_dir = DMA_FROM_DEVICE; } else { scsi_cmd[1] = (3 << 1); /* Non-data */ scsi_cmd[2] = 0x20; /* cc but no off.line or data xfer */ data_dir = DMA_NONE; } scsi_cmd[0] = ATA_16; scsi_cmd[4] = args[2]; if (args[0] == WIN_SMART) { /* hack -- ide driver does this too... */ scsi_cmd[6] = args[3]; scsi_cmd[8] = args[1]; scsi_cmd[10] = 0x4f; scsi_cmd[12] = 0xc2; } else { scsi_cmd[6] = args[1]; } scsi_cmd[14] = args[0]; /* Good values for timeout and retries? Values below from scsi_ioctl_send_command() for default case... */ cmd_result = scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, argsize, sensebuf, (10*HZ), 5, 0); if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */ u8 *desc = sensebuf + 8; cmd_result &= ~(0xFF<<24); /* DRIVER_SENSE is not an error */ /* If we set cc then ATA pass-through will cause a * check condition even if no error. Filter that. */ if (cmd_result & SAM_STAT_CHECK_CONDITION) { struct scsi_sense_hdr sshdr; scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sshdr); if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0) cmd_result &= ~SAM_STAT_CHECK_CONDITION; } /* Send userspace a few ATA registers (same as drivers/ide) */ if (sensebuf[0] == 0x72 && /* format is "descriptor" */ desc[0] == 0x09) { /* code is "ATA Descriptor" */ args[0] = desc[13]; /* status */ args[1] = desc[3]; /* error */ args[2] = desc[5]; /* sector count (0:7) */ if (copy_to_user(arg, args, sizeof(args))) rc = -EFAULT; } } if (cmd_result) { rc = -EIO; goto error; } if ((argbuf) && copy_to_user(arg + sizeof(args), argbuf, argsize)) rc = -EFAULT;error: kfree(sensebuf); kfree(argbuf); return rc;}/** * ata_task_ioctl - Handler for HDIO_DRIVE_TASK 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. * * RETURNS: * Zero on success, negative errno on error. */int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg){ int rc = 0; u8 scsi_cmd[MAX_COMMAND_SIZE]; u8 args[7], *sensebuf = NULL; int cmd_result; if (arg == NULL) return -EINVAL; if (copy_from_user(args, arg, sizeof(args))) return -EFAULT; sensebuf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO); if (!sensebuf) return -ENOMEM; memset(scsi_cmd, 0, sizeof(scsi_cmd)); scsi_cmd[0] = ATA_16; scsi_cmd[1] = (3 << 1); /* Non-data */ scsi_cmd[2] = 0x20; /* cc but no off.line or data xfer */ scsi_cmd[4] = args[1]; scsi_cmd[6] = args[2]; scsi_cmd[8] = args[3]; scsi_cmd[10] = args[4]; scsi_cmd[12] = args[5]; scsi_cmd[13] = args[6] & 0x4f; scsi_cmd[14] = args[0]; /* Good values for timeout and retries? Values below from scsi_ioctl_send_command() for default case... */ cmd_result = scsi_execute(scsidev, scsi_cmd, DMA_NONE, NULL, 0, sensebuf, (10*HZ), 5, 0); if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */ u8 *desc = sensebuf + 8; cmd_result &= ~(0xFF<<24); /* DRIVER_SENSE is not an error */ /* If we set cc then ATA pass-through will cause a * check condition even if no error. Filter that. */ if (cmd_result & SAM_STAT_CHECK_CONDITION) { struct scsi_sense_hdr sshdr; scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sshdr); if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0) cmd_result &= ~SAM_STAT_CHECK_CONDITION; } /* Send userspace ATA registers */ if (sensebuf[0] == 0x72 && /* format is "descriptor" */ desc[0] == 0x09) {/* code is "ATA Descriptor" */ args[0] = desc[13]; /* status */ args[1] = desc[3]; /* error */ args[2] = desc[5]; /* sector count (0:7) */ args[3] = desc[7]; /* lbal */ args[4] = desc[9]; /* lbam */ args[5] = desc[11]; /* lbah */ args[6] = desc[12]; /* select */ if (copy_to_user(arg, args, sizeof(args))) rc = -EFAULT; } } if (cmd_result) { rc = -EIO; goto error; } error: kfree(sensebuf); return rc;}int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg){ int val = -EINVAL, rc = -EINVAL; switch (cmd) { case ATA_IOC_GET_IO32: val = 0; if (copy_to_user(arg, &val, 1)) return -EFAULT; return 0; case ATA_IOC_SET_IO32: val = (unsigned long) arg; if (val != 0) return -EINVAL; return 0; case HDIO_GET_IDENTITY: return ata_get_identity(scsidev, arg); case HDIO_DRIVE_CMD: if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; return ata_cmd_ioctl(scsidev, arg); case HDIO_DRIVE_TASK: if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; return ata_task_ioctl(scsidev, arg); default: rc = -ENOTTY; break; } return rc;}/** * ata_scsi_qc_new - acquire new ata_queued_cmd reference * @dev: ATA device to which the new command is attached * @cmd: SCSI command that originated this ATA command * @done: SCSI command completion function * * Obtain a reference to an unused ata_queued_cmd structure, * which is the basic libata structure representing a single * ATA command sent to the hardware. * * If a command was available, fill in the SCSI-specific * portions of the structure with information on the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -