gotop/vendor/github.com/distatus/battery/battery_openbsd.go

280 lines
6.1 KiB
Go

// battery
// Copyright (C) 2016-2017 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package battery
import (
"bytes"
"fmt"
"strings"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
var errValueNotFound = fmt.Errorf("Value not found")
var sensorW = [4]int32{
2, // SENSOR_VOLTS_DC (uV)
5, // SENSOR_WATTS (uW)
7, // SENSOR_WATTHOUR (uWh)
10, // SENSOR_INTEGER
}
const (
sensorA = 6 // SENSOR_AMPS (uA)
sensorAH = 8 // SENSOR_AMPHOUR (uAh)
)
type sensordev struct {
num int32
xname [16]byte
maxnumt [21]int32
sensors_count int32
}
type sensorStatus int32
const (
unspecified sensorStatus = iota
ok
warning
critical
unknown
)
type sensor struct {
desc [32]byte
tv [16]byte // struct timeval
value int64
typ [4]byte // enum sensor_type
status sensorStatus
numt int32
flags int32
}
type interValue struct {
v *float64
s *State
e *error
}
func sysctl(mib []int32, out unsafe.Pointer, n uintptr) syscall.Errno {
_, _, e := unix.Syscall6(
unix.SYS___SYSCTL,
uintptr(unsafe.Pointer(&mib[0])),
uintptr(len(mib)),
uintptr(out),
uintptr(unsafe.Pointer(&n)),
uintptr(unsafe.Pointer(nil)),
0,
)
return e
}
func readValue(s sensor, div float64) (float64, error) {
if s.status == unknown {
return 0, fmt.Errorf("Unknown value received")
}
return float64(s.value) / div, nil
}
func readValues(mib []int32, c int32, values map[string]*interValue) {
var s sensor
var i int32
for i = 0; i < c; i++ {
mib[4] = i
if err := sysctl(mib, unsafe.Pointer(&s), unsafe.Sizeof(s)); err != 0 {
for _, value := range values {
if *value.e == errValueNotFound {
*value.e = err
}
}
}
desc := string(s.desc[:bytes.IndexByte(s.desc[:], 0)])
isState := strings.HasPrefix(desc, "battery ")
var value *interValue
var ok bool
if isState {
value, ok = values["state"]
} else {
value, ok = values[desc]
}
if !ok {
continue
}
if isState {
//TODO:battery idle(?)
if desc == "battery critical" {
*value.s, *value.e = Empty, nil
} else {
*value.s, *value.e = newState(desc[8:])
}
continue
}
if strings.HasSuffix(desc, "voltage") {
*value.v, *value.e = readValue(s, 1000000)
} else {
*value.v, *value.e = readValue(s, 1000)
}
}
}
func sensordevIter(cb func(sd sensordev, i int, err error) bool) {
mib := []int32{6, 11, 0}
var sd sensordev
var idx int
var i int32
for i = 0; ; i++ {
mib[2] = i
e := sysctl(mib, unsafe.Pointer(&sd), unsafe.Sizeof(sd))
if e != 0 {
if e == unix.ENXIO {
continue
}
if e == unix.ENOENT {
break
}
}
if bytes.HasPrefix(sd.xname[:], []byte("acpibat")) {
var err error
if e != 0 {
err = e
}
if cb(sd, idx, err) {
return
}
idx++
}
}
}
func getBattery(sd sensordev) (*Battery, error) {
b := &Battery{}
e := ErrPartial{
Design: errValueNotFound,
Full: errValueNotFound,
Current: errValueNotFound,
ChargeRate: errValueNotFound,
State: errValueNotFound,
Voltage: errValueNotFound,
DesignVoltage: errValueNotFound,
}
mib := []int32{6, 11, sd.num, 0, 0}
for _, w := range sensorW {
mib[3] = w
readValues(mib, sd.maxnumt[w], map[string]*interValue{
"rate": {v: &b.ChargeRate, e: &e.ChargeRate},
"design capacity": {v: &b.Design, e: &e.Design},
"last full capacity": {v: &b.Full, e: &e.Full},
"remaining capacity": {v: &b.Current, e: &e.Current},
"current voltage": {v: &b.Voltage, e: &e.Voltage},
"voltage": {v: &b.DesignVoltage, e: &e.DesignVoltage},
"state": {s: &b.State, e: &e.State},
})
}
if e.DesignVoltage != nil && e.Voltage == nil {
b.DesignVoltage, e.DesignVoltage = b.Voltage, nil
}
if e.ChargeRate == errValueNotFound {
if e.Voltage == nil {
mib[3] = sensorA
readValues(mib, sd.maxnumt[sensorA], map[string]*interValue{
"rate": {v: &b.ChargeRate, e: &e.ChargeRate},
})
b.ChargeRate *= b.Voltage
} else {
e.ChargeRate = e.Voltage
}
}
if e.Design == errValueNotFound || e.Full == errValueNotFound || e.Current == errValueNotFound {
mib[3] = sensorAH
readValues(mib, sd.maxnumt[sensorAH], map[string]*interValue{
"design capacity": {v: &b.Design, e: &e.Design},
"last full capacity": {v: &b.Full, e: &e.Full},
"remaining capacity": {v: &b.Current, e: &e.Current},
})
b.Design *= b.DesignVoltage
b.Full *= b.Voltage
b.Current *= b.Voltage
}
return b, e
}
func systemGet(idx int) (*Battery, error) {
var b *Battery
var e error
sensordevIter(func(sd sensordev, i int, err error) bool {
if i == idx {
if err == nil {
b, e = getBattery(sd)
} else {
e = err
}
return true
}
return false
})
if b == nil {
return nil, ErrNotFound
}
return b, e
}
func systemGetAll() ([]*Battery, error) {
var batteries []*Battery
var errors Errors
sensordevIter(func(sd sensordev, i int, err error) bool {
var b *Battery
if err == nil {
b, err = getBattery(sd)
}
batteries = append(batteries, b)
errors = append(errors, err)
return false
})
return batteries, errors
}