build: upgrade to go 1.17 and dependencies

This commit is contained in:
2021-09-01 21:34:31 +02:00
parent bb6726a4b2
commit 36482fc9b8
749 changed files with 110609 additions and 117714 deletions

13
vendor/periph.io/x/host/v3/sysfs/doc.go generated vendored Normal file
View File

@ -0,0 +1,13 @@
// 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 implements a sane library to interact with sysfs provided
// hardware access.
//
// sysfs a virtual file system rooted at /sys/.
//
// This package also include drivers using devfs.
//
// https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt
package sysfs

319
vendor/periph.io/x/host/v3/sysfs/fs_linux.go generated vendored Normal file
View File

@ -0,0 +1,319 @@
// Copyright 2017 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"
"os"
"strconv"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
)
// syscall.EpollCtl() commands.
//
// These are defined here so we don't need to import golang.org/x/sys/unix.
//
// http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
const (
epollCTLAdd = 1 // EPOLL_CTL_ADD
epollCTLDel = 2 // EPOLL_CTL_DEL
epollCTLMod = 3 // EPOLL_CTL_MOD
)
// Bitmask for field syscall.EpollEvent.Events.
//
// These are defined here so we don't need to import golang.org/x/sys/unix.
//
// http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
type epollEvent uint32
const (
epollIN epollEvent = 0x1 // EPOLLIN: available for read
epollOUT epollEvent = 0x4 // EPOLLOUT: available for write
epollPRI epollEvent = 0x2 // EPOLLPRI: exceptional urgent condition
epollERR epollEvent = 0x8 // EPOLLERR: error
epollHUP epollEvent = 0x10 // EPOLLHUP: hangup
epollET epollEvent = 0x80000000 // EPOLLET: Edge Triggered behavior
epollONESHOT epollEvent = 0x40000000 // EPOLLONESHOT: One shot
epollWAKEUP epollEvent = 0x20000000 // EPOLLWAKEUP: disable system sleep; kernel >=3.5
epollEXCLUSIVE epollEvent = 0x10000000 // EPOLLEXCLUSIVE: only wake one; kernel >=4.5
)
var bitmaskString = [...]struct {
e epollEvent
s string
}{
{epollIN, "IN"},
{epollOUT, "OUT"},
{epollPRI, "PRI"},
{epollERR, "ERR"},
{epollHUP, "HUP"},
{epollET, "ET"},
{epollONESHOT, "ONESHOT"},
{epollWAKEUP, "WAKEUP"},
{epollEXCLUSIVE, "EXCLUSIVE"},
}
// String is useful for debugging.
func (e epollEvent) String() string {
var out []string
for _, b := range bitmaskString {
if e&b.e != 0 {
out = append(out, b.s)
e &^= b.e
}
}
if e != 0 {
out = append(out, "0x"+strconv.FormatUint(uint64(e), 16))
}
if len(out) == 0 {
out = []string{"0"}
}
return strings.Join(out, "|")
}
// eventsListener listens for events for multiple files as a single system call.
//
// One OS thread is needed for all the events. This is more efficient on single
// core system.
type eventsListener struct {
// Atomic value set to one once fully initialized.
initialized int32
// Mapping of file descriptors to wait on with their corresponding channels.
mu sync.Mutex
// File descriptor of the epoll handle itself.
epollFd int
// Pipes to wake up the EpollWait() system call inside loop().
r, w *os.File
// Return channel to confirm that EpollWait() was woken up.
wakeUp <-chan time.Time
// Map of file handles to user listening channel.
fds map[int32]chan<- time.Time
}
// init must be called on a fresh instance.
func (e *eventsListener) init() error {
if atomic.LoadInt32(&e.initialized) != 0 {
// Was already initialized.
return nil
}
e.mu.Lock()
if atomic.LoadInt32(&e.initialized) != 0 {
// Was already initialized, but this was done concurrently with another
// thread.
e.mu.Unlock()
return nil
}
err := e.initLocked()
atomic.StoreInt32(&e.initialized, 1)
if err != nil {
e.mu.Unlock()
return err
}
wakeUp := make(chan time.Time)
e.wakeUp = wakeUp
e.fds = map[int32]chan<- time.Time{}
e.fds[int32(e.r.Fd())] = wakeUp
// The mutex is still held after this function exits, it's loop() that will
// release the mutex.
//
// This forces loop() to be started before addFd() can be called by users.
go e.loop()
return nil
}
func (e *eventsListener) initLocked() error {
var err error
e.epollFd, err = syscall.EpollCreate(1)
switch {
case err == nil:
break
case err.Error() == "function not implemented":
// Some arch (arm64) do not implement EpollCreate().
if e.epollFd, err = syscall.EpollCreate1(0); err != nil {
return err
}
default:
return err
}
e.r, e.w, err = os.Pipe()
if err != nil {
return err
}
// Only need epollIN. epollPRI has no effect on pipes.
return e.addFdInner(e.r.Fd(), epollET|epollIN)
}
// loop is the main event loop.
func (e *eventsListener) loop() {
var events []syscall.EpollEvent
type lookup struct {
c chan<- time.Time
event epollEvent
}
var lookups []lookup
for first := true; ; {
if !first {
e.mu.Lock()
}
if len(events) < len(e.fds) {
events = make([]syscall.EpollEvent, len(e.fds))
}
e.mu.Unlock()
first = false
if len(events) == 0 {
panic("internal error: there's should be at least one pipe")
}
// http://man7.org/linux/man-pages/man2/epoll_wait.2.html
n, err := syscall.EpollWait(e.epollFd, events, -1)
if n <= 0 {
// -1 if an error occurred (EBADF, EFAULT, EINVAL) or the call was
// interrupted by a signal (EINTR).
// 0 is the timeout occurred. In this case there's no timeout specified.
// Still handle this explicitly in case a timeout could be triggered by
// external events, like system sleep.
continue
}
if err != nil {
// TODO(maruel): It'd be nice to be able to surface this.
// This may cause a busy loop. Hopefully the user will notice and will
// fix their code.
// This can happen when removeFd() is called, in this case silently
// ignore the error.
continue
}
now := time.Now()
// Create a look up table with the lock, so that then the channel can be
// pushed to without the lock.
if cap(lookups) < n {
lookups = make([]lookup, 0, n)
} else {
lookups = lookups[:0]
}
e.mu.Lock()
for _, ev := range events[:n] {
ep := epollEvent(ev.Events)
// Skip over file descriptors that are not present.
c, ok := e.fds[ev.Fd]
if !ok {
// That's a race condition where the file descriptor was removed by
// removeFd() but it still triggered. Ignore this event.
continue
}
// Look at the event to determine if it's worth sending a pulse. It's
// maybe not worth it. Ignore epollERR, since it's always set for GPIO
// sysfs.
// Pipe and socket trigger epollIN and epollOUT, but GPIO sysfs triggers
// epollPRI.
if ep&(epollPRI|epollIN|epollOUT) != 0 {
lookups = append(lookups, lookup{c: c, event: ep})
}
}
e.mu.Unlock()
// Once the lock is released, send the timestamps.
for _, t := range lookups {
t.c <- now
}
}
}
// addFd starts listening to events generated by file descriptor |fd|.
//
// fd is the OS file descriptor. In practice, it must fit a int32 value. It
// works on pipes, sockets and sysfs objects like GPIO but not on standard
// files.
//
// c is the channel to send events to. Unbuffered channel will block the event
// loop, which may mean lost events, especially if multiple files are listened
// to simultaneously.
//
// flags is the events to listen to. No need to specify epollERR and epollHUP,
// they are sent anyway.
//
// addFd lazy initializes eventsListener if it was not initialized yet.
//
// It can fail due to various reasons, a few are:
// ENOSPC: /proc/sys/fs/epoll/max_user_watches limit was exceeded
// ENOMEM: No memory available
// EPERM: fd is a regular file or directory
func (e *eventsListener) addFd(fd uintptr, c chan<- time.Time, flags epollEvent) error {
if c == nil {
return errors.New("fd: addFd requires a valid channel")
}
if err := e.init(); err != nil {
return err
}
if err := e.addFdInner(fd, flags); err != nil {
return err
}
e.mu.Lock()
e.fds[int32(fd)] = c
e.mu.Unlock()
// Wake up the poller so it notices there's one new file.
e.wakeUpLoop(nil)
return nil
}
func (e *eventsListener) addFdInner(fd uintptr, flags epollEvent) error {
ev := syscall.EpollEvent{Events: uint32(flags), Fd: int32(fd)}
return syscall.EpollCtl(e.epollFd, epollCTLAdd, int(fd), &ev)
}
// removeFd stop listening to events on this file descriptor.
func (e *eventsListener) removeFd(fd uintptr) error {
if err := syscall.EpollCtl(e.epollFd, epollCTLDel, int(fd), nil); err != nil {
return err
}
e.mu.Lock()
delete(e.fds, int32(fd))
e.mu.Unlock()
// Wake up the poller so it notices there's one less file.
e.wakeUpLoop(nil)
return nil
}
// wakeUpLoop wakes up the poller and waits for it.
//
// Must not be called with the lock held.
func (e *eventsListener) wakeUpLoop(c <-chan time.Time) time.Time {
// TODO(maruel): Figure out a way to wake up that doesn't require emptying.
var b [1]byte
_, _ = e.w.Write(b[:])
var t time.Time
if c != nil {
// To prevent deadlock, also empty c.
for {
select {
case <-c:
case t = <-e.wakeUp:
goto out
}
}
out:
} else {
t = <-e.wakeUp
}
// Don't forget to empty the pipe. Sadly, this will wake up the loop a second
// time.
_, _ = e.r.Read(b[:])
return t
}
// events is the global events listener.
//
// It uses a single global goroutine lazily initialized to call
// syscall.EpollWait() to listen to many file descriptors at once.
var events eventsListener

