161 lines
4.4 KiB
Go
161 lines
4.4 KiB
Go
package sgio
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
SG_GET_VERSION_NUM = 0x2282
|
|
SG_IO = 0x2285
|
|
SG_INFO_OK_MASK = 0x1
|
|
SG_INFO_OK = 0x0
|
|
SG_DXFER_TO_DEV = -2
|
|
SG_DXFER_FROM_DEV = -3
|
|
SG_DXFER_TO_FROM_DEV = -4
|
|
INQ_CMD_CODE = 0x12
|
|
INQ_REPLY_LEN = 96
|
|
SENSE_BUF_LEN = 32
|
|
TIMEOUT_20_SECS = 20000
|
|
)
|
|
|
|
// pahole for sg_io_hdr_t on amd64
|
|
/*
|
|
* struct sg_io_hdr {
|
|
* int interface_id; // 0 4
|
|
* int dxfer_direction; // 4 4
|
|
* unsigned char cmd_len; // 8 1
|
|
* unsigned char mx_sb_len; // 9 1
|
|
* short unsigned int iovec_count; // 10 2
|
|
* unsigned int dxfer_len; // 12 4
|
|
* void * dxferp; // 16 8
|
|
* unsigned char * cmdp; // 24 8
|
|
* unsigned char * sbp; // 32 8
|
|
* unsigned int timeout; // 40 4
|
|
* unsigned int flags; // 44 4
|
|
* int pack_id; // 48 4
|
|
*
|
|
* // XXX 4 bytes hole, try to pack
|
|
*
|
|
* void * usr_ptr; // 56 8
|
|
* // --- cacheline 1 boundary (64 bytes) ---
|
|
* unsigned char status; // 64 1
|
|
* unsigned char masked_status; // 65 1
|
|
* unsigned char msg_status; // 66 1
|
|
* unsigned char sb_len_wr; // 67 1
|
|
* short unsigned int host_status; // 68 2
|
|
* short unsigned int driver_status; // 70 2
|
|
* int resid; // 72 4
|
|
* unsigned int duration; // 76 4
|
|
* unsigned int info; // 80 4
|
|
*
|
|
* // size: 88, cachelines: 2, members: 22
|
|
* // sum members: 80, holes: 1, sum holes: 4
|
|
* // padding: 4
|
|
* // last cacheline: 24 bytes
|
|
* };
|
|
*/
|
|
|
|
// SgIoHdr is our version of sg_io_hdr_t that gets passed to the SG_IO ioctl
|
|
type SgIoHdr struct {
|
|
InterfaceID int32
|
|
DxferDirection int32
|
|
CmdLen uint8
|
|
MxSbLen uint8
|
|
IovecCount uint16
|
|
DxferLen uint32
|
|
Dxferp *byte
|
|
Cmdp *uint8
|
|
Sbp *byte
|
|
Timeout uint32
|
|
Flags uint32
|
|
PackID int32
|
|
pad0 [4]byte
|
|
UsrPtr *byte
|
|
Status uint8
|
|
MaskedStatus uint8
|
|
MsgStatus uint8
|
|
SbLenWr uint8
|
|
HostStatus uint16
|
|
DriverStatus uint16
|
|
Resid int32
|
|
Duration uint32
|
|
Info uint32
|
|
}
|
|
|
|
func TestUnitReady(f *os.File) error {
|
|
senseBuf := make([]byte, SENSE_BUF_LEN)
|
|
inqCmdBlk := []uint8{0, 0, 0, 0, 0, 0}
|
|
ioHdr := &SgIoHdr{
|
|
InterfaceID: int32('S'),
|
|
CmdLen: uint8(len(inqCmdBlk)),
|
|
MxSbLen: SENSE_BUF_LEN,
|
|
DxferDirection: SG_DXFER_FROM_DEV,
|
|
Cmdp: &inqCmdBlk[0],
|
|
Sbp: &senseBuf[0],
|
|
Timeout: TIMEOUT_20_SECS,
|
|
}
|
|
|
|
err := SgioSyscall(f, ioHdr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = CheckSense(ioHdr, &senseBuf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func CheckSense(i *SgIoHdr, s *[]byte) error {
|
|
var b bytes.Buffer
|
|
if (i.Info & SG_INFO_OK_MASK) != SG_INFO_OK {
|
|
_, err := b.WriteString(
|
|
fmt.Sprintf("SCSI response not ok\n"+
|
|
"SCSI status: %v host status: %v driver status: %v",
|
|
i.Status, i.HostStatus, i.DriverStatus))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if i.SbLenWr > 0 {
|
|
_, err := b.WriteString(
|
|
fmt.Sprintf("\nSENSE:\n%v\n%v",
|
|
dumpHex(*s), GetErrString((*s)[12], (*s)[13])))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return fmt.Errorf(b.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func SgioSyscall(f *os.File, i *SgIoHdr) error {
|
|
return ioctl(f.Fd(), SG_IO, uintptr(unsafe.Pointer(i)))
|
|
}
|
|
|
|
func ioctl(fd, cmd, ptr uintptr) error {
|
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
|
|
if err != 0 {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func OpenScsiDevice(fname string) (*os.File, error) {
|
|
f, err := os.OpenFile(fname, os.O_RDWR, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var version uint32
|
|
if (ioctl(f.Fd(), SG_GET_VERSION_NUM, uintptr(unsafe.Pointer(&version))) != nil) || (version < 30000) {
|
|
return nil, fmt.Errorf("device does not appear to be an sg device")
|
|
}
|
|
return f, nil
|
|
}
|