github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/smart/ataidentify.go (about)

     1  /*
     2  Copyright 2018 The OpenEBS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6      http://www.apache.org/licenses/LICENSE-2.0
     7  Unless required by applicable law or agreed to in writing, software
     8  distributed under the License is distributed on an "AS IS" BASIS,
     9  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  See the License for the specific language governing permissions and
    11  limitations under the License.
    12  */
    13  
    14  package smart
    15  
    16  import (
    17  	"fmt"
    18  )
    19  
    20  // swapByteOrder swaps the order of every second byte in a byte slice and
    21  // modifies the slice in place.
    22  func (d *ATACSPage) swapByteOrder(b []byte) []byte {
    23  	tmp := make([]byte, len(b))
    24  
    25  	for i := 0; i < len(b); i += 2 {
    26  		tmp[i], tmp[i+1] = b[i+1], b[i]
    27  	}
    28  	return tmp
    29  }
    30  
    31  // getSerialNumber returns the serial number of a disk device
    32  // using an ATA IDENTIFY command.
    33  func (d *ATACSPage) getSerialNumber() []byte {
    34  	// Needed a byte swap since each pair of bytes in an ATA string is swapped
    35  	// word 0 | offset 0 -> second character, offset 1 -> First character and so on.
    36  	return d.swapByteOrder(d.SerialNumber[:])
    37  }
    38  
    39  // getWWN returns the worldwide unique name for a disk
    40  // The World Wide Name (WWN) uses the NAA IEEE Registered designator format
    41  // defined in SPC-5 with the NAA field set to 5h.
    42  func (d *ATACSPage) getWWN() string {
    43  	// NAA field indicates the format of the world wide name
    44  	NAA := d.WWN[0] >> 12
    45  	// IEEE OUI field shall contain the 24-bit canonical form company
    46  	// identifier (i.e., OUI) that the IEEE has assigned to the device manufacturer
    47  	IEEEOUI := (uint32(d.WWN[0]&0x0fff) << 12) | (uint32(d.WWN[1]) >> 4)
    48  	// UNIQUE ID field shall contain a value assigned by the device manufacturer
    49  	// that is unique for the device within the OUI domain
    50  	UniqueID := ((uint64(d.WWN[1]) & 0xf) << 32) | (uint64(d.WWN[2]) << 16) | uint64(d.WWN[3])
    51  
    52  	return fmt.Sprintf("%x %06x %09x", NAA, IEEEOUI, UniqueID)
    53  }
    54  
    55  // getSectorSize returns logical and physical sector sizes of a disk
    56  func (d *ATACSPage) getSectorSize() (uint32, uint32) {
    57  	// By default, we are assuming the physical and logical sector size to be 512
    58  	// based on further check conditions, it would be altered.
    59  	var LogSec, PhySec uint32 = 512, 512
    60  
    61  	if (d.SectorSize & 0xc000) != 0x4000 {
    62  		return LogSec, PhySec
    63  	}
    64  	// TODO : Add support for Long Logical/Physical Sectors (LLS/LPS)
    65  	if (d.SectorSize & 0x2000) != 0x0000 {
    66  		// Physical sector size is multiple of logical sector size
    67  		PhySec <<= (d.SectorSize & 0x0f)
    68  	}
    69  	return LogSec, PhySec
    70  }
    71  
    72  // getATAMajorVersion returns the ATA major version of a disk using an ATA IDENTIFY command.
    73  func (d *ATACSPage) getATAMajorVersion() (s string) {
    74  	if (d.MajorVer == 0) || (d.MajorVer == 0xffff) {
    75  		return "This device does not report ATA major version"
    76  	}
    77  	// ATA Major version word is a bitmask, hence we will get the most significant bit
    78  	// of this word and then do a map lookup
    79  	majorVer := MSignificantBit(uint(d.MajorVer))
    80  	if s, ok := ataMajorVersions[majorVer]; ok {
    81  		return s
    82  	}
    83  	return "unknown"
    84  }
    85  
    86  // getATAMinorVersion returns the ATA minor version using an ATA IDENTIFY command.
    87  func (d *ATACSPage) getATAMinorVersion() string {
    88  	if (d.MinorVer == 0) || (d.MinorVer == 0xffff) {
    89  		return "This device does not report ATA minor version"
    90  	}
    91  	// Since ATA minor version word is not a bitmask, we simply do a map lookup here
    92  	if s, ok := ataMinorVersions[d.MinorVer]; ok {
    93  		return s
    94  	}
    95  	return "unknown"
    96  }
    97  
    98  // ataTransport returns the type of ata Transport being
    99  // used such as serial ATA, parallel ATA, etc.
   100  func (d *ATACSPage) ataTransport() (s string) {
   101  	if (d.AtaTransportMajor == 0) || (d.AtaTransportMajor == 0xffff) {
   102  		s = "This device does not report Transport"
   103  		return
   104  	}
   105  	switch d.AtaTransportMajor >> 12 {
   106  	case 0x0:
   107  		s = "Parallel ATA" // parallel ata transport
   108  	case 0x1:
   109  		s = d.IdentifySerialATAType() // identify the type of serial ata as it is a serial ata transport
   110  	case 0xe:
   111  		s = fmt.Sprintf("PCIe (%#03x)", d.AtaTransportMajor&0x0fff)
   112  	default:
   113  		s = fmt.Sprintf("Unknown (%#04x)", d.AtaTransportMajor)
   114  	}
   115  	return
   116  }
   117  
   118  // IdentifySerialATAType identifies the type of SATA transport being used by a disk
   119  func (d *ATACSPage) IdentifySerialATAType() (s string) {
   120  	s = "Serial ATA"
   121  	// Get the most significant bit of the ata transport major word as it is a bitmask
   122  	// and then get the serial ata transport type based on the value
   123  	transportMajor := MSignificantBit(uint(d.AtaTransportMajor & 0x0fff))
   124  	// Lookup in the map for the type of serial ata transport based on key
   125  	if serialATAType, ok := serialATAType[transportMajor]; ok {
   126  		s += serialATAType
   127  		return s
   128  	}
   129  	s += fmt.Sprintf(" SATA (%#03x)", d.AtaTransportMajor&0x0fff)
   130  	return s
   131  }