15
vendor/periph.io/x/host/v3/sysfs/fs_other.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
// Copyright 2017 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.
// +build !linux
package sysfs
type eventsListener struct {
}
// events is the global events listener.
//
// It is not used outside linux.
var events eventsListener

520
vendor/periph.io/x/host/v3/sysfs/gpio.go generated vendored Normal file
View File

@ -0,0 +1,520 @@
// 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"
"io"
"os"
"path/filepath"
"strconv"
"sync"
"time"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/driver/driverreg"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/pin"
"periph.io/x/host/v3/fs"
)
// Pins is all the pins exported by GPIO sysfs.
//
// Some CPU architectures have the pin numbers start at 0 and use consecutive
// pin numbers but this is not the case for all CPU architectures, some
// have gaps in the pin numbering.
//
// This global variable is initialized once at driver initialization and isn't
// mutated afterward. Do not modify it.
var Pins map[int]*Pin
// Pin represents one GPIO pin as found by sysfs.
type Pin struct {
number int
name string
root string // Something like /sys/class/gpio/gpio%d/
mu sync.Mutex
err error // If open() failed
direction direction // Cache of the last known direction
edge gpio.Edge // Cache of the last edge used.
fDirection fileIO // handle to /sys/class/gpio/gpio*/direction; never closed
fEdge fileIO // handle to /sys/class/gpio/gpio*/edge; never closed
fValue fileIO // handle to /sys/class/gpio/gpio*/value; never closed
event fs.Event // Initialized once
buf [4]byte // scratch buffer for Func(), Read() and Out()
}
// String implements conn.Resource.
func (p *Pin) String() string {
return p.name
}
// Halt implements conn.Resource.
//
// It stops edge detection if enabled.
func (p *Pin) Halt() error {
p.mu.Lock()
defer p.mu.Unlock()
return p.haltEdge()
}
// Name implements pin.Pin.
func (p *Pin) Name() string {
return p.name
}
// Number implements pin.Pin.
func (p *Pin) Number() int {
return p.number
}
// Function implements pin.Pin.
func (p *Pin) Function() string {
return string(p.Func())
}
// Func implements pin.PinFunc.
func (p *Pin) Func() pin.Func {
p.mu.Lock()
defer p.mu.Unlock()
// TODO(maruel): There's an internal bug which causes p.direction to be
// invalid (!?) Need to figure it out ASAP.
if err := p.open(); err != nil {
return pin.FuncNone
}
if _, err := seekRead(p.fDirection, p.buf[:]); err != nil {
return pin.FuncNone
}
if p.buf[0] == 'i' && p.buf[1] == 'n' {
p.direction = dIn
} else if p.buf[0] == 'o' && p.buf[1] == 'u' && p.buf[2] == 't' {
p.direction = dOut
}
if p.direction == dIn {
if p.Read() {
return gpio.IN_HIGH
}
return gpio.IN_LOW
} else if p.direction == dOut {
if p.Read() {
return gpio.OUT_HIGH
}
return gpio.OUT_LOW
}
return pin.FuncNone
}
// SupportedFuncs implements pin.PinFunc.
func (p *Pin) SupportedFuncs() []pin.Func {
return []pin.Func{gpio.IN, gpio.OUT}
}
// SetFunc implements pin.PinFunc.
func (p *Pin) SetFunc(f pin.Func) error {
switch f {
case gpio.IN:
return p.In(gpio.PullNoChange, gpio.NoEdge)
case gpio.OUT_HIGH:
return p.Out(gpio.High)
case gpio.OUT, gpio.OUT_LOW:
return p.Out(gpio.Low)
default:
return p.wrap(errors.New("unsupported function"))
}
}
// In implements gpio.PinIn.
func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error {
if pull != gpio.PullNoChange && pull != gpio.Float {
return p.wrap(errors.New("doesn't support pull-up/pull-down"))
}
p.mu.Lock()
defer p.mu.Unlock()
if p.direction != dIn {
if err := p.open(); err != nil {
return p.wrap(err)
}
if err := seekWrite(p.fDirection, bIn); err != nil {
return p.wrap(err)
}
p.direction = dIn
}
// Always push none to help accumulated flush edges. This is not fool proof
// but it seems to help.
if p.fEdge != nil {
if err := seekWrite(p.fEdge, bNone); err != nil {
return p.wrap(err)
}
}
// Assume that when the pin was switched, the driver doesn't recall if edge
// triggering was enabled.
if edge != gpio.NoEdge {
if p.fEdge == nil {
var err error
p.fEdge, err = fileIOOpen(p.root+"edge", os.O_RDWR)
if err != nil {
return p.wrap(err)
}
if err = p.event.MakeEvent(p.fValue.Fd()); err != nil {
_ = p.fEdge.Close()
p.fEdge = nil
return p.wrap(err)
}
}
// Always reset the edge detection mode to none after starting the epoll
// otherwise edges are not always delivered, as observed on an Allwinner A20
// running kernel 4.14.14.
if err := seekWrite(p.fEdge, bNone); err != nil {
return p.wrap(err)
}
var b []byte
switch edge {
case gpio.RisingEdge:
b = bRising
case gpio.FallingEdge:
b = bFalling
case gpio.BothEdges:
b = bBoth
}
if err := seekWrite(p.fEdge, b); err != nil {
return p.wrap(err)
}
}
p.edge = edge
// This helps to remove accumulated edges but this is not 100% sufficient.
// Most of the time the interrupts are handled promptly enough that this loop
// flushes the accumulated interrupt.
// Sometimes the kernel may have accumulated interrupts that haven't been
// processed for a long time, it can easily be >300µs even on a quite idle
// CPU. In this case, the loop below is not sufficient, since the interrupt
// will happen afterward "out of the blue".
if edge != gpio.NoEdge {
p.WaitForEdge(0)
}
return nil
}
// Read implements gpio.PinIn.
func (p *Pin) Read() gpio.Level {
// There's no lock here.
if p.fValue == nil {
return gpio.Low
}
if _, err := seekRead(p.fValue, p.buf[:]); err != nil {
// Error.
return gpio.Low
}
if p.buf[0] == '0' {
return gpio.Low
}
if p.buf[0] == '1' {
return gpio.High
}
// Error.
return gpio.Low
}
// WaitForEdge implements gpio.PinIn.
func (p *Pin) WaitForEdge(timeout time.Duration) bool {
// Run lockless, as the normal use is to call in a busy loop.
var ms int
if timeout == -1 {
ms = -1
} else {
ms = int(timeout / time.Millisecond)
}
start := time.Now()
for {
if nr, err := p.event.Wait(ms); err != nil {
return false
} else if nr == 1 {
// TODO(maruel): According to pigpio, the correct way to consume the
// interrupt is to call Seek().
return true
}
// A signal occurred.
if timeout != -1 {
ms = int((timeout - time.Since(start)) / time.Millisecond)
}
if ms <= 0 {
return false
}
}
}
// Pull implements gpio.PinIn.
//
// It returns gpio.PullNoChange since gpio sysfs has no support for input pull
// resistor.
func (p *Pin) Pull() gpio.Pull {
return gpio.PullNoChange
}
// DefaultPull implements gpio.PinIn.
//
// It returns gpio.PullNoChange since gpio sysfs has no support for input pull
// resistor.
func (p *Pin) DefaultPull() gpio.Pull {
return gpio.PullNoChange
}
// Out implements gpio.PinOut.
func (p *Pin) Out(l gpio.Level) error {
p.mu.Lock()
defer p.mu.Unlock()
if p.direction != dOut {
if err := p.open(); err != nil {
return p.wrap(err)
}
if err := p.haltEdge(); err != nil {
return err
}
// "To ensure glitch free operation, values "low" and "high" may be written
// to configure the GPIO as an output with that initial value."
var d []byte
if l == gpio.Low {
d = bLow
} else {
d = bHigh
}
if err := seekWrite(p.fDirection, d); err != nil {
return p.wrap(err)
}
p.direction = dOut
return nil
}
if l == gpio.Low {
p.buf[0] = '0'
} else {
p.buf[0] = '1'
}
if err := seekWrite(p.fValue, p.buf[:1]); err != nil {
return p.wrap(err)
}
return nil
}
// PWM implements gpio.PinOut.
//
// This is not supported on sysfs.
func (p *Pin) PWM(gpio.Duty, physic.Frequency) error {
return p.wrap(errors.New("pwm is not supported via sysfs"))
}
//
// open opens the gpio sysfs handle to /value and /direction.
//
// lock must be held.
func (p *Pin) open() error {
if p.fDirection != nil || p.err != nil {
return p.err
}
if drvGPIO.exportHandle == nil {
return errors.New("sysfs gpio is not initialized")
}
// Try to open the pin if it was there. It's possible it had been exported
// already.
if p.fValue, p.err = fileIOOpen(p.root+"value", os.O_RDWR); p.err == nil {
// Fast track.
goto direction
} else if !os.IsNotExist(p.err) {
// It exists but not accessible, not worth doing the remainder.
p.err = fmt.Errorf("need more access, try as root or setup udev rules: %v", p.err)
return p.err
}
if _, p.err = drvGPIO.exportHandle.Write([]byte(strconv.Itoa(p.number))); p.err != nil && !isErrBusy(p.err) {
if os.IsPermission(p.err) {
p.err = fmt.Errorf("need more access, try as root or setup udev rules: %v", p.err)
}
return p.err
}
// There's a race condition where the file may be created but udev is still
// running the Raspbian udev rule to make it readable to the current user.
// It's simpler to just loop a little as if /export is accessible, it doesn't
// make sense that gpioN/value doesn't become accessible eventually.
for start := time.Now(); time.Since(start) < 5*time.Second; {
// The virtual file creation is synchronous when writing to /export; albeit
// udev rule execution is asynchronous, so file mode change via udev rules
// takes some time to propagate.
if p.fValue, p.err = fileIOOpen(p.root+"value", os.O_RDWR); p.err == nil || !os.IsPermission(p.err) {
// Either success or a failure that is not a permission error.
break
}
}
if p.err != nil {
return p.err
}
direction:
if p.fDirection, p.err = fileIOOpen(p.root+"direction", os.O_RDWR); p.err != nil {
_ = p.fValue.Close()
p.fValue = nil
}
return p.err
}
// haltEdge stops any on-going edge detection.
func (p *Pin) haltEdge() error {
if p.edge != gpio.NoEdge {
if err := seekWrite(p.fEdge, bNone); err != nil {
return p.wrap(err)
}
p.edge = gpio.NoEdge
// This is still important to remove an accumulated edge.
p.WaitForEdge(0)
}
return nil
}
func (p *Pin) wrap(err error) error {
return fmt.Errorf("sysfs-gpio (%s): %v", p, err)
}
//
type direction int
const (
dUnknown direction = 0
dIn direction = 1
dOut direction = 2
)
var (
bIn = []byte("in")
bLow = []byte("low")
bHigh = []byte("high")
bNone = []byte("none")
bRising = []byte("rising")
bFalling = []byte("falling")
bBoth = []byte("both")
)
// readInt reads a pseudo-file (sysfs) that is known to contain an integer and
// returns the parsed number.
func readInt(path string) (int, error) {
f, err := fileIOOpen(path, os.O_RDONLY)
if err != nil {
return 0, err
}
defer f.Close()
var b [24]byte
n, err := f.Read(b[:])
if err != nil {
return 0, err
}
raw := b[:n]
if len(raw) == 0 || raw[len(raw)-1] != '\n' {
return 0, errors.New("invalid value")
}
return strconv.Atoi(string(raw[:len(raw)-1]))
}
// driverGPIO implements periph.Driver.
type driverGPIO struct {
exportHandle io.Writer // handle to /sys/class/gpio/export
}
func (d *driverGPIO) String() string {
return "sysfs-gpio"
}
func (d *driverGPIO) Prerequisites() []string {
return nil
}
func (d *driverGPIO) After() []string {
return nil
}
// Init initializes GPIO sysfs handling code.
//
// Uses gpio sysfs as described at
// https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
//
// GPIO sysfs is often the only way to do edge triggered interrupts. Doing this
// requires cooperation from a driver in the kernel.
//
// The main drawback of GPIO sysfs is that it doesn't expose internal pull
// resistor and it is much slower than using memory mapped hardware registers.
func (d *driverGPIO) Init() (bool, error) {
items, err := filepath.Glob("/sys/class/gpio/gpiochip*")
if err != nil {
return true, err
}
if len(items) == 0 {
return false, errors.New("no GPIO pin found")
}
// There are hosts that use non-continuous pin numbering so use a map instead
// of an array.
Pins = map[int]*Pin{}
for _, item := range items {
if err = d.parseGPIOChip(item + "/"); err != nil {
return true, err
}
}
drvGPIO.exportHandle, err = fileIOOpen("/sys/class/gpio/export", os.O_WRONLY)
if os.IsPermission(err) {
return true, fmt.Errorf("need more access, try as root or setup udev rules: %v", err)
}
return true, err
}
func (d *driverGPIO) parseGPIOChip(path string) error {
base, err := readInt(path + "base")
if err != nil {
return err
}
number, err := readInt(path + "ngpio")
if err != nil {
return err
}
// TODO(maruel): The chip driver may lie and lists GPIO pins that cannot be
// exported. The only way to know about it is to export it before opening.
for i := base; i < base+number; i++ {
if _, ok := Pins[i]; ok {
return fmt.Errorf("found two pins with number %d", i)
}
p := &Pin{
number: i,
name: fmt.Sprintf("GPIO%d", i),
root: fmt.Sprintf("/sys/class/gpio/gpio%d/", i),
}
Pins[i] = p
if err := gpioreg.Register(p); err != nil {
return err
}
// If there is a CPU memory mapped gpio pin with the same number, the
// driver has to unregister this pin and map its own after.
if err := gpioreg.RegisterAlias(strconv.Itoa(i), p.name); err != nil {
return err
}
}
return nil
}
func init() {
if isLinux {
driverreg.MustRegister(&drvGPIO)
}
}
var drvGPIO driverGPIO
var _ conn.Resource = &Pin{}
var _ gpio.PinIn = &Pin{}
var _ gpio.PinOut = &Pin{}
var _ gpio.PinIO = &Pin{}
var _ pin.PinFunc = &Pin{}

