iPod Classic: reads HDD S.M.A.R.T. data

Adds ata_read_smart() function to storage ATA driver, current
SMART data can be displayed and optionally written to hard
disk using System->Debug menu.

Change-Id: Ie8817bb311d5d956df2f0fbfaf554e2d53e89a93
This commit is contained in:
Cástor Muñoz 2014-12-09 19:38:47 +01:00
parent 32b4558511
commit d20185ac96
5 changed files with 373 additions and 0 deletions

View file

@ -25,6 +25,107 @@
#include "config.h" /* for HAVE_MULTIVOLUME or not */
#include "mv.h" /* for IF_MV() and friends */
#ifdef HAVE_ATA_SMART
/* S.M.A.R.T. headers from smartmontools-5.42 */
#define NUMBER_ATA_SMART_ATTRIBUTES 30
struct ata_smart_attribute {
unsigned char id;
/* meaning of flag bits: see MACROS just below */
/* WARNING: MISALIGNED! */
unsigned short flags;
unsigned char current;
unsigned char worst;
unsigned char raw[6];
unsigned char reserv;
} __attribute__((packed));
/* MACROS to interpret the flags bits in the previous structure. */
/* These have not been implemented using bitflags and a union, to make */
/* it portable across bit/little endian and different platforms. */
/* 0: Prefailure bit */
/* From SFF 8035i Revision 2 page 19: Bit 0 (pre-failure/advisory bit) */
/* - If the value of this bit equals zero, an attribute value less */
/* than or equal to its corresponding attribute threshold indicates an */
/* advisory condition where the usage or age of the device has */
/* exceeded its intended design life period. If the value of this bit */
/* equals one, an attribute value less than or equal to its */
/* corresponding attribute threshold indicates a prefailure condition */
/* where imminent loss of data is being predicted. */
#define ATTRIBUTE_FLAGS_PREFAILURE(x) (x & 0x01)
/* 1: Online bit */
/* From SFF 8035i Revision 2 page 19: Bit 1 (on-line data collection */
/* bit) - If the value of this bit equals zero, then the attribute */
/* value is updated only during off-line data collection */
/* activities. If the value of this bit equals one, then the attribute */
/* value is updated during normal operation of the device or during */
/* both normal operation and off-line testing. */
#define ATTRIBUTE_FLAGS_ONLINE(x) (x & 0x02)
/* The following are (probably) IBM's, Maxtors and Quantum's definitions for the */
/* vendor-specific bits: */
/* 2: Performance type bit */
#define ATTRIBUTE_FLAGS_PERFORMANCE(x) (x & 0x04)
/* 3: Errorrate type bit */
#define ATTRIBUTE_FLAGS_ERRORRATE(x) (x & 0x08)
/* 4: Eventcount bit */
#define ATTRIBUTE_FLAGS_EVENTCOUNT(x) (x & 0x10)
/* 5: Selfpereserving bit */
#define ATTRIBUTE_FLAGS_SELFPRESERVING(x) (x & 0x20)
/* 6-15: Reserved for future use */
#define ATTRIBUTE_FLAGS_OTHER(x) ((x) & 0xffc0)
struct ata_smart_values
{
unsigned short int revnumber;
struct ata_smart_attribute vendor_attributes [NUMBER_ATA_SMART_ATTRIBUTES];
unsigned char offline_data_collection_status;
unsigned char self_test_exec_status;
unsigned short int total_time_to_complete_off_line;
unsigned char vendor_specific_366;
unsigned char offline_data_collection_capability;
unsigned short int smart_capability;
unsigned char errorlog_capability;
unsigned char vendor_specific_371;
unsigned char short_test_completion_time;
unsigned char extend_test_completion_time;
unsigned char conveyance_test_completion_time;
unsigned char reserved_375_385[11];
unsigned char vendor_specific_386_510[125];
unsigned char chksum;
} __attribute__((packed));
/* Raw attribute value print formats */
enum ata_attr_raw_format
{
RAWFMT_DEFAULT,
RAWFMT_RAW8,
RAWFMT_RAW16,
RAWFMT_RAW48,
RAWFMT_HEX48,
RAWFMT_RAW64,
RAWFMT_HEX64,
RAWFMT_RAW16_OPT_RAW16,
RAWFMT_RAW16_OPT_AVG16,
RAWFMT_RAW24_DIV_RAW24,
RAWFMT_RAW24_DIV_RAW32,
RAWFMT_SEC2HOUR,
RAWFMT_MIN2HOUR,
RAWFMT_HALFMIN2HOUR,
RAWFMT_MSEC24_HOUR32,
RAWFMT_TEMPMINMAX,
RAWFMT_TEMP10X,
};
#endif /* HAVE_ATA_SMART */
struct storage_info;
void ata_enable(bool on);
@ -69,4 +170,9 @@ int ata_spinup_time(void); /* ticks */
int ata_get_dma_mode(void);
#endif /* HAVE_ATA_DMA */
#ifdef HAVE_ATA_SMART
/* Returns current S.M.A.R.T. data */
void* ata_read_smart(void);
#endif
#endif /* __ATA_H__ */

