Files
i3status-go/vendor/github.com/distatus/battery/battery_openbsd.go
2024-01-06 18:33:44 +03:00

283 lines
6.7 KiB
Go

// battery
// Copyright (C) 2016-2017,2019,2023 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"
"unsafe"
"golang.org/x/sys/unix"
)
func sysctl(mib []int32, out unsafe.Pointer, n uintptr) unix.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
}
var errValueNotFound = fmt.Errorf("Value not found")
func setErr(err *error, newErr error) {
if *err == errValueNotFound {
*err = newErr
}
}
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 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
}
func (s *sensor) readValue(div float64) (float64, error) {
if s.status == unknown {
return 0, fmt.Errorf("Unknown value received")
}
return float64(s.value) / div, nil
}
func (s *sensor) handleA(factor float64, field *float64, fieldErr *error) {
*field, *fieldErr = s.readValue(1000)
if *fieldErr == nil {
*field *= factor
}
}
func (s *sensor) handleAH(factor float64, factorErr error, field *float64, fieldErr *error) {
if factorErr != nil {
*fieldErr = factorErr
return
}
s.handleA(factor, field, fieldErr)
}
type sensordev struct {
num int32
xname [16]byte
maxnumt [23]int32
sensors_count int32
}
func (sd *sensordev) get() (*Battery, error) {
var battery Battery
err := ErrPartial{
Design: errValueNotFound,
Full: errValueNotFound,
Current: errValueNotFound,
ChargeRate: errValueNotFound,
State: errValueNotFound,
Voltage: errValueNotFound,
DesignVoltage: errValueNotFound,
}
mib := []int32{unix.CTL_HW, 11, sd.num, 0, 0}
var s sensor
iter := func(maxnumtIdx int32, cb func(string)) {
mib[3] = maxnumtIdx
for i := int32(0); i < sd.maxnumt[maxnumtIdx]; i++ {
mib[4] = i
if errno := sysctl(mib, unsafe.Pointer(&s), unsafe.Sizeof(s)); errno != 0 {
setErr(&err.Design, errno)
setErr(&err.Full, errno)
setErr(&err.Current, errno)
setErr(&err.ChargeRate, errno)
setErr(&err.Voltage, errno)
setErr(&err.DesignVoltage, errno)
setErr(&err.State, errno)
}
// Convert 0-terminated C-string to a Go string
cb(string(s.desc[:bytes.IndexByte(s.desc[:], 0)]))
}
}
for _, w := range sensorW {
mib[3] = w
iter(w, func(desc string) {
if strings.HasPrefix(desc, "battery ") {
battery.State.specific, err.State = desc, nil
switch desc[8:] {
case "unknown":
battery.State.Raw = Unknown
case "full":
battery.State.Raw = Full
case "charging":
battery.State.Raw = Charging
case "discharging":
battery.State.Raw = Discharging
case "idle":
battery.State.Raw = Idle
case "critical":
battery.State.Raw = Empty
default:
battery.State.Raw = Undefined
}
return
}
switch desc {
case "rate":
battery.ChargeRate, err.ChargeRate = s.readValue(1000)
case "design capacity":
battery.Design, err.Design = s.readValue(1000)
case "last full capacity":
battery.Full, err.Full = s.readValue(1000)
case "remaining capacity":
battery.Current, err.Current = s.readValue(1000)
case "current voltage":
battery.Voltage, err.Voltage = s.readValue(1000_000)
case "voltage":
battery.DesignVoltage, err.DesignVoltage = s.readValue(1000_000)
}
})
}
if err.DesignVoltage != nil && err.Voltage == nil {
battery.DesignVoltage, err.DesignVoltage = battery.Voltage, nil
}
if err.ChargeRate == errValueNotFound {
if err.Voltage == nil {
iter(sensorA, func(desc string) {
if desc == "rate" {
s.handleA(battery.Voltage, &battery.ChargeRate, &err.ChargeRate)
}
})
} else {
err.ChargeRate = err.Voltage
}
}
if err.Design == errValueNotFound || err.Full == errValueNotFound || err.Current == errValueNotFound {
iter(sensorAH, func(desc string) {
switch desc {
case "design capacity":
s.handleAH(battery.DesignVoltage, err.DesignVoltage, &battery.Design, &err.Design)
case "last full capacity":
s.handleAH(battery.Voltage, err.Voltage, &battery.Full, &err.Full)
case "remaining capacity":
s.handleAH(battery.Voltage, err.Voltage, &battery.Current, &err.Current)
}
})
}
return &battery, err
}
func getBatteryAtMIBIndex(i int32) (*Battery, int32, error) {
mib := []int32{
unix.CTL_HW,
11, // HW_SENSORS
0,
}
var sd sensordev
for ; ; i++ {
mib[2] = i
errno := sysctl(mib, unsafe.Pointer(&sd), unsafe.Sizeof(sd))
if errno == unix.ENOENT {
break
}
if errno != 0 {
continue
}
if bytes.HasPrefix(sd.xname[:], []byte("acpibat")) {
battery, err := sd.get()
return battery, i + 1, err
}
}
return nil, i, nil
}
func systemGet(idx int) (*Battery, error) {
var i int32
for idxCurr := 0; ; idxCurr++ {
battery, iNext, err := getBatteryAtMIBIndex(i)
// If this is the index we look for, grab it.
// Otherwise just move on, regardless of errors etc.
if idxCurr == idx {
return battery, err
}
i = iNext
}
return nil, ErrNotFound
}
func systemGetAll() ([]*Battery, error) {
var batteries []*Battery
var errors Errors
var i int32
for {
battery, iNext, err := getBatteryAtMIBIndex(i)
if battery == nil && err == nil { // no more batteries
break
}
batteries = append(batteries, battery)
errors = append(errors, err)
i = iNext
}
return batteries, errors
}