SATA 硬盘/SSD温度读取

37 篇文章 0 订阅
6 篇文章 0 订阅

 

        目前SATA硬盘温度读取都是通过SMART协议来读取,当前可利用的开源工具有两个:

1、hddtemp工具:

 

        hddtemp是专门读取sata硬盘温度的工具,如果只需要知道硬盘的温度,此工具足以。

        该工具小巧玲珑,而且不需要依赖第三方库,可以直接使用,缺点:如果是自己的的代码需要知道硬盘温度,不能直接调用hddtemp接口。

使用方法:   hddtemp /dev/sdx

 

Usage: hddtemp [OPTIONS] [TYPE:]DISK1 [[TYPE:]DISK2]...

   hddtemp displays the temperature of drives supplied in argument.
   Drives must support S.M.A.R.T.

  TYPE could be SATA, PATA or SCSI. If omitted hddtemp will try to guess.

  -b   --drivebase   :  display database file content that allow hddtemp to
                        recognize supported drives.
  -D   --debug       :  display various S.M.A.R.T. fields and their values.
                        Useful to find a value that seems to match the
                        temperature and/or to send me a report.
                        (done for every drive supplied).
  -d   --daemon      :  run hddtemp in TCP/IP daemon mode (port 7634 by default.)
  -f   --file=FILE   :  specify database file to use.
  -F   --foreground  :  don't daemonize, stay in foreground.
  -l   --listen=addr :  listen on a specific interface (in TCP/IP daemon mode).
  -n   --numeric     :  print only the temperature.
  -p   --port=#      :  port to listen to (in TCP/IP daemon mode).
  -s   --separator=C :  separator to use between fields (in TCP/IP daemon mode).
  -S   --syslog=s    :  log temperature to syslog every s seconds.
  -u   --unit=[C|F]  :  force output temperature either in Celsius or Fahrenheit.
  -q   --quiet       :  do not check if the drive is supported.
  -v   --version     :  display hddtemp version number.
  -w   --wake-up     :  wake-up the drive if need.
  -4                 :  listen on IPv4 sockets only.
  -6                 :  listen on IPv6 sockets only.

Report bugs or new drives to <hddtemp@guzu.net>.
hddtemp version 0.3-beta15

2、smartmontools工具:

     此工具功能强大,可以读取你所了解的所有硬盘信息,包括硬盘读写时间,读写数据了、温度等等其他一切硬盘信息。

优点:你所有想了解的硬盘信息,此工具皆能获取到,缺点:smartmontools依赖其他第三方库,比如mailx等等。

使用方法: smartctl -a /dev/sdx     smartctl -x /dev/sdx

=== START OF INFORMATION SECTION ===
Device Model:     LITEON V5G+  128
Serial Number:    P02743137697
LU WWN Device Id: 5 002303 10103438e
Firmware Version: 2870306
User Capacity:    128,035,676,160 bytes [128 GB]
Sector Size:      512 bytes logical/physical
Rotation Rate:    Solid State Device
Form Factor:      M.2
Device is:        Not in smartctl database [for details use: -P showall]
ATA Version is:   ACS-2 (minor revision not indicated)
SATA Version is:  SATA 3.1, 6.0 Gb/s (current: 6.0 Gb/s)
Local Time is:    Mon Jul 16 15:41:47 2018 CST
SMART support is: Available - device has SMART capability.
SMART support is: Enabled

Device Statistics (GP Log 0x04)
Page  Offset Size        Value Flags Description
0x01  =====  =               =  ===  == General Statistics (rev 1) ==
0x01  0x008  4             121  ---  Lifetime Power-On Resets
0x01  0x010  4            1512  ---  Power-on Hours
0x01  0x018  6      2287532613  ---  Logical Sectors Written
0x01  0x020  6        38912385  ---  Number of Write Commands
0x01  0x028  6        78111346  ---  Logical Sectors Read
0x01  0x030  6         2053933  ---  Number of Read Commands
0x04  =====  =               =  ===  == General Errors Statistics (rev 1) ==
0x04  0x008  4               0  ---  Number of Reported Uncorrectable Errors
0x04  0x010  4         8241353  ---  Resets Between Cmd Acceptance and Completion
0x06  =====  =               =  ===  == Transport Statistics (rev 1) ==
0x06  0x008  4             465  ---  Number of Hardware Resets
0x06  0x018  4               0  ---  Number of Interface CRC Errors
0x07  =====  =               =  ===  == Solid State Device Statistics (rev 1) ==
0x07  0x008  1               0  ---  Percentage Used Endurance Indicator
                                |||_ C monitored condition met
                                ||__ D supports DSN
                                |___ N normalized value

