forked from bougou/go-ipmi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcmd_read_fru_data.go
133 lines (111 loc) · 3.27 KB
/
cmd_read_fru_data.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package ipmi
import (
"fmt"
)
// 34.2 Read FRU Data Command
type ReadFRUDataRequest struct {
FRUDeviceID uint8
ReadOffset uint16
ReadCount uint8
}
type ReadFRUDataResponse struct {
CountReturned uint8
Data []byte
}
func (req *ReadFRUDataRequest) Command() Command {
return CommandReadFRUData
}
func (req *ReadFRUDataRequest) Pack() []byte {
out := make([]byte, 4)
packUint8(req.FRUDeviceID, out, 0)
packUint16L(req.ReadOffset, out, 1)
packUint8(req.ReadCount, out, 3)
return out
}
func (res *ReadFRUDataResponse) Unpack(msg []byte) error {
if len(msg) < 1 {
return ErrUnpackedDataTooShort
}
res.CountReturned, _, _ = unpackUint8(msg, 0)
res.Data, _, _ = unpackBytes(msg, 1, len(msg)-1)
return nil
}
func (r *ReadFRUDataResponse) CompletionCodes() map[uint8]string {
return map[uint8]string{
0x81: "FRU device busy",
}
}
func (res *ReadFRUDataResponse) Format() string {
return fmt.Sprintf(`Count returned : %d
Data : %02x`,
res.CountReturned,
res.Data,
)
}
// The command returns the specified data from the FRU Inventory Info area.
func (c *Client) ReadFRUData(fruDeviceID uint8, readOffset uint16, readCount uint8) (response *ReadFRUDataResponse, err error) {
request := &ReadFRUDataRequest{
FRUDeviceID: fruDeviceID,
ReadOffset: readOffset,
ReadCount: readCount,
}
response = &ReadFRUDataResponse{}
err = c.Exchange(request, response)
return
}
// readFRUDataByLength reads FRU Data in loop until reaches the specified data length
func (c *Client) readFRUDataByLength(deviceID uint8, offset uint16, length uint16) ([]byte, error) {
var data []byte
c.Debugf("Read FRU Data by Length, offset: (%d), length: (%d)\n", offset, length)
for {
if length <= 0 {
break
}
res, err := c.tryReadFRUData(deviceID, offset, length)
if err != nil {
return nil, fmt.Errorf("tryReadFRUData failed, err: %s", err)
}
c.Debug("", res.Format())
data = append(data, res.Data...)
length -= uint16(res.CountReturned)
c.Debugf("left length: %d\n", length)
// update offset
offset += uint16(res.CountReturned)
}
return data, nil
}
// tryReadFRUData will try to read FRU data with a read count which starts with
// the minimal number of the specified length and the hard-coded 32, if the
// ReadFRUData failed, it try another request with a decreased read count.
func (c *Client) tryReadFRUData(deviceID uint8, readOffset uint16, length uint16) (response *ReadFRUDataResponse, err error) {
var readCount uint8 = 32
if length <= uint16(readCount) {
readCount = uint8(length)
}
for {
if readCount <= 0 {
return nil, fmt.Errorf("nothing to read")
}
c.Debugf("Try Read FRU Data, offset: (%d), count: (%d)\n", readOffset, readCount)
res, err := c.ReadFRUData(deviceID, readOffset, readCount)
if err == nil {
return res, nil
}
resErr, ok := err.(*ResponseError)
if !ok {
return nil, fmt.Errorf("ReadFRUData failed, err: %s", err)
}
cc := resErr.CompletionCode()
if readFRUDataLength2Big(cc) {
readCount -= 1
continue
} else {
return nil, fmt.Errorf("ReadFRUData failed, err: %s", err)
}
}
}
func readFRUDataLength2Big(cc CompletionCode) bool {
return cc == CompletionCodeRequestDataLengthInvalid ||
cc == CompletionCodeRequestDataLengthLimitExceeded ||
cc == CompletionCodeCannotReturnRequestedDataBytes
}