-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
391 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package ipmigo | ||
|
||
import ( | ||
"encoding/binary" | ||
) | ||
|
||
// Get SEL Info (Section 31.2) | ||
type GetSELInfoCommand struct { | ||
// Response Data | ||
SELVersion uint8 | ||
Entries uint16 | ||
FreeSpace uint16 | ||
LastAddTime uint32 | ||
LastDelTime uint32 | ||
SupportAllocInfo bool | ||
SupportReserve bool | ||
SupportPartialAdd bool | ||
SupportDelete bool | ||
Overflow bool | ||
} | ||
|
||
func (c *GetSELInfoCommand) Name() string { return "Get SEL Info" } | ||
func (c *GetSELInfoCommand) Code() uint8 { return 0x40 } | ||
|
||
func (c *GetSELInfoCommand) NetFnRsLUN() NetFnRsLUN { | ||
return NewNetFnRsLUN(NetFnStorageReq, 0) | ||
} | ||
|
||
func (c *GetSELInfoCommand) String() string { return cmdToJSON(c) } | ||
func (c *GetSELInfoCommand) Marshal() ([]byte, error) { return []byte{}, nil } | ||
|
||
func (c *GetSELInfoCommand) Unmarshal(buf []byte) ([]byte, error) { | ||
if err := cmdValidateLength(c, buf, 14); err != nil { | ||
return nil, err | ||
} | ||
|
||
c.SELVersion = buf[0] | ||
c.Entries = binary.LittleEndian.Uint16(buf[1:3]) | ||
c.FreeSpace = binary.LittleEndian.Uint16(buf[3:5]) | ||
c.LastAddTime = binary.LittleEndian.Uint32(buf[5:9]) | ||
c.LastDelTime = binary.LittleEndian.Uint32(buf[9:13]) | ||
c.SupportAllocInfo = buf[13]&0x01 != 0 | ||
c.SupportReserve = buf[13]&0x02 != 0 | ||
c.SupportPartialAdd = buf[13]&0x04 != 0 | ||
c.SupportDelete = buf[13]&0x08 != 0 | ||
c.Overflow = buf[13]&0x80 != 0 | ||
|
||
return buf[14:], nil | ||
} | ||
|
||
// Reserve SEL Command (Section 31.4) | ||
type ReserveSELCommand struct { | ||
// Response Data | ||
ReservationID uint16 | ||
} | ||
|
||
func (c *ReserveSELCommand) Name() string { return "Reserve SEL" } | ||
func (c *ReserveSELCommand) Code() uint8 { return 0x42 } | ||
|
||
func (c *ReserveSELCommand) NetFnRsLUN() NetFnRsLUN { | ||
return NewNetFnRsLUN(NetFnStorageReq, 0) | ||
} | ||
|
||
func (c *ReserveSELCommand) String() string { return cmdToJSON(c) } | ||
func (c *ReserveSELCommand) Marshal() ([]byte, error) { return []byte{}, nil } | ||
|
||
func (c *ReserveSELCommand) Unmarshal(buf []byte) ([]byte, error) { | ||
if err := cmdValidateLength(c, buf, 2); err != nil { | ||
return nil, err | ||
} | ||
c.ReservationID = binary.LittleEndian.Uint16(buf) | ||
return buf[2:], nil | ||
} | ||
|
||
// Get SEL Entry Command (Section 31.5) | ||
type GetSELEntryCommand struct { | ||
// Request Data | ||
ReservationID uint16 | ||
RecordID uint16 | ||
RecordOffset uint8 | ||
ReadBytes uint8 | ||
|
||
// Response Data | ||
NextRecordID uint16 | ||
RecordData []byte | ||
} | ||
|
||
func (c *GetSELEntryCommand) Name() string { return "Get SDR" } | ||
func (c *GetSELEntryCommand) Code() uint8 { return 0x43 } | ||
func (c *GetSELEntryCommand) NetFnRsLUN() NetFnRsLUN { return NewNetFnRsLUN(NetFnStorageReq, 0) } | ||
func (c *GetSELEntryCommand) String() string { return cmdToJSON(c) } | ||
|
||
func (c *GetSELEntryCommand) Marshal() ([]byte, error) { | ||
return []byte{byte(c.ReservationID), byte(c.ReservationID >> 8), byte(c.RecordID), byte(c.RecordID >> 8), | ||
byte(c.RecordOffset), byte(c.ReadBytes)}, nil | ||
} | ||
|
||
func (c *GetSELEntryCommand) Unmarshal(buf []byte) ([]byte, error) { | ||
if err := cmdValidateLength(c, buf, 2); err != nil { | ||
return nil, err | ||
} | ||
|
||
c.NextRecordID = binary.LittleEndian.Uint16(buf) | ||
buf = buf[2:] | ||
if l := len(buf); l <= int(c.ReadBytes) { | ||
c.RecordData = make([]byte, l) | ||
copy(c.RecordData, buf) | ||
return nil, nil | ||
} else { | ||
c.RecordData = make([]byte, c.ReadBytes) | ||
copy(c.RecordData, buf) | ||
return buf[c.ReadBytes:], nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package ipmigo | ||
|
||
// Event/Reading Type (Table 42-2) | ||
type EventType uint8 | ||
|
||
func (e EventType) IsUnspecified() bool { return e == 0x00 } | ||
func (e EventType) IsThreshold() bool { return e == 0x01 } | ||
func (e EventType) IsGeneric() bool { return e >= 0x02 && e <= 0x0c } | ||
func (e EventType) IsSensorSpecific() bool { return e == 0x6f } | ||
func (e EventType) IsOEM() bool { return e >= 0x70 && e <= 0x7f } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
package ipmigo | ||
|
||
import ( | ||
"encoding/binary" | ||
"encoding/hex" | ||
"fmt" | ||
) | ||
|
||
const ( | ||
selFirstID uint16 = 0x0000 | ||
selLastID uint16 = 0xffff | ||
|
||
selRecordSize = 16 | ||
) | ||
|
||
// Sensor Event Log Record Type | ||
type SELType uint8 | ||
|
||
func (t SELType) IsTimestampedOEM() bool { return t >= 0xc0 && t <= 0xdf } | ||
func (t SELType) IsNonTimestampedOEM() bool { return t >= 0xe0 && t <= 0xff } | ||
|
||
// Sensor Event Log Record | ||
type SELRecord interface { | ||
// Returns record type | ||
Type() SELType | ||
// Returns record id | ||
ID() uint16 | ||
// Returns bytes of the record key and body | ||
Data() []byte | ||
} | ||
|
||
// SEL Event Record (Section 32.1) | ||
type SELEventRecord struct { | ||
data []byte | ||
|
||
RecordID uint16 | ||
RecordType SELType | ||
Timestamp Timestamp | ||
GeneratorID uint16 | ||
EvMRev uint8 | ||
SensorType SensorType | ||
SensorNumber uint8 | ||
EventType EventType | ||
EventDir uint8 | ||
EventData1 uint8 // (Table 29-6) | ||
EventData2 uint8 // (Table 29-6) | ||
EventData3 uint8 // (Table 29-6) | ||
} | ||
|
||
func (r *SELEventRecord) Type() SELType { return r.RecordType } | ||
func (r *SELEventRecord) ID() uint16 { return r.RecordID } | ||
func (r *SELEventRecord) Data() []byte { return r.data } | ||
|
||
func (r *SELEventRecord) Unmarshal(buf []byte) ([]byte, error) { | ||
if l := len(buf); l < selRecordSize { | ||
return nil, &MessageError{ | ||
Message: fmt.Sprintf("Invalid SELEventRecord size : %d/%d", l, selRecordSize), | ||
Detail: hex.EncodeToString(buf), | ||
} | ||
} | ||
r.data = buf[:selRecordSize] | ||
r.RecordID = binary.LittleEndian.Uint16(buf[0:2]) | ||
r.RecordType = SELType(buf[2]) | ||
r.Timestamp.Value = binary.LittleEndian.Uint32(buf[3:7]) | ||
r.GeneratorID = binary.LittleEndian.Uint16(buf[7:9]) | ||
r.EvMRev = buf[9] | ||
r.SensorType = SensorType(buf[10]) | ||
r.SensorNumber = buf[11] | ||
r.EventType = EventType(buf[12] & 0x7f) | ||
r.EventDir = buf[12] & 0x80 >> 7 | ||
r.EventData1 = buf[13] | ||
r.EventData2 = buf[14] | ||
r.EventData3 = buf[15] | ||
|
||
return buf[selRecordSize:], nil | ||
} | ||
|
||
// Timestamped OEM SEL record (Section 32.2) | ||
type SELTimestampedOEMRecord struct { | ||
data []byte | ||
|
||
RecordID uint16 | ||
RecordType SELType | ||
Timestamp Timestamp | ||
ManufacturerID uint32 | ||
OEMDefined []byte | ||
} | ||
|
||
func (r *SELTimestampedOEMRecord) Type() SELType { return r.RecordType } | ||
func (r *SELTimestampedOEMRecord) ID() uint16 { return r.RecordID } | ||
func (r *SELTimestampedOEMRecord) Data() []byte { return r.data } | ||
|
||
func (r *SELTimestampedOEMRecord) Unmarshal(buf []byte) ([]byte, error) { | ||
if l := len(buf); l < selRecordSize { | ||
return nil, &MessageError{ | ||
Message: fmt.Sprintf("Invalid SELTimestampedOEMRecord size : %d/%d", l, selRecordSize), | ||
Detail: hex.EncodeToString(buf), | ||
} | ||
} | ||
r.data = buf[:selRecordSize] | ||
r.RecordID = binary.LittleEndian.Uint16(buf[0:2]) | ||
r.RecordType = SELType(buf[2]) | ||
r.Timestamp.Value = binary.LittleEndian.Uint32(buf[3:7]) | ||
r.ManufacturerID = uint32(buf[7]) | uint32(buf[8])<<8 | uint32(buf[9])<<16 | ||
r.OEMDefined = buf[10:selRecordSize] | ||
|
||
return buf[selRecordSize:], nil | ||
} | ||
|
||
// Non-Timestamped OEM SEL record (Section 32.3) | ||
type SELNonTimestampedOEMRecord struct { | ||
data []byte | ||
|
||
RecordID uint16 | ||
RecordType SELType | ||
OEM []byte | ||
} | ||
|
||
func (r *SELNonTimestampedOEMRecord) Type() SELType { return r.RecordType } | ||
func (r *SELNonTimestampedOEMRecord) ID() uint16 { return r.RecordID } | ||
func (r *SELNonTimestampedOEMRecord) Data() []byte { return r.data } | ||
|
||
func (r *SELNonTimestampedOEMRecord) Unmarshal(buf []byte) ([]byte, error) { | ||
if l := len(buf); l < selRecordSize { | ||
return nil, &MessageError{ | ||
Message: fmt.Sprintf("Invalid SELNonTimestampedOEMRecord size : %d/%d", l, selRecordSize), | ||
Detail: hex.EncodeToString(buf), | ||
} | ||
} | ||
r.data = buf[:selRecordSize] | ||
r.RecordID = binary.LittleEndian.Uint16(buf[0:2]) | ||
r.RecordType = SELType(buf[2]) | ||
r.OEM = buf[3:selRecordSize] | ||
|
||
return buf[selRecordSize:], nil | ||
} | ||
|
||
func selGetRecord(c *Client, reservation, id uint16) (record SELRecord, nextID uint16, err error) { | ||
nextID = selLastID | ||
|
||
gse := &GetSELEntryCommand{ | ||
ReservationID: reservation, | ||
RecordID: id, | ||
RecordOffset: 0x00, | ||
ReadBytes: 0xff, | ||
} | ||
if err = c.Execute(gse); err != nil { | ||
return | ||
} | ||
if l := len(gse.RecordData); l < 3 { | ||
err = &MessageError{Message: fmt.Sprintf("Invalid SELRecord size : %d", l)} | ||
return | ||
} | ||
|
||
if t := SELType(gse.RecordData[2]); t.IsTimestampedOEM() { | ||
r := &SELTimestampedOEMRecord{} | ||
if _, err = r.Unmarshal(gse.RecordData); err != nil { | ||
return | ||
} | ||
record = r | ||
} else if t.IsNonTimestampedOEM() { | ||
r := &SELNonTimestampedOEMRecord{} | ||
if _, err = r.Unmarshal(gse.RecordData); err != nil { | ||
return | ||
} | ||
record = r | ||
} else { | ||
r := &SELEventRecord{} | ||
if _, err = r.Unmarshal(gse.RecordData); err != nil { | ||
return | ||
} | ||
record = r | ||
} | ||
|
||
return record, gse.NextRecordID, nil | ||
} | ||
|
||
func SELGetEntries(c *Client, offset, num int) (records []SELRecord, total int, err error) { | ||
gsi := &GetSELInfoCommand{} | ||
if err = c.Execute(gsi); err != nil { | ||
return | ||
} | ||
|
||
if v := gsi.SELVersion; v != 0x51 && v != 0x02 { | ||
return nil, 0, &MessageError{ | ||
Message: fmt.Sprintf("Unknown SEL version : %d", v), | ||
} | ||
} | ||
total = int(gsi.Entries) | ||
|
||
if total == 0 || num <= 0 || offset < 0 || offset >= total { | ||
return | ||
} | ||
if n := total - offset; num > n { | ||
num = n | ||
} | ||
|
||
roffset := selFirstID | ||
if offset > 0 { | ||
// get first record | ||
r, n, e := selGetRecord(c, 0x00, selFirstID) | ||
if e != nil { | ||
return | ||
} | ||
|
||
delta := n - r.ID() | ||
roffset = delta*uint16(offset) + r.ID() | ||
} | ||
|
||
records = make([]SELRecord, 0, num) | ||
|
||
rsc := &ReserveSELCommand{} | ||
if err = c.Execute(rsc); err != nil { | ||
return | ||
} | ||
|
||
for n, id := 0, roffset; n < num && id != selLastID; n++ { | ||
var r SELRecord | ||
if r, id, err = selGetRecord(c, rsc.ReservationID, id); err != nil { | ||
return | ||
} | ||
records = append(records, r) | ||
} | ||
|
||
if l := len(records); l > num { | ||
records = records[l-num:] | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package ipmigo | ||
|
||
import ( | ||
"time" | ||
) | ||
|
||
const ( | ||
timestampUnspecified = 0xffffffff | ||
timestampPostInitMin = 0x00000000 | ||
timestampPostInitMax = 0x20000000 | ||
) | ||
|
||
// Timestamp (Section 37) | ||
type Timestamp struct { | ||
Value uint32 | ||
} | ||
|
||
func (t *Timestamp) IsUnspecified() bool { | ||
return t.Value == timestampUnspecified | ||
} | ||
|
||
func (t *Timestamp) IsPostInit() bool { | ||
return t.Value >= timestampPostInitMin && t.Value <= timestampPostInitMax | ||
} | ||
|
||
func (t *Timestamp) Format(format string) string { | ||
if t.IsUnspecified() { | ||
return "Unspecified" | ||
} | ||
if t.IsPostInit() { | ||
return "Post-Init" | ||
} | ||
return time.Unix(int64(t.Value), 0).Format(format) | ||
} | ||
|
||
func (t *Timestamp) String() string { | ||
return t.Format(time.RFC3339) | ||
} |