Skip to content

Commit

Permalink
Merge pull request #206 from jfroy/disjoint
Browse files Browse the repository at this point in the history
Support disjoint fan and pwm configurations
  • Loading branch information
markusressel authored Mar 15, 2023
2 parents 71b3438 + b1fa829 commit da70287
Show file tree
Hide file tree
Showing 13 changed files with 472 additions and 147 deletions.
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,23 +115,31 @@ system run `fan2go detect`, which will print a list of devices exposed by the hw
```shell
> fan2go detect
nct6798
Fans Index Label RPM PWM Auto
1 hwmon4 0 153 false
2 hwmon4 1223 104 false
3 hwmon4 677 107 false
Fans Index Channel Label RPM PWM Auto
1 1 hwmon4/fan1 0 153 false
2 2 hwmon4/fan2 1223 104 false
3 3 hwmon4/fan3 677 107 false
Sensors Index Label Value
1 SYSTIN 41000
2 CPUTIN 64000

amdgpu-pci-0031
Fans Index Label RPM PWM Auto
1 hwmon8 561 43 false
Fans Index Channel Label RPM PWM Auto
1 1 hwmon8/fan1 561 43 false
Sensors Index Label Value
1 edge 58000
2 junction 61000
3 mem 56000
```

The fan index is based on device enumeration and is not stable for a given fan if hardware configuration changes.
The Linux kernel hwmon channel is a better identifier for configuration as it is largely based on the fan headers
in use.

Fan RPM, PWM, and temperature sensors are independent and Linux does not associate them automatically. A given PWM
may control more than one fan, and a fan may not be under the control of a PWM. By default, fan2go guesses and sets
the pwm channel number for a given fan to the fan's RPM sensor channel. You can override this in the config.

#### HwMon

