add: humidity (WIP)
This commit is contained in:
		
							
								
								
									
										13
									
								
								vendor/periph.io/x/periph/host/sysfs/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/periph.io/x/periph/host/sysfs/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										515
									
								
								vendor/periph.io/x/periph/host/sysfs/gpio.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										515
									
								
								vendor/periph.io/x/periph/host/sysfs/gpio.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,515 @@
 | 
			
		||||
// 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/periph"
 | 
			
		||||
	"periph.io/x/periph/conn"
 | 
			
		||||
	"periph.io/x/periph/conn/gpio"
 | 
			
		||||
	"periph.io/x/periph/conn/gpio/gpioreg"
 | 
			
		||||
	"periph.io/x/periph/conn/physic"
 | 
			
		||||
	"periph.io/x/periph/conn/pin"
 | 
			
		||||
	"periph.io/x/periph/host/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 Function(), 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.Func("ERR")
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := seekRead(p.fDirection, p.buf[:]); err != nil {
 | 
			
		||||
		return pin.Func("ERR")
 | 
			
		||||
	}
 | 
			
		||||
	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.Func("ERR")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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")
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
	_, err = drvGPIO.exportHandle.Write([]byte(strconv.Itoa(p.number)))
 | 
			
		||||
	if err != nil && !isErrBusy(err) {
 | 
			
		||||
		p.err = err
 | 
			
		||||
		if os.IsPermission(p.err) {
 | 
			
		||||
			return 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.
 | 
			
		||||
	timeout := 5 * time.Second
 | 
			
		||||
	for start := time.Now(); time.Since(start) < timeout; {
 | 
			
		||||
		p.fValue, err = fileIOOpen(p.root+"value", os.O_RDWR)
 | 
			
		||||
		// The virtual file creation is synchronous when writing to /export for
 | 
			
		||||
		// udev rule execution is asynchronous.
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if !os.IsPermission(err) {
 | 
			
		||||
			p.err = err
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if p.err != nil {
 | 
			
		||||
		return p.err
 | 
			
		||||
	}
 | 
			
		||||
	p.fDirection, err = fileIOOpen(p.root+"direction", os.O_RDWR)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		p.err = err
 | 
			
		||||
		_ = 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 {
 | 
			
		||||
		periph.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/periph/host/sysfs/i2c.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								vendor/periph.io/x/periph/host/sysfs/i2c.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal 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/periph"
 | 
			
		||||
	"periph.io/x/periph/conn/gpio"
 | 
			
		||||
	"periph.io/x/periph/conn/gpio/gpioreg"
 | 
			
		||||
	"periph.io/x/periph/conn/i2c"
 | 
			
		||||
	"periph.io/x/periph/conn/i2c/i2creg"
 | 
			
		||||
	"periph.io/x/periph/conn/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/periph/conn/i2c Linux-specific implementation.
 | 
			
		||||
//
 | 
			
		||||
// periph.io works on many OSes!
 | 
			
		||||
//
 | 
			
		||||
// Instead, use https://periph.io/x/periph/conn/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 {
 | 
			
		||||
		periph.MustRegister(&drvI2C)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var drvI2C driverI2C
 | 
			
		||||
 | 
			
		||||
var _ i2c.Bus = &I2C{}
 | 
			
		||||
var _ i2c.BusCloser = &I2C{}
 | 
			
		||||
							
								
								
									
										267
									
								
								vendor/periph.io/x/periph/host/sysfs/led.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								vendor/periph.io/x/periph/host/sysfs/led.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,267 @@
 | 
			
		||||
// 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{}
 | 
			
		||||
							
								
								
									
										651
									
								
								vendor/periph.io/x/periph/host/sysfs/spi.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										651
									
								
								vendor/periph.io/x/periph/host/sysfs/spi.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,651 @@
 | 
			
		||||
// 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/periph"
 | 
			
		||||
	"periph.io/x/periph/conn"
 | 
			
		||||
	"periph.io/x/periph/conn/gpio"
 | 
			
		||||
	"periph.io/x/periph/conn/gpio/gpioreg"
 | 
			
		||||
	"periph.io/x/periph/conn/physic"
 | 
			
		||||
	"periph.io/x/periph/conn/spi"
 | 
			
		||||
	"periph.io/x/periph/conn/spi/spireg"
 | 
			
		||||
	"periph.io/x/periph/host/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/periph/conn/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 {
 | 
			
		||||
	if err := s.f.Ioctl(op|0x40000000, uintptr(unsafe.Pointer(&arg))); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if false {
 | 
			
		||||
		// Verification.
 | 
			
		||||
		actual := uint64(0)
 | 
			
		||||
		// getFlag() equivalent.
 | 
			
		||||
		if err := s.f.Ioctl(op|0x80000000, uintptr(unsafe.Pointer(&actual))); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if actual != arg {
 | 
			
		||||
			return fmt.Errorf("sysfs-spi: op 0x%x: set 0x%x, read 0x%x", op, arg, actual)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 (
 | 
			
		||||
	spiIOCMode        = 0x16B01 // SPI_IOC_WR_MODE (8 bits)
 | 
			
		||||
	spiIOLSBFirst     = 0x16B02 // SPI_IOC_WR_LSB_FIRST
 | 
			
		||||
	spiIOCBitsPerWord = 0x16B03 // SPI_IOC_WR_BITS_PER_WORD
 | 
			
		||||
	spiIOCMaxSpeedHz  = 0x46B04 // SPI_IOC_WR_MAX_SPEED_HZ
 | 
			
		||||
	spiIOCMode32      = 0x46B05 // SPI_IOC_WR_MODE32 (32 bits)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// spiIOCTx(l) calculates the equivalent of SPI_IOC_MESSAGE(l) to execute a
 | 
			
		||||
// transaction.
 | 
			
		||||
//
 | 
			
		||||
// The IOCTL for TX was deduced from this C code:
 | 
			
		||||
//
 | 
			
		||||
//   #include "linux/spi/spidev.h"
 | 
			
		||||
//   #include "sys/ioctl.h"
 | 
			
		||||
//   #include <stdio.h>
 | 
			
		||||
//   int main() {
 | 
			
		||||
//     for (int i = 1; i < 10; i++) {
 | 
			
		||||
//       printf("len(%d) = 0x%08X\n", i, SPI_IOC_MESSAGE(i));
 | 
			
		||||
//     }
 | 
			
		||||
//     return 0;
 | 
			
		||||
//   }
 | 
			
		||||
//
 | 
			
		||||
//   $ gcc a.cc && ./a.out
 | 
			
		||||
//   len(1) = 0x40206B00
 | 
			
		||||
//   len(2) = 0x40406B00
 | 
			
		||||
//   len(3) = 0x40606B00
 | 
			
		||||
//   len(4) = 0x40806B00
 | 
			
		||||
//   len(5) = 0x40A06B00
 | 
			
		||||
//   len(6) = 0x40C06B00
 | 
			
		||||
//   len(7) = 0x40E06B00
 | 
			
		||||
//   len(8) = 0x41006B00
 | 
			
		||||
//   len(9) = 0x41206B00
 | 
			
		||||
func spiIOCTx(l int) uint {
 | 
			
		||||
	op := uint(0x40006B00)
 | 
			
		||||
	return op | uint(0x200000)*uint(l)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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, err := filepath.Glob(prefix + "*")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, err
 | 
			
		||||
	}
 | 
			
		||||
	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 {
 | 
			
		||||
		periph.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/periph/host/sysfs/sysfs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								vendor/periph.io/x/periph/host/sysfs/sysfs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal 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/periph/host/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/periph/host/sysfs/sysfs_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/periph.io/x/periph/host/sysfs/sysfs_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal 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/periph/host/sysfs/sysfs_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/periph.io/x/periph/host/sysfs/sysfs_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										199
									
								
								vendor/periph.io/x/periph/host/sysfs/thermal_sensor.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								vendor/periph.io/x/periph/host/sysfs/thermal_sensor.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,199 @@
 | 
			
		||||
// 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/physic"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ThermalSensors is all the sensors discovered on this host via sysfs.
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
	mu        sync.Mutex
 | 
			
		||||
	nameType  string
 | 
			
		||||
	f         fileIO
 | 
			
		||||
	precision physic.Temperature
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *ThermalSensor) String() string {
 | 
			
		||||
	return t.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Halt implements conn.Resource. It is a noop.
 | 
			
		||||
func (t *ThermalSensor) Halt() error {
 | 
			
		||||
	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 == "" {
 | 
			
		||||
		f, err := fileIOOpen(t.root+"type", os.O_RDONLY)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Sprintf("sysfs-thermal: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		defer f.Close()
 | 
			
		||||
		var buf [256]byte
 | 
			
		||||
		n, err := f.Read(buf[:])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Sprintf("sysfs-thermal: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if n < 2 {
 | 
			
		||||
			t.nameType = "<unknown>"
 | 
			
		||||
		} else {
 | 
			
		||||
			t.nameType = string(buf[:n-1])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return t.nameType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	// TODO(maruel): Manually poll in a loop via time.NewTicker.
 | 
			
		||||
	return nil, errors.New("sysfs-thermal: not implemented")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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+"temp", 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) {
 | 
			
		||||
	// This driver is only registered on linux, so there is no legitimate time to
 | 
			
		||||
	// skip it.
 | 
			
		||||
	items, err := filepath.Glob("/sys/class/thermal/*/temp")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(items) == 0 {
 | 
			
		||||
		return false, errors.New("sysfs-thermal: no sensor found")
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(items)
 | 
			
		||||
	for _, item := range items {
 | 
			
		||||
		base := filepath.Dir(item)
 | 
			
		||||
		ThermalSensors = append(ThermalSensors, &ThermalSensor{
 | 
			
		||||
			name: filepath.Base(base),
 | 
			
		||||
			root: base + "/",
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	if isLinux {
 | 
			
		||||
		periph.MustRegister(&drvThermalSensor)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var drvThermalSensor driverThermalSensor
 | 
			
		||||
 | 
			
		||||
var _ conn.Resource = &ThermalSensor{}
 | 
			
		||||
var _ physic.SenseEnv = &ThermalSensor{}
 | 
			
		||||
		Reference in New Issue
	
	Block a user