| //go:build linux | 
 |  | 
 | package scsi | 
 |  | 
 | import ( | 
 | 	"encoding/binary" | 
 | 	"errors" | 
 | 	"fmt" | 
 | 	"math" | 
 | 	"runtime" | 
 | 	"unsafe" | 
 |  | 
 | 	"golang.org/x/sys/unix" | 
 | ) | 
 |  | 
 | // RawCommand issues a raw command against the device. | 
 | func (d *Device) RawCommand(c *CommandDataBuffer) error { | 
 | 	cdb, err := c.Bytes() | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("error encoding CDB: %w", err) | 
 | 	} | 
 | 	conn, err := d.fd.SyscallConn() | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("unable to get RawConn: %w", err) | 
 | 	} | 
 | 	var dxferDir int32 | 
 | 	switch c.DataTransferDirection { | 
 | 	case DataTransferNone: | 
 | 		dxferDir = SG_DXFER_NONE | 
 | 	case DataTransferFromDevice: | 
 | 		dxferDir = SG_DXFER_FROM_DEV | 
 | 	case DataTransferToDevice: | 
 | 		dxferDir = SG_DXFER_TO_DEV | 
 | 	case DataTransferBidirectional: | 
 | 		dxferDir = SG_DXFER_TO_FROM_DEV | 
 | 	default: | 
 | 		return errors.New("invalid DataTransferDirection") | 
 | 	} | 
 | 	var timeout uint32 | 
 | 	if c.Timeout.Milliseconds() > math.MaxUint32 { | 
 | 		timeout = math.MaxUint32 | 
 | 	} | 
 | 	if len(c.Data) > math.MaxUint32 { | 
 | 		return errors.New("payload larger than 2^32 bytes, unable to issue") | 
 | 	} | 
 | 	if len(cdb) > math.MaxUint8 { | 
 | 		return errors.New("CDB larger than 2^8 bytes, unable to issue") | 
 | 	} | 
 | 	var senseBuf [32]byte | 
 | 	cmdRaw := sgIOHdr{ | 
 | 		Interface_id:    'S', | 
 | 		Dxfer_direction: dxferDir, | 
 | 		Dxfer_len:       uint32(len(c.Data)), | 
 | 		Dxferp:          uintptr(unsafe.Pointer(&c.Data[0])), | 
 | 		Cmd_len:         uint8(len(cdb)), | 
 | 		Cmdp:            uintptr(unsafe.Pointer(&cdb[0])), | 
 | 		Mx_sb_len:       uint8(len(senseBuf)), | 
 | 		Sbp:             uintptr(unsafe.Pointer(&senseBuf[0])), | 
 | 		Timeout:         timeout, | 
 | 	} | 
 | 	var errno unix.Errno | 
 | 	err = conn.Control(func(fd uintptr) { | 
 | 		_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, SG_IO, uintptr(unsafe.Pointer(&cmdRaw))) | 
 | 	}) | 
 | 	runtime.KeepAlive(cmdRaw) | 
 | 	runtime.KeepAlive(c.Data) | 
 | 	runtime.KeepAlive(senseBuf) | 
 | 	runtime.KeepAlive(cdb) | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("unable to get fd: %w", err) | 
 | 	} | 
 | 	if errno != 0 { | 
 | 		return errno | 
 | 	} | 
 | 	if cmdRaw.Masked_status != 0 { | 
 | 		if senseBuf[0] == 0x70 || senseBuf[0] == 0x71 { | 
 | 			err := &FixedError{ | 
 | 				Deferred:    senseBuf[0] == 0x71, | 
 | 				SenseKey:    SenseKey(senseBuf[2] & 0b1111), | 
 | 				Information: binary.BigEndian.Uint32(senseBuf[3:7]), | 
 | 			} | 
 | 			length := int(senseBuf[7]) | 
 | 			if length >= 4 { | 
 | 				err.CommandSpecificInformation = binary.BigEndian.Uint32(senseBuf[8:12]) | 
 | 				if length >= 6 { | 
 | 					err.AdditionalSenseCode = AdditionalSenseCode(uint16(senseBuf[12])<<8 | uint16(senseBuf[13])) | 
 | 				} | 
 | 			} | 
 | 			return err | 
 | 		} | 
 | 		return &UnknownError{ | 
 | 			RawSenseData: senseBuf[:], | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } |