PKGBUILD/vendor/periph.io/x/periph/host/sysfs/led.go
2018-12-07 20:42:30 +01:00

268 lines
5.5 KiB
Go

// Copyright 2016 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package sysfs
import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"sync"
"time"
"periph.io/x/periph"
"periph.io/x/periph/conn"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/physic"
"periph.io/x/periph/conn/pin"
"periph.io/x/periph/host/fs"
)
// LEDs is all the leds discovered on this host via sysfs.
//
// Depending on the user context, the LEDs may be read-only or writeable.
var LEDs []*LED
// LEDByName returns a *LED for the LED name, if any.
//
// For all practical purpose, a LED is considered an output-only gpio.PinOut.
func LEDByName(name string) (*LED, error) {
// TODO(maruel): Use a bisect or a map. For now we don't expect more than a
// handful of LEDs so it doesn't matter.
for _, led := range LEDs {
if led.name == name {
if err := led.open(); err != nil {
return nil, err
}
return led, nil
}
}
return nil, errors.New("sysfs-led: invalid LED name")
}
// LED represents one LED on the system.
type LED struct {
number int
name string
root string
mu sync.Mutex
fBrightness *fs.File // handle to /sys/class/gpio/gpio*/direction; never closed
}
// String implements conn.Resource.
func (l *LED) String() string {
return fmt.Sprintf("%s(%d)", l.name, l.number)
}
// Halt implements conn.Resource.
//
// It turns the light off.
func (l *LED) Halt() error {
return l.Out(gpio.Low)
}
// Name implements pin.Pin.
func (l *LED) Name() string {
return l.name
}
// Number implements pin.Pin.
func (l *LED) Number() int {
return l.number
}
// Function implements pin.Pin.
func (l *LED) Function() string {
if l.Read() {
return "LED/On"
}
return "LED/Off"
}
// Func implements pin.PinFunc.
func (l *LED) Func() pin.Func {
if l.Read() {
return gpio.OUT_HIGH
}
return gpio.OUT_LOW
}
// SupportedFuncs implements pin.PinFunc.
func (l *LED) SupportedFuncs() []pin.Func {
return []pin.Func{gpio.OUT}
}
// SetFunc implements pin.PinFunc.
func (l *LED) SetFunc(f pin.Func) error {
switch f {
case gpio.OUT_HIGH:
return l.Out(gpio.High)
case gpio.OUT, gpio.OUT_LOW:
return l.Out(gpio.Low)
default:
return errors.New("sysfs-led: unsupported function")
}
}
// In implements gpio.PinIn.
func (l *LED) In(pull gpio.Pull, edge gpio.Edge) error {
if pull != gpio.Float && pull != gpio.PullNoChange {
return errors.New("sysfs-led: pull is not supported on LED")
}
if edge != gpio.NoEdge {
return errors.New("sysfs-led: edge is not supported on LED")
}
return nil
}
// Read implements gpio.PinIn.
func (l *LED) Read() gpio.Level {
err := l.open()
if err != nil {
return gpio.Low
}
l.mu.Lock()
defer l.mu.Unlock()
if _, err := l.fBrightness.Seek(0, 0); err != nil {
return gpio.Low
}
var b [4]byte
if _, err := l.fBrightness.Read(b[:]); err != nil {
return gpio.Low
}
if b[0] != '0' {
return gpio.High
}
return gpio.Low
}
// WaitForEdge implements gpio.PinIn.
func (l *LED) WaitForEdge(timeout time.Duration) bool {
return false
}
// Pull implements gpio.PinIn.
func (l *LED) Pull() gpio.Pull {
return gpio.PullNoChange
}
// DefaultPull implements gpio.PinIn.
func (l *LED) DefaultPull() gpio.Pull {
return gpio.PullNoChange
}
// Out implements gpio.PinOut.
func (l *LED) Out(level gpio.Level) error {
err := l.open()
if err != nil {
return err
}
l.mu.Lock()
defer l.mu.Unlock()
if _, err = l.fBrightness.Seek(0, 0); err != nil {
return err
}
if level {
_, err = l.fBrightness.Write([]byte("255"))
} else {
_, err = l.fBrightness.Write([]byte("0"))
}
return err
}
// PWM implements gpio.PinOut.
//
// This sets the intensity level, if supported. The frequency is ignored.
func (l *LED) PWM(d gpio.Duty, f physic.Frequency) error {
err := l.open()
if err != nil {
return err
}
l.mu.Lock()
defer l.mu.Unlock()
if _, err = l.fBrightness.Seek(0, 0); err != nil {
return err
}
v := (d + gpio.DutyMax/512) / (gpio.DutyMax / 256)
_, err = l.fBrightness.Write([]byte(strconv.Itoa(int(v))))
return err
}
//
func (l *LED) open() error {
l.mu.Lock()
defer l.mu.Unlock()
// trigger, max_brightness.
var err error
if l.fBrightness == nil {
p := l.root + "brightness"
if l.fBrightness, err = fs.Open(p, os.O_RDWR); err != nil {
// Retry with read-only. This is the default setting.
l.fBrightness, err = fs.Open(p, os.O_RDONLY)
}
}
return err
}
// driverLED implements periph.Driver.
type driverLED struct {
}
func (d *driverLED) String() string {
return "sysfs-led"
}
func (d *driverLED) Prerequisites() []string {
return nil
}
func (d *driverLED) After() []string {
return nil
}
// Init initializes LEDs sysfs handling code.
//
// Uses led sysfs as described* at
// https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-led
//
// * for the most minimalistic meaning of 'described'.
func (d *driverLED) Init() (bool, error) {
items, err := filepath.Glob("/sys/class/leds/*")
if err != nil {
return true, err
}
if len(items) == 0 {
return false, errors.New("no LED found")
}
// This make the LEDs in deterministic order.
sort.Strings(items)
for i, item := range items {
LEDs = append(LEDs, &LED{
number: i,
name: filepath.Base(item),
root: item + "/",
})
}
return true, nil
}
func init() {
if isLinux {
periph.MustRegister(&drvLED)
}
}
var drvLED driverLED
var _ conn.Resource = &LED{}
var _ gpio.PinIn = &LED{}
var _ gpio.PinOut = &LED{}
var _ gpio.PinIO = &LED{}
var _ pin.PinFunc = &LED{}