Skip to content

Commit

Permalink
Add a function to get SEL
Browse files Browse the repository at this point in the history
  • Loading branch information
k-sone committed Jul 26, 2019
1 parent 72a7e93 commit 8480675
Show file tree
Hide file tree
Showing 4 changed files with 391 additions and 0 deletions.
114 changes: 114 additions & 0 deletions command_sel.go
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
}
}
10 changes: 10 additions & 0 deletions event.go
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 }
229 changes: 229 additions & 0 deletions sel.go
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
}
38 changes: 38 additions & 0 deletions time.go
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)
}

0 comments on commit 8480675

Please sign in to comment.