View file

@ -201,6 +201,8 @@
#define STORAGE_NEEDS_ALIGN
#define HAVE_ATA_SMART
/* define this if the device has larger sectors when accessed via USB */
/* (only relevant in disk.c, fat.c now always supports large virtual sectors) */
//#define MAX_LOG_SECTOR_SIZE 4096

View file

@ -50,6 +50,9 @@
/** static, private data **/
static uint8_t ceata_taskfile[16] STORAGE_ALIGN_ATTR;
static uint16_t ata_identify_data[0x100] STORAGE_ALIGN_ATTR;
#ifdef HAVE_ATA_SMART
static uint16_t ata_smart_data[0x100] STORAGE_ALIGN_ATTR;
#endif
static bool ceata;
static bool ata_swap;
static bool ata_lba48;
@ -1211,6 +1214,62 @@ int ata_init(void)
return 0;
}
#ifdef HAVE_ATA_SMART
static int ata_smart(uint16_t* buf)
{
mutex_lock(&ata_mutex);
ata_power_up();
if (ceata)
{
memset(ceata_taskfile, 0, 16);
ceata_taskfile[0xc] = 0x4f;
ceata_taskfile[0xd] = 0xc2;
ceata_taskfile[0xe] = 0x40; /* Device/Head Register, bit6: 0->CHS, 1->LBA */
ceata_taskfile[0xf] = 0xb0;
PASS_RC(ceata_wait_idle(), 3, 0);
if (((uint8_t*)ata_identify_data)[54] != 'A') /* Model != aAmsung */
{
ceata_taskfile[0x9] = 0xd8; /* SMART enable operations */
PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 1);
PASS_RC(ceata_check_error(), 3, 2);
}
ceata_taskfile[0x9] = 0xd0; /* SMART read data */
PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 3);
PASS_RC(ceata_rw_multiple_block(false, buf, 1, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 3, 4);
}
else
{
int i;
uint32_t old = ATA_CFG;
ATA_CFG |= BIT(6); /* 16bit big-endian */
PASS_RC(ata_wait_for_not_bsy(10000000), 3, 5);
ata_write_cbr(&ATA_PIO_DAD, 0);
ata_write_cbr(&ATA_PIO_FED, 0xd0);
ata_write_cbr(&ATA_PIO_SCR, 0);
ata_write_cbr(&ATA_PIO_LLR, 0);
ata_write_cbr(&ATA_PIO_LMR, 0x4f);
ata_write_cbr(&ATA_PIO_LHR, 0xc2);
ata_write_cbr(&ATA_PIO_DVR, BIT(6));
ata_write_cbr(&ATA_PIO_CSD, 0xb0);
PASS_RC(ata_wait_for_start_of_transfer(10000000), 3, 6);
for (i = 0; i < 0x100; i++) buf[i] = ata_read_cbr(&ATA_PIO_DTR);
ATA_CFG = old;
}
ata_set_active();
mutex_unlock(&ata_mutex);
return 0;
}
void* ata_read_smart(void)
{
ata_smart(ata_smart_data);
return ata_smart_data;
}
#endif /* HAVE_ATA_SMART */
#ifdef CONFIG_STORAGE_MULTI
static int ata_num_drives(int first_drive)
{