SATA Phy Event Counters (GP Log 0x11)
ID      Size     Value  Description
0x0001  2            0  Command failed due to ICRC error
0x0002  2            0  R_ERR response for data FIS
0x0003  2            0  R_ERR response for device-to-host data FIS
0x0004  2            0  R_ERR response for host-to-device data FIS
0x0005  2            0  R_ERR response for non-data FIS
0x0006  2            0  R_ERR response for device-to-host non-data FIS
0x0007  2            0  R_ERR response for host-to-device non-data FIS
0x0008  2            0  Device-to-host non-data FIS retries
0x0009  2            0  Transition from drive PhyRdy to drive PhyNRdy
0x000a  2           49  Device-to-host register FISes sent due to a COMRESET
0x000b  2            0  CRC errors within host-to-device FIS
0x000d  2            0  Non-CRC errors within host-to-device FIS
0x000f  2            0  R_ERR response for host-to-device data FIS, CRC
0x0010  2            0  R_ERR response for host-to-device data FIS, non-CRC
0x0012  2            0  R_ERR response for host-to-device non-data FIS, CRC
0x0013  2            0  R_ERR response for host-to-device non-data FIS, non-CRC


SMART Attributes Data Structure revision number: 1
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
  1 Raw_Read_Error_Rate     0x002f   100   100   000    Pre-fail  Always       -       0
  5 Reallocated_Sector_Ct   0x0003   100   100   000    Pre-fail  Always       -       0
  9 Power_On_Hours          0x0002   100   100   000    Old_age   Always       -       1512
 12 Power_Cycle_Count       0x0003   100   100   000    Pre-fail  Always       -       121
170 Unknown_Attribute       0x0032   100   100   000    Old_age   Always       -       0
171 Unknown_Attribute       0x0003   100   100   000    Pre-fail  Always       -       0
172 Unknown_Attribute       0x0003   100   100   000    Pre-fail  Always       -       0
173 Unknown_Attribute       0x0003   100   100   000    Pre-fail  Always       -       8
174 Unknown_Attribute       0x0003   100   100   000    Pre-fail  Always       -       23
175 Program_Fail_Count_Chip 0x0003   100   100   000    Pre-fail  Always       -       0
176 Erase_Fail_Count_Chip   0x0003   100   100   000    Pre-fail  Always       -       0
178 Used_Rsvd_Blk_Cnt_Chip  0x0003   100   100   000    Pre-fail  Always       -       0
179 Used_Rsvd_Blk_Cnt_Tot   0x0003   100   100   000    Pre-fail  Always       -       0
180 Unused_Rsvd_Blk_Cnt_Tot 0x0033   100   100   005    Pre-fail  Always       -       61
181 Program_Fail_Cnt_Total  0x0003   100   100   000    Pre-fail  Always       -       0
182 Erase_Fail_Count_Total  0x0003   100   100   000    Pre-fail  Always       -       0
183 Runtime_Bad_Block       0x0032   100   100   000    Old_age   Always       -       0
195 Hardware_ECC_Recovered  0x0003   100   100   000    Pre-fail  Always       -       0
199 UDMA_CRC_Error_Count    0x0003   100   100   000    Pre-fail  Always       -       0
232 Available_Reservd_Space 0x0003   100   100   010    Pre-fail  Always       -       100
233 Media_Wearout_Indicator 0x0003   100   100   000    Pre-fail  Always       -       1139
241 Total_LBAs_Written      0x0003   100   100   000    Pre-fail  Always       -       34904
242 Total_LBAs_Read         0x0003   100   100   000    Pre-fail  Always       -       1191

 

3、再有就是自己提取smart接口来自定义读取温度了。以下是提取hddtemp代码中的接口自己弄得读硬盘温度的代码,好处是可以放入自己的代码当中,直接调用接口就可以读取硬盘温度。aarch64和x64环境通过验证。

     使用方法:

      Usage:
                   satatemp  [-d]  /dev/sda
                   -d print debug  info

