/*
* Copyright (C) 2016 Red Hat, Inc.
* (C) Copyright (C) 2017 Hewlett Packard Enterprise Development LP
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; If not, see <http://www.gnu.org/licenses/>.
*
* Author: Gris Ge <fge@redhat.com>
*/
#include "libata.h"
#include "utils.h"
#include "libstoragemgmt/libstoragemgmt_error.h"
#include "libstoragemgmt/libstoragemgmt_types.h"
#include <stdint.h>
#include <assert.h>
/*
* Serial ATA Additional Capabilities
*/
#define _ATA_SATA_ADD_CAP_WORD 77
#define _ATA_SPEED_UNKNOWN 0
#define _ATA_SPEED_GEN1_0 1
/* SATA revision 1.0 -- 1.5 Gbps */
#define _ATA_SPEED_GEN2_0 2
/* SATA revision 2.0 -- 3 Gbps */
#define _ATA_SPEED_GEN3_0 3
/* SATA revision 3.0 -- 6 Gbps */
#define _ATA_SMART_RETURN_STATUS_LBA_MID_NO_ERR 0x4f
#define _ATA_SMART_RETURN_STATUS_LBA_MID_ERR 0xf4
#define _ATA_SMART_RETURN_STATUS_LBA_HIGH_NO_ERR 0xc2
#define _ATA_SMART_RETURN_STATUS_LBA_HIGH_ERR 0x2c
/* ACS-3 Table 210 - SMART Return Status Normal Output
* LBA 2CF4h The device has detected a threshold exceeded condition.
* LBA C24Fh The subcommand specified a captive self-test that has completed
* without error.
*/
#define _ATA_SMART_RETURN_STATUS_DEVICE_FAULT_BIT 5
/* ACS-3 Table 210 - SMART Return Status Normal Output
* ACS-3 6.2.7 DEVICE FAULT bit
*/
#define _bit_field_extract(i, end_include, start_include) \
((i >> start_include) & ((1 << (end_include - start_include + 1)) - 1))
#pragma pack(push, 1)
struct _ata_sata_add_cap {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint8_t zero : 1;
uint8_t cur_speed : 3;
uint8_t we_dont_care_0 : 4;
#else
uint8_t we_dont_care_0 : 4;
uint8_t cur_speed : 3;
uint8_t zero : 1;
#endif
uint8_t we_dont_care_1;
};
#pragma pack(pop)
int _ata_cur_speed_get(char *err_msg, uint8_t *id_dev_data,
uint32_t *link_speed)
{
int rc = LSM_ERR_OK;
struct _ata_sata_add_cap *add_cap = NULL;
assert(id_dev_data != NULL);
assert(link_speed != NULL);
add_cap = (struct _ata_sata_add_cap *)
(id_dev_data + _ATA_SATA_ADD_CAP_WORD * 2);
*link_speed = LSM_DISK_LINK_SPEED_UNKNOWN;
switch(add_cap->cur_speed) {
case _ATA_SPEED_UNKNOWN:
rc = LSM_ERR_NO_SUPPORT;
_lsm_err_msg_set(err_msg, "No support: specified disk does not "
"expose SATA speed information in 'Serial ATA "
"Capabilities' word");
break;
case _ATA_SPEED_GEN1_0:
*link_speed = 1500;
break;
case _ATA_SPEED_GEN2_0:
*link_speed = 3000;
break;
case _ATA_SPEED_GEN3_0:
*link_speed = 6000;
break;
default:
rc = LSM_ERR_LIB_BUG;
_lsm_err_msg_set(err_msg,
"BUG: Got unexpected ATA speed code 0x%02x",
add_cap->cur_speed);
}
return rc;
}
int32_t _ata_health_status(uint8_t status, uint8_t lba_mid, uint8_t lba_high)
{
if _bit_field_extract(status, _ATA_SMART_RETURN_STATUS_DEVICE_FAULT_BIT,
_ATA_SMART_RETURN_STATUS_DEVICE_FAULT_BIT)
return LSM_DISK_HEALTH_STATUS_FAIL;
if ((lba_mid == _ATA_SMART_RETURN_STATUS_LBA_MID_NO_ERR) &&
(lba_high == _ATA_SMART_RETURN_STATUS_LBA_HIGH_NO_ERR))
return LSM_DISK_HEALTH_STATUS_GOOD;
else if ((lba_mid == _ATA_SMART_RETURN_STATUS_LBA_MID_ERR) &&
(lba_high == _ATA_SMART_RETURN_STATUS_LBA_HIGH_ERR))
return LSM_DISK_HEALTH_STATUS_FAIL;
return LSM_DISK_HEALTH_STATUS_UNKNOWN;
}