in libata-core.c [1753:1937]
int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
unsigned int flags, u16 *id)
{
struct ata_port *ap = dev->link->ap;
unsigned int class = *p_class;
struct ata_taskfile tf;
unsigned int err_mask = 0;
const char *reason;
bool is_semb = class == ATA_DEV_SEMB;
int may_fallback = 1, tried_spinup = 0;
int rc;
retry:
ata_tf_init(dev, &tf);
switch (class) {
case ATA_DEV_SEMB:
class = ATA_DEV_ATA; /* some hard drives report SEMB sig */
fallthrough;
case ATA_DEV_ATA:
case ATA_DEV_ZAC:
tf.command = ATA_CMD_ID_ATA;
break;
case ATA_DEV_ATAPI:
tf.command = ATA_CMD_ID_ATAPI;
break;
default:
rc = -ENODEV;
reason = "unsupported class";
goto err_out;
}
tf.protocol = ATA_PROT_PIO;
/* Some devices choke if TF registers contain garbage. Make
* sure those are properly initialized.
*/
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
/* Device presence detection is unreliable on some
* controllers. Always poll IDENTIFY if available.
*/
tf.flags |= ATA_TFLAG_POLLING;
if (ap->ops->read_id)
err_mask = ap->ops->read_id(dev, &tf, (__le16 *)id);
else
err_mask = ata_do_dev_read_id(dev, &tf, (__le16 *)id);
if (err_mask) {
if (err_mask & AC_ERR_NODEV_HINT) {
ata_dev_dbg(dev, "NODEV after polling detection\n");
return -ENOENT;
}
if (is_semb) {
ata_dev_info(dev,
"IDENTIFY failed on device w/ SEMB sig, disabled\n");
/* SEMB is not supported yet */
*p_class = ATA_DEV_SEMB_UNSUP;
return 0;
}
if ((err_mask == AC_ERR_DEV) && (tf.feature & ATA_ABORTED)) {
/* Device or controller might have reported
* the wrong device class. Give a shot at the
* other IDENTIFY if the current one is
* aborted by the device.
*/
if (may_fallback) {
may_fallback = 0;
if (class == ATA_DEV_ATA)
class = ATA_DEV_ATAPI;
else
class = ATA_DEV_ATA;
goto retry;
}
/* Control reaches here iff the device aborted
* both flavors of IDENTIFYs which happens
* sometimes with phantom devices.
*/
ata_dev_dbg(dev,
"both IDENTIFYs aborted, assuming NODEV\n");
return -ENOENT;
}
rc = -EIO;
reason = "I/O error";
goto err_out;
}
if (dev->horkage & ATA_HORKAGE_DUMP_ID) {
ata_dev_info(dev, "dumping IDENTIFY data, "
"class=%d may_fallback=%d tried_spinup=%d\n",
class, may_fallback, tried_spinup);
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET,
16, 2, id, ATA_ID_WORDS * sizeof(*id), true);
}
/* Falling back doesn't make sense if ID data was read
* successfully at least once.
*/
may_fallback = 0;
swap_buf_le16(id, ATA_ID_WORDS);
/* sanity check */
rc = -EINVAL;
reason = "device reports invalid type";
if (class == ATA_DEV_ATA || class == ATA_DEV_ZAC) {
if (!ata_id_is_ata(id) && !ata_id_is_cfa(id))
goto err_out;
if (ap->host->flags & ATA_HOST_IGNORE_ATA &&
ata_id_is_ata(id)) {
ata_dev_dbg(dev,
"host indicates ignore ATA devices, ignored\n");
return -ENOENT;
}
} else {
if (ata_id_is_ata(id))
goto err_out;
}
if (!tried_spinup && (id[2] == 0x37c8 || id[2] == 0x738c)) {
tried_spinup = 1;
/*
* Drive powered-up in standby mode, and requires a specific
* SET_FEATURES spin-up subcommand before it will accept
* anything other than the original IDENTIFY command.
*/
err_mask = ata_dev_set_feature(dev, SETFEATURES_SPINUP, 0);
if (err_mask && id[2] != 0x738c) {
rc = -EIO;
reason = "SPINUP failed";
goto err_out;
}
/*
* If the drive initially returned incomplete IDENTIFY info,
* we now must reissue the IDENTIFY command.
*/
if (id[2] == 0x37c8)
goto retry;
}
if ((flags & ATA_READID_POSTRESET) &&
(class == ATA_DEV_ATA || class == ATA_DEV_ZAC)) {
/*
* The exact sequence expected by certain pre-ATA4 drives is:
* SRST RESET
* IDENTIFY (optional in early ATA)
* INITIALIZE DEVICE PARAMETERS (later IDE and ATA)
* anything else..
* Some drives were very specific about that exact sequence.
*
* Note that ATA4 says lba is mandatory so the second check
* should never trigger.
*/
if (ata_id_major_version(id) < 4 || !ata_id_has_lba(id)) {
err_mask = ata_dev_init_params(dev, id[3], id[6]);
if (err_mask) {
rc = -EIO;
reason = "INIT_DEV_PARAMS failed";
goto err_out;
}
/* current CHS translation info (id[53-58]) might be
* changed. reread the identify device info.
*/
flags &= ~ATA_READID_POSTRESET;
goto retry;
}
}
*p_class = class;
return 0;
err_out:
ata_dev_warn(dev, "failed to IDENTIFY (%s, err_mask=0x%x)\n",
reason, err_mask);
return rc;
}