blob: 21af01dcb6d31a84c6c2cc4f9ccdb751addb9cc6 [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
Lorenz Brunb4f50242023-11-14 21:51:07 +010050
51 var ioctlPins runtime.Pinner
52 ioctlPins.Pin(&c.Data[0])
53 ioctlPins.Pin(&cdb[0])
54 ioctlPins.Pin(&senseBuf[0])
55 defer ioctlPins.Unpin()
56
Lorenz Brunbffdda82023-01-10 05:00:36 +000057 cmdRaw := sgIOHdr{
58 Interface_id: 'S',
59 Dxfer_direction: dxferDir,
60 Dxfer_len: uint32(len(c.Data)),
61 Dxferp: uintptr(unsafe.Pointer(&c.Data[0])),
62 Cmd_len: uint8(len(cdb)),
63 Cmdp: uintptr(unsafe.Pointer(&cdb[0])),
64 Mx_sb_len: uint8(len(senseBuf)),
65 Sbp: uintptr(unsafe.Pointer(&senseBuf[0])),
66 Timeout: timeout,
67 }
68 var errno unix.Errno
69 err = conn.Control(func(fd uintptr) {
70 _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, SG_IO, uintptr(unsafe.Pointer(&cmdRaw)))
71 })
72 runtime.KeepAlive(cmdRaw)
73 runtime.KeepAlive(c.Data)
74 runtime.KeepAlive(senseBuf)
75 runtime.KeepAlive(cdb)
76 if err != nil {
77 return fmt.Errorf("unable to get fd: %w", err)
78 }
79 if errno != 0 {
80 return errno
81 }
82 if cmdRaw.Masked_status != 0 {
83 if senseBuf[0] == 0x70 || senseBuf[0] == 0x71 {
84 err := &FixedError{
85 Deferred: senseBuf[0] == 0x71,
86 SenseKey: SenseKey(senseBuf[2] & 0b1111),
87 Information: binary.BigEndian.Uint32(senseBuf[3:7]),
88 }
89 length := int(senseBuf[7])
90 if length >= 4 {
91 err.CommandSpecificInformation = binary.BigEndian.Uint32(senseBuf[8:12])
92 if length >= 6 {
93 err.AdditionalSenseCode = AdditionalSenseCode(uint16(senseBuf[12])<<8 | uint16(senseBuf[13]))
94 }
95 }
96 return err
97 }
98 return &UnknownError{
99 RawSenseData: senseBuf[:],
100 }
101 }
102 return nil
103}