388
vendor/periph.io/x/host/v3/sysfs/i2c.go generated vendored Normal file
View File

@ -0,0 +1,388 @@
// 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"
"strings"
"sync"
"unsafe"
"periph.io/x/conn/v3/driver/driverreg"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/i2c/i2creg"
"periph.io/x/conn/v3/physic"
)
// I2CSetSpeedHook can be set by a driver to enable changing the I²C buses
// speed.
func I2CSetSpeedHook(h func(f physic.Frequency) error) error {
if h == nil {
return errors.New("sysfs-i2c: hook must not be nil")
}
drvI2C.mu.Lock()
defer drvI2C.mu.Unlock()
if drvI2C.setSpeed != nil {
return errors.New("sysfs-i2c: a speed hook was already set")
}
drvI2C.setSpeed = h
return nil
}
// NewI2C opens an I²C bus via its sysfs interface as described at
// https://www.kernel.org/doc/Documentation/i2c/dev-interface.
//
// busNumber is the bus number as exported by sysfs. For example if the path is
// /dev/i2c-1, busNumber should be 1.
//
// The resulting object is safe for concurent use.
//
// Do not use sysfs.NewI2C() directly as the package sysfs is providing a
// https://periph.io/x/conn/v3/i2c Linux-specific implementation.
//
// periph.io works on many OSes!
//
// Instead, use https://periph.io/x/conn/v3/i2c/i2creg#Open. This permits
// it to work on all operating systems, or devices like I²C over USB.
func NewI2C(busNumber int) (*I2C, error) {
if isLinux {
return newI2C(busNumber)
}
return nil, errors.New("sysfs-i2c: is not supported on this platform")
}
// I2C is an open I²C bus via sysfs.
//
// It can be used to communicate with multiple devices from multiple goroutines.
type I2C struct {
f ioctlCloser
busNumber int
mu sync.Mutex // In theory the kernel probably has an internal lock but not taking any chance.
fn functionality
scl gpio.PinIO
sda gpio.PinIO
}
// Close closes the handle to the I²C driver. It is not a requirement to close
// before process termination.
func (i *I2C) Close() error {
i.mu.Lock()
defer i.mu.Unlock()
if err := i.f.Close(); err != nil {
return fmt.Errorf("sysfs-i2c: %v", err)
}
return nil
}
func (i *I2C) String() string {
return fmt.Sprintf("I2C%d", i.busNumber)
}
// Tx execute a transaction as a single operation unit.
func (i *I2C) Tx(addr uint16, w, r []byte) error {
if addr >= 0x400 || (addr >= 0x80 && i.fn&func10BitAddr == 0) {
return errors.New("sysfs-i2c: invalid address")
}
if len(w) == 0 && len(r) == 0 {
return nil
}
// Convert the messages to the internal format.
var buf [2]i2cMsg
msgs := buf[0:0]
if len(w) != 0 {
msgs = buf[:1]
buf[0].addr = addr
buf[0].length = uint16(len(w))
buf[0].buf = uintptr(unsafe.Pointer(&w[0]))
}
if len(r) != 0 {
l := len(msgs)
msgs = msgs[:l+1] // extend the slice by one
buf[l].addr = addr
buf[l].flags = flagRD
buf[l].length = uint16(len(r))
buf[l].buf = uintptr(unsafe.Pointer(&r[0]))
}
p := rdwrIoctlData{
msgs: uintptr(unsafe.Pointer(&msgs[0])),
nmsgs: uint32(len(msgs)),
}
pp := uintptr(unsafe.Pointer(&p))
i.mu.Lock()
defer i.mu.Unlock()
if err := i.f.Ioctl(ioctlRdwr, pp); err != nil {
return fmt.Errorf("sysfs-i2c: %v", err)
}
return nil
}
// SetSpeed implements i2c.Bus.
func (i *I2C) SetSpeed(f physic.Frequency) error {
if f > 100*physic.MegaHertz {
return fmt.Errorf("sysfs-i2c: invalid speed %s; maximum supported clock is 100MHz", f)
}
if f < physic.KiloHertz {
return fmt.Errorf("sysfs-i2c: invalid speed %s; minimum supported clock is 1KHz; did you forget to multiply by physic.KiloHertz?", f)
}
drvI2C.mu.Lock()
defer drvI2C.mu.Unlock()
if drvI2C.setSpeed != nil {
return drvI2C.setSpeed(f)
}
return errors.New("sysfs-i2c: not supported")
}
// SCL implements i2c.Pins.
func (i *I2C) SCL() gpio.PinIO {
i.initPins()
return i.scl
}
// SDA implements i2c.Pins.
func (i *I2C) SDA() gpio.PinIO {
i.initPins()
return i.sda
}
// Private details.
func newI2C(busNumber int) (*I2C, error) {
// Use the devfs path for now instead of sysfs path.
f, err := ioctlOpen(fmt.Sprintf("/dev/i2c-%d", busNumber), os.O_RDWR)
if err != nil {
// Try to be helpful here. There are generally two cases:
// - /dev/i2c-X doesn't exist. In this case, /boot/config.txt has to be
// edited to enable I²C then the device must be rebooted.
// - permission denied. In this case, the user has to be added to plugdev.
if os.IsNotExist(err) {
return nil, fmt.Errorf("sysfs-i2c: bus #%d is not configured: %v", busNumber, err)
}
// TODO(maruel): This is a debianism.
return nil, fmt.Errorf("sysfs-i2c: are you member of group 'plugdev'? %v", err)
}
i := &I2C{f: f, busNumber: busNumber}
// TODO(maruel): Changing the speed is currently doing this for all devices.
// https://github.com/raspberrypi/linux/issues/215
// Need to access /sys/module/i2c_bcm2708/parameters/baudrate
// Query to know if 10 bits addresses are supported.
if err = i.f.Ioctl(ioctlFuncs, uintptr(unsafe.Pointer(&i.fn))); err != nil {
return nil, fmt.Errorf("sysfs-i2c: %v", err)
}
return i, nil
}
func (i *I2C) initPins() {
i.mu.Lock()
if i.scl == nil {
if i.scl = gpioreg.ByName(fmt.Sprintf("I2C%d_SCL", i.busNumber)); i.scl == nil {
i.scl = gpio.INVALID
}
if i.sda = gpioreg.ByName(fmt.Sprintf("I2C%d_SDA", i.busNumber)); i.sda == nil {
i.sda = gpio.INVALID
}
}
i.mu.Unlock()
}
// i2cdev driver IOCTL control codes.
//
// Constants and structure definition can be found at
// /usr/include/linux/i2c-dev.h and /usr/include/linux/i2c.h.
const (
ioctlRetries = 0x701 // TODO(maruel): Expose this
ioctlTimeout = 0x702 // TODO(maruel): Expose this; in units of 10ms
ioctlSlave = 0x703
ioctlTenBits = 0x704 // TODO(maruel): Expose this but the header says it's broken (!?)
ioctlFuncs = 0x705
ioctlRdwr = 0x707
)
// flags
const (
flagTEN = 0x0010 // this is a ten bit chip address
flagRD = 0x0001 // read data, from slave to master
flagSTOP = 0x8000 // if funcProtocolMangling
flagNOSTART = 0x4000 // if I2C_FUNC_NOSTART
flagRevDirAddr = 0x2000 // if funcProtocolMangling
flagIgnoreNAK = 0x1000 // if funcProtocolMangling
flagNoRDACK = 0x0800 // if funcProtocolMangling
flagRecvLen = 0x0400 // length will be first received byte
)
type functionality uint64
const (
funcI2C = 0x00000001
func10BitAddr = 0x00000002
funcProtocolMangling = 0x00000004 // I2C_M_IGNORE_NAK etc.
funcSMBusPEC = 0x00000008
funcNOSTART = 0x00000010 // I2C_M_NOSTART
funcSMBusBlockProcCall = 0x00008000 // SMBus 2.0
funcSMBusQuick = 0x00010000
funcSMBusReadByte = 0x00020000
funcSMBusWriteByte = 0x00040000
funcSMBusReadByteData = 0x00080000
funcSMBusWriteByteData = 0x00100000
funcSMBusReadWordData = 0x00200000
funcSMBusWriteWordData = 0x00400000
funcSMBusProcCall = 0x00800000
funcSMBusReadBlockData = 0x01000000
funcSMBusWriteBlockData = 0x02000000
funcSMBusReadI2CBlock = 0x04000000 // I2C-like block xfer
funcSMBusWriteI2CBlock = 0x08000000 // w/ 1-byte reg. addr.
)
func (f functionality) String() string {
var out []string
if f&funcI2C != 0 {
out = append(out, "I2C")
}
if f&func10BitAddr != 0 {
out = append(out, "10BIT_ADDR")
}
if f&funcProtocolMangling != 0 {
out = append(out, "PROTOCOL_MANGLING")
}
if f&funcSMBusPEC != 0 {
out = append(out, "SMBUS_PEC")
}
if f&funcNOSTART != 0 {
out = append(out, "NOSTART")
}
if f&funcSMBusBlockProcCall != 0 {
out = append(out, "SMBUS_BLOCK_PROC_CALL")
}
if f&funcSMBusQuick != 0 {
out = append(out, "SMBUS_QUICK")
}
if f&funcSMBusReadByte != 0 {
out = append(out, "SMBUS_READ_BYTE")
}
if f&funcSMBusWriteByte != 0 {
out = append(out, "SMBUS_WRITE_BYTE")
}
if f&funcSMBusReadByteData != 0 {
out = append(out, "SMBUS_READ_BYTE_DATA")
}
if f&funcSMBusWriteByteData != 0 {
out = append(out, "SMBUS_WRITE_BYTE_DATA")
}
if f&funcSMBusReadWordData != 0 {
out = append(out, "SMBUS_READ_WORD_DATA")
}
if f&funcSMBusWriteWordData != 0 {
out = append(out, "SMBUS_WRITE_WORD_DATA")
}
if f&funcSMBusProcCall != 0 {
out = append(out, "SMBUS_PROC_CALL")
}
if f&funcSMBusReadBlockData != 0 {
out = append(out, "SMBUS_READ_BLOCK_DATA")
}
if f&funcSMBusWriteBlockData != 0 {
out = append(out, "SMBUS_WRITE_BLOCK_DATA")
}
if f&funcSMBusReadI2CBlock != 0 {
out = append(out, "SMBUS_READ_I2C_BLOCK")
}
if f&funcSMBusWriteI2CBlock != 0 {
out = append(out, "SMBUS_WRITE_I2C_BLOCK")
}
return strings.Join(out, "|")
}
type rdwrIoctlData struct {
msgs uintptr // Pointer to i2cMsg
nmsgs uint32
}
type i2cMsg struct {
addr uint16 // Address to communicate with
flags uint16 // 1 for read, see i2c.h for more details
length uint16
buf uintptr
}
//
// driverI2C implements periph.Driver.
type driverI2C struct {
mu sync.Mutex
buses []string
setSpeed func(f physic.Frequency) error
}
func (d *driverI2C) String() string {
return "sysfs-i2c"
}
func (d *driverI2C) Prerequisites() []string {
return nil
}
func (d *driverI2C) After() []string {
return nil
}
func (d *driverI2C) Init() (bool, error) {
// Do not use "/sys/bus/i2c/devices/i2c-" as Raspbian's provided udev rules
// only modify the ACL of /dev/i2c-* but not the ones in /sys/bus/...
prefix := "/dev/i2c-"
items, err := filepath.Glob(prefix + "*")
if err != nil {
return true, err
}
if len(items) == 0 {
return false, errors.New("no I²C bus found")
}
// Make sure they are registered in order.
sort.Strings(items)
for _, item := range items {
bus, err := strconv.Atoi(item[len(prefix):])
if err != nil {
continue
}
name := fmt.Sprintf("/dev/i2c-%d", bus)
d.buses = append(d.buses, name)
aliases := []string{fmt.Sprintf("I2C%d", bus)}
if err := i2creg.Register(name, aliases, bus, openerI2C(bus).Open); err != nil {
return true, err
}
}
return true, nil
}
type openerI2C int
func (o openerI2C) Open() (i2c.BusCloser, error) {
b, err := NewI2C(int(o))
if err != nil {
return nil, err
}
return b, nil
}
func init() {
if isLinux {
driverreg.MustRegister(&drvI2C)
}
}
var drvI2C driverI2C
var _ i2c.Bus = &I2C{}
var _ i2c.BusCloser = &I2C{}