源代码如下:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <scsi/scsi_ioctl.h>
#include <fcntl.h>  


#define   DEF(X)   1
int debug = 0;



#if DEF(ATA)
typedef unsigned short  u16;
#define swapb(x) \
({ \
        u16 __x = (x); \
        (x) = ((u16)( \
                (((u16)(__x) & (u16)0x00ffU) << 8) | \
                (((u16)(__x) & (u16)0xff00U) >> 8) )); \
})
#define GBUF_SIZE 65535
#define DEFAULT_ATTRIBUTE_ID   194
#define DEFAULT_ATTRIBUTE_ID2  190
#define SBUFF_SIZE 512
static char sbuff[SBUFF_SIZE];


static int ata_probe(int device) {
  if(device == -1 || ioctl(device, HDIO_GET_IDENTITY, sbuff))
    return 0;
  else
    return 1;
}



int ata_enable_smart(int device) {
  unsigned char cmd[4] = { WIN_SMART, 0, SMART_ENABLE, 0 };

  return ioctl(device, HDIO_DRIVE_CMD, cmd);
}


int ata_get_smart_values(int device, unsigned char* buff) {
  unsigned char  cmd[516] = { WIN_SMART, 0, SMART_READ_VALUES, 1 };
  int            ret;

  ret = ioctl(device, HDIO_DRIVE_CMD, cmd);
  if(ret)
    return ret;
  memcpy(buff, cmd+4, 512);
  return 0;
}

static char *ata_model (int device) {
  if(device == -1 || ioctl(device, HDIO_GET_IDENTITY, sbuff))
    return strdup("unknown");
  else
    return strdup((char*) ((u16*)sbuff + 27));
}



unsigned char* ata_search_temperature(const unsigned char* smart_data, int attribute_id) {
  int i, n;

  n = 3;
  i = 0;
  if(debug)
     printf("============ ata ============\n");
  while((debug || *(smart_data + n) != attribute_id) && i < 30) {
    if(debug && *(smart_data + n))
      printf("field(%d)\t = %d\t(0x%02x)\n",
	     (int)*(smart_data + n),
	     (int)*(smart_data + n + 3),
	     *(smart_data + n + 3));

    n += 12;
    i++;
  }

  if(i >= 30)
    return NULL;
  else
    return (unsigned char*)(smart_data + n);
}


int ata_get_temperature(int fd) 
{ 
    unsigned char    values[512]/*, thresholds[512]*/;
    unsigned char  *field;
    int              i;
    unsigned short  *p;

    if(ata_enable_smart(fd) != 0) 
    {
        printf("ATA S.M.A.R.T. not available!\n");
        return -1;
      }
    
      if(ata_get_smart_values(fd, values)) 
      {
        printf("ATA Enable S.M.A.R.T. err!\n");
        return -1;
      }
    
      p = (u16*)values;
      for(i = 0; i < 256; i++) {
        swapb(*(p+i));
      }
    
      /* get SMART threshold values */
      /*
      if(get_smart_threshold_values(fd, thresholds)) {
        perror("ioctl");
        exit(3);
      }
    
      p = (u16*)thresholds;
      for(i = 0; i < 256; i++) {
        swapb(*(p+i));
      }
      */
    
      /* temperature */
      field = ata_search_temperature(values, DEFAULT_ATTRIBUTE_ID);
      if(!field)
        field = ata_search_temperature(values, DEFAULT_ATTRIBUTE_ID2);
    
      if(field)
        return *(field+3);
     else
        return -1;
}

#endif


#if DEF(SCSI)
#define TEMPERATURE_PAGE				0x0d
#define CDB_12_HDR_SIZE 14
#define CDB_12_MAX_DATA_SIZE		0xffffffff
#define CDB_6_HDR_SIZE			14
#define CDB_6_MAX_DATA_SIZE		0xff

#define DEXCPT_DISABLE  0xf7
#define DEXCPT_ENABLE	0x08
#define EWASC_ENABLE	0x10
#define EWASC_DISABLE	0xef
#define GBUF_SIZE 65535
#define MODE_DATA_HDR_SIZE	12
#define SMART_SUPPORT	0x00


struct cdb10hdr {
	unsigned int inbufsize;
	unsigned int outbufsize;
	unsigned int cdb [10];
} ;



struct cdb6hdr{
	unsigned int inbufsize;
	unsigned int outbufsize;
	unsigned char cdb [6];
};



static void scsi_fixstring(unsigned char *s, int bytecount)
{
  unsigned char *p;
  unsigned char *end;

  p = s;
  end = s + bytecount;

  /* strip leading blanks */
  while (s != end && *s == ' ')
    ++s;
  /* compress internal blanks and strip trailing blanks */
  while (s != end && *s) {
    if (*s++ != ' ' || (s != end && *s && *s != ' '))
      *p++ = *(s-1);
  }
  /* wipe out trailing garbage */
  while (p != end)
    *p++ = '\0';
}

int scsi_SG_IO(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, unsigned char *sense, unsigned char sense_len, int dxfer_direction) {
  struct sg_io_hdr io_hdr;

  memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
  io_hdr.interface_id = 'S';
  io_hdr.cmdp = cdb;
  io_hdr.cmd_len = cdb_len;
  io_hdr.dxfer_len = buffer_len;
  io_hdr.dxferp = buffer;
  io_hdr.mx_sb_len = sense_len;
  io_hdr.sbp = sense;
  io_hdr.dxfer_direction = dxfer_direction;
  io_hdr.timeout = 3000; /* 3 seconds should be ample */

  return ioctl(device, SG_IO, &io_hdr);
}

int scsi_SEND_COMMAND(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction)
{
  unsigned char buf[2048];
  unsigned int inbufsize, outbufsize, ret;

  switch(dxfer_direction) {
    case SG_DXFER_FROM_DEV: 
      inbufsize = 0;
      outbufsize = buffer_len;
      break;
    case SG_DXFER_TO_DEV:
      inbufsize = buffer_len;
      outbufsize = 0;
      break;
    default:
      inbufsize = 0;
      outbufsize = 0;
      break;
  }
  memcpy(buf, &inbufsize , sizeof(inbufsize));
  memcpy(buf + sizeof(inbufsize), &outbufsize , sizeof(outbufsize));
  memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize), cdb, cdb_len);
  memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize) + cdb_len, buffer, buffer_len);

  ret = ioctl(device, SCSI_IOCTL_SEND_COMMAND, buf);
  
  memcpy(buffer, buf + sizeof(inbufsize) + sizeof(outbufsize), buffer_len);
   
  return ret;
}

int scsi_command(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction)
{
  static int SG_IO_supported = -1;
  int ret;

  if (SG_IO_supported == 1)
    return scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction);
  else if (SG_IO_supported == 0)
    return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction);
  else {
    ret = scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction);
    if (ret == 0) {
      SG_IO_supported = 1;
      return ret;
    } else {
      SG_IO_supported = 0;
      return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction);
    }
  }	 
}

int scsi_inquiry(int device, unsigned char *buffer)
{
  unsigned char cdb[6];
 
  memset(cdb, 0, sizeof(cdb));
  cdb[0] = INQUIRY;
  cdb[4] = 36;  /* should be 36 for unsafe devices (like USB mass storage stuff)
                 *      otherwise they can lock up! SPC sections 7.4 and 8.6 */

  if (scsi_command(device, cdb, sizeof(cdb), buffer, cdb[4], SG_DXFER_FROM_DEV) != 0)
    return 1;
  else {
    scsi_fixstring(buffer + 8, 24);
    return 0;
  }
}



unsigned char modesense (int device,  unsigned char pagenum, unsigned char *pBuf)
{
  
  unsigned char tBuf[CDB_6_MAX_DATA_SIZE + CDB_6_HDR_SIZE ];
 
  struct cdb6hdr *ioctlhdr;
  	
  unsigned char status;

  memset ( &tBuf, 0, CDB_6_MAX_DATA_SIZE + CDB_6_HDR_SIZE );
  
  ioctlhdr = (struct cdb6hdr *) &tBuf;
  
  ioctlhdr->inbufsize = 0;
  ioctlhdr->outbufsize = 0xff;
   
  ioctlhdr->cdb[0] = MODE_SENSE;
  ioctlhdr->cdb[1] = 0x00;
  ioctlhdr->cdb[2] = pagenum;
  ioctlhdr->cdb[3] = 0x00;
  ioctlhdr->cdb[4] = CDB_6_MAX_DATA_SIZE;
  ioctlhdr->cdb[5] = 0x00;
  
  
  status =  ioctl( device, 1 , &tBuf);
  
  memcpy ( pBuf, &tBuf[8], 256); 

  return status;

}




