blob: be937d01faa7c86b7f61c4b782561df338f67512 [file] [log] [blame]
Lorenz Brunbffdda82023-01-10 05:00:36 +00001//go:build linux
2
3package scsi
4
5import (
6 "encoding/binary"
7 "errors"
8 "fmt"
9 "math"
10 "runtime"
11 "unsafe"
12
13 "golang.org/x/sys/unix"
14)
15
16// RawCommand issues a raw command against the device.
17func (d *Device) RawCommand(c *CommandDataBuffer) error {
18 cdb, err := c.Bytes()
19 if err != nil {
20 return fmt.Errorf("error encoding CDB: %w", err)
21 }
22 conn, err := d.fd.SyscallConn()
23 if err != nil {
24 return fmt.Errorf("unable to get RawConn: %w", err)
25 }
26 var dxferDir int32
27 switch c.DataTransferDirection {
28 case DataTransferNone:
29 dxferDir = SG_DXFER_NONE
30 case DataTransferFromDevice:
31 dxferDir = SG_DXFER_FROM_DEV
32 case DataTransferToDevice:
33 dxferDir = SG_DXFER_TO_DEV
34 case DataTransferBidirectional:
35 dxferDir = SG_DXFER_TO_FROM_DEV
36 default:
37 return errors.New("invalid DataTransferDirection")
38 }
39 var timeout uint32
40 if c.Timeout.Milliseconds() > math.MaxUint32 {
41 timeout = math.MaxUint32
42 }
43 if len(c.Data) > math.MaxUint32 {
44 return errors.New("payload larger than 2^32 bytes, unable to issue")
45 }
46 if len(cdb) > math.MaxUint8 {
47 return errors.New("CDB larger than 2^8 bytes, unable to issue")
48 }
49 var senseBuf [32]byte
50 cmdRaw := sgIOHdr{
51 Interface_id: 'S',
52 Dxfer_direction: dxferDir,
53 Dxfer_len: uint32(len(c.Data)),
54 Dxferp: uintptr(unsafe.Pointer(&c.Data[0])),
55 Cmd_len: uint8(len(cdb)),
56 Cmdp: uintptr(unsafe.Pointer(&cdb[0])),
57 Mx_sb_len: uint8(len(senseBuf)),
58 Sbp: uintptr(unsafe.Pointer(&senseBuf[0])),
59 Timeout: timeout,
60 }
61 var errno unix.Errno
62 err = conn.Control(func(fd uintptr) {
63 _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, SG_IO, uintptr(unsafe.Pointer(&cmdRaw)))
64 })
65 runtime.KeepAlive(cmdRaw)
66 runtime.KeepAlive(c.Data)
67 runtime.KeepAlive(senseBuf)
68 runtime.KeepAlive(cdb)
69 if err != nil {
70 return fmt.Errorf("unable to get fd: %w", err)
71 }
72 if errno != 0 {
73 return errno
74 }
75 if cmdRaw.Masked_status != 0 {
76 if senseBuf[0] == 0x70 || senseBuf[0] == 0x71 {
77 err := &FixedError{
78 Deferred: senseBuf[0] == 0x71,
79 SenseKey: SenseKey(senseBuf[2] & 0b1111),
80 Information: binary.BigEndian.Uint32(senseBuf[3:7]),
81 }
82 length := int(senseBuf[7])
83 if length >= 4 {
84 err.CommandSpecificInformation = binary.BigEndian.Uint32(senseBuf[8:12])
85 if length >= 6 {
86 err.AdditionalSenseCode = AdditionalSenseCode(uint16(senseBuf[12])<<8 | uint16(senseBuf[13]))
87 }
88 }
89 return err
90 }
91 return &UnknownError{
92 RawSenseData: senseBuf[:],
93 }
94 }
95 return nil
96}