257
vendor/periph.io/x/host/v3/sysfs/led.go generated vendored Normal file
View File

@ -0,0 +1,257 @@
// 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/conn/v3"
"periph.io/x/conn/v3/driver/driverreg"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/pin"
"periph.io/x/host/v3/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 {
return string(l.Func())
}
// Func implements pin.PinFunc.
func (l *LED) Func() pin.Func {
if l.Read() {
return "LED/On"
}
return "LED/Off"
}
// SupportedFuncs implements pin.PinFunc.
func (l *LED) SupportedFuncs() []pin.Func {
return []pin.Func{"LED"}
}
// SetFunc implements pin.PinFunc.
func (l *LED) SetFunc(f pin.Func) error {
return errors.New("sysfs-led: not implemented")
}
// 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 {
driverreg.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{}

622
vendor/periph.io/x/host/v3/sysfs/spi.go generated vendored Normal file
View File

@ -0,0 +1,622 @@
// 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"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"unsafe"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/driver/driverreg"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/spi"
"periph.io/x/conn/v3/spi/spireg"
"periph.io/x/host/v3/fs"
)
// NewSPI opens a SPI port via its devfs interface as described at
// https://www.kernel.org/doc/Documentation/spi/spidev and
// https://www.kernel.org/doc/Documentation/spi/spi-summary
//
// The resulting object is safe for concurrent use.
//
// busNumber is the bus number as exported by devfs. For example if the path is
// /dev/spidev0.1, busNumber should be 0 and chipSelect should be 1.
//
// It is recommended to use https://periph.io/x/conn/v3/spi/spireg#Open
// instead of using NewSPI() directly as the package sysfs is providing a
// Linux-specific implementation. periph.io works on many OSes! This permits
// it to work on all operating systems, or devices like SPI over USB.
func NewSPI(busNumber, chipSelect int) (*SPI, error) {
if isLinux {
return newSPI(busNumber, chipSelect)
}
return nil, errors.New("sysfs-spi: not implemented on non-linux OSes")
}
// SPI is an open SPI port.
type SPI struct {
conn spiConn
}
// Close closes the handle to the SPI driver. It is not a requirement to close
// before process termination.
//
// Note that the object is not reusable afterward.
func (s *SPI) Close() error {
s.conn.mu.Lock()
defer s.conn.mu.Unlock()
if err := s.conn.f.Close(); err != nil {
return fmt.Errorf("sysfs-spi: %v", err)
}
s.conn.f = nil
return nil
}
func (s *SPI) String() string {
return s.conn.String()
}
// LimitSpeed implements spi.ConnCloser.
func (s *SPI) LimitSpeed(f physic.Frequency) error {
if f > physic.GigaHertz {
return fmt.Errorf("sysfs-spi: invalid speed %s; maximum supported clock is 1GHz", f)
}
if f < 100*physic.Hertz {
return fmt.Errorf("sysfs-spi: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?", f)
}
s.conn.mu.Lock()
defer s.conn.mu.Unlock()
s.conn.freqPort = f
return nil
}
// Connect implements spi.Port.
//
// It must be called before any I/O.
func (s *SPI) Connect(f physic.Frequency, mode spi.Mode, bits int) (spi.Conn, error) {
if f > physic.GigaHertz {
return nil, fmt.Errorf("sysfs-spi: invalid speed %s; maximum supported clock is 1GHz", f)
}
if f < 100*physic.Hertz {
return nil, fmt.Errorf("sysfs-spi: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?", f)
}
if mode&^(spi.Mode3|spi.HalfDuplex|spi.NoCS|spi.LSBFirst) != 0 {
return nil, fmt.Errorf("sysfs-spi: invalid mode %v", mode)
}
if bits < 1 || bits >= 256 {
return nil, fmt.Errorf("sysfs-spi: invalid bits %d", bits)
}
s.conn.mu.Lock()
defer s.conn.mu.Unlock()
if s.conn.connected {
return nil, errors.New("sysfs-spi: Connect() can only be called exactly once")
}
s.conn.connected = true
s.conn.freqConn = f
s.conn.bitsPerWord = uint8(bits)
// Only mode needs to be set via an IOCTL, others can be specified in the
// spiIOCTransfer packet, which saves a kernel call.
m := mode & spi.Mode3
s.conn.muPins.Lock()
{
if mode&spi.HalfDuplex != 0 {
m |= threeWire
s.conn.halfDuplex = true
// In case initPins() had been called before Connect().
s.conn.mosi = gpio.INVALID
}
if mode&spi.NoCS != 0 {
m |= noCS
s.conn.noCS = true
// In case initPins() had been called before Connect().
s.conn.cs = gpio.INVALID
}
}
s.conn.muPins.Unlock()
if mode&spi.LSBFirst != 0 {
m |= lSBFirst
}
// Only the first 8 bits are used. This only works because the system is
// running in little endian.
if err := s.conn.setFlag(spiIOCMode, uint64(m)); err != nil {
return nil, fmt.Errorf("sysfs-spi: setting mode %v failed: %v", mode, err)
}
return &s.conn, nil
}
// MaxTxSize implements conn.Limits
func (s *SPI) MaxTxSize() int {
return drvSPI.bufSize
}
// CLK implements spi.Pins.
func (s *SPI) CLK() gpio.PinOut {
return s.conn.CLK()
}
// MISO implements spi.Pins.
func (s *SPI) MISO() gpio.PinIn {
return s.conn.MISO()
}
// MOSI implements spi.Pins.
func (s *SPI) MOSI() gpio.PinOut {
return s.conn.MOSI()
}
// CS implements spi.Pins.
func (s *SPI) CS() gpio.PinOut {
return s.conn.CS()
}
// Private details.
func newSPI(busNumber, chipSelect int) (*SPI, error) {
if busNumber < 0 || busNumber >= 1<<16 {
return nil, fmt.Errorf("sysfs-spi: invalid bus %d", busNumber)
}
if chipSelect < 0 || chipSelect > 255 {
return nil, fmt.Errorf("sysfs-spi: invalid chip select %d", chipSelect)
}
// Use the devfs path for now.
f, err := ioctlOpen(fmt.Sprintf("/dev/spidev%d.%d", busNumber, chipSelect), os.O_RDWR)
if err != nil {
return nil, fmt.Errorf("sysfs-spi: %v", err)
}
return &SPI{
spiConn{
name: fmt.Sprintf("SPI%d.%d", busNumber, chipSelect),
f: f,
busNumber: busNumber,
chipSelect: chipSelect,
},
}, nil
}
//
// spiConn implements spi.Conn.
type spiConn struct {
// Immutable
name string
f ioctlCloser
busNumber int
chipSelect int
mu sync.Mutex
freqPort physic.Frequency // Frequency specified at LimitSpeed()
freqConn physic.Frequency // Frequency specified at Connect()
bitsPerWord uint8
connected bool
halfDuplex bool
noCS bool
// Heap optimization: reduce the amount of memory allocations during
// transactions.
io [4]spiIOCTransfer
p [2]spi.Packet
// Use a separate lock for the pins, so that they can be queried while a
// transaction is happening.
muPins sync.Mutex
clk gpio.PinOut
mosi gpio.PinOut
miso gpio.PinIn
cs gpio.PinOut
}
func (s *spiConn) String() string {
return s.name
}
// Read implements io.Reader.
func (s *spiConn) Read(b []byte) (int, error) {
if len(b) == 0 {
return 0, errors.New("sysfs-spi: Read() with empty buffer")
}
if drvSPI.bufSize != 0 && len(b) > drvSPI.bufSize {
return 0, fmt.Errorf("sysfs-spi: maximum Read length is %d, got %d bytes", drvSPI.bufSize, len(b))
}
s.mu.Lock()
defer s.mu.Unlock()
s.p[0].W = nil
s.p[0].R = b
if err := s.txPackets(s.p[:1]); err != nil {
return 0, fmt.Errorf("sysfs-spi: Read() failed: %v", err)
}
return len(b), nil
}
// Write implements io.Writer.
func (s *spiConn) Write(b []byte) (int, error) {
if len(b) == 0 {
return 0, errors.New("sysfs-spi: Write() with empty buffer")
}
if drvSPI.bufSize != 0 && len(b) > drvSPI.bufSize {
return 0, fmt.Errorf("sysfs-spi: maximum Write length is %d, got %d bytes", drvSPI.bufSize, len(b))
}
s.mu.Lock()
defer s.mu.Unlock()
s.p[0].W = b
s.p[0].R = nil
if err := s.txPackets(s.p[:1]); err != nil {
return 0, fmt.Errorf("sysfs-spi: Write() failed: %v", err)
}
return len(b), nil
}
// Tx sends and receives data simultaneously.
//
// It is OK if both w and r point to the same underlying byte slice.
//
// spidev enforces the maximum limit of transaction size. It can be as low as
// 4096 bytes. See the platform documentation to learn how to increase the
// limit.
func (s *spiConn) Tx(w, r []byte) error {
l := len(w)
if l == 0 {
if l = len(r); l == 0 {
return errors.New("sysfs-spi: Tx() with empty buffers")
}
} else {
// It's not a big deal to read halfDuplex without the lock.
if !s.halfDuplex && len(r) != 0 && len(r) != len(w) {
return fmt.Errorf("sysfs-spi: Tx(): when both w and r are used, they must be the same size; got %d and %d bytes", len(w), len(r))
}
}
if drvSPI.bufSize != 0 && l > drvSPI.bufSize {
return fmt.Errorf("sysfs-spi: maximum Tx length is %d, got %d bytes", drvSPI.bufSize, l)
}
s.mu.Lock()
defer s.mu.Unlock()
s.p[0].W = w
s.p[0].R = r
p := s.p[:1]
if s.halfDuplex && len(w) != 0 && len(r) != 0 {
// Create two packets for HalfDuplex operation: one write then one read.
s.p[0].R = nil
s.p[0].KeepCS = true
s.p[1].W = nil
s.p[1].R = r
s.p[1].KeepCS = false
p = s.p[:2]
} else {
s.p[0].KeepCS = false
}
if err := s.txPackets(p); err != nil {
return fmt.Errorf("sysfs-spi: Tx() failed: %v", err)
}
return nil
}
// TxPackets sends and receives packets as specified by the user.
//
// spidev enforces the maximum limit of transaction size. It can be as low as
// 4096 bytes. See the platform documentation to learn how to increase the
// limit.
func (s *spiConn) TxPackets(p []spi.Packet) error {
total := 0
for i := range p {
lW := len(p[i].W)
lR := len(p[i].R)
if lW != lR && lW != 0 && lR != 0 {
return fmt.Errorf("sysfs-spi: when both w and r are used, they must be the same size; got %d and %d bytes", lW, lR)
}
l := lW
if l == 0 {
l = lR
}
total += l
}
if total == 0 {
return errors.New("sysfs-spi: empty packets")
}
if drvSPI.bufSize != 0 && total > drvSPI.bufSize {
return fmt.Errorf("sysfs-spi: maximum TxPackets length is %d, got %d bytes", drvSPI.bufSize, total)
}
s.mu.Lock()
defer s.mu.Unlock()
if s.halfDuplex {
for i := range p {
if len(p[i].W) != 0 && len(p[i].R) != 0 {
return errors.New("sysfs-spi: can only specify one of w or r when in half duplex")
}
}
}
if err := s.txPackets(p); err != nil {
return fmt.Errorf("sysfs-spi: TxPackets() failed: %v", err)
}
return nil
}
// Duplex implements conn.Conn.
func (s *spiConn) Duplex() conn.Duplex {
if s.halfDuplex {
return conn.Half
}
return conn.Full
}
// MaxTxSize implements conn.Limits.
func (s *spiConn) MaxTxSize() int {
return drvSPI.bufSize
}
// CLK implements spi.Pins.
func (s *spiConn) CLK() gpio.PinOut {
s.initPins()
return s.clk
}
// MISO implements spi.Pins.
func (s *spiConn) MISO() gpio.PinIn {
s.initPins()
return s.miso
}
// MOSI implements spi.Pins.
func (s *spiConn) MOSI() gpio.PinOut {
s.initPins()
return s.mosi
}
// CS implements spi.Pins.
func (s *spiConn) CS() gpio.PinOut {
s.initPins()
return s.cs
}
//
func (s *spiConn) txPackets(p []spi.Packet) error {
// Convert the packets.
f := s.freqPort
if s.freqConn != 0 && (s.freqPort == 0 || s.freqConn < s.freqPort) {
f = s.freqConn
}
var m []spiIOCTransfer
if len(p) > len(s.io) {
m = make([]spiIOCTransfer, len(p))
} else {
m = s.io[:len(p)]
}
for i := range p {
bits := p[i].BitsPerWord
if bits == 0 {
bits = s.bitsPerWord
}
csInvert := false
if !s.noCS {
// Invert CS behavior when a packet has KeepCS false, except for the last
// packet when KeepCS is true.
last := i == len(p)-1
csInvert = p[i].KeepCS == last
}
m[i].reset(p[i].W, p[i].R, f, bits, csInvert)
}
return s.f.Ioctl(spiIOCTx(len(m)), uintptr(unsafe.Pointer(&m[0])))
}
func (s *spiConn) setFlag(op uint, arg uint64) error {
return s.f.Ioctl(op, uintptr(unsafe.Pointer(&arg)))
}
// GetFlag allows to read back flags set via a ioctl, i.e. setFlag. It is
// exported to allow calling it from the smoke test.
func (s *spiConn) GetFlag(op uint) (arg uint64, err error) {
err = s.f.Ioctl(op, uintptr(unsafe.Pointer(&arg)))
return
}
func (s *spiConn) initPins() {
s.muPins.Lock()
defer s.muPins.Unlock()
if s.clk != nil {
return
}
if s.clk = gpioreg.ByName(fmt.Sprintf("SPI%d_CLK", s.busNumber)); s.clk == nil {
s.clk = gpio.INVALID
}
if s.miso = gpioreg.ByName(fmt.Sprintf("SPI%d_MISO", s.busNumber)); s.miso == nil {
s.miso = gpio.INVALID
}
// s.mosi is set to INVALID if HalfDuplex was specified.
if s.mosi != gpio.INVALID {
if s.mosi = gpioreg.ByName(fmt.Sprintf("SPI%d_MOSI", s.busNumber)); s.mosi == nil {
s.mosi = gpio.INVALID
}
}
// s.cs is set to INVALID if NoCS was specified.
if s.cs != gpio.INVALID {
if s.cs = gpioreg.ByName(fmt.Sprintf("SPI%d_CS%d", s.busNumber, s.chipSelect)); s.cs == nil {
s.cs = gpio.INVALID
}
}
}
const (
cSHigh spi.Mode = 0x4 // CS active high instead of default low (not recommended)
lSBFirst spi.Mode = 0x8 // Use little endian encoding for each word
threeWire spi.Mode = 0x10 // half-duplex; MOSI and MISO are shared
loop spi.Mode = 0x20 // loopback mode
noCS spi.Mode = 0x40 // do not assert CS
ready spi.Mode = 0x80 // slave pulls low to pause
// The driver optionally support dual and quad data lines.
)
// spidev driver IOCTL control codes.
//
// Constants and structure definition can be found at
// /usr/include/linux/spi/spidev.h.
const spiIOCMagic uint = 'k'
var (
spiIOCMode = fs.IOW(spiIOCMagic, 1, 1) // SPI_IOC_WR_MODE (8 bits)
spiIOLSBFirst = fs.IOW(spiIOCMagic, 2, 1) // SPI_IOC_WR_LSB_FIRST
spiIOCBitsPerWord = fs.IOW(spiIOCMagic, 3, 1) // SPI_IOC_WR_BITS_PER_WORD
spiIOCMaxSpeedHz = fs.IOW(spiIOCMagic, 4, 4) // SPI_IOC_WR_MAX_SPEED_HZ
spiIOCMode32 = fs.IOW(spiIOCMagic, 5, 4) // SPI_IOC_WR_MODE32 (32 bits)
)
// spiIOCTx(l) calculates the equivalent of SPI_IOC_MESSAGE(l) to execute a
// transaction.
func spiIOCTx(l int) uint {
return fs.IOW(spiIOCMagic, 0, uint(l)*32)
}
// spiIOCTransfer is spi_ioc_transfer in linux/spi/spidev.h.
//
// Also documented as struct spi_transfer at
// https://www.kernel.org/doc/html/latest/driver-api/spi.html
type spiIOCTransfer struct {
tx uint64 // Pointer to byte slice
rx uint64 // Pointer to byte slice
length uint32 // buffer length of tx and rx in bytes
speedHz uint32 // temporarily override the speed
delayUsecs uint16 // µs to sleep before selecting the device before the next transfer
bitsPerWord uint8 // temporarily override the number of bytes per word
csChange uint8 // true to deassert CS before next transfer
txNBits uint8
rxNBits uint8
pad uint16
}
func (s *spiIOCTransfer) reset(w, r []byte, f physic.Frequency, bitsPerWord uint8, csInvert bool) {
s.tx = 0
s.rx = 0
s.length = 0
// w and r must be the same length.
if l := len(w); l != 0 {
s.tx = uint64(uintptr(unsafe.Pointer(&w[0])))
s.length = uint32(l)
}
if l := len(r); l != 0 {
s.rx = uint64(uintptr(unsafe.Pointer(&r[0])))
s.length = uint32(l)
}
s.speedHz = uint32((f + 500*physic.MilliHertz) / physic.Hertz)
s.delayUsecs = 0
s.bitsPerWord = bitsPerWord
if csInvert {
s.csChange = 1
} else {
s.csChange = 0
}
s.txNBits = 0
s.rxNBits = 0
s.pad = 0
}
//
// driverSPI implements periph.Driver.
type driverSPI struct {
// bufSize is the maximum number of bytes allowed per I/O on the SPI port.
bufSize int
}
func (d *driverSPI) String() string {
return "sysfs-spi"
}
func (d *driverSPI) Prerequisites() []string {
return nil
}
func (d *driverSPI) After() []string {
return nil
}
func (d *driverSPI) Init() (bool, error) {
// This driver is only registered on linux, so there is no legitimate time to
// skip it.
// Do not use "/sys/bus/spi/devices/spi" as Raspbian's provided udev rules
// only modify the ACL of /dev/spidev* but not the ones in /sys/bus/...
prefix := "/dev/spidev"
items, err2 := filepath.Glob(prefix + "*")
if err2 != nil {
return true, err2
}
if len(items) == 0 {
return false, errors.New("no SPI port found")
}
sort.Strings(items)
for _, item := range items {
parts := strings.Split(item[len(prefix):], ".")
if len(parts) != 2 {
continue
}
bus, err := strconv.Atoi(parts[0])
if err != nil {
continue
}
cs, err := strconv.Atoi(parts[1])
if err != nil {
continue
}
name := fmt.Sprintf("/dev/spidev%d.%d", bus, cs)
aliases := []string{fmt.Sprintf("SPI%d.%d", bus, cs)}
n := bus
if cs != 0 {
n = -1
}
if err := spireg.Register(name, aliases, n, (&openerSPI{bus, cs}).Open); err != nil {
return true, err
}
}
f, err := fs.Open("/sys/module/spidev/parameters/bufsiz", os.O_RDONLY)
if err != nil {
return true, err
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return true, err
}
// Update the global value.
drvSPI.bufSize, err = strconv.Atoi(strings.TrimSpace(string(b)))
return true, err
}
type openerSPI struct {
bus int
cs int
}
func (o *openerSPI) Open() (spi.PortCloser, error) {
return NewSPI(o.bus, o.cs)
}
func init() {
if isLinux {
driverreg.MustRegister(&drvSPI)
}
}
var drvSPI driverSPI
var _ conn.Limits = &SPI{}
var _ conn.Limits = &spiConn{}
var _ io.Reader = &spiConn{}
var _ io.Writer = &spiConn{}
var _ spi.Conn = &spiConn{}
var _ spi.Pins = &SPI{}
var _ spi.Pins = &spiConn{}
var _ spi.Port = &SPI{}
var _ spi.PortCloser = &SPI{}
var _ fmt.Stringer = &SPI{}

