github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/smart/scsicommands.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 // SCSI command definitions. 15 16 package smart 17 18 import ( 19 "encoding/binary" 20 "fmt" 21 "unsafe" 22 ) 23 24 // SCSI commands being used 25 const ( 26 SCSIModeSense = 0x1a // mode sense command 27 SCSIReadCapacity10 = 0x25 // read capacity (10) command 28 SCSIReadCapacity16 = 0x9e // read capacity (16) command 29 SCSIReadCapacityServiceAction = 0x10 // read capacity (16) service action 30 SCSIATAPassThru = 0x85 // ata passthru command 31 ) 32 33 // SCSI Command Descriptor Block types are the various type of scsi cdbs which are used 34 // to specify the various parameters or data required to send a particular scsi command 35 36 // CDB6 is an array of 6 byte 37 type CDB6 [6]byte 38 39 // CDB10 is an array of 10 byte 40 type CDB10 [10]byte 41 42 // CDB16 is an array of 16 byte 43 type CDB16 [16]byte 44 45 // getLBSize returns the logical block size of a SCSI device 46 func (d *SCSIDev) getLBSize() (uint32, error) { 47 response := make([]byte, 8) 48 // Use cdb10 to send a scsi read capacity command to get the 49 // logical block size 50 cdb := CDB10{SCSIReadCapacity10} 51 52 // If sending scsi read capacity scsi command fails then return 53 // logical size value 0 with error 54 if err := d.sendSCSICDB(cdb[:], &response); err != nil { 55 return 0, err 56 } 57 LBsize := binary.BigEndian.Uint32(response[4:]) // logical block size 58 59 return LBsize, nil 60 } 61 62 // runSCSIGen executes SCSI generic commands i.e sgIO commands 63 func (d *SCSIDev) runSCSIGen(header *sgIOHeader) error { 64 // send an scsi generic ioctl command to the given file descriptor, 65 // returns err if it fails to send it. 66 67 if err := Ioctl(uintptr(d.fd), SGIO, uintptr(unsafe.Pointer(header))); err != nil { 68 return err 69 } 70 // See http://www.t10.org/lists/2status.htm for SCSI status codes 71 // TODO : Decode the status codes and return descriptive errors 72 if header.info&SGInfoOkMask != SGInfoOk { 73 err := sgIOErr{ 74 scsiStatus: header.status, // status code returned by an SCSI device 75 hostStatus: header.hostStatus, // status code returned by a host 76 driverStatus: header.driverStatus, // status code returned by a scsi driver 77 } 78 return err 79 } 80 81 return nil 82 } 83 84 // Error returns error string of error occurred while sending SGIO(scsi generic ioctl) 85 // to a scsi device using the sgIOErr format 86 func (s sgIOErr) Error() string { 87 return fmt.Sprintf("SCSI status: %#02x, host status: %#02x, driver status: %#02x", 88 s.scsiStatus, s.hostStatus, s.driverStatus) 89 } 90 91 // sendSCSICDB sends a SCSI Command Descriptor Block to the device and writes the response into the 92 // supplied []byte pointer. 93 func (d *SCSIDev) sendSCSICDB(cdb []byte, respBuf *[]byte) error { 94 senseBuf := make([]byte, 32) 95 96 // Populate all the required fields of "sg_io_hdr_t" struct while sending 97 // scsi command 98 header := sgIOHeader{ 99 interfaceID: 'S', 100 dxferDirection: SGDxferFromDev, 101 cmdLen: uint8(len(cdb)), 102 mxSBLen: uint8(len(senseBuf)), 103 dxferLen: uint32(len(*respBuf)), 104 dxferp: uintptr(unsafe.Pointer(&(*respBuf)[0])), 105 cmdp: uintptr(unsafe.Pointer(&cdb[0])), 106 sbp: uintptr(unsafe.Pointer(&senseBuf[0])), // nosec 107 timeout: DefaultTimeout, 108 } 109 110 return d.runSCSIGen(&header) 111 } 112 113 // modeSense function is used to send a SCSI MODE SENSE(6) command to a device. 114 // TODO : Implement SCSI MODE SENSE(10) command also 115 func (d *SCSIDev) modeSense(pageNo uint8, subPageNo uint8, pageCtrl uint8, disableBlockDesc bool) ([]byte, error) { 116 respBuf := make([]byte, 64) 117 var cdb1 uint8 118 119 // if disable block descriptor value set to true then set cdb1 value 120 // else it is 0 121 if disableBlockDesc { 122 cdb1 = (1 << 3) 123 } 124 125 // Populate all the required fields of cdb6 to send a scsi mode sense command 126 cdb := CDB6{SCSIModeSense} 127 cdb[0] = SCSIModeSense 128 cdb[1] = cdb1 129 cdb[2] = (pageCtrl << 6) | pageNo 130 cdb[3] = subPageNo 131 cdb[4] = uint8(len(respBuf)) 132 cdb[5] = 0 133 134 // return error if sending mode sense command using cdb6 fails 135 // TODO: Implement mode sense 10 and mode sense 16 also in order to 136 // use them if sending mode sense 6 fails for a particular device for 137 // a particular device page 138 if err := d.sendSCSICDB(cdb[:], &respBuf); err != nil { 139 return respBuf, err 140 } 141 142 return respBuf, nil 143 }