unsigned char modeselect (int device,  unsigned char pagenum, unsigned char *pBuf)
{
  struct cdb6hdr *ioctlhdr;
  unsigned char tBuf[CDB_6_MAX_DATA_SIZE + CDB_6_HDR_SIZE ];
  unsigned char status;

  memset ( &tBuf, 0, CDB_6_MAX_DATA_SIZE + CDB_6_HDR_SIZE );

  ioctlhdr = (struct cdb6hdr *) &tBuf;
  
  ioctlhdr->inbufsize = pBuf[0] + 1;
  ioctlhdr->outbufsize = 0;
  
  
  ioctlhdr->cdb[0] = MODE_SELECT;
  ioctlhdr->cdb[1] = 0x11;
  ioctlhdr->cdb[2] = 0x00;
  ioctlhdr->cdb[3] = 0x00;
  ioctlhdr->cdb[4] = pBuf[0] + 1;
  ioctlhdr->cdb[5] = 0x00;
  
  tBuf[CDB_6_HDR_SIZE + 3]  = 0x08;
  tBuf[CDB_6_HDR_SIZE + 10] = 0x02;
  
    
  memcpy ( &tBuf[ CDB_6_HDR_SIZE + MODE_DATA_HDR_SIZE],
			 pBuf +  MODE_DATA_HDR_SIZE,
			pBuf[0] - MODE_DATA_HDR_SIZE + 1);

  tBuf[26] &= 0x3f;		
 
  status = ioctl( device, 1 , &tBuf);

  return status;

}

	

unsigned char scsi_smart_mode_page1c_handler(int device, unsigned char setting, unsigned char *retval)
{
	char tBuf[CDB_6_MAX_DATA_SIZE];
	
	if (modesense ( device, 0x1c, (unsigned char *) &tBuf) != 0)
	{
		return 1;
	}
	
	switch (setting)
	{
		case DEXCPT_DISABLE:
 			tBuf[14] &= 0xf7;
			tBuf[15] = 0x04;
			break;
		case DEXCPT_ENABLE:
			tBuf[14] |= 0x08;
			break;
		case EWASC_ENABLE:
			tBuf[14] |= 0x10;
			break;
		case EWASC_DISABLE:
			tBuf[14] &= 0xef;
			break;
		case SMART_SUPPORT:
			*retval = tBuf[14] & 0x08;
			return 0;
			break;
		default:
			return 1;
	}
			
	if (modeselect ( device, 0x1c, (unsigned char *) &tBuf ) != 0)
	{
		return 1;
	}
	
	return 0;
}




unsigned char log_sense (int device, unsigned char pagenum, unsigned char *pBuf)
{
  struct cdb10hdr *ioctlhdr;
  unsigned char tBuf[1024 + CDB_12_HDR_SIZE];
  unsigned char status;

  
  memset ( &tBuf, 0, 255);
    
  ioctlhdr = (struct cdb10hdr *) tBuf;
  
  ioctlhdr->inbufsize = 0;
  ioctlhdr->outbufsize = 1024;
   
  ioctlhdr->cdb[0] = LOG_SENSE;
  ioctlhdr->cdb[1] = 0x00;
  ioctlhdr->cdb[2] = 0x40 | pagenum;
  ioctlhdr->cdb[3] = 0x00;
  ioctlhdr->cdb[4] = 0x00;
  ioctlhdr->cdb[5] = 0x00;
  ioctlhdr->cdb[6] = 0x00;
  ioctlhdr->cdb[7] = 0x04;
  ioctlhdr->cdb[8] = 0x00;
  ioctlhdr->cdb[9] = 0x00;

  status =  ioctl( device, 1 , &tBuf);

	
  memcpy ( pBuf, &tBuf[8], 1024); 

  return status;
  
}

static int scsi_probe(int device) {
  int bus_num;

  if(ioctl(device, SCSI_IOCTL_GET_BUS_NUMBER, &bus_num))
    return 0;
  else
    return 1;
}

static char *scsi_model (int device) {
  unsigned char buf[36];

  if (scsi_inquiry(device, buf) != 0)
    return strdup("unknown");
  else {
    return strdup(buf + 8);
  }
}



int scsi_get_temperature(int fd)
{
    unsigned char buf[1024];
    unsigned char smartsupport;
    char gBuf[GBUF_SIZE];

    if(0 != scsi_smart_mode_page1c_handler( fd, SMART_SUPPORT, &smartsupport))
    {
        printf("SCSI S.M.A.R.T. not available!\n");
        return -1;
    }

    if(0 != scsi_smart_mode_page1c_handler(fd, DEXCPT_DISABLE, NULL))
    {
        printf("SCSI Enable S.M.A.R.T. err!\n");
        return -1;
    }

    if (log_sense(fd , TEMPERATURE_PAGE, buf) != 0)
    {
	    printf("SCSI read err!\n");
	    return -1;
   }
    return buf[9];
}

#endif



#if DEF(SATA)
#ifndef ATA_16
/* Values for T10/04-262r7 */
#define		ATA_16			0x85      /* 16-byte pass-thru */
#endif

int sata_pass_thru(int device, unsigned char *cmd, unsigned char *buffer) {
  unsigned char cdb[16];
  unsigned char sense[32];
  int dxfer_direction;
  int ret;
  
  memset(cdb, 0, sizeof(cdb));
  cdb[0] = ATA_16;
  if (cmd[3]) {
    cdb[1] = (4 << 1); /* PIO Data-in */
    cdb[2] = 0x2e;     /* no off.line, cc, read from dev, lock count in sector count field */
    dxfer_direction = SG_DXFER_FROM_DEV;
  } else {
    cdb[1] = (3 << 1); /* Non-data */	  
    cdb[2] = 0x20;     /* cc */
    dxfer_direction = SG_DXFER_NONE;
  }
  cdb[4] = cmd[2];
  if (cmd[0] == WIN_SMART) {
    cdb[6] = cmd[3];
    cdb[8] = cmd[1];
    cdb[10] = 0x4f;
    cdb[12] = 0xc2;
  }
  else
    cdb[6] = cmd[1];
  cdb[14] = cmd[0];

  ret = scsi_SG_IO(device, cdb, sizeof(cdb), buffer, cmd[3] * 512, sense, sizeof(sense), dxfer_direction);
 
  /* Verify SATA magics */
  if (sense[0] != 0x72)
    return 1;		  
  else 
    return ret;
}

void sata_fixstring(unsigned char *s, int bytecount)
{
  unsigned char *p;
  unsigned char *end;

  p = s;
  end = &s[bytecount & ~1]; /* bytecount must be even */

  /* convert from big-endian to host byte order */
  for (p = end ; p != s;) {
    unsigned short *pp = (unsigned short *) (p -= 2);
    *pp = ntohs(*pp);
  }

  /* strip leading blanks */
  while (s != end && *s == ' ')
    ++s;
  /* compress internal blanks and strip trailing blanks */
  while (s != end && *s) {
    if (*s++ != ' ' || (s != end && *s && *s != ' '))
      *p++ = *(s-1);
  }
  /* wipe out trailing garbage */
  while (p != end)
    *p++ = '\0';
}



static int sata_probe(int device) {
  int bus_num;
  unsigned char cmd[4] = { WIN_IDENTIFY, 0, 0, 1 };
  unsigned char identify[512];
  char buf[36]; /* should be 36 for unsafe devices (like USB mass storage stuff)
                        otherwise they can lock up! SPC sections 7.4 and 8.6 */
 
  /* SATA disks are difficult to detect as they answer to both ATA and SCSI 
     commands */

  /* First check that the device is accessible through SCSI */
  if(ioctl(device, SCSI_IOCTL_GET_BUS_NUMBER, &bus_num))
    return 0;
  
  /* Get SCSI name and verify it starts with "ATA " */
  if (scsi_inquiry(device, buf))
    return 0;
  else if (strncmp(buf + 8, "ATA ", 4))
     return 0;

  /* Verify that it supports ATA pass thru */
  if (sata_pass_thru(device, cmd, identify) != 0)
    return 0;
  else
    return 1;
}


