ata_id.c revision 5168f84a29b9f0002c9d44133cc9830046bb0619
/*
*
* Copyright (C) 2005-2008 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2009 Lennart Poettering <lennart@poettering.net>
* Copyright (C) 2009-2010 David Zeuthen <zeuthen@gmail.com>
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <scsi/scsi_ioctl.h>
#include "libudev.h"
#include "libudev-private.h"
#include "log.h"
static int disk_scsi_inquiry_command(int fd,
void *buf,
{
/*
* INQUIRY, see SPC-4 section 6.4
*/
[0] = 0x12, /* OPERATION CODE: INQUIRY */
};
.guard = 'Q',
.request_len = sizeof(cdb),
.max_response_len = sizeof(sense),
.din_xfer_len = buf_len,
};
int ret;
if (ret != 0) {
/* could be that the driver doesn't do version 4, try version 3 */
.interface_id = 'S',
};
if (ret != 0)
return ret;
/* even if the ioctl succeeds, we need to check the return value */
io_hdr.host_status == 0 &&
io_hdr.driver_status == 0)) {
return -1;
}
} else
return ret;
}
/* even if the ioctl succeeds, we need to check the return value */
if (!(io_v4.device_status == 0 &&
io_v4.transport_status == 0 &&
io_v4.driver_status == 0)) {
return -1;
}
return 0;
}
static int disk_identify_command(int fd,
void *buf,
{
/*
* ATA Pass-Through 12 byte command, as described in
*
* T10 04-262r8 ATA Command Pass-Through
*
*/
[0] = 0xa1, /* OPERATION CODE: 12 byte pass through */
[1] = 4 << 1, /* PROTOCOL: PIO Data-in */
[2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
[3] = 0, /* FEATURES */
[4] = 1, /* SECTORS */
[5] = 0, /* LBA LOW */
[6] = 0, /* LBA MID */
[7] = 0, /* LBA HIGH */
[8] = 0 & 0x4F, /* SELECT */
[9] = 0xEC, /* Command: ATA IDENTIFY DEVICE */
};
.guard = 'Q',
.request_len = sizeof(cdb),
.max_response_len = sizeof(sense),
.din_xfer_len = buf_len,
};
int ret;
if (ret != 0) {
/* could be that the driver doesn't do version 4, try version 3 */
.interface_id = 'S',
};
if (ret != 0)
return ret;
} else
return ret;
}
return -1;
}
return 0;
}
static int disk_identify_packet_device_command(int fd,
void *buf,
{
/*
* ATA Pass-Through 16 byte command, as described in
*
* T10 04-262r8 ATA Command Pass-Through
*
*/
[0] = 0x85, /* OPERATION CODE: 16 byte pass through */
[1] = 4 << 1, /* PROTOCOL: PIO Data-in */
[2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
[3] = 0, /* FEATURES */
[4] = 0, /* FEATURES */
[5] = 0, /* SECTORS */
[6] = 1, /* SECTORS */
[7] = 0, /* LBA LOW */
[8] = 0, /* LBA LOW */
[9] = 0, /* LBA MID */
[10] = 0, /* LBA MID */
[11] = 0, /* LBA HIGH */
[12] = 0, /* LBA HIGH */
[13] = 0, /* DEVICE */
[14] = 0xA1, /* Command: ATA IDENTIFY PACKET DEVICE */
[15] = 0, /* CONTROL */
};
.guard = 'Q',
.request_len = sizeof (cdb),
.max_response_len = sizeof (sense),
.din_xfer_len = buf_len,
};
int ret;
if (ret != 0) {
/* could be that the driver doesn't do version 4, try version 3 */
.interface_id = 'S',
};
if (ret != 0)
return ret;
} else
return ret;
}
return -1;
}
return 0;
}
/**
* disk_identify_get_string:
* @identify: A block of IDENTIFY data
* @offset_words: Offset of the string to get, in words.
* @dest: Destination buffer for the string.
* @dest_len: Length of destination buffer, in bytes.
*
* Copies the ATA string from @identify located at @offset_words into @dest.
*/
unsigned int offset_words,
char *dest,
{
unsigned int c1;
unsigned int c2;
while (dest_len > 0) {
dest++;
dest++;
offset_words++;
dest_len -= 2;
}
}
unsigned int offset_words,
{
}
{
uint16_t *p;
}
/**
* disk_identify:
* @udev: The libudev context.
* @fd: File descriptor for the block device.
* @out_identify: Return location for IDENTIFY data.
* @out_is_packet_device: Return location for whether returned data is from a IDENTIFY PACKET DEVICE.
*
* Sends the IDENTIFY DEVICE or IDENTIFY PACKET DEVICE command to the
* device represented by @fd. If successful, then the result will be
* copied into @out_identify and @out_is_packet_device.
*
* This routine is based on code from libatasmart, Copyright 2008
* Lennart Poettering, LGPL v2.1.
*
* Returns: 0 if the data was successfully obtained, otherwise
* non-zero with errno set.
*/
int fd,
int *out_is_packet_device)
{
int ret;
int all_nul_bytes;
int n;
int is_packet_device = 0;
/* init results */
/* If we were to use ATA PASS_THROUGH (12) on an ATAPI device
* we could accidentally blank media. This is because MMC's BLANK
* command has the same op-code (0x61).
*
* To prevent this from happening we bail out if the device
* isn't a Direct Access Block Device, e.g. SCSI type 0x00
* command first... libata is handling this via its SCSI
* emulation layer.
*
* This also ensures that we're actually dealing with a device
* that understands SCSI commands.
*
* (Yes, it is a bit perverse that we're tunneling the ATA
* command through SCSI and relying on the ATA driver
* emulating SCSI well-enough...)
*
* (See commit 160b069c25690bfb0c785994c7c3710289179107 for
* the original bug-fix and see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=556635
* for the original bug-report.)
*/
if (ret != 0)
goto out;
/* SPC-4, section 6.4.2: Standard INQUIRY data */
if (peripheral_device_type == 0x05)
{
is_packet_device = 1;
goto check_nul_bytes;
}
if (peripheral_device_type != 0x00) {
ret = -1;
goto out;
}
/* OK, now issue the IDENTIFY DEVICE command */
if (ret != 0)
goto out;
/* Check if IDENTIFY data is all NUL bytes - if so, bail */
all_nul_bytes = 1;
for (n = 0; n < 512; n++) {
if (out_identify[n] != '\0') {
all_nul_bytes = 0;
break;
}
}
if (all_nul_bytes) {
ret = -1;
goto out;
}
out:
if (out_is_packet_device != NULL)
return ret;
}
_printf_(6,0)
{
}
{
struct hd_driveid id;
char model[41];
char model_enc[256];
char serial[21];
char revision[9];
int export = 0;
int fd;
int rc = 0;
int is_packet_device = 0;
{}
};
log_open();
goto exit;
while (1) {
int option;
if (option == -1)
break;
switch (option) {
case 'x':
export = 1;
break;
case 'h':
printf("Usage: ata_id [--export] [--help] <device>\n"
" --export print values as environment keys\n"
" --help print this help text\n\n");
goto exit;
}
}
log_error("no node specified");
rc = 1;
goto exit;
}
if (fd < 0) {
rc = 1;
goto exit;
}
/*
* fix up only the fields from the IDENTIFY data that we are going to
* use and copy it into the hd_driveid struct for convenience
*/
} else {
/* If this fails, then try HDIO_GET_IDENTITY */
rc = 2;
goto close;
}
}
if (export) {
/* Set this to convey the disk speaks the ATA protocol */
printf("ID_ATA=1\n");
/* This is an ATAPI device */
case 0:
printf("ID_TYPE=cd\n");
break;
case 1:
printf("ID_TYPE=tape\n");
break;
case 5:
printf("ID_TYPE=cd\n");
break;
case 7:
printf("ID_TYPE=optical\n");
break;
default:
printf("ID_TYPE=generic\n");
break;
}
} else {
printf("ID_TYPE=disk\n");
}
printf("ID_BUS=ata\n");
if (serial[0] != '\0') {
} else {
}
printf ("ID_ATA_WRITE_CACHE=1\n");
}
printf("ID_ATA_FEATURE_SET_HPA=1\n");
/*
* TODO: use the READ NATIVE MAX ADDRESS command to get the native max address
* so it is easy to check whether the protected area is in use.
*/
}
printf("ID_ATA_FEATURE_SET_PM=1\n");
}
printf("ID_ATA_FEATURE_SET_SECURITY=1\n");
printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=maximum\n");
else
printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=high\n");
}
printf("ID_ATA_FEATURE_SET_SECURITY_EXPIRE=1\n");
printf("ID_ATA_FEATURE_SET_SECURITY_FROZEN=1\n");
printf("ID_ATA_FEATURE_SET_SECURITY_LOCKED=1\n");
}
printf("ID_ATA_FEATURE_SET_SMART=1\n");
}
printf("ID_ATA_FEATURE_SET_AAM=1\n");
}
printf("ID_ATA_FEATURE_SET_PUIS=1\n");
}
printf("ID_ATA_FEATURE_SET_APM=1\n");
}
printf("ID_ATA_DOWNLOAD_MICROCODE=1\n");
/*
* Word 76 indicates the capabilities of a SATA device. A PATA device shall set
* word 76 to 0000h or FFFFh. If word 76 is set to 0000h or FFFFh, then
* the device does not claim compliance with the Serial ATA specification and words
* 76 through 79 are not valid and shall be ignored.
*/
printf("ID_ATA_SATA=1\n");
/*
* If bit 2 of word 76 is set to one, then the device supports the Gen2
* signaling rate of 3.0 Gb/s (see SATA 2.6).
*
* If bit 1 of word 76 is set to one, then the device supports the Gen1
* signaling rate of 1.5 Gb/s (see SATA 2.6).
*/
printf("ID_ATA_SATA_SIGNAL_RATE_GEN2=1\n");
printf("ID_ATA_SATA_SIGNAL_RATE_GEN1=1\n");
}
/* Word 217 indicates the nominal media rotation rate of the device */
if (word != 0x0000) {
if (word == 0x0001) {
}
}
/*
* Words 108-111 contain a mandatory World Wide Name (WWN) in the NAA IEEE Registered identifier
* format. Word 108 bits (15:12) shall contain 5h, indicating that the naming authority is IEEE.
* All other values are reserved.
*/
wwwn <<= 16;
wwwn <<= 16;
wwwn <<= 16;
/* ATA devices have no vendor extension */
}
printf("ID_ATA_CFA=1\n");
} else {
printf("ID_ATA_CFA=1\n");
}
}
} else {
if (serial[0] != '\0')
else
}
exit:
log_close();
return rc;
}