2010-06-21 18:55:16 +04:00
/*
2005-06-26 01:54:28 +04:00
* ata_id - reads product / serial number from ATA drives
*
2008-09-06 17:45:31 +04:00
* Copyright ( C ) 2005 - 2008 Kay Sievers < kay . sievers @ vrfy . org >
2009-11-04 18:34:22 +03:00
* Copyright ( C ) 2009 Lennart Poettering < lennart @ poettering . net >
2010-10-30 19:44:06 +04:00
* Copyright ( C ) 2009 - 2010 David Zeuthen < zeuthen @ gmail . com >
2005-06-26 01:54:28 +04:00
*
2008-09-10 04:40:42 +04:00
* 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 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/>.
2005-06-26 01:54:28 +04:00
*/
# include <stdio.h>
# include <stdlib.h>
2009-11-04 18:34:22 +03:00
# include <stdint.h>
2005-06-26 01:54:28 +04:00
# include <unistd.h>
# include <fcntl.h>
# include <ctype.h>
2009-11-04 18:34:22 +03:00
# include <assert.h>
2006-01-09 23:18:00 +03:00
# include <string.h>
2005-06-26 01:54:28 +04:00
# include <errno.h>
2007-05-01 16:19:31 +04:00
# include <getopt.h>
2009-11-04 18:34:22 +03:00
# include <scsi/scsi.h>
# include <scsi/sg.h>
# include <scsi/scsi_ioctl.h>
2005-06-26 01:54:28 +04:00
# include <sys/ioctl.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <linux/types.h>
# include <linux/hdreg.h>
2009-11-04 18:34:22 +03:00
# include <linux/fs.h>
2009-11-20 05:03:27 +03:00
# include <linux/cdrom.h>
2010-10-30 19:44:06 +04:00
# include <linux/bsg.h>
2009-11-04 18:34:22 +03:00
# include <arpa/inet.h>
2005-06-26 01:54:28 +04:00
2009-06-10 00:47:48 +04:00
# include "libudev.h"
# include "libudev-private.h"
2005-06-26 01:54:28 +04:00
2010-10-30 19:44:06 +04:00
# define COMMAND_TIMEOUT_MSEC (30 * 1000)
2009-11-04 18:34:22 +03:00
2010-10-30 19:44:06 +04:00
static int disk_scsi_inquiry_command ( int fd ,
void * buf ,
size_t buf_len )
2009-11-04 18:34:22 +03:00
{
2010-10-30 19:44:06 +04:00
struct sg_io_v4 io_v4 ;
uint8_t cdb [ 12 ] ;
uint8_t sense [ 32 ] ;
int ret ;
/*
* INQUIRY , see SPC - 4 section 6.4
*/
memset ( cdb , 0 , sizeof ( cdb ) ) ;
cdb [ 0 ] = 0x12 ; /* OPERATION CODE: INQUIRY */
cdb [ 3 ] = ( buf_len > > 8 ) ; /* ALLOCATION LENGTH */
cdb [ 4 ] = ( buf_len & 0xff ) ;
2009-11-04 18:34:22 +03:00
2010-10-30 19:44:06 +04:00
memset ( sense , 0 , sizeof ( sense ) ) ;
memset ( & io_v4 , 0 , sizeof ( struct sg_io_v4 ) ) ;
io_v4 . guard = ' Q ' ;
io_v4 . protocol = BSG_PROTOCOL_SCSI ;
io_v4 . subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD ;
io_v4 . request_len = sizeof ( cdb ) ;
io_v4 . request = ( uintptr_t ) cdb ;
io_v4 . max_response_len = sizeof ( sense ) ;
io_v4 . response = ( uintptr_t ) sense ;
io_v4 . din_xfer_len = buf_len ;
io_v4 . din_xferp = ( uintptr_t ) buf ;
io_v4 . timeout = COMMAND_TIMEOUT_MSEC ;
ret = ioctl ( fd , SG_IO , & io_v4 ) ;
if ( ret ! = 0 ) {
/* could be that the driver doesn't do version 4, try version 3 */
if ( errno = = EINVAL ) {
struct sg_io_hdr io_hdr ;
memset ( & io_hdr , 0 , sizeof ( struct sg_io_hdr ) ) ;
io_hdr . interface_id = ' S ' ;
io_hdr . cmdp = ( unsigned char * ) cdb ;
io_hdr . cmd_len = sizeof ( cdb ) ;
io_hdr . dxferp = buf ;
io_hdr . dxfer_len = buf_len ;
io_hdr . sbp = sense ;
io_hdr . mx_sb_len = sizeof ( sense ) ;
io_hdr . dxfer_direction = SG_DXFER_FROM_DEV ;
io_hdr . timeout = COMMAND_TIMEOUT_MSEC ;
ret = ioctl ( fd , SG_IO , & io_hdr ) ;
if ( ret ! = 0 )
goto out ;
/* even if the ioctl succeeds, we need to check the return value */
if ( ! ( io_hdr . status = = 0 & &
io_hdr . host_status = = 0 & &
io_hdr . driver_status = = 0 ) ) {
errno = EIO ;
ret = - 1 ;
goto out ;
}
} else {
goto out ;
}
}
/* 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 ) ) {
errno = EIO ;
ret = - 1 ;
goto out ;
}
out :
return ret ;
2009-11-04 18:34:22 +03:00
}
2010-10-30 19:44:06 +04:00
static int disk_identify_command ( int fd ,
void * buf ,
size_t buf_len )
2009-11-04 18:34:22 +03:00
{
2010-10-30 19:44:06 +04:00
struct sg_io_v4 io_v4 ;
2009-11-04 18:34:22 +03:00
uint8_t cdb [ 12 ] ;
uint8_t sense [ 32 ] ;
uint8_t * desc = sense + 8 ;
int ret ;
/*
2010-10-30 19:44:06 +04:00
* ATA Pass - Through 12 byte command , as described in
*
* T10 04 - 262 r8 ATA Command Pass - Through
*
* from http : //www.t10.org/ftp/t10/document.04/04-262r8.pdf
2009-11-04 18:34:22 +03:00
*/
memset ( cdb , 0 , sizeof ( cdb ) ) ;
2010-10-30 19:44:06 +04:00
cdb [ 0 ] = 0xa1 ; /* OPERATION CODE: 12 byte pass through */
cdb [ 1 ] = 4 < < 1 ; /* PROTOCOL: PIO Data-in */
cdb [ 2 ] = 0x2e ; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
cdb [ 3 ] = 0 ; /* FEATURES */
cdb [ 4 ] = 1 ; /* SECTORS */
cdb [ 5 ] = 0 ; /* LBA LOW */
cdb [ 6 ] = 0 ; /* LBA MID */
cdb [ 7 ] = 0 ; /* LBA HIGH */
cdb [ 8 ] = 0 & 0x4F ; /* SELECT */
cdb [ 9 ] = 0xEC ; /* Command: ATA IDENTIFY DEVICE */ ;
2009-11-04 18:34:22 +03:00
memset ( sense , 0 , sizeof ( sense ) ) ;
2010-10-30 19:44:06 +04:00
memset ( & io_v4 , 0 , sizeof ( struct sg_io_v4 ) ) ;
io_v4 . guard = ' Q ' ;
io_v4 . protocol = BSG_PROTOCOL_SCSI ;
io_v4 . subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD ;
io_v4 . request_len = sizeof ( cdb ) ;
io_v4 . request = ( uintptr_t ) cdb ;
io_v4 . max_response_len = sizeof ( sense ) ;
io_v4 . response = ( uintptr_t ) sense ;
io_v4 . din_xfer_len = buf_len ;
io_v4 . din_xferp = ( uintptr_t ) buf ;
io_v4 . timeout = COMMAND_TIMEOUT_MSEC ;
ret = ioctl ( fd , SG_IO , & io_v4 ) ;
if ( ret ! = 0 ) {
/* could be that the driver doesn't do version 4, try version 3 */
if ( errno = = EINVAL ) {
struct sg_io_hdr io_hdr ;
memset ( & io_hdr , 0 , sizeof ( struct sg_io_hdr ) ) ;
io_hdr . interface_id = ' S ' ;
io_hdr . cmdp = ( unsigned char * ) cdb ;
io_hdr . cmd_len = sizeof ( cdb ) ;
io_hdr . dxferp = buf ;
io_hdr . dxfer_len = buf_len ;
io_hdr . sbp = sense ;
io_hdr . mx_sb_len = sizeof ( sense ) ;
io_hdr . dxfer_direction = SG_DXFER_FROM_DEV ;
io_hdr . timeout = COMMAND_TIMEOUT_MSEC ;
ret = ioctl ( fd , SG_IO , & io_hdr ) ;
if ( ret ! = 0 )
goto out ;
} else {
goto out ;
}
}
if ( ! ( sense [ 0 ] = = 0x72 & & desc [ 0 ] = = 0x9 & & desc [ 1 ] = = 0x0c ) ) {
2009-11-04 18:34:22 +03:00
errno = EIO ;
2010-10-30 19:44:06 +04:00
ret = - 1 ;
goto out ;
2009-11-04 18:34:22 +03:00
}
2010-10-30 19:44:06 +04:00
out :
2009-11-04 18:34:22 +03:00
return ret ;
}
2010-11-04 15:55:58 +03:00
static int disk_identify_packet_device_command ( int fd ,
void * buf ,
size_t buf_len )
{
struct sg_io_v4 io_v4 ;
uint8_t cdb [ 16 ] ;
uint8_t sense [ 32 ] ;
uint8_t * desc = sense + 8 ;
int ret ;
/*
* ATA Pass - Through 16 byte command , as described in
*
* T10 04 - 262 r8 ATA Command Pass - Through
*
* from http : //www.t10.org/ftp/t10/document.04/04-262r8.pdf
*/
memset ( cdb , 0 , sizeof ( cdb ) ) ;
cdb [ 0 ] = 0x85 ; /* OPERATION CODE: 16 byte pass through */
cdb [ 1 ] = 4 < < 1 ; /* PROTOCOL: PIO Data-in */
cdb [ 2 ] = 0x2e ; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
cdb [ 3 ] = 0 ; /* FEATURES */
cdb [ 4 ] = 0 ; /* FEATURES */
cdb [ 5 ] = 0 ; /* SECTORS */
cdb [ 6 ] = 1 ; /* SECTORS */
cdb [ 7 ] = 0 ; /* LBA LOW */
cdb [ 8 ] = 0 ; /* LBA LOW */
cdb [ 9 ] = 0 ; /* LBA MID */
cdb [ 10 ] = 0 ; /* LBA MID */
cdb [ 11 ] = 0 ; /* LBA HIGH */
cdb [ 12 ] = 0 ; /* LBA HIGH */
cdb [ 13 ] = 0 ; /* DEVICE */
cdb [ 14 ] = 0xA1 ; /* Command: ATA IDENTIFY PACKET DEVICE */ ;
cdb [ 15 ] = 0 ; /* CONTROL */
memset ( sense , 0 , sizeof ( sense ) ) ;
memset ( & io_v4 , 0 , sizeof ( struct sg_io_v4 ) ) ;
io_v4 . guard = ' Q ' ;
io_v4 . protocol = BSG_PROTOCOL_SCSI ;
io_v4 . subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD ;
io_v4 . request_len = sizeof ( cdb ) ;
io_v4 . request = ( uintptr_t ) cdb ;
io_v4 . max_response_len = sizeof ( sense ) ;
io_v4 . response = ( uintptr_t ) sense ;
io_v4 . din_xfer_len = buf_len ;
io_v4 . din_xferp = ( uintptr_t ) buf ;
io_v4 . timeout = COMMAND_TIMEOUT_MSEC ;
ret = ioctl ( fd , SG_IO , & io_v4 ) ;
if ( ret ! = 0 ) {
/* could be that the driver doesn't do version 4, try version 3 */
if ( errno = = EINVAL ) {
struct sg_io_hdr io_hdr ;
memset ( & io_hdr , 0 , sizeof ( struct sg_io_hdr ) ) ;
io_hdr . interface_id = ' S ' ;
io_hdr . cmdp = ( unsigned char * ) cdb ;
io_hdr . cmd_len = sizeof ( cdb ) ;
io_hdr . dxferp = buf ;
io_hdr . dxfer_len = buf_len ;
io_hdr . sbp = sense ;
io_hdr . mx_sb_len = sizeof ( sense ) ;
io_hdr . dxfer_direction = SG_DXFER_FROM_DEV ;
io_hdr . timeout = COMMAND_TIMEOUT_MSEC ;
ret = ioctl ( fd , SG_IO , & io_hdr ) ;
if ( ret ! = 0 )
goto out ;
} else {
goto out ;
}
}
if ( ! ( sense [ 0 ] = = 0x72 & & desc [ 0 ] = = 0x9 & & desc [ 1 ] = = 0x0c ) ) {
errno = EIO ;
ret = - 1 ;
goto out ;
}
out :
return ret ;
}
2009-11-04 18:34:22 +03:00
/**
* 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 .
*/
static void disk_identify_get_string ( uint8_t identify [ 512 ] ,
unsigned int offset_words ,
char * dest ,
size_t dest_len )
{
unsigned int c1 ;
unsigned int c2 ;
assert ( identify ! = NULL ) ;
assert ( dest ! = NULL ) ;
assert ( ( dest_len & 1 ) = = 0 ) ;
while ( dest_len > 0 ) {
c1 = ( ( uint16_t * ) identify ) [ offset_words ] > > 8 ;
c2 = ( ( uint16_t * ) identify ) [ offset_words ] & 0xff ;
* dest = c1 ;
dest + + ;
* dest = c2 ;
dest + + ;
offset_words + + ;
dest_len - = 2 ;
}
}
static void disk_identify_fixup_string ( uint8_t identify [ 512 ] ,
unsigned int offset_words ,
size_t len )
{
disk_identify_get_string ( identify , offset_words ,
( char * ) identify + offset_words * 2 , len ) ;
}
static void disk_identify_fixup_uint16 ( uint8_t identify [ 512 ] , unsigned int offset_words )
{
uint16_t * p ;
p = ( uint16_t * ) identify ;
p [ offset_words ] = le16toh ( p [ offset_words ] ) ;
}
/**
* disk_identify :
* @ udev : The libudev context .
* @ fd : File descriptor for the block device .
* @ out_identify : Return location for IDENTIFY data .
2010-11-04 15:55:58 +03:00
* @ out_is_packet_device : Return location for whether returned data is from a IDENTIFY PACKET DEVICE .
2009-11-04 18:34:22 +03:00
*
2010-11-04 15:55:58 +03:00
* 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 .
2009-11-04 18:34:22 +03:00
*
* This routine is based on code from libatasmart , Copyright 2008
* Lennart Poettering , LGPL v2 .1 .
*
2010-11-04 15:55:58 +03:00
* Returns : 0 if the data was successfully obtained , otherwise
* non - zero with errno set .
2009-11-04 18:34:22 +03:00
*/
static int disk_identify ( struct udev * udev ,
2010-10-30 19:44:06 +04:00
int fd ,
2010-11-04 15:55:58 +03:00
uint8_t out_identify [ 512 ] ,
int * out_is_packet_device )
2009-11-04 18:34:22 +03:00
{
int ret ;
2010-10-30 19:44:06 +04:00
uint8_t inquiry_buf [ 36 ] ;
int peripheral_device_type ;
int all_nul_bytes ;
int n ;
2010-11-04 15:55:58 +03:00
int is_packet_device ;
2009-11-04 18:34:22 +03:00
assert ( out_identify ! = NULL ) ;
2010-11-04 15:55:58 +03:00
2009-11-04 18:34:22 +03:00
/* init results */
ret = - 1 ;
memset ( out_identify , ' \0 ' , 512 ) ;
2010-11-04 15:55:58 +03:00
is_packet_device = 0 ;
2009-11-04 18:34:22 +03:00
2010-10-30 19:44:06 +04:00
/* 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
* ( CD / DVD devices are type 0x05 ) . So we send a SCSI INQUIRY
* 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 160 b069c25690bfb0c785994c7c3710289179107 for
* the original bug - fix and see http : //bugs.debian.org/cgi-bin/bugreport.cgi?bug=556635
* for the original bug - report . )
2009-11-20 05:03:27 +03:00
*/
2010-10-30 19:44:06 +04:00
ret = disk_scsi_inquiry_command ( fd , inquiry_buf , sizeof ( inquiry_buf ) ) ;
if ( ret ! = 0 )
goto out ;
2009-11-04 18:34:22 +03:00
2010-10-30 19:44:06 +04:00
/* SPC-4, section 6.4.2: Standard INQUIRY data */
peripheral_device_type = inquiry_buf [ 0 ] & 0x1f ;
2010-11-04 15:55:58 +03:00
if ( peripheral_device_type = = 0x05 )
{
is_packet_device = 1 ;
ret = disk_identify_packet_device_command ( fd , out_identify , 512 ) ;
goto check_nul_bytes ;
}
2010-10-30 19:44:06 +04:00
if ( peripheral_device_type ! = 0x00 ) {
ret = - 1 ;
2009-11-04 18:34:22 +03:00
errno = EIO ;
2010-10-30 19:44:06 +04:00
goto out ;
2009-11-04 18:34:22 +03:00
}
2010-10-30 19:44:06 +04:00
/* OK, now issue the IDENTIFY DEVICE command */
ret = disk_identify_command ( fd , out_identify , 512 ) ;
2009-11-04 18:34:22 +03:00
if ( ret ! = 0 )
2010-10-30 19:44:06 +04:00
goto out ;
2009-11-04 18:34:22 +03:00
2010-11-04 15:55:58 +03:00
check_nul_bytes :
2010-10-30 19:44:06 +04:00
/* 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 ;
2009-11-04 18:34:22 +03:00
break ;
}
}
2010-10-30 19:44:06 +04:00
if ( all_nul_bytes ) {
ret = - 1 ;
2009-11-04 18:34:22 +03:00
errno = EIO ;
2010-10-30 19:44:06 +04:00
goto out ;
2009-11-04 18:34:22 +03:00
}
2010-10-30 19:44:06 +04:00
out :
2010-11-04 15:55:58 +03:00
if ( out_is_packet_device ! = NULL )
* out_is_packet_device = is_packet_device ;
2009-11-04 18:34:22 +03:00
return ret ;
}
2008-09-06 17:45:31 +04:00
static void log_fn ( struct udev * udev , int priority ,
const char * file , int line , const char * fn ,
const char * format , va_list args )
2005-06-26 01:54:28 +04:00
{
vsyslog ( priority , format , args ) ;
}
int main ( int argc , char * argv [ ] )
{
2008-09-06 17:45:31 +04:00
struct udev * udev ;
2005-06-26 01:54:28 +04:00
struct hd_driveid id ;
2010-11-04 15:55:58 +03:00
uint8_t identify [ 512 ] ;
2005-06-26 01:54:28 +04:00
char model [ 41 ] ;
2009-02-17 22:15:17 +03:00
char model_enc [ 256 ] ;
2005-06-26 01:54:28 +04:00
char serial [ 21 ] ;
char revision [ 9 ] ;
const char * node = NULL ;
int export = 0 ;
int fd ;
2010-11-04 15:55:58 +03:00
uint16_t word ;
2005-06-26 01:54:28 +04:00
int rc = 0 ;
2010-11-04 15:55:58 +03:00
int is_packet_device = 0 ;
2007-05-01 16:19:31 +04:00
static const struct option options [ ] = {
2008-10-02 18:49:05 +04:00
{ " export " , no_argument , NULL , ' x ' } ,
{ " help " , no_argument , NULL , ' h ' } ,
2007-05-01 16:19:31 +04:00
{ }
} ;
2005-06-26 01:54:28 +04:00
2008-09-06 17:45:31 +04:00
udev = udev_new ( ) ;
if ( udev = = NULL )
goto exit ;
2009-06-10 00:47:48 +04:00
udev_log_init ( " ata_id " ) ;
2008-09-06 17:45:31 +04:00
udev_set_log_fn ( udev , log_fn ) ;
2005-07-13 13:23:21 +04:00
2007-05-01 16:19:31 +04:00
while ( 1 ) {
int option ;
2005-06-26 01:54:28 +04:00
2007-05-01 16:19:31 +04:00
option = getopt_long ( argc , argv , " xh " , options , NULL ) ;
if ( option = = - 1 )
break ;
switch ( option ) {
case ' x ' :
2005-06-26 01:54:28 +04:00
export = 1 ;
2007-05-01 16:19:31 +04:00
break ;
case ' h ' :
printf ( " Usage: ata_id [--export] [--help] <device> \n "
2009-01-06 21:43:01 +03:00
" --export print values as environment keys \n "
2007-05-01 16:19:31 +04:00
" --help print this help text \n \n " ) ;
goto exit ;
}
2005-06-26 01:54:28 +04:00
}
2007-05-01 16:19:31 +04:00
node = argv [ optind ] ;
if ( node = = NULL ) {
2008-09-06 17:45:31 +04:00
err ( udev , " no node specified \n " ) ;
2005-06-26 01:54:28 +04:00
rc = 1 ;
goto exit ;
}
2005-09-16 23:23:36 +04:00
fd = open ( node , O_RDONLY | O_NONBLOCK ) ;
2005-06-26 01:54:28 +04:00
if ( fd < 0 ) {
2008-09-06 17:45:31 +04:00
err ( udev , " unable to open '%s' \n " , node ) ;
2005-06-26 01:54:28 +04:00
rc = 1 ;
goto exit ;
}
2010-11-04 15:55:58 +03:00
if ( disk_identify ( udev , fd , identify , & is_packet_device ) = = 0 ) {
2009-11-04 18:34:22 +03:00
/*
* 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
*/
disk_identify_fixup_string ( identify , 10 , 20 ) ; /* serial */
disk_identify_fixup_string ( identify , 23 , 6 ) ; /* fwrev */
disk_identify_fixup_string ( identify , 27 , 40 ) ; /* model */
2010-11-04 15:55:58 +03:00
disk_identify_fixup_uint16 ( identify , 0 ) ; /* configuration */
2009-11-04 18:34:22 +03:00
disk_identify_fixup_uint16 ( identify , 75 ) ; /* queue depth */
disk_identify_fixup_uint16 ( identify , 75 ) ; /* SATA capabilities */
disk_identify_fixup_uint16 ( identify , 82 ) ; /* command set supported */
disk_identify_fixup_uint16 ( identify , 83 ) ; /* command set supported */
disk_identify_fixup_uint16 ( identify , 84 ) ; /* command set supported */
disk_identify_fixup_uint16 ( identify , 85 ) ; /* command set supported */
disk_identify_fixup_uint16 ( identify , 86 ) ; /* command set supported */
disk_identify_fixup_uint16 ( identify , 87 ) ; /* command set supported */
disk_identify_fixup_uint16 ( identify , 89 ) ; /* time required for SECURITY ERASE UNIT */
disk_identify_fixup_uint16 ( identify , 90 ) ; /* time required for enhanced SECURITY ERASE UNIT */
disk_identify_fixup_uint16 ( identify , 91 ) ; /* current APM values */
disk_identify_fixup_uint16 ( identify , 94 ) ; /* current AAM value */
disk_identify_fixup_uint16 ( identify , 128 ) ; /* device lock function */
disk_identify_fixup_uint16 ( identify , 217 ) ; /* nominal media rotation rate */
memcpy ( & id , identify , sizeof id ) ;
} else {
/* If this fails, then try HDIO_GET_IDENTITY */
if ( ioctl ( fd , HDIO_GET_IDENTITY , & id ) ! = 0 ) {
if ( errno = = ENOTTY ) {
info ( udev , " HDIO_GET_IDENTITY unsupported for '%s' \n " , node ) ;
rc = 2 ;
} else {
err ( udev , " HDIO_GET_IDENTITY failed for '%s' \n " , node ) ;
rc = 3 ;
}
goto close ;
2007-04-29 02:08:30 +04:00
}
2005-06-26 01:54:28 +04:00
}
2009-02-17 22:15:17 +03:00
memcpy ( model , id . model , 40 ) ;
model [ 40 ] = ' \0 ' ;
udev_util_encode_string ( model , model_enc , sizeof ( model_enc ) ) ;
2008-11-05 23:49:52 +03:00
udev_util_replace_whitespace ( ( char * ) id . model , model , 40 ) ;
2008-11-20 09:52:33 +03:00
udev_util_replace_chars ( model , NULL ) ;
2008-11-05 23:49:52 +03:00
udev_util_replace_whitespace ( ( char * ) id . serial_no , serial , 20 ) ;
2008-11-20 09:52:33 +03:00
udev_util_replace_chars ( serial , NULL ) ;
2008-11-05 23:49:52 +03:00
udev_util_replace_whitespace ( ( char * ) id . fw_rev , revision , 8 ) ;
2008-11-20 09:52:33 +03:00
udev_util_replace_chars ( revision , NULL ) ;
2005-06-26 01:54:28 +04:00
if ( export ) {
2010-11-04 15:55:58 +03:00
/* Set this to convey the disk speaks the ATA protocol */
printf ( " ID_ATA=1 \n " ) ;
2009-11-04 18:34:22 +03:00
2005-06-27 19:04:56 +04:00
if ( ( id . config > > 8 ) & 0x80 ) {
/* This is an ATAPI device */
switch ( ( id . config > > 8 ) & 0x1f ) {
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 " ) ;
}
2009-06-28 04:58:12 +04:00
printf ( " ID_BUS=ata \n " ) ;
2005-06-26 01:54:28 +04:00
printf ( " ID_MODEL=%s \n " , model ) ;
2009-02-17 22:15:17 +03:00
printf ( " ID_MODEL_ENC=%s \n " , model_enc ) ;
2005-06-26 01:54:28 +04:00
printf ( " ID_REVISION=%s \n " , revision ) ;
2011-02-09 14:49:19 +03:00
if ( serial [ 0 ] ! = ' \0 ' ) {
printf ( " ID_SERIAL=%s_%s \n " , model , serial ) ;
printf ( " ID_SERIAL_SHORT=%s \n " , serial ) ;
} else {
printf ( " ID_SERIAL=%s \n " , model ) ;
}
2009-11-04 18:34:22 +03:00
if ( id . command_set_1 & ( 1 < < 5 ) ) {
printf ( " ID_ATA_WRITE_CACHE=1 \n " ) ;
printf ( " ID_ATA_WRITE_CACHE_ENABLED=%d \n " , ( id . cfs_enable_1 & ( 1 < < 5 ) ) ? 1 : 0 ) ;
}
if ( id . command_set_1 & ( 1 < < 10 ) ) {
printf ( " ID_ATA_FEATURE_SET_HPA=1 \n " ) ;
printf ( " ID_ATA_FEATURE_SET_HPA_ENABLED=%d \n " , ( id . cfs_enable_1 & ( 1 < < 10 ) ) ? 1 : 0 ) ;
/*
* 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 .
*/
}
if ( id . command_set_1 & ( 1 < < 3 ) ) {
printf ( " ID_ATA_FEATURE_SET_PM=1 \n " ) ;
printf ( " ID_ATA_FEATURE_SET_PM_ENABLED=%d \n " , ( id . cfs_enable_1 & ( 1 < < 3 ) ) ? 1 : 0 ) ;
}
if ( id . command_set_1 & ( 1 < < 1 ) ) {
printf ( " ID_ATA_FEATURE_SET_SECURITY=1 \n " ) ;
printf ( " ID_ATA_FEATURE_SET_SECURITY_ENABLED=%d \n " , ( id . cfs_enable_1 & ( 1 < < 1 ) ) ? 1 : 0 ) ;
printf ( " ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=%d \n " , id . trseuc * 2 ) ;
if ( ( id . cfs_enable_1 & ( 1 < < 1 ) ) ) /* enabled */ {
if ( id . dlf & ( 1 < < 8 ) )
printf ( " ID_ATA_FEATURE_SET_SECURITY_LEVEL=maximum \n " ) ;
else
printf ( " ID_ATA_FEATURE_SET_SECURITY_LEVEL=high \n " ) ;
}
if ( id . dlf & ( 1 < < 5 ) )
printf ( " ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=%d \n " , id . trsEuc * 2 ) ;
if ( id . dlf & ( 1 < < 4 ) )
printf ( " ID_ATA_FEATURE_SET_SECURITY_EXPIRE=1 \n " ) ;
if ( id . dlf & ( 1 < < 3 ) )
printf ( " ID_ATA_FEATURE_SET_SECURITY_FROZEN=1 \n " ) ;
if ( id . dlf & ( 1 < < 2 ) )
printf ( " ID_ATA_FEATURE_SET_SECURITY_LOCKED=1 \n " ) ;
}
if ( id . command_set_1 & ( 1 < < 0 ) ) {
printf ( " ID_ATA_FEATURE_SET_SMART=1 \n " ) ;
printf ( " ID_ATA_FEATURE_SET_SMART_ENABLED=%d \n " , ( id . cfs_enable_1 & ( 1 < < 0 ) ) ? 1 : 0 ) ;
}
if ( id . command_set_2 & ( 1 < < 9 ) ) {
printf ( " ID_ATA_FEATURE_SET_AAM=1 \n " ) ;
printf ( " ID_ATA_FEATURE_SET_AAM_ENABLED=%d \n " , ( id . cfs_enable_2 & ( 1 < < 9 ) ) ? 1 : 0 ) ;
printf ( " ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=%d \n " , id . acoustic > > 8 ) ;
printf ( " ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=%d \n " , id . acoustic & 0xff ) ;
}
if ( id . command_set_2 & ( 1 < < 5 ) ) {
printf ( " ID_ATA_FEATURE_SET_PUIS=1 \n " ) ;
printf ( " ID_ATA_FEATURE_SET_PUIS_ENABLED=%d \n " , ( id . cfs_enable_2 & ( 1 < < 5 ) ) ? 1 : 0 ) ;
}
if ( id . command_set_2 & ( 1 < < 3 ) ) {
printf ( " ID_ATA_FEATURE_SET_APM=1 \n " ) ;
printf ( " ID_ATA_FEATURE_SET_APM_ENABLED=%d \n " , ( id . cfs_enable_2 & ( 1 < < 3 ) ) ? 1 : 0 ) ;
if ( ( id . cfs_enable_2 & ( 1 < < 3 ) ) )
printf ( " ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=%d \n " , id . CurAPMvalues & 0xff ) ;
}
if ( id . command_set_2 & ( 1 < < 0 ) )
printf ( " ID_ATA_DOWNLOAD_MICROCODE=1 \n " ) ;
/*
* Word 76 indicates the capabilities of a SATA device . A PATA device shall set
* word 76 to 0000 h or FFFFh . If word 76 is set to 0000 h 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 .
*/
word = * ( ( uint16_t * ) identify + 76 ) ;
if ( word ! = 0x0000 & & word ! = 0xffff ) {
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 ) .
*/
if ( word & ( 1 < < 2 ) )
printf ( " ID_ATA_SATA_SIGNAL_RATE_GEN2=1 \n " ) ;
if ( word & ( 1 < < 1 ) )
printf ( " ID_ATA_SATA_SIGNAL_RATE_GEN1=1 \n " ) ;
}
/* Word 217 indicates the nominal media rotation rate of the device */
word = * ( ( uint16_t * ) identify + 217 ) ;
if ( word ! = 0x0000 ) {
if ( word = = 0x0001 ) {
printf ( " ID_ATA_ROTATION_RATE_RPM=0 \n " ) ; /* non-rotating e.g. SSD */
} else if ( word > = 0x0401 & & word < = 0xfffe ) {
printf ( " ID_ATA_ROTATION_RATE_RPM=%d \n " , word ) ;
}
}
/*
* Words 108 - 111 contain a mandatory World Wide Name ( WWN ) in the NAA IEEE Registered identifier
* format . Word 108 bits ( 15 : 12 ) shall contain 5 h , indicating that the naming authority is IEEE .
* All other values are reserved .
*/
word = * ( ( uint16_t * ) identify + 108 ) ;
if ( ( word & 0xf000 ) = = 0x5000 ) {
uint64_t wwwn ;
wwwn = * ( ( uint16_t * ) identify + 108 ) ;
wwwn < < = 16 ;
wwwn | = * ( ( uint16_t * ) identify + 109 ) ;
wwwn < < = 16 ;
wwwn | = * ( ( uint16_t * ) identify + 110 ) ;
wwwn < < = 16 ;
wwwn | = * ( ( uint16_t * ) identify + 111 ) ;
printf ( " ID_WWN=0x%llx \n " , ( unsigned long long int ) wwwn ) ;
2009-12-04 19:25:09 +03:00
/* ATA devices have no vendor extension */
printf ( " ID_WWN_WITH_EXTENSION=0x%llx \n " , ( unsigned long long int ) wwwn ) ;
2009-11-04 18:34:22 +03:00
}
2005-07-12 13:41:09 +04:00
} else {
if ( serial [ 0 ] ! = ' \0 ' )
printf ( " %s_%s \n " , model , serial ) ;
else
printf ( " %s \n " , model ) ;
}
2005-06-26 01:54:28 +04:00
close :
close ( fd ) ;
exit :
2008-09-06 17:45:31 +04:00
udev_unref ( udev ) ;
2009-06-10 00:47:48 +04:00
udev_log_close ( ) ;
2005-06-26 01:54:28 +04:00
return rc ;
}