int sata_enable_smart(int device) {
  unsigned char cmd[4] = { WIN_SMART, 0, SMART_ENABLE, 0 };

  return sata_pass_thru(device, cmd, NULL);
}

int sata_get_smart_values(int device, unsigned char* buff) {
  unsigned char cmd[4] = { WIN_SMART, 0, SMART_READ_VALUES, 1 };

  return sata_pass_thru(device, cmd, buff);
}

static char *sata_model (int device) {
  unsigned char cmd[4] = { WIN_IDENTIFY, 0, 0, 1 };
  unsigned char identify[512];
  
  if(device == -1 || sata_pass_thru(device, cmd, identify))
    return strdup("unknown");
  else
  {
    sata_fixstring(identify + 54, 40);
    return strdup(identify + 54);
  }
}


static unsigned char* sata_search_temperature(const unsigned char* smart_data, int attribute_id) {
  int i, n;

  n = 3;
  i = 0;
  if(debug)
     printf("============ sata ============\n");
  while((debug || *(smart_data + n) != attribute_id) && i < 30) {
    if(debug && *(smart_data + n))
      printf("field(%d)\t = %d\t(0x%02x)\n", *(smart_data + n), *(smart_data + n + 3), *(smart_data + n + 3));

    n += 12;
    i++;
  }

  if(i >= 30)
    return NULL;
  else
    return (unsigned char*)(smart_data + n);
}


int sata_get_temperature(int fd)
{
    unsigned char    values[512];
    unsigned char  *field;
    int              i;
    u16 *            p;
    /* get SMART values */
  if(sata_enable_smart(fd) != 0) 
  {
    printf("SATA S.M.A.R.T. not available!\n");
    return -1;
  }

  if(sata_get_smart_values(fd, values)) {
    printf("SATA Enable S.M.A.R.T. err!\n");
    return -1;
  }

  p = (u16*)values;
  for(i = 0; i < 256; i++) {
    swapb(*(p+i));
  }

  /* temperature */
  field = sata_search_temperature(values, DEFAULT_ATTRIBUTE_ID);
  if(!field)
    field = sata_search_temperature(values, DEFAULT_ATTRIBUTE_ID2);

  if(field)
    return  *(field+3);
  else
    return -1;
}



#endif


int print_usage()
{
    printf("Usage:\n");
    printf("          satatemp  [-d]  /dev/sda\n");
    printf("          -d print debug  info\n");
    return 0;
}



int main(int argc, char* argv[])
{
    int fd = 0;
    int value = -1;
    char type[16] = "";
    char *mode = NULL;
    char *device = NULL;

    if(3 == argc)
    {
        if(0 != strncmp(argv[1], "-d", strlen("-d")))
        {
            print_usage();
            return 0;
        }  
        device = argv[2];
        debug = 1;
    }
    else if(argc == 2)
    {
        if(0 != strncmp(argv[1], "/dev/", strlen("/dev/")))
        {
            print_usage();
            return 0;
        }    
        device = argv[1];
        debug = 0;
    }
    else
    {
        print_usage();
        return 0;
    }
    

    fd = open(device, O_RDONLY | O_NONBLOCK);
    if (fd < 0)
    {
        printf("open err!\n");
        return (-1);
    }

    if(sata_probe(fd))
    {
        value = sata_get_temperature(fd);
        memset(type, 0 ,sizeof(type));
        strcpy(type,"SATA mode");
        mode = sata_model(fd);
    }
    else if(ata_probe(fd))
    {
        value = ata_get_temperature(fd);
        memset(type, 0 ,sizeof(type));
        strcpy(type,"ATA mode");
        mode = ata_model(fd);
    }
    else if(scsi_probe(fd))
    {
        value = scsi_get_temperature(fd);
        memset(type, 0 ,sizeof(type));
        strcpy(type,"SCSI mode");
        mode = scsi_model(fd);
    }



    if(value > 0)
        printf("%s: %s, temperature: %d C\n", type, mode, value);
    else if(mode)
        printf("%s: %s:  no sensor\n", device, mode);
    else
        printf("get temperature failed!\n");
    close(fd);
    free(mode);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值