mirror of
https://github.com/hybridgroup/gobot.git
synced 2025-04-24 13:48:49 +08:00
BUGFIX: I2C connection-bus caching and multiple device usage
This commit is contained in:
parent
9ce45c0056
commit
c8335aaf85
101
adaptor.go
101
adaptor.go
@ -93,10 +93,67 @@ type PWMPinnerProvider interface {
|
||||
PWMPin(id string) (PWMPinner, error)
|
||||
}
|
||||
|
||||
// I2cSystemDevicer is the interface to a i2c bus at system level.
|
||||
// I2cSystemDevicer is the interface to a i2c bus at system level, according to I2C/SMBus specification.
|
||||
// Some functions are not in the interface yet:
|
||||
// * Process Call (WriteWordDataReadWordData)
|
||||
// * Block Write - Block Read (WriteBlockDataReadBlockData)
|
||||
// * Host Notify - WriteWordData() can be used instead
|
||||
//
|
||||
// see: https://docs.kernel.org/i2c/smbus-protocol.html#key-to-symbols
|
||||
//
|
||||
// S: Start condition; Sr: Repeated start condition, used to switch from write to read mode.
|
||||
// P: Stop condition; Rd/Wr (1 bit): Read/Write bit. Rd equals 1, Wr equals 0.
|
||||
// A, NA (1 bit): Acknowledge (ACK) and Not Acknowledge (NACK) bit
|
||||
// Addr (7 bits): I2C 7 bit address. (10 bit I2C address not yet supported by gobot).
|
||||
// Comm (8 bits): Command byte, a data byte which often selects a register on the device.
|
||||
// Data (8 bits): A plain data byte. DataLow and DataHigh represent the low and high byte of a 16 bit word.
|
||||
// Count (8 bits): A data byte containing the length of a block operation.
|
||||
// [..]: Data sent by I2C device, as opposed to data sent by the host adapter.
|
||||
//
|
||||
type I2cSystemDevicer interface {
|
||||
I2cOperations
|
||||
SetAddress(int) error
|
||||
// ReadByte must be implemented as the sequence:
|
||||
// "S Addr Rd [A] [Data] NA P"
|
||||
ReadByte(address int) (byte, error)
|
||||
|
||||
// ReadByteData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Sr Addr Rd [A] [Data] NA P"
|
||||
ReadByteData(address int, reg uint8) (uint8, error)
|
||||
|
||||
// ReadWordData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Sr Addr Rd [A] [DataLow] A [DataHigh] NA P"
|
||||
ReadWordData(address int, reg uint8) (uint16, error)
|
||||
|
||||
// ReadBlockData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Sr Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P"
|
||||
ReadBlockData(address int, reg uint8, data []byte) error
|
||||
|
||||
// WriteByte must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Data [A] P"
|
||||
WriteByte(address int, val byte) error
|
||||
|
||||
// WriteByteData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Data [A] P"
|
||||
WriteByteData(address int, reg uint8, val uint8) error
|
||||
|
||||
// WriteBlockData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P"
|
||||
WriteBlockData(address int, reg uint8, data []byte) error
|
||||
|
||||
// WriteWordData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P"
|
||||
WriteWordData(address int, reg uint8, val uint16) error
|
||||
|
||||
// WriteBytes writes the given data starting from the current register of bus device.
|
||||
WriteBytes(address int, data []byte) error
|
||||
|
||||
// Read implements direct read operations.
|
||||
Read(address int, b []byte) (int, error)
|
||||
|
||||
// Write implements direct write operations.
|
||||
Write(address int, b []byte) (n int, err error)
|
||||
|
||||
// Close closes the character device file.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// SpiSystemDevicer is the interface to a SPI bus at system level.
|
||||
@ -123,46 +180,14 @@ type BusOperations interface {
|
||||
}
|
||||
|
||||
// I2cOperations represents the i2c methods according to I2C/SMBus specification.
|
||||
// Some functions are not in the interface yet:
|
||||
// * Process Call (WriteWordDataReadWordData)
|
||||
// * Block Write - Block Read (WriteBlockDataReadBlockData)
|
||||
// * Host Notify - WriteWordData() can be used instead
|
||||
//
|
||||
// see: https://docs.kernel.org/i2c/smbus-protocol.html#key-to-symbols
|
||||
//
|
||||
// S: Start condition; Sr: Repeated start condition, used to switch from write to read mode.
|
||||
// P: Stop condition; Rd/Wr (1 bit): Read/Write bit. Rd equals 1, Wr equals 0.
|
||||
// A, NA (1 bit): Acknowledge (ACK) and Not Acknowledge (NACK) bit
|
||||
// Addr (7 bits): I2C 7 bit address. (10 bit I2C address not yet supported by gobot).
|
||||
// Comm (8 bits): Command byte, a data byte which often selects a register on the device.
|
||||
// Data (8 bits): A plain data byte. DataLow and DataHigh represent the low and high byte of a 16 bit word.
|
||||
// Count (8 bits): A data byte containing the length of a block operation.
|
||||
// [..]: Data sent by I2C device, as opposed to data sent by the host adapter.
|
||||
//
|
||||
// ReadByteData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Sr Addr Rd [A] [Data] NA P"
|
||||
// ReadBlockData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Sr Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P"
|
||||
// WriteByte must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Data [A] P"
|
||||
// WriteByteData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Data [A] P"
|
||||
// WriteBlockData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P"
|
||||
type I2cOperations interface {
|
||||
io.ReadWriteCloser
|
||||
BusOperations
|
||||
|
||||
// ReadByte must be implemented as the sequence:
|
||||
// "S Addr Rd [A] [Data] NA P"
|
||||
// ReadByte reads a byte from the current register of an i2c device.
|
||||
ReadByte() (byte, error)
|
||||
|
||||
// ReadWordData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] Sr Addr Rd [A] [DataLow] A [DataHigh] NA P"
|
||||
// ReadWordData reads a 16 bit value starting from the given register of an i2c device.
|
||||
ReadWordData(reg uint8) (uint16, error)
|
||||
|
||||
// WriteWordData must be implemented as the sequence:
|
||||
// "S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P"
|
||||
// WriteWordData writes the given 16 bit value starting from the given register of an i2c device.
|
||||
WriteWordData(reg uint8, val uint16) error
|
||||
}
|
||||
|
||||
|
@ -187,7 +187,7 @@ func (d *Adafruit1109Driver) Halt() error {
|
||||
// This is called by HD44780 driver to set one gpio output. We redirect the call to the i2c driver MCP23017.
|
||||
// The given id is the same as defined in dataPins and has the syntax "<port>_<pin>".
|
||||
func (d *Adafruit1109Driver) DigitalWrite(id string, val byte) error {
|
||||
portio := adafruit1109ParseId(id)
|
||||
portio := adafruit1109ParseID(id)
|
||||
return d.writePin(portio, val)
|
||||
}
|
||||
|
||||
@ -195,7 +195,7 @@ func (d *Adafruit1109Driver) DigitalWrite(id string, val byte) error {
|
||||
// This is called by HD44780 driver to read one gpio input. We redirect the call to the i2c driver MCP23017.
|
||||
// The given id is the same as defined in dataPins and has the syntax "<port>_<pin>".
|
||||
func (d *Adafruit1109Driver) DigitalRead(id string) (int, error) {
|
||||
portio := adafruit1109ParseId(id)
|
||||
portio := adafruit1109ParseID(id)
|
||||
uval, err := d.readPin(portio)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@ -276,7 +276,7 @@ func (ap *adafruit1109PortPin) String() string {
|
||||
return fmt.Sprintf("%s_%d", ap.port, ap.pin)
|
||||
}
|
||||
|
||||
func adafruit1109ParseId(id string) adafruit1109PortPin {
|
||||
func adafruit1109ParseID(id string) adafruit1109PortPin {
|
||||
items := strings.Split(id, "_")
|
||||
io := uint8(0)
|
||||
if io64, err := strconv.ParseUint(items[1], 10, 32); err == nil {
|
||||
|
@ -81,7 +81,7 @@ func TestAdafruit1109StartReadErr(t *testing.T) {
|
||||
adaptor.i2cReadImpl = func([]byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
gobottest.Assert(t, d.Start(), errors.New("read error"))
|
||||
gobottest.Assert(t, d.Start(), errors.New("MCP write-read: MCP write-ReadByteData(reg=0): read error"))
|
||||
}
|
||||
|
||||
func TestAdafruit1109Halt(t *testing.T) {
|
||||
@ -287,7 +287,7 @@ func TestAdafruit1109RightButton(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdafruit1109_parseId(t *testing.T) {
|
||||
func TestAdafruit1109_parseID(t *testing.T) {
|
||||
// arrange
|
||||
ports := []string{"A", "B"}
|
||||
for _, port := range ports {
|
||||
@ -295,7 +295,7 @@ func TestAdafruit1109_parseId(t *testing.T) {
|
||||
id := fmt.Sprintf("%s_%d", port, pin)
|
||||
t.Run(id, func(t *testing.T) {
|
||||
// act
|
||||
got := adafruit1109ParseId(id)
|
||||
got := adafruit1109ParseID(id)
|
||||
// assert
|
||||
gobottest.Assert(t, got, adafruit1109PortPin{port, pin})
|
||||
})
|
||||
|
@ -1,8 +1,7 @@
|
||||
package i2c
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"fmt"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
)
|
||||
@ -21,10 +20,10 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEncryptedBytes = errors.New("Encrypted bytes")
|
||||
ErrNotEnoughBytes = errors.New("Not enough bytes read")
|
||||
ErrNotReady = errors.New("Device is not ready")
|
||||
ErrInvalidPosition = errors.New("Invalid position value")
|
||||
// ErrNotEnoughBytes is used when the count of read bytes was too small
|
||||
ErrNotEnoughBytes = fmt.Errorf("Not enough bytes read")
|
||||
// ErrNotReady is used when the device is not ready
|
||||
ErrNotReady = fmt.Errorf("Device is not ready")
|
||||
)
|
||||
|
||||
type bitState uint8
|
||||
@ -44,144 +43,71 @@ type Connection gobot.I2cOperations
|
||||
type i2cConnection struct {
|
||||
bus gobot.I2cSystemDevicer
|
||||
address int
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// NewConnection creates and returns a new connection to a specific
|
||||
// i2c device on a bus and address.
|
||||
// NewConnection creates and returns a new connection to a specific i2c device on a bus and address.
|
||||
func NewConnection(bus gobot.I2cSystemDevicer, address int) (connection *i2cConnection) {
|
||||
return &i2cConnection{bus: bus, address: address}
|
||||
}
|
||||
|
||||
// Read data from an i2c device.
|
||||
func (c *i2cConnection) Read(data []byte) (read int, err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err = c.bus.SetAddress(c.address); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
read, err = c.bus.Read(data)
|
||||
return
|
||||
return c.bus.Read(c.address, data)
|
||||
}
|
||||
|
||||
// Write data to an i2c device.
|
||||
func (c *i2cConnection) Write(data []byte) (written int, err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err = c.bus.SetAddress(c.address); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
written, err = c.bus.Write(data)
|
||||
return
|
||||
return c.bus.Write(c.address, data)
|
||||
}
|
||||
|
||||
// Close connection to i2c device.
|
||||
// Close connection to i2c device. The bus was created by adaptor and will be closed there.
|
||||
func (c *i2cConnection) Close() error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
return c.bus.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the i2c device.
|
||||
func (c *i2cConnection) ReadByte() (val byte, err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err := c.bus.SetAddress(c.address); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return c.bus.ReadByte()
|
||||
func (c *i2cConnection) ReadByte() (byte, error) {
|
||||
return c.bus.ReadByte(c.address)
|
||||
}
|
||||
|
||||
// ReadByteData reads a byte value for a register on the i2c device.
|
||||
func (c *i2cConnection) ReadByteData(reg uint8) (val uint8, err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err := c.bus.SetAddress(c.address); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return c.bus.ReadByteData(reg)
|
||||
func (c *i2cConnection) ReadByteData(reg uint8) (uint8, error) {
|
||||
return c.bus.ReadByteData(c.address, reg)
|
||||
}
|
||||
|
||||
// ReadWordData reads a word value for a register on the i2c device.
|
||||
func (c *i2cConnection) ReadWordData(reg uint8) (val uint16, err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err := c.bus.SetAddress(c.address); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return c.bus.ReadWordData(reg)
|
||||
func (c *i2cConnection) ReadWordData(reg uint8) (uint16, error) {
|
||||
return c.bus.ReadWordData(c.address, reg)
|
||||
}
|
||||
|
||||
// ReadBlockData reads a block of bytes from a register on the i2c device.
|
||||
func (c *i2cConnection) ReadBlockData(reg uint8, b []byte) (err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err := c.bus.SetAddress(c.address); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.bus.ReadBlockData(reg, b)
|
||||
func (c *i2cConnection) ReadBlockData(reg uint8, b []byte) error {
|
||||
return c.bus.ReadBlockData(c.address, reg, b)
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte to the i2c device.
|
||||
func (c *i2cConnection) WriteByte(val byte) (err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err := c.bus.SetAddress(c.address); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.bus.WriteByte(val)
|
||||
func (c *i2cConnection) WriteByte(val byte) error {
|
||||
return c.bus.WriteByte(c.address, val)
|
||||
}
|
||||
|
||||
// WriteByteData writes a byte value to a register on the i2c device.
|
||||
func (c *i2cConnection) WriteByteData(reg uint8, val uint8) (err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err := c.bus.SetAddress(c.address); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.bus.WriteByteData(reg, val)
|
||||
func (c *i2cConnection) WriteByteData(reg uint8, val uint8) error {
|
||||
return c.bus.WriteByteData(c.address, reg, val)
|
||||
}
|
||||
|
||||
// WriteWordData writes a word value to a register on the i2c device.
|
||||
func (c *i2cConnection) WriteWordData(reg uint8, val uint16) (err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err := c.bus.SetAddress(c.address); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.bus.WriteWordData(reg, val)
|
||||
return c.bus.WriteWordData(c.address, reg, val)
|
||||
}
|
||||
|
||||
// WriteBlockData writes a block of bytes to a register on the i2c device.
|
||||
func (c *i2cConnection) WriteBlockData(reg uint8, b []byte) (err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err := c.bus.SetAddress(c.address); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.bus.WriteBlockData(reg, b)
|
||||
return c.bus.WriteBlockData(c.address, reg, b)
|
||||
}
|
||||
|
||||
// WriteBytes writes a block of bytes to the current register on the i2c device.
|
||||
func (c *i2cConnection) WriteBytes(b []byte) (err error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if err := c.bus.SetAddress(c.address); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.bus.WriteBytes(b)
|
||||
return c.bus.WriteBytes(c.address, b)
|
||||
}
|
||||
|
||||
// setBit is used to set a bit at a given position to 1.
|
||||
|
@ -16,37 +16,45 @@ import (
|
||||
|
||||
const dev = "/dev/i2c-1"
|
||||
|
||||
func syscallImpl(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
if (trap == syscall.SYS_IOCTL) && (a2 == system.I2C_FUNCS) {
|
||||
var funcPtr *uint64 = (*uint64)(unsafe.Pointer(a3))
|
||||
*funcPtr = system.I2C_FUNC_SMBUS_READ_BYTE | system.I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||
system.I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||
system.I2C_FUNC_SMBUS_WRITE_BYTE | system.I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||||
system.I2C_FUNC_SMBUS_WRITE_WORD_DATA
|
||||
}
|
||||
// Let all operations succeed
|
||||
return 0, 0, 0
|
||||
}
|
||||
func getSyscallFuncImpl(errorMask byte) func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
// bit 0: error on function query
|
||||
// bit 1: error on set address
|
||||
// bit 2: error on command
|
||||
return func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
// function query
|
||||
if (trap == syscall.SYS_IOCTL) && (a2 == system.I2C_FUNCS) {
|
||||
if errorMask&0x01 == 0x01 {
|
||||
return 0, 0, 1
|
||||
}
|
||||
|
||||
func syscallImplFail(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
if (trap == syscall.SYS_IOCTL) && (a2 == system.I2C_FUNCS) {
|
||||
var funcPtr *uint64 = (*uint64)(unsafe.Pointer(a3))
|
||||
*funcPtr = system.I2C_FUNC_SMBUS_READ_BYTE | system.I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||
system.I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||
system.I2C_FUNC_SMBUS_WRITE_BYTE | system.I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||||
system.I2C_FUNC_SMBUS_WRITE_WORD_DATA
|
||||
// retrieve functions call succeed
|
||||
var funcPtr *uint64 = (*uint64)(unsafe.Pointer(a3))
|
||||
*funcPtr = system.I2C_FUNC_SMBUS_READ_BYTE | system.I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||
system.I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||
system.I2C_FUNC_SMBUS_WRITE_BYTE | system.I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||||
system.I2C_FUNC_SMBUS_WRITE_WORD_DATA
|
||||
}
|
||||
// set address
|
||||
if (trap == syscall.SYS_IOCTL) && (a2 == system.I2C_SLAVE) {
|
||||
if errorMask&0x02 == 0x02 {
|
||||
return 0, 0, 1
|
||||
}
|
||||
}
|
||||
// command
|
||||
if (trap == syscall.SYS_IOCTL) && (a2 == system.I2C_SMBUS) {
|
||||
if errorMask&0x04 == 0x04 {
|
||||
return 0, 0, 1
|
||||
}
|
||||
}
|
||||
// Let all operations succeed
|
||||
return 0, 0, 0
|
||||
}
|
||||
// Let all operations fail
|
||||
return 0, 0, 1
|
||||
}
|
||||
|
||||
func initI2CDevice() gobot.I2cSystemDevicer {
|
||||
a := system.NewAccesser()
|
||||
a.UseMockFilesystem([]string{dev})
|
||||
msc := a.UseMockSyscall()
|
||||
msc.Impl = syscallImpl
|
||||
msc.Impl = getSyscallFuncImpl(0x00)
|
||||
|
||||
d, _ := a.NewI2cDevice(dev)
|
||||
return d
|
||||
@ -56,7 +64,7 @@ func initI2CDeviceAddressError() gobot.I2cSystemDevicer {
|
||||
a := system.NewAccesser()
|
||||
a.UseMockFilesystem([]string{dev})
|
||||
msc := a.UseMockSyscall()
|
||||
msc.Impl = syscallImplFail
|
||||
msc.Impl = getSyscallFuncImpl(0x02)
|
||||
|
||||
d, _ := a.NewI2cDevice(dev)
|
||||
return d
|
||||
|
@ -67,6 +67,8 @@ var CustomLCDChars = map[string][8]byte{
|
||||
"frowney": {0, 0, 10, 0, 0, 0, 14, 17},
|
||||
}
|
||||
|
||||
var jhd1313m1ErrInvalidPosition = fmt.Errorf("Invalid position value")
|
||||
|
||||
// JHD1313M1Driver is a driver for the Jhd1313m1 LCD display which has two i2c addreses,
|
||||
// one belongs to a controller and the other controls solely the backlight.
|
||||
// This module was tested with the Seed Grove LCD RGB Backlight v2.0 display which requires 5V to operate.
|
||||
@ -258,7 +260,7 @@ func (h *JHD1313M1Driver) Write(message string) error {
|
||||
// 16..32 are the positions in the second display line.
|
||||
func (h *JHD1313M1Driver) SetPosition(pos int) (err error) {
|
||||
if pos < 0 || pos > 31 {
|
||||
err = ErrInvalidPosition
|
||||
err = jhd1313m1ErrInvalidPosition
|
||||
return
|
||||
}
|
||||
offset := byte(pos)
|
||||
|
@ -162,8 +162,8 @@ func TestJHD1313MDriverSetSecondLinePosition(t *testing.T) {
|
||||
func TestJHD1313MDriverSetPositionInvalid(t *testing.T) {
|
||||
d, _ := initTestJHD1313M1DriverWithStubbedAdaptor()
|
||||
d.Start()
|
||||
gobottest.Assert(t, d.SetPosition(-1), ErrInvalidPosition)
|
||||
gobottest.Assert(t, d.SetPosition(32), ErrInvalidPosition)
|
||||
gobottest.Assert(t, d.SetPosition(-1), jhd1313m1ErrInvalidPosition)
|
||||
gobottest.Assert(t, d.SetPosition(32), jhd1313m1ErrInvalidPosition)
|
||||
}
|
||||
|
||||
func TestJHD1313MDriverScroll(t *testing.T) {
|
||||
|
@ -36,9 +36,9 @@ type bank struct {
|
||||
portB port
|
||||
}
|
||||
|
||||
// MCP23017Config contains the device configuration for the IOCON register.
|
||||
// mcp23017Config contains the device configuration for the IOCON register.
|
||||
// These fields should only be set with values 0 or 1.
|
||||
type MCP23017Config struct {
|
||||
type mcp23017Config struct {
|
||||
bank uint8
|
||||
mirror uint8
|
||||
seqop uint8
|
||||
@ -48,7 +48,7 @@ type MCP23017Config struct {
|
||||
intpol uint8
|
||||
}
|
||||
|
||||
type MCP23017Behavior struct {
|
||||
type mcp23017Behavior struct {
|
||||
forceRefresh bool
|
||||
autoIODirOff bool
|
||||
}
|
||||
@ -56,8 +56,8 @@ type MCP23017Behavior struct {
|
||||
// MCP23017Driver contains the driver configuration parameters.
|
||||
type MCP23017Driver struct {
|
||||
*Driver
|
||||
mcpConf MCP23017Config
|
||||
mcpBehav MCP23017Behavior
|
||||
mcpConf mcp23017Config
|
||||
mcpBehav mcp23017Behavior
|
||||
gobot.Eventer
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ type MCP23017Driver struct {
|
||||
func NewMCP23017Driver(c Connector, options ...func(Config)) *MCP23017Driver {
|
||||
d := &MCP23017Driver{
|
||||
Driver: NewDriver(c, "MCP23017", mcp23017DefaultAddress),
|
||||
mcpConf: MCP23017Config{},
|
||||
mcpConf: mcp23017Config{},
|
||||
Eventer: gobot.NewEventer(),
|
||||
}
|
||||
d.afterStart = d.initialize
|
||||
@ -321,7 +321,7 @@ func (m *MCP23017Driver) initialize() (err error) {
|
||||
func (m *MCP23017Driver) write(reg uint8, pin uint8, state bitState) (err error) {
|
||||
valOrg, err := m.read(reg)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("MCP write-read: %v", err)
|
||||
}
|
||||
|
||||
var val uint8
|
||||
@ -337,7 +337,7 @@ func (m *MCP23017Driver) write(reg uint8, pin uint8, state bitState) (err error)
|
||||
m.mcpBehav.forceRefresh, m.GetAddressOrDefault(mcp23017DefaultAddress), reg, m.getRegName(reg), val)
|
||||
}
|
||||
if err = m.connection.WriteByteData(reg, val); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("MCP write-WriteByteData(reg=%d,val=%d): %v", reg, val, err)
|
||||
}
|
||||
} else {
|
||||
if mcp23017Debug {
|
||||
@ -353,7 +353,7 @@ func (m *MCP23017Driver) write(reg uint8, pin uint8, state bitState) (err error)
|
||||
func (m *MCP23017Driver) read(reg uint8) (val uint8, err error) {
|
||||
val, err = m.connection.ReadByteData(reg)
|
||||
if err != nil {
|
||||
return val, err
|
||||
return val, fmt.Errorf("MCP write-ReadByteData(reg=%d): %v", reg, err)
|
||||
}
|
||||
if mcp23017Debug {
|
||||
log.Printf("reading done: MCP autoIODirOff: %t, address: 0x%X, register:0x%X, name: %s, value: 0x%X\n",
|
||||
@ -377,7 +377,7 @@ func (m *MCP23017Driver) getPort(portStr string) (selectedPort port) {
|
||||
}
|
||||
|
||||
// getUint8Value returns the configuration data as a packed value.
|
||||
func (mc *MCP23017Config) getUint8Value() uint8 {
|
||||
func (mc *mcp23017Config) getUint8Value() uint8 {
|
||||
return mc.bank<<7 | mc.mirror<<6 | mc.seqop<<5 | mc.disslw<<4 | mc.haen<<3 | mc.odr<<2 | mc.intpol<<1
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,7 @@ func TestMCP23017CommandsWriteGPIOErrIODIR(t *testing.T) {
|
||||
// act
|
||||
err := d.WriteGPIO(7, "A", 0)
|
||||
// assert
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, err, errors.New("MCP write-read: MCP write-ReadByteData(reg=0): write error"))
|
||||
}
|
||||
|
||||
func TestMCP23017CommandsWriteGPIOErrOLAT(t *testing.T) {
|
||||
@ -258,7 +258,7 @@ func TestMCP23017CommandsWriteGPIOErrOLAT(t *testing.T) {
|
||||
// act
|
||||
err := d.WriteGPIO(7, "A", 0)
|
||||
// assert
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, err, errors.New("MCP write-read: MCP write-ReadByteData(reg=20): write error"))
|
||||
}
|
||||
|
||||
func TestMCP23017ReadGPIO(t *testing.T) {
|
||||
@ -381,7 +381,7 @@ func TestMCP23017ReadGPIOErr(t *testing.T) {
|
||||
// act
|
||||
_, err := d.ReadGPIO(7, "A")
|
||||
// assert
|
||||
gobottest.Assert(t, err, errors.New("read error"))
|
||||
gobottest.Assert(t, err, errors.New("MCP write-read: MCP write-ReadByteData(reg=0): read error"))
|
||||
}
|
||||
|
||||
func TestMCP23017SetPinMode(t *testing.T) {
|
||||
@ -431,7 +431,7 @@ func TestMCP23017SetPinModeErr(t *testing.T) {
|
||||
// act
|
||||
err := d.SetPinMode(7, "A", 0)
|
||||
// assert
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, err, errors.New("MCP write-read: MCP write-ReadByteData(reg=0): write error"))
|
||||
}
|
||||
|
||||
func TestMCP23017SetPullUp(t *testing.T) {
|
||||
@ -481,7 +481,7 @@ func TestMCP23017SetPullUpErr(t *testing.T) {
|
||||
// act
|
||||
err := d.SetPullUp(7, "A", 0)
|
||||
// assert
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, err, errors.New("MCP write-read: MCP write-ReadByteData(reg=12): write error"))
|
||||
}
|
||||
|
||||
func TestMCP23017SetGPIOPolarity(t *testing.T) {
|
||||
@ -531,7 +531,7 @@ func TestMCP23017SetGPIOPolarityErr(t *testing.T) {
|
||||
// act
|
||||
err := d.SetGPIOPolarity(7, "A", 0)
|
||||
// assert
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, err, errors.New("MCP write-read: MCP write-ReadByteData(reg=2): write error"))
|
||||
}
|
||||
|
||||
func TestMCP23017_write(t *testing.T) {
|
||||
@ -553,7 +553,7 @@ func TestMCP23017_write(t *testing.T) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
err = d.write(port.IODIR, uint8(7), 0)
|
||||
gobottest.Assert(t, err, errors.New("write error"))
|
||||
gobottest.Assert(t, err, errors.New("MCP write-read: MCP write-ReadByteData(reg=1): write error"))
|
||||
|
||||
// read error
|
||||
d, a = initTestMCP23017WithStubbedAdaptor(0)
|
||||
@ -561,7 +561,7 @@ func TestMCP23017_write(t *testing.T) {
|
||||
return len(b), errors.New("read error")
|
||||
}
|
||||
err = d.write(port.IODIR, uint8(7), 0)
|
||||
gobottest.Assert(t, err, errors.New("read error"))
|
||||
gobottest.Assert(t, err, errors.New("MCP write-read: MCP write-ReadByteData(reg=1): read error"))
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
@ -588,7 +588,7 @@ func TestMCP23017_read(t *testing.T) {
|
||||
|
||||
val, err := d.read(port.IODIR)
|
||||
gobottest.Assert(t, val, uint8(0))
|
||||
gobottest.Assert(t, err, errors.New("read error"))
|
||||
gobottest.Assert(t, err, errors.New("MCP write-ReadByteData(reg=0): read error"))
|
||||
|
||||
// read
|
||||
d, a = initTestMCP23017WithStubbedAdaptor(0)
|
||||
|
@ -147,7 +147,7 @@ func (p *PCA9685Driver) SetPWMFreq(freq float32) error {
|
||||
prescalevel /= 4096
|
||||
// Ratio between desired frequency and maximum
|
||||
prescalevel /= freq
|
||||
prescalevel -= 1
|
||||
prescalevel--
|
||||
// Round value to nearest whole
|
||||
prescale := byte(prescalevel + 0.5)
|
||||
|
||||
|
@ -471,19 +471,19 @@ func TestPCF8583WriteRam(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
wantRamAddress := uint8(0xFF)
|
||||
wantRamValue := uint8(0xEF)
|
||||
wantRAMAddress := uint8(0xFF)
|
||||
wantRAMValue := uint8(0xEF)
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
err := d.WriteRAM(wantRamAddress-pcf8583RamOffset, wantRamValue)
|
||||
err := d.WriteRAM(wantRAMAddress-pcf8583RamOffset, wantRAMValue)
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, len(a.written), 2)
|
||||
gobottest.Assert(t, a.written[0], wantRamAddress)
|
||||
gobottest.Assert(t, a.written[1], wantRamValue)
|
||||
gobottest.Assert(t, a.written[0], wantRAMAddress)
|
||||
gobottest.Assert(t, a.written[1], wantRAMValue)
|
||||
}
|
||||
|
||||
func TestPCF8583WriteRamAddressOverflowFails(t *testing.T) {
|
||||
@ -505,7 +505,7 @@ func TestPCF8583ReadRam(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
wantRamAddress := uint8(pcf8583RamOffset)
|
||||
wantRAMAddress := uint8(pcf8583RamOffset)
|
||||
want := uint8(0xAB)
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
@ -519,12 +519,12 @@ func TestPCF8583ReadRam(t *testing.T) {
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
got, err := d.ReadRAM(wantRamAddress - pcf8583RamOffset)
|
||||
got, err := d.ReadRAM(wantRAMAddress - pcf8583RamOffset)
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, got, want)
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], wantRamAddress)
|
||||
gobottest.Assert(t, a.written[0], wantRAMAddress)
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
// and tests all implementations, so no further tests needed here for gobot.Driver interface
|
||||
var _ gobot.Driver = (*TSL2561Driver)(nil)
|
||||
|
||||
func testIdReader(b []byte) (int, error) {
|
||||
func testIDReader(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// Mock device responding 0xA
|
||||
binary.Write(buf, binary.LittleEndian, uint8(0x0A))
|
||||
@ -26,7 +26,7 @@ func testIdReader(b []byte) (int, error) {
|
||||
func initTestTSL2561Driver() (*TSL2561Driver, *i2cTestAdaptor) {
|
||||
a := newI2cTestAdaptor()
|
||||
d := NewTSL2561Driver(a)
|
||||
a.i2cReadImpl = testIdReader
|
||||
a.i2cReadImpl = testIDReader
|
||||
if err := d.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -58,7 +58,7 @@ func TestTSL2561DriverOptions(t *testing.T) {
|
||||
func TestTSL2561DriverStart(t *testing.T) {
|
||||
a := newI2cTestAdaptor()
|
||||
d := NewTSL2561Driver(a)
|
||||
a.i2cReadImpl = testIdReader
|
||||
a.i2cReadImpl = testIDReader
|
||||
|
||||
gobottest.Assert(t, d.Start(), nil)
|
||||
}
|
||||
@ -82,7 +82,7 @@ func TestTSL2561DriverHalt(t *testing.T) {
|
||||
|
||||
func TestTSL2561DriverRead16(t *testing.T) {
|
||||
d, a := initTestTSL2561Driver()
|
||||
a.i2cReadImpl = testIdReader
|
||||
a.i2cReadImpl = testIDReader
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
// send low
|
||||
|
@ -1,6 +1,7 @@
|
||||
package i2c
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -20,6 +21,7 @@ const (
|
||||
|
||||
const wiichuckDefaultAddress = 0x52
|
||||
|
||||
// WiichuckDriver contains the attributes for the i2c driver
|
||||
type WiichuckDriver struct {
|
||||
*Driver
|
||||
interval time.Duration
|
||||
@ -82,16 +84,16 @@ func (w *WiichuckDriver) Joystick() map[string]float64 {
|
||||
|
||||
// update parses value to update buttons and joystick.
|
||||
// If value is encrypted, warning message is printed
|
||||
func (w *WiichuckDriver) update(value []byte) (err error) {
|
||||
func (w *WiichuckDriver) update(value []byte) error {
|
||||
if w.isEncrypted(value) {
|
||||
return ErrEncryptedBytes
|
||||
} else {
|
||||
w.parse(value)
|
||||
w.adjustOrigins()
|
||||
w.updateButtons()
|
||||
w.updateJoystick()
|
||||
return fmt.Errorf("Encrypted bytes")
|
||||
}
|
||||
return
|
||||
|
||||
w.parse(value)
|
||||
w.adjustOrigins()
|
||||
w.updateButtons()
|
||||
w.updateJoystick()
|
||||
return nil
|
||||
}
|
||||
|
||||
// setJoystickDefaultValue sets default value if value is -1
|
||||
|
@ -15,17 +15,19 @@ const yl40DefaultAddress = 0x48
|
||||
|
||||
const yl40Debug = false
|
||||
|
||||
// YL40Pin wraps the underlying string type for type safety
|
||||
type YL40Pin string
|
||||
|
||||
const (
|
||||
// brightness sensor, high brightness - low raw value, scaled to 0..1000 (high brightness - high value)
|
||||
// YL40Bri for brightness sensor, high brightness - low raw value, scaled to 0..1000 (high brightness - high value)
|
||||
YL40Bri YL40Pin = "brightness"
|
||||
// temperature sensor, high temperature - low raw value, scaled to °C
|
||||
// YL40Temp for temperature sensor, high temperature - low raw value, scaled to °C
|
||||
YL40Temp YL40Pin = "temperature"
|
||||
// wired to AOUT, scaled to voltage 3.3V
|
||||
// YL40AIN2 is wired to AOUT, scaled to voltage 3.3V
|
||||
YL40AIN2 YL40Pin = "analog input AIN2"
|
||||
// adjustable resistor, turn clockwise will lower the raw value, scaled to -100..+100% (clockwise)
|
||||
// YL40Poti is adjustable resistor, turn clockwise will lower the raw value, scaled to -100..+100% (clockwise)
|
||||
YL40Poti YL40Pin = "potentiometer"
|
||||
// YL40AOUT is the analog output
|
||||
YL40AOUT YL40Pin = "analog output"
|
||||
)
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"sync"
|
||||
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/drivers/i2c"
|
||||
"gobot.io/x/gobot/system"
|
||||
)
|
||||
@ -17,7 +18,7 @@ type I2cBusAdaptor struct {
|
||||
validateNumber i2cBusNumberValidator
|
||||
defaultBusNumber int
|
||||
mutex sync.Mutex
|
||||
connections map[string]i2c.Connection
|
||||
buses map[int]gobot.I2cSystemDevicer
|
||||
}
|
||||
|
||||
// NewI2cBusAdaptor provides the access to i2c buses of the board. The validator is used to check the bus number,
|
||||
@ -36,51 +37,49 @@ func (a *I2cBusAdaptor) Connect() error {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
a.connections = make(map[string]i2c.Connection)
|
||||
a.buses = make(map[int]gobot.I2cSystemDevicer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Finalize closes all i2c connections.
|
||||
// Finalize closes all i2c buses.
|
||||
func (a *I2cBusAdaptor) Finalize() error {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
var err error
|
||||
for _, con := range a.connections {
|
||||
if con != nil {
|
||||
if e := con.Close(); e != nil {
|
||||
for _, bus := range a.buses {
|
||||
if bus != nil {
|
||||
if e := bus.Close(); e != nil {
|
||||
err = multierror.Append(err, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
a.connections = nil
|
||||
a.buses = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// GetI2cConnection returns a connection to a device on a specified i2c bus
|
||||
func (a *I2cBusAdaptor) GetI2cConnection(address int, busNum int) (connection i2c.Connection, err error) {
|
||||
func (a *I2cBusAdaptor) GetI2cConnection(address int, busNum int) (i2c.Connection, error) {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
if a.connections == nil {
|
||||
if a.buses == nil {
|
||||
return nil, fmt.Errorf("not connected")
|
||||
}
|
||||
|
||||
id := fmt.Sprintf("%d_%d", busNum, address)
|
||||
|
||||
con := a.connections[id]
|
||||
if con == nil {
|
||||
if err := a.validateNumber(busNum); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bus, err := a.sys.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", busNum))
|
||||
bus := a.buses[busNum]
|
||||
if bus == nil {
|
||||
err := a.validateNumber(busNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
con = i2c.NewConnection(bus, address)
|
||||
a.connections[id] = con
|
||||
bus, err = a.sys.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", busNum))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.buses[busNum] = bus
|
||||
}
|
||||
return con, err
|
||||
return i2c.NewConnection(bus, address), nil
|
||||
}
|
||||
|
||||
// DefaultI2cBus returns the default i2c bus number for this platform.
|
||||
|
@ -34,11 +34,11 @@ func initTestI2cAdaptorWithMockedFilesystem(mockPaths []string) (*I2cBusAdaptor,
|
||||
|
||||
func TestI2cWorkflow(t *testing.T) {
|
||||
a, _ := initTestI2cAdaptorWithMockedFilesystem([]string{i2cBus1})
|
||||
gobottest.Assert(t, len(a.connections), 0)
|
||||
gobottest.Assert(t, len(a.buses), 0)
|
||||
|
||||
con, err := a.GetI2cConnection(0xff, 1)
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, len(a.connections), 1)
|
||||
gobottest.Assert(t, len(a.buses), 1)
|
||||
|
||||
_, err = con.Write([]byte{0x00, 0x01})
|
||||
gobottest.Assert(t, err, nil)
|
||||
@ -49,7 +49,7 @@ func TestI2cWorkflow(t *testing.T) {
|
||||
gobottest.Assert(t, data, []byte{0x00, 0x01})
|
||||
|
||||
gobottest.Assert(t, a.Finalize(), nil)
|
||||
gobottest.Assert(t, len(a.connections), 0)
|
||||
gobottest.Assert(t, len(a.buses), 0)
|
||||
}
|
||||
|
||||
func TestI2cGetI2cConnection(t *testing.T) {
|
||||
@ -59,18 +59,18 @@ func TestI2cGetI2cConnection(t *testing.T) {
|
||||
c1, e1 := a.GetI2cConnection(0xff, 1)
|
||||
gobottest.Assert(t, e1, nil)
|
||||
gobottest.Refute(t, c1, nil)
|
||||
gobottest.Assert(t, len(a.connections), 1)
|
||||
gobottest.Assert(t, len(a.buses), 1)
|
||||
// assert invalid bus gets error
|
||||
c2, e2 := a.GetI2cConnection(0x01, 99)
|
||||
gobottest.Assert(t, e2, fmt.Errorf("99 not valid"))
|
||||
gobottest.Assert(t, c2, nil)
|
||||
gobottest.Assert(t, len(a.connections), 1)
|
||||
gobottest.Assert(t, len(a.buses), 1)
|
||||
// assert unconnected gets error
|
||||
gobottest.Assert(t, a.Finalize(), nil)
|
||||
c3, e3 := a.GetI2cConnection(0x01, 99)
|
||||
gobottest.Assert(t, e3, fmt.Errorf("not connected"))
|
||||
gobottest.Assert(t, c3, nil)
|
||||
gobottest.Assert(t, len(a.connections), 0)
|
||||
gobottest.Assert(t, len(a.buses), 0)
|
||||
}
|
||||
|
||||
func TestI2cFinalize(t *testing.T) {
|
||||
@ -81,16 +81,16 @@ func TestI2cFinalize(t *testing.T) {
|
||||
// arrange
|
||||
gobottest.Assert(t, a.Connect(), nil)
|
||||
a.GetI2cConnection(0xaf, 1)
|
||||
gobottest.Assert(t, len(a.connections), 1)
|
||||
gobottest.Assert(t, len(a.buses), 1)
|
||||
// assert that Finalize after GetI2cConnection is working and clean up
|
||||
gobottest.Assert(t, a.Finalize(), nil)
|
||||
gobottest.Assert(t, len(a.connections), 0)
|
||||
gobottest.Assert(t, len(a.buses), 0)
|
||||
// assert that finalize after finalize is working
|
||||
gobottest.Assert(t, a.Finalize(), nil)
|
||||
// assert that close error is recognized
|
||||
gobottest.Assert(t, a.Connect(), nil)
|
||||
con, _ := a.GetI2cConnection(0xbf, 1)
|
||||
gobottest.Assert(t, len(a.connections), 1)
|
||||
gobottest.Assert(t, len(a.buses), 1)
|
||||
con.Write([]byte{0xbf})
|
||||
fs.WithCloseError = true
|
||||
err := a.Finalize()
|
||||
@ -104,8 +104,8 @@ func TestI2cReConnect(t *testing.T) {
|
||||
// act
|
||||
gobottest.Assert(t, a.Connect(), nil)
|
||||
// assert
|
||||
gobottest.Refute(t, a.connections, nil)
|
||||
gobottest.Assert(t, len(a.connections), 0)
|
||||
gobottest.Refute(t, a.buses, nil)
|
||||
gobottest.Assert(t, len(a.buses), 0)
|
||||
}
|
||||
|
||||
func TestI2cGetDefaultBus(t *testing.T) {
|
||||
|
@ -4,11 +4,15 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const i2cDeviceDebug = false
|
||||
const (
|
||||
i2cDeviceDebug = false
|
||||
forceSetAddress = false // normally address will be written only when changed, this behavior can be overridden
|
||||
)
|
||||
|
||||
const (
|
||||
// From /usr/include/linux/i2c-dev.h:
|
||||
@ -51,39 +55,42 @@ type i2cSmbusIoctlData struct {
|
||||
}
|
||||
|
||||
type i2cDevice struct {
|
||||
location string
|
||||
file File
|
||||
funcs uint64 // adapter functionality mask
|
||||
sys systemCaller
|
||||
fs filesystem
|
||||
location string
|
||||
sys systemCaller
|
||||
fs filesystem
|
||||
file File
|
||||
funcs uint64 // adapter functionality mask
|
||||
lastAddress int
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// NewI2cDevice returns an io.ReadWriteCloser with the proper ioctrl given
|
||||
// an i2c bus location.
|
||||
// NewI2cDevice returns a Linux Kernel access by ioctrl to the given i2c bus location (character device).
|
||||
// Important note for "os.ModeExclusive": this is undefined without create the file for character devices, this means
|
||||
// a second open will not return an error e.g. due to a busy resource. If this is not wanted, e.g. to minimize count of
|
||||
// open fd's this needs to be prevented at caller side by implementing a caching mechanism. Furthermore this behavior
|
||||
// can lead to problems with multiple devices on the same bus because the cycle SetAddress()...Read()/Write() etc. can
|
||||
// be interrupted when using multiple instances for the same location.
|
||||
func (a *Accesser) NewI2cDevice(location string) (*i2cDevice, error) {
|
||||
if location == "" {
|
||||
return nil, fmt.Errorf("the given character device location is empty")
|
||||
}
|
||||
|
||||
d := &i2cDevice{
|
||||
location: location,
|
||||
sys: a.sys,
|
||||
fs: a.fs,
|
||||
location: location,
|
||||
sys: a.sys,
|
||||
fs: a.fs,
|
||||
lastAddress: -1,
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// SetAddress sets the address of the i2c device to use.
|
||||
func (d *i2cDevice) SetAddress(address int) error {
|
||||
// for go vet false positives, see: https://github.com/golang/go/issues/41205
|
||||
if err := d.syscallIoctl(I2C_SLAVE, unsafe.Pointer(uintptr(byte(address))), "Setting address"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the character device file.
|
||||
// Close closes the character device file and resets the lazy variables.
|
||||
func (d *i2cDevice) Close() error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
d.funcs = 0
|
||||
d.lastAddress = -1
|
||||
if d.file != nil {
|
||||
return d.file.Close()
|
||||
}
|
||||
@ -91,40 +98,52 @@ func (d *i2cDevice) Close() error {
|
||||
}
|
||||
|
||||
// ReadByte reads a byte from the current register of an i2c device.
|
||||
func (d *i2cDevice) ReadByte() (byte, error) {
|
||||
func (d *i2cDevice) ReadByte(address int) (byte, error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
if err := d.queryFunctionality(I2C_FUNC_SMBUS_READ_BYTE, "read byte"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var data uint8 = 0xFC // set value for debugging purposes
|
||||
err := d.smbusAccess(I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, unsafe.Pointer(&data))
|
||||
err := d.smbusAccess(address, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, unsafe.Pointer(&data))
|
||||
return data, err
|
||||
}
|
||||
|
||||
// ReadByteData reads a byte from the given register of an i2c device.
|
||||
func (d *i2cDevice) ReadByteData(reg uint8) (val uint8, err error) {
|
||||
func (d *i2cDevice) ReadByteData(address int, reg uint8) (val uint8, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
if err := d.queryFunctionality(I2C_FUNC_SMBUS_READ_BYTE_DATA, "read byte data"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var data uint8 = 0xFD // set value for debugging purposes
|
||||
err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, unsafe.Pointer(&data))
|
||||
err = d.smbusAccess(address, I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, unsafe.Pointer(&data))
|
||||
return data, err
|
||||
}
|
||||
|
||||
// ReadWordData reads a 16 bit value starting from the given register of an i2c device.
|
||||
func (d *i2cDevice) ReadWordData(reg uint8) (val uint16, err error) {
|
||||
func (d *i2cDevice) ReadWordData(address int, reg uint8) (val uint16, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
if err := d.queryFunctionality(I2C_FUNC_SMBUS_READ_WORD_DATA, "read word data"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var data uint16 = 0xFFFE // set value for debugging purposes
|
||||
err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA, unsafe.Pointer(&data))
|
||||
err = d.smbusAccess(address, I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA, unsafe.Pointer(&data))
|
||||
return data, err
|
||||
}
|
||||
|
||||
// ReadBlockData fills the given buffer with reads starting from the given register of an i2c device.
|
||||
func (d *i2cDevice) ReadBlockData(reg uint8, data []byte) error {
|
||||
func (d *i2cDevice) ReadBlockData(address int, reg uint8, data []byte) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
dataLen := len(data)
|
||||
if dataLen > 32 {
|
||||
return fmt.Errorf("Reading blocks larger than 32 bytes (%v) not supported", len(data))
|
||||
@ -135,14 +154,14 @@ func (d *i2cDevice) ReadBlockData(reg uint8, data []byte) error {
|
||||
if i2cDeviceDebug {
|
||||
log.Printf("%s, use fallback\n", err.Error())
|
||||
}
|
||||
return d.readBlockDataFallback(reg, data)
|
||||
return d.readBlockDataFallback(address, reg, data)
|
||||
}
|
||||
|
||||
// set the first element with the data size
|
||||
buf := make([]byte, dataLen+1)
|
||||
buf[0] = byte(dataLen)
|
||||
copy(buf[1:], data)
|
||||
if err := d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_I2C_BLOCK_DATA, unsafe.Pointer(&buf[0])); err != nil {
|
||||
if err := d.smbusAccess(address, I2C_SMBUS_READ, reg, I2C_SMBUS_I2C_BLOCK_DATA, unsafe.Pointer(&buf[0])); err != nil {
|
||||
return err
|
||||
}
|
||||
// get data from buffer without first size element
|
||||
@ -151,36 +170,48 @@ func (d *i2cDevice) ReadBlockData(reg uint8, data []byte) error {
|
||||
}
|
||||
|
||||
// WriteByte writes the given byte value to the current register of an i2c device.
|
||||
func (d *i2cDevice) WriteByte(val byte) error {
|
||||
func (d *i2cDevice) WriteByte(address int, val byte) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
if err := d.queryFunctionality(I2C_FUNC_SMBUS_WRITE_BYTE, "write byte"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.smbusAccess(I2C_SMBUS_WRITE, val, I2C_SMBUS_BYTE, nil)
|
||||
return d.smbusAccess(address, I2C_SMBUS_WRITE, val, I2C_SMBUS_BYTE, nil)
|
||||
}
|
||||
|
||||
// WriteByteData writes the given byte value to the given register of an i2c device.
|
||||
func (d *i2cDevice) WriteByteData(reg uint8, val uint8) error {
|
||||
func (d *i2cDevice) WriteByteData(address int, reg uint8, val uint8) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
if err := d.queryFunctionality(I2C_FUNC_SMBUS_WRITE_BYTE_DATA, "write byte data"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var data = val
|
||||
return d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, unsafe.Pointer(&data))
|
||||
return d.smbusAccess(address, I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, unsafe.Pointer(&data))
|
||||
}
|
||||
|
||||
// WriteWordData writes the given 16 bit value starting from the given register of an i2c device.
|
||||
func (d *i2cDevice) WriteWordData(reg uint8, val uint16) error {
|
||||
func (d *i2cDevice) WriteWordData(address int, reg uint8, val uint16) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
if err := d.queryFunctionality(I2C_FUNC_SMBUS_WRITE_WORD_DATA, "write word data"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var data = val
|
||||
return d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA, unsafe.Pointer(&data))
|
||||
return d.smbusAccess(address, I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA, unsafe.Pointer(&data))
|
||||
}
|
||||
|
||||
// WriteBlockData writes the given buffer starting from the given register of an i2c device.
|
||||
func (d *i2cDevice) WriteBlockData(reg uint8, data []byte) error {
|
||||
func (d *i2cDevice) WriteBlockData(address int, reg uint8, data []byte) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
dataLen := len(data)
|
||||
if dataLen > 32 {
|
||||
return fmt.Errorf("Writing blocks larger than 32 bytes (%v) not supported", len(data))
|
||||
@ -190,7 +221,7 @@ func (d *i2cDevice) WriteBlockData(reg uint8, data []byte) error {
|
||||
if i2cDeviceDebug {
|
||||
log.Printf("%s, use fallback\n", err.Error())
|
||||
}
|
||||
return d.writeBlockDataFallback(reg, data)
|
||||
return d.writeBlockDataFallback(address, reg, data)
|
||||
}
|
||||
|
||||
// set the first element with the data size
|
||||
@ -198,12 +229,56 @@ func (d *i2cDevice) WriteBlockData(reg uint8, data []byte) error {
|
||||
buf[0] = byte(dataLen)
|
||||
copy(buf[1:], data)
|
||||
|
||||
return d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_I2C_BLOCK_DATA, unsafe.Pointer(&buf[0]))
|
||||
return d.smbusAccess(address, I2C_SMBUS_WRITE, reg, I2C_SMBUS_I2C_BLOCK_DATA, unsafe.Pointer(&buf[0]))
|
||||
}
|
||||
|
||||
// WriteBytes writes the given buffer starting from the current register of an i2c device.
|
||||
func (d *i2cDevice) WriteBytes(data []byte) error {
|
||||
n, err := d.Write(data)
|
||||
func (d *i2cDevice) WriteBytes(address int, data []byte) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
return d.writeBytes(address, data)
|
||||
}
|
||||
|
||||
// Read implements direct I2C read operations.
|
||||
func (d *i2cDevice) Read(address int, b []byte) (n int, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
return d.read(address, b)
|
||||
}
|
||||
|
||||
// Write implements the io.ReadWriteCloser method by direct I2C write operations.
|
||||
func (d *i2cDevice) Write(address int, b []byte) (n int, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
return d.write(address, b)
|
||||
}
|
||||
|
||||
func (d *i2cDevice) readBlockDataFallback(address int, reg uint8, data []byte) error {
|
||||
if err := d.writeBytes(address, []byte{reg}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.readAndCheckCount(address, data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *i2cDevice) writeBlockDataFallback(address int, reg uint8, data []byte) error {
|
||||
buf := make([]byte, len(data)+1)
|
||||
copy(buf[1:], data)
|
||||
buf[0] = reg
|
||||
|
||||
if err := d.writeBytes(address, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *i2cDevice) writeBytes(address int, data []byte) error {
|
||||
n, err := d.write(address, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -213,67 +288,18 @@ func (d *i2cDevice) WriteBytes(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read implements the io.ReadWriteCloser method by direct I2C read operations.
|
||||
func (d *i2cDevice) Read(b []byte) (n int, err error) {
|
||||
// lazy initialization
|
||||
if d.file == nil {
|
||||
if d.file, err = d.fs.openFile(d.location, os.O_RDWR, os.ModeExclusive); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
func (d *i2cDevice) write(address int, b []byte) (n int, err error) {
|
||||
if err = d.setAddress(address); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return d.file.Read(b)
|
||||
}
|
||||
|
||||
// Write implements the io.ReadWriteCloser method by direct I2C write operations.
|
||||
func (d *i2cDevice) Write(b []byte) (n int, err error) {
|
||||
// lazy initialization
|
||||
if d.file == nil {
|
||||
if d.file, err = d.fs.openFile(d.location, os.O_RDWR, os.ModeExclusive); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := d.openFileLazy("Write"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return d.file.Write(b)
|
||||
}
|
||||
|
||||
func (d *i2cDevice) smbusAccess(readWrite byte, command byte, protocol uint32, dataStart unsafe.Pointer) error {
|
||||
smbus := i2cSmbusIoctlData{
|
||||
readWrite: readWrite,
|
||||
command: command,
|
||||
protocol: protocol,
|
||||
data: dataStart, // the reflected value of unsafePointer equals uintptr(dataStart),
|
||||
}
|
||||
|
||||
if err := d.syscallIoctl(I2C_SMBUS, unsafe.Pointer(&smbus), "SMBus access"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *i2cDevice) readBlockDataFallback(reg uint8, data []byte) error {
|
||||
if err := d.WriteBytes([]byte{reg}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.readAndCheckCount(data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *i2cDevice) writeBlockDataFallback(reg uint8, data []byte) error {
|
||||
buf := make([]byte, len(data)+1)
|
||||
copy(buf[1:], data)
|
||||
buf[0] = reg
|
||||
|
||||
if err := d.WriteBytes(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *i2cDevice) readAndCheckCount(data []byte) error {
|
||||
n, err := d.Read(data)
|
||||
func (d *i2cDevice) readAndCheckCount(address int, data []byte) error {
|
||||
n, err := d.read(address, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -283,6 +309,17 @@ func (d *i2cDevice) readAndCheckCount(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *i2cDevice) read(address int, b []byte) (n int, err error) {
|
||||
if err = d.setAddress(address); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := d.openFileLazy("Read"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return d.file.Read(b)
|
||||
}
|
||||
|
||||
func (d *i2cDevice) queryFunctionality(requested uint64, sender string) error {
|
||||
// lazy initialization
|
||||
if d.funcs == 0 {
|
||||
@ -298,12 +335,45 @@ func (d *i2cDevice) queryFunctionality(requested uint64, sender string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *i2cDevice) syscallIoctl(signal uintptr, payload unsafe.Pointer, sender string) (err error) {
|
||||
// lazy initialization
|
||||
if d.file == nil {
|
||||
if d.file, err = d.fs.openFile(d.location, os.O_RDWR, os.ModeExclusive); err != nil {
|
||||
return err
|
||||
func (d *i2cDevice) smbusAccess(address int, readWrite byte, command byte, protocol uint32, dataStart unsafe.Pointer) error {
|
||||
if err := d.setAddress(address); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
smbus := i2cSmbusIoctlData{
|
||||
readWrite: readWrite,
|
||||
command: command,
|
||||
protocol: protocol,
|
||||
data: dataStart, // the reflected value of unsafePointer equals uintptr(dataStart),
|
||||
}
|
||||
|
||||
sender := fmt.Sprintf("SMBus access r/w: %d, command: %d, protocol: %d, address: %d", readWrite, command, protocol, d.lastAddress)
|
||||
if err := d.syscallIoctl(I2C_SMBUS, unsafe.Pointer(&smbus), sender); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setAddress sets the address of the i2c device to use.
|
||||
func (d *i2cDevice) setAddress(address int) error {
|
||||
if d.lastAddress == address && !forceSetAddress {
|
||||
if i2cDeviceDebug {
|
||||
log.Printf("I2C address %d was already sent - skip", address)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// for go vet false positives, see: https://github.com/golang/go/issues/41205
|
||||
if err := d.syscallIoctl(I2C_SLAVE, unsafe.Pointer(uintptr(byte(address))), "Setting address"); err != nil {
|
||||
return err
|
||||
}
|
||||
d.lastAddress = address
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *i2cDevice) syscallIoctl(signal uintptr, payload unsafe.Pointer, sender string) (err error) {
|
||||
if err := d.openFileLazy(sender); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, _, errno := d.sys.syscall(syscall.SYS_IOCTL, d.file, signal, payload); errno != 0 {
|
||||
return fmt.Errorf("%s failed with syscall.Errno %v", sender, errno)
|
||||
@ -311,3 +381,16 @@ func (d *i2cDevice) syscallIoctl(signal uintptr, payload unsafe.Pointer, sender
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *i2cDevice) openFileLazy(sender string) (err error) {
|
||||
// lazy initialization
|
||||
// note: "os.ModeExclusive" is undefined without create the file. This means for the existing character device,
|
||||
// a second open will not return an error e.g. due to a busy resource, so most likely "os.ModeExclusive" is not really
|
||||
// helpful and we drop it to the default "0" used by normal Open().
|
||||
if d.file == nil {
|
||||
if d.file, err = d.fs.openFile(d.location, os.O_RDWR, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -13,16 +13,38 @@ import (
|
||||
|
||||
const dev = "/dev/i2c-1"
|
||||
|
||||
func syscallFuncsImpl(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
if (trap == syscall.SYS_IOCTL) && (a2 == I2C_FUNCS) {
|
||||
var funcPtr *uint64 = (*uint64)(unsafe.Pointer(a3))
|
||||
*funcPtr = I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_WORD_DATA
|
||||
func getSyscallFuncImpl(errorMask byte) func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
// bit 0: error on function query
|
||||
// bit 1: error on set address
|
||||
// bit 2: error on command
|
||||
return func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
// function query
|
||||
if (trap == syscall.SYS_IOCTL) && (a2 == I2C_FUNCS) {
|
||||
if errorMask&0x01 == 0x01 {
|
||||
return 0, 0, 1
|
||||
}
|
||||
|
||||
var funcPtr *uint64 = (*uint64)(unsafe.Pointer(a3))
|
||||
*funcPtr = I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_WORD_DATA
|
||||
}
|
||||
// set address
|
||||
if (trap == syscall.SYS_IOCTL) && (a2 == I2C_SLAVE) {
|
||||
if errorMask&0x02 == 0x02 {
|
||||
return 0, 0, 1
|
||||
}
|
||||
}
|
||||
// command
|
||||
if (trap == syscall.SYS_IOCTL) && (a2 == I2C_SMBUS) {
|
||||
if errorMask&0x04 == 0x04 {
|
||||
return 0, 0, 1
|
||||
}
|
||||
}
|
||||
// Let all operations succeed
|
||||
return 0, 0, 0
|
||||
}
|
||||
// Let all operations succeed
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
func initTestI2cDeviceWithMockedSys() (*i2cDevice, *mockSyscall) {
|
||||
@ -76,24 +98,14 @@ func TestClose(t *testing.T) {
|
||||
gobottest.Assert(t, d.Close(), nil)
|
||||
}
|
||||
|
||||
func TestSetAddress(t *testing.T) {
|
||||
// arrange
|
||||
d, msc := initTestI2cDeviceWithMockedSys()
|
||||
// act
|
||||
err := d.SetAddress(0xff)
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, msc.devAddress, uintptr(0xff))
|
||||
}
|
||||
|
||||
func TestWriteRead(t *testing.T) {
|
||||
// arrange
|
||||
d, _ := initTestI2cDeviceWithMockedSys()
|
||||
wbuf := []byte{0x01, 0x02, 0x03}
|
||||
rbuf := make([]byte, 4)
|
||||
// act
|
||||
wn, werr := d.Write(wbuf)
|
||||
rn, rerr := d.Read(rbuf)
|
||||
wn, werr := d.Write(1, wbuf)
|
||||
rn, rerr := d.Read(1, rbuf)
|
||||
// assert
|
||||
gobottest.Assert(t, werr, nil)
|
||||
gobottest.Assert(t, rerr, nil)
|
||||
@ -113,8 +125,8 @@ func TestReadByte(t *testing.T) {
|
||||
},
|
||||
"error_syscall": {
|
||||
funcs: I2C_FUNC_SMBUS_READ_BYTE,
|
||||
syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 },
|
||||
wantErr: "SMBus access failed with syscall.Errno operation not permitted",
|
||||
syscallImpl: getSyscallFuncImpl(0x04),
|
||||
wantErr: "SMBus access r/w: 1, command: 0, protocol: 1, address: 2 failed with syscall.Errno operation not permitted",
|
||||
},
|
||||
"error_not_supported": {
|
||||
wantErr: "SMBus read byte not supported",
|
||||
@ -129,7 +141,7 @@ func TestReadByte(t *testing.T) {
|
||||
const want = byte(5)
|
||||
msc.dataSlice = []byte{want}
|
||||
// act
|
||||
got, err := d.ReadByte()
|
||||
got, err := d.ReadByte(2)
|
||||
// assert
|
||||
if tc.wantErr != "" {
|
||||
gobottest.Assert(t, err.Error(), tc.wantErr)
|
||||
@ -157,8 +169,8 @@ func TestReadByteData(t *testing.T) {
|
||||
},
|
||||
"error_syscall": {
|
||||
funcs: I2C_FUNC_SMBUS_READ_BYTE_DATA,
|
||||
syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 },
|
||||
wantErr: "SMBus access failed with syscall.Errno operation not permitted",
|
||||
syscallImpl: getSyscallFuncImpl(0x04),
|
||||
wantErr: "SMBus access r/w: 1, command: 1, protocol: 2, address: 3 failed with syscall.Errno operation not permitted",
|
||||
},
|
||||
"error_not_supported": {
|
||||
wantErr: "SMBus read byte data not supported",
|
||||
@ -176,7 +188,7 @@ func TestReadByteData(t *testing.T) {
|
||||
)
|
||||
msc.dataSlice = []byte{want}
|
||||
// act
|
||||
got, err := d.ReadByteData(reg)
|
||||
got, err := d.ReadByteData(3, reg)
|
||||
// assert
|
||||
if tc.wantErr != "" {
|
||||
gobottest.Assert(t, err.Error(), tc.wantErr)
|
||||
@ -204,8 +216,8 @@ func TestReadWordData(t *testing.T) {
|
||||
},
|
||||
"error_syscall": {
|
||||
funcs: I2C_FUNC_SMBUS_READ_WORD_DATA,
|
||||
syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 },
|
||||
wantErr: "SMBus access failed with syscall.Errno operation not permitted",
|
||||
syscallImpl: getSyscallFuncImpl(0x04),
|
||||
wantErr: "SMBus access r/w: 1, command: 2, protocol: 3, address: 4 failed with syscall.Errno operation not permitted",
|
||||
},
|
||||
"error_not_supported": {
|
||||
wantErr: "SMBus read word data not supported",
|
||||
@ -226,7 +238,7 @@ func TestReadWordData(t *testing.T) {
|
||||
// all common drivers read LSByte first
|
||||
msc.dataSlice = []byte{lsbyte, msbyte}
|
||||
// act
|
||||
got, err := d.ReadWordData(reg)
|
||||
got, err := d.ReadWordData(4, reg)
|
||||
// assert
|
||||
if tc.wantErr != "" {
|
||||
gobottest.Assert(t, err.Error(), tc.wantErr)
|
||||
@ -268,8 +280,8 @@ func TestReadBlockData(t *testing.T) {
|
||||
},
|
||||
"error_syscall": {
|
||||
funcs: I2C_FUNC_SMBUS_READ_I2C_BLOCK,
|
||||
syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 },
|
||||
wantErr: "SMBus access failed with syscall.Errno operation not permitted",
|
||||
syscallImpl: getSyscallFuncImpl(0x04),
|
||||
wantErr: "SMBus access r/w: 1, command: 3, protocol: 8, address: 5 failed with syscall.Errno operation not permitted",
|
||||
},
|
||||
"error_from_used_fallback_if_not_supported": {
|
||||
wantErr: "Read 1 bytes from device by sysfs, expected 10",
|
||||
@ -284,7 +296,7 @@ func TestReadBlockData(t *testing.T) {
|
||||
msc.dataSlice = []byte{wantB0, wantB1, wantB2, wantB3, wantB4, wantB5, wantB6, wantB7, wantB8, wantB9}
|
||||
buf := []byte{12, 23, 34, 45, 56, 67, 78, 89, 98, 87}
|
||||
// act
|
||||
err := d.ReadBlockData(reg, buf)
|
||||
err := d.ReadBlockData(5, reg, buf)
|
||||
// assert
|
||||
if tc.wantErr != "" {
|
||||
gobottest.Assert(t, err.Error(), tc.wantErr)
|
||||
@ -313,8 +325,8 @@ func TestWriteByte(t *testing.T) {
|
||||
},
|
||||
"error_syscall": {
|
||||
funcs: I2C_FUNC_SMBUS_WRITE_BYTE,
|
||||
syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 },
|
||||
wantErr: "SMBus access failed with syscall.Errno operation not permitted",
|
||||
syscallImpl: getSyscallFuncImpl(0x04),
|
||||
wantErr: "SMBus access r/w: 0, command: 68, protocol: 1, address: 6 failed with syscall.Errno operation not permitted",
|
||||
},
|
||||
"error_not_supported": {
|
||||
wantErr: "SMBus write byte not supported",
|
||||
@ -328,7 +340,7 @@ func TestWriteByte(t *testing.T) {
|
||||
d.funcs = tc.funcs
|
||||
const val = byte(0x44)
|
||||
// act
|
||||
err := d.WriteByte(val)
|
||||
err := d.WriteByte(6, val)
|
||||
// assert
|
||||
if tc.wantErr != "" {
|
||||
gobottest.Assert(t, err.Error(), tc.wantErr)
|
||||
@ -355,8 +367,8 @@ func TestWriteByteData(t *testing.T) {
|
||||
},
|
||||
"error_syscall": {
|
||||
funcs: I2C_FUNC_SMBUS_WRITE_BYTE_DATA,
|
||||
syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 },
|
||||
wantErr: "SMBus access failed with syscall.Errno operation not permitted",
|
||||
syscallImpl: getSyscallFuncImpl(0x04),
|
||||
wantErr: "SMBus access r/w: 0, command: 4, protocol: 2, address: 7 failed with syscall.Errno operation not permitted",
|
||||
},
|
||||
"error_not_supported": {
|
||||
wantErr: "SMBus write byte data not supported",
|
||||
@ -373,7 +385,7 @@ func TestWriteByteData(t *testing.T) {
|
||||
val = byte(0x55)
|
||||
)
|
||||
// act
|
||||
err := d.WriteByteData(reg, val)
|
||||
err := d.WriteByteData(7, reg, val)
|
||||
// assert
|
||||
if tc.wantErr != "" {
|
||||
gobottest.Assert(t, err.Error(), tc.wantErr)
|
||||
@ -402,8 +414,8 @@ func TestWriteWordData(t *testing.T) {
|
||||
},
|
||||
"error_syscall": {
|
||||
funcs: I2C_FUNC_SMBUS_WRITE_WORD_DATA,
|
||||
syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 },
|
||||
wantErr: "SMBus access failed with syscall.Errno operation not permitted",
|
||||
syscallImpl: getSyscallFuncImpl(0x04),
|
||||
wantErr: "SMBus access r/w: 0, command: 5, protocol: 3, address: 8 failed with syscall.Errno operation not permitted",
|
||||
},
|
||||
"error_not_supported": {
|
||||
wantErr: "SMBus write word data not supported",
|
||||
@ -422,7 +434,7 @@ func TestWriteWordData(t *testing.T) {
|
||||
wantMSByte = byte(0xD4)
|
||||
)
|
||||
// act
|
||||
err := d.WriteWordData(reg, val)
|
||||
err := d.WriteWordData(8, reg, val)
|
||||
// assert
|
||||
if tc.wantErr != "" {
|
||||
gobottest.Assert(t, err.Error(), tc.wantErr)
|
||||
@ -467,8 +479,8 @@ func TestWriteBlockData(t *testing.T) {
|
||||
},
|
||||
"error_syscall": {
|
||||
funcs: I2C_FUNC_SMBUS_WRITE_I2C_BLOCK,
|
||||
syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 },
|
||||
wantErr: "SMBus access failed with syscall.Errno operation not permitted",
|
||||
syscallImpl: getSyscallFuncImpl(0x04),
|
||||
wantErr: "SMBus access r/w: 0, command: 6, protocol: 8, address: 9 failed with syscall.Errno operation not permitted",
|
||||
},
|
||||
}
|
||||
for name, tc := range tests {
|
||||
@ -479,7 +491,7 @@ func TestWriteBlockData(t *testing.T) {
|
||||
d.funcs = tc.funcs
|
||||
data := []byte{b0, b1, b2, b3, b4, b5, b6, b7, b8, b9}
|
||||
// act
|
||||
err := d.WriteBlockData(reg, data)
|
||||
err := d.WriteBlockData(9, reg, data)
|
||||
// assert
|
||||
if tc.wantErr != "" {
|
||||
gobottest.Assert(t, err.Error(), tc.wantErr)
|
||||
@ -502,12 +514,22 @@ func TestWriteBlockDataTooMuch(t *testing.T) {
|
||||
// arrange
|
||||
d, _ := initTestI2cDeviceWithMockedSys()
|
||||
// act
|
||||
err := d.WriteBlockData(0x01, make([]byte, 33))
|
||||
err := d.WriteBlockData(10, 0x01, make([]byte, 33))
|
||||
// assert
|
||||
gobottest.Assert(t, err, errors.New("Writing blocks larger than 32 bytes (33) not supported"))
|
||||
}
|
||||
|
||||
func Test_lazyInit(t *testing.T) {
|
||||
func Test_setAddress(t *testing.T) {
|
||||
// arrange
|
||||
d, msc := initTestI2cDeviceWithMockedSys()
|
||||
// act
|
||||
err := d.setAddress(0xff)
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, msc.devAddress, uintptr(0xff))
|
||||
}
|
||||
|
||||
func Test_queryFunctionality(t *testing.T) {
|
||||
var tests = map[string]struct {
|
||||
requested uint64
|
||||
dev string
|
||||
@ -519,18 +541,18 @@ func Test_lazyInit(t *testing.T) {
|
||||
"ok": {
|
||||
requested: I2C_FUNC_SMBUS_READ_BYTE,
|
||||
dev: dev,
|
||||
syscallImpl: syscallFuncsImpl,
|
||||
syscallImpl: getSyscallFuncImpl(0x00),
|
||||
wantFile: true,
|
||||
wantFuncs: 0x7E0000,
|
||||
},
|
||||
"dev_null_error": {
|
||||
dev: os.DevNull,
|
||||
syscallImpl: syscallFuncsImpl,
|
||||
syscallImpl: getSyscallFuncImpl(0x00),
|
||||
wantErr: " : /dev/null: No such file.",
|
||||
},
|
||||
"query_funcs_error": {
|
||||
dev: dev,
|
||||
syscallImpl: func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, 1 },
|
||||
syscallImpl: getSyscallFuncImpl(0x01),
|
||||
wantErr: "Querying functionality failed with syscall.Errno operation not permitted",
|
||||
wantFile: true,
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user