To use detected devices in your configuration, use the `hwmon` fan type:
Expand All @@ -147,8 +155,10 @@ fans:
# The platform of the controller which is
# connected to this fan (see sensor.platform below)
platform: nct6798
# The index of this fan as displayed by `fan2go detect`
index: 1
# The channel of this fan's RPM sensor as displayed by `fan2go detect`
rpmChannel: 1
# The pwm channel that controls this fan; fan2go defaults to same channel number as fan RPM
pwmChannel: 1
# Indicates whether this fan should never stop rotating, regardless of
# how low the curve value is
neverStop: true
Expand Down
25 changes: 9 additions & 16 deletions cmd/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package cmd
import (
"bytes"
"fmt"
"path/filepath"
"sort"
"strconv"

"github.com/markusressel/fan2go/cmd/global"
"github.com/markusressel/fan2go/internal/configuration"
"github.com/markusressel/fan2go/internal/fans"
Expand All @@ -11,9 +15,6 @@ import (
"github.com/mgutz/ansi"
"github.com/spf13/cobra"
"github.com/tomlazar/table"
"path/filepath"
"sort"
"strconv"
)

var detectCmd = &cobra.Command{
Expand Down Expand Up @@ -42,25 +43,17 @@ var detectCmd = &cobra.Command{
continue
}

fanMap := controller.Fans
fanSlice := controller.Fans
sensorMap := controller.Sensors

if len(fanMap) <= 0 && len(sensorMap) <= 0 {
if len(fanSlice) <= 0 && len(sensorMap) <= 0 {
continue
}

ui.Printfln("> %s", controller.Name)

fanMapKeys := make([]int, 0, len(fanMap))
for k := range fanMap {
fanMapKeys = append(fanMapKeys, k)
}
sort.Ints(fanMapKeys)

var fanRows [][]string
for _, index := range fanMapKeys {
fan := fanMap[index]

for _, fan := range fanSlice {
pwmText := "N/A"
pwm, err := fan.GetPwm()
if err == nil {
Expand All @@ -77,10 +70,10 @@ var detectCmd = &cobra.Command{

isAuto, _ := fan.IsPwmAuto()
fanRows = append(fanRows, []string{
"", strconv.Itoa(fan.Index), fan.Label, rpmText, pwmText, fmt.Sprintf("%v", isAuto),
"", strconv.Itoa(fan.Index), strconv.Itoa(fan.Config.HwMon.RpmChannel), fan.Label, rpmText, pwmText, fmt.Sprintf("%v", isAuto),
})
}
var fanHeaders = []string{"Fans ", "Index", "Label", "RPM", "PWM", "Auto"}
var fanHeaders = []string{"Fans ", "Index", "Channel", "Label", "RPM", "PWM", "Auto"}

fanTable := table.Table{
Headers: fanHeaders,
Expand Down
16 changes: 2 additions & 14 deletions cmd/fan/fan.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package fan
import (
"errors"
"fmt"

"github.com/markusressel/fan2go/internal/configuration"
"github.com/markusressel/fan2go/internal/fans"
"github.com/markusressel/fan2go/internal/hwmon"
"github.com/markusressel/fan2go/internal/ui"
"github.com/spf13/cobra"
"regexp"
)

var fanId string
Expand Down Expand Up @@ -46,19 +46,7 @@ func getFan(id string) (fans.Fan, error) {
availableFanIds = append(availableFanIds, config.ID)
if config.ID == id {
if config.HwMon != nil {
for _, controller := range controllers {
matched, err := regexp.MatchString("(?i)"+config.HwMon.Platform, controller.Platform)
if err != nil {
return nil, errors.New(fmt.Sprintf("Failed to match platform regex of %s (%s) against controller platform %s", config.ID, config.HwMon.Platform, controller.Platform))
}

if matched {
fan := controller.Fans[config.HwMon.Index]
config.HwMon.PwmOutput = fan.Config.HwMon.PwmOutput
config.HwMon.RpmInput = fan.Config.HwMon.RpmInput
break
}
}
_ = hwmon.UpdateFanConfigFromHwMonControllers(controllers, &config)
}

fan, err := fans.NewFan(config)
Expand Down
10 changes: 6 additions & 4 deletions fan2go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ fans:
# The platform of the controller which is
# connected to this fan (see sensor.platform below)
platform: nct6798-isa-0
# The index of this fan as displayed by `fan2go detect`
index: 1
# The channel of this fan's RPM sensor as displayed by `fan2go detect`
rpmChannel: 1
# The pwm channel that controls this fan; fan2go defaults to same channel number as fan RPM
pwmChannel: 1
# Indicates whether this fan should never stop rotating, regardless of
# how low the curve value is
neverStop: true
Expand Down Expand Up @@ -65,14 +67,14 @@ fans:
- id: in_front
hwmon:
platform: it8620
index: 4
rpmChannel: 4
neverStop: true
curve: case_avg_curve

- id: out_back
hwmon:
platform: it8620
index: 5
rpmChannel: 5
neverStop: true
curve: case_avg_curve

Expand Down
36 changes: 12 additions & 24 deletions internal/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ package internal
import (
"context"
"fmt"
"net/http"
"net/http/pprof"
"os"
"os/signal"
"os/user"
"regexp"
"syscall"
"time"

"github.com/labstack/echo/v4"
"github.com/markusressel/fan2go/internal/api"
"github.com/markusressel/fan2go/internal/configuration"
Expand All @@ -16,14 +25,6 @@ import (
"github.com/markusressel/fan2go/internal/ui"
"github.com/markusressel/fan2go/internal/util"
"github.com/oklog/run"
"net/http"
"net/http/pprof"
"os"
"os/signal"
"os/user"
"regexp"
"syscall"
"time"
)

func RunDaemon() {
Expand Down Expand Up @@ -320,22 +321,9 @@ func initializeFans(controllers []*hwmon.HwMonController) map[configuration.FanC

for _, config := range configuration.CurrentConfig.Fans {
if config.HwMon != nil {
found := false
for _, c := range controllers {
matched, err := regexp.MatchString("(?i)"+config.HwMon.Platform, c.Platform)
if err != nil {
ui.Fatal("Failed to match platform regex of %s (%s) against controller platform %s", config.ID, config.HwMon.Platform, c.Platform)
}
if matched {
found = true
fan := c.Fans[config.HwMon.Index].Config.HwMon
config.HwMon.PwmOutput = fan.PwmOutput
config.HwMon.RpmInput = fan.RpmInput
break
}
}
if !found {
ui.Fatal("Couldn't find hwmon device with platform '%s' for fan: %s", config.HwMon.Platform, config.ID)
err := hwmon.UpdateFanConfigFromHwMonControllers(controllers, &config)
if err != nil {
ui.Fatal("Couldn't update fan config from hwmon: %s", err)
}
}

Expand Down
12 changes: 8 additions & 4 deletions internal/configuration/fans.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ type FanConfig struct {
}

type HwMonFanConfig struct {
Platform string `json:"platform"`
Index int `json:"index"`
PwmOutput string
RpmInput string
Platform string `json:"platform"`
Index int `json:"index"`
RpmChannel int `json:"rpmChannel"`
PwmChannel int `json:"pwmChannel"`
SysfsPath string
RpmInputPath string
PwmPath string
PwmEnablePath string
}

type FileFanConfig struct {
Expand Down
14 changes: 12 additions & 2 deletions internal/configuration/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package configuration
import (
"errors"
"fmt"
"strings"

"github.com/looplab/tarjan"
"github.com/markusressel/fan2go/internal/ui"
"github.com/markusressel/fan2go/internal/util"
"golang.org/x/exp/slices"
"strings"
)

func Validate(configPath string) error {
Expand Down Expand Up @@ -266,9 +267,18 @@ func validateFans(config *Configuration) error {
}

if fanConfig.HwMon != nil {
if fanConfig.HwMon.Index <= 0 {
if (fanConfig.HwMon.Index != 0 && fanConfig.HwMon.RpmChannel != 0) || (fanConfig.HwMon.Index == 0 && fanConfig.HwMon.RpmChannel == 0) {
return errors.New(fmt.Sprintf("Fan %s: must have one of index or rpmChannel, must be >= 1", fanConfig.ID))
}
if fanConfig.HwMon.Index < 0 {
return errors.New(fmt.Sprintf("Fan %s: invalid index, must be >= 1", fanConfig.ID))
}
if fanConfig.HwMon.RpmChannel < 0 {
return errors.New(fmt.Sprintf("Fan %s: invalid rpmChannel, must be >= 1", fanConfig.ID))
}
if fanConfig.HwMon.PwmChannel < 0 {
return errors.New(fmt.Sprintf("Fan %s: invalid pwmChannel, must be >= 1", fanConfig.ID))
}
}

if fanConfig.File != nil {
Expand Down
Loading

0 comments on commit da70287

Please sign in to comment.