62
vendor/periph.io/x/host/v3/sysfs/sysfs.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
// Copyright 2017 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 (
"io"
"periph.io/x/host/v3/fs"
)
var ioctlOpen = ioctlOpenDefault
func ioctlOpenDefault(path string, flag int) (ioctlCloser, error) {
f, err := fs.Open(path, flag)
if err != nil {
return nil, err
}
return f, nil
}
var fileIOOpen = fileIOOpenDefault
func fileIOOpenDefault(path string, flag int) (fileIO, error) {
f, err := fs.Open(path, flag)
if err != nil {
return nil, err
}
return f, nil
}
type ioctlCloser interface {
io.Closer
fs.Ioctler
}
type fileIO interface {
Fd() uintptr
fs.Ioctler
io.Closer
io.Reader
io.Seeker
io.Writer
}
// seekRead seeks to the beginning of a file and reads it.
func seekRead(f fileIO, b []byte) (int, error) {
if _, err := f.Seek(0, 0); err != nil {
return 0, err
}
return f.Read(b)
}
// seekWrite seeks to the beginning of a file and writes to it.
func seekWrite(f fileIO, b []byte) error {
if _, err := f.Seek(0, 0); err != nil {
return err
}
_, err := f.Write(b)
return err
}

17
vendor/periph.io/x/host/v3/sysfs/sysfs_linux.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
// 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 (
"os"
"syscall"
)
const isLinux = true
func isErrBusy(err error) bool {
e, ok := err.(*os.PathError)
return ok && e.Err == syscall.EBUSY
}

14
vendor/periph.io/x/host/v3/sysfs/sysfs_other.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
// 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.
// +build !linux
package sysfs
const isLinux = false
func isErrBusy(err error) bool {
// This function is not used on non-linux.
return false
}

261
vendor/periph.io/x/host/v3/sysfs/thermal_sensor.go generated vendored Normal file
View File

@ -0,0 +1,261 @@
// 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/conn/v3"
"periph.io/x/conn/v3/driver/driverreg"
"periph.io/x/conn/v3/physic"
)
// ThermalSensors is all the sensors discovered on this host via sysfs. It
// includes 'thermal' devices as well as temperature 'hwmon' devices, so
// pre-configured onewire temperature sensors will be discovered automatically.
var ThermalSensors []*ThermalSensor
// ThermalSensorByName returns a *ThermalSensor for the sensor name, if any.
func ThermalSensorByName(name string) (*ThermalSensor, error) {
// TODO(maruel): Use a bisect or a map. For now we don't expect more than a
// handful of thermal sensors so it doesn't matter.
for _, t := range ThermalSensors {
if t.name == name {
if err := t.open(); err != nil {
return nil, err
}
return t, nil
}
}
return nil, errors.New("sysfs-thermal: invalid sensor name")
}
// ThermalSensor represents one thermal sensor on the system.
type ThermalSensor struct {
name string
root string
sensorFilename string
typeFilename string
mu sync.Mutex
nameType string
f fileIO
precision physic.Temperature
done chan struct{}
}
func (t *ThermalSensor) String() string {
return t.name
}
// Halt stops a continuous sense that was started with SenseContinuous.
func (t *ThermalSensor) Halt() error {
t.mu.Lock()
defer t.mu.Unlock()
if t.done != nil {
close(t.done)
t.done = nil
}
return nil
}
// Type returns the type of sensor as exported by sysfs.
func (t *ThermalSensor) Type() string {
t.mu.Lock()
defer t.mu.Unlock()
if t.nameType == "" {
nameType, err := t.readType()
if err != nil {
return err.Error()
}
t.nameType = nameType
}
return t.nameType
}
func (t *ThermalSensor) readType() (string, error) {
f, err := fileIOOpen(t.root+t.typeFilename, os.O_RDONLY)
if os.IsNotExist(err) {
return "<unknown>", nil
}
if err != nil {
return "", fmt.Errorf("sysfs-thermal: %v", err)
}
defer f.Close()
var buf [256]byte
n, err := f.Read(buf[:])
if err != nil {
return "", fmt.Errorf("sysfs-thermal: %v", err)
}
if n < 2 {
return "<unknown>", nil
}
return string(buf[:n-1]), nil
}
// Sense implements physic.SenseEnv.
func (t *ThermalSensor) Sense(e *physic.Env) error {
if err := t.open(); err != nil {
return err
}
t.mu.Lock()
defer t.mu.Unlock()
var buf [24]byte
n, err := seekRead(t.f, buf[:])
if err != nil {
return fmt.Errorf("sysfs-thermal: %v", err)
}
if n < 2 {
return errors.New("sysfs-thermal: failed to read temperature")
}
i, err := strconv.Atoi(string(buf[:n-1]))
if err != nil {
return fmt.Errorf("sysfs-thermal: %v", err)
}
if t.precision == 0 {
t.precision = physic.MilliKelvin
if i < 100 {
t.precision *= 1000
}
}
e.Temperature = physic.Temperature(i)*t.precision + physic.ZeroCelsius
return nil
}
// SenseContinuous implements physic.SenseEnv.
func (t *ThermalSensor) SenseContinuous(interval time.Duration) (<-chan physic.Env, error) {
t.mu.Lock()
defer t.mu.Unlock()
if t.done != nil {
return nil, nil
}
done := make(chan struct{})
ret := make(chan physic.Env)
ticker := time.NewTicker(interval)
go func() {
defer ticker.Stop()
for {
select {
case <-done:
close(ret)
return
case <-ticker.C:
var e physic.Env
if err := t.Sense(&e); err == nil {
ret <- e
}
}
}
}()
t.done = done
return ret, nil
}
// Precision implements physic.SenseEnv.
func (t *ThermalSensor) Precision(e *physic.Env) {
if t.precision == 0 {
dummy := physic.Env{}
// Ignore the error.
_ = t.Sense(&dummy)
}
t.mu.Lock()
defer t.mu.Unlock()
e.Temperature = t.precision
}
//
func (t *ThermalSensor) open() error {
t.mu.Lock()
defer t.mu.Unlock()
if t.f != nil {
return nil
}
f, err := fileIOOpen(t.root+t.sensorFilename, os.O_RDONLY)
if err != nil {
return fmt.Errorf("sysfs-thermal: %v", err)
}
t.f = f
return nil
}
// driverThermalSensor implements periph.Driver.
type driverThermalSensor struct {
}
func (d *driverThermalSensor) String() string {
return "sysfs-thermal"
}
func (d *driverThermalSensor) Prerequisites() []string {
return nil
}
func (d *driverThermalSensor) After() []string {
return nil
}
// Init initializes thermal sysfs handling code.
//
// Uses sysfs as described* at
// https://www.kernel.org/doc/Documentation/thermal/sysfs-api.txt
//
// * for the most minimalistic meaning of 'described'.
func (d *driverThermalSensor) Init() (bool, error) {
if err := d.discoverDevices("/sys/class/thermal/*/temp", "type"); err != nil {
return true, err
}
if err := d.discoverDevices("/sys/class/hwmon/*/temp*_input", "device/name"); err != nil {
return true, err
}
if len(ThermalSensors) == 0 {
return false, errors.New("sysfs-thermal: no sensor found")
}
return true, nil
}
func (d *driverThermalSensor) discoverDevices(glob, typeFilename string) error {
// This driver is only registered on linux, so there is no legitimate time to
// skip it.
items, err := filepath.Glob(glob)
if err != nil {
return err
}
if len(items) == 0 {
return nil
}
sort.Strings(items)
for _, item := range items {
base := filepath.Dir(item)
ThermalSensors = append(ThermalSensors, &ThermalSensor{
name: filepath.Base(base),
root: base + "/",
sensorFilename: filepath.Base(item),
typeFilename: typeFilename,
})
}
return nil
}
func init() {
if isLinux {
driverreg.MustRegister(&drvThermalSensor)
}
}
var drvThermalSensor driverThermalSensor
var _ conn.Resource = &ThermalSensor{}
var _ physic.SenseEnv = &ThermalSensor{}