hybridgroup.gobot/sysfs/i2c_device.go

211 lines
5.1 KiB
Go

package sysfs
import (
"fmt"
"io"
"os"
"syscall"
"unsafe"
)
const (
// ioctl signals
I2C_SLAVE = 0x0703
I2C_FUNCS = 0x0705
I2C_SMBUS = 0x0720
// Read/write markers
I2C_SMBUS_READ = 1
I2C_SMBUS_WRITE = 0
// Adapter functionality
I2C_FUNC_SMBUS_READ_BLOCK_DATA = 0x01000000
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA = 0x02000000
// Transaction types
I2C_SMBUS_BYTE = 1
I2C_SMBUS_BYTE_DATA = 2
I2C_SMBUS_WORD_DATA = 3
I2C_SMBUS_PROC_CALL = 4
I2C_SMBUS_BLOCK_DATA = 5
I2C_SMBUS_I2C_BLOCK_DATA = 6
I2C_SMBUS_BLOCK_PROC_CALL = 7 /* SMBus 2.0 */
I2C_SMBUS_BLOCK_DATA_PEC = 8 /* SMBus 2.0 */
I2C_SMBUS_PROC_CALL_PEC = 9 /* SMBus 2.0 */
I2C_SMBUS_BLOCK_PROC_CALL_PEC = 10 /* SMBus 2.0 */
I2C_SMBUS_WORD_DATA_PEC = 11 /* SMBus 2.0 */
)
type i2cSmbusIoctlData struct {
readWrite byte
command byte
size uint32
data uintptr
}
type I2cOperations interface {
io.ReadWriteCloser
ReadByte() (val uint8, err error)
ReadByteData(reg uint8) (val uint8, err error)
ReadWordData(reg uint8) (val uint16, err error)
ReadBlockData(reg uint8, b []byte) (n int, err error)
WriteByte(val uint8) (err error)
WriteByteData(reg uint8, val uint8) (err error)
WriteWordData(reg uint8, val uint16) (err error)
WriteBlockData(reg uint8, b []byte) (err error)
}
// I2cDevice is the interface to a specific i2c bus
type I2cDevice interface {
I2cOperations
SetAddress(int) error
}
type i2cDevice struct {
file File
funcs uint64 // adapter functionality mask
}
// NewI2cDevice returns an io.ReadWriteCloser with the proper ioctrl given
// an i2c bus location.
func NewI2cDevice(location string) (d *i2cDevice, err error) {
d = &i2cDevice{}
if d.file, err = OpenFile(location, os.O_RDWR, os.ModeExclusive); err != nil {
return
}
if err = d.queryFunctionality(); err != nil {
return
}
return
}
func (d *i2cDevice) queryFunctionality() (err error) {
_, _, errno := Syscall(
syscall.SYS_IOCTL,
d.file.Fd(),
I2C_FUNCS,
uintptr(unsafe.Pointer(&d.funcs)),
)
if errno != 0 {
err = fmt.Errorf("Querying functionality failed with syscall.Errno %v", errno)
}
return
}
func (d *i2cDevice) SetAddress(address int) (err error) {
_, _, errno := Syscall(
syscall.SYS_IOCTL,
d.file.Fd(),
I2C_SLAVE,
uintptr(byte(address)),
)
if errno != 0 {
err = fmt.Errorf("Setting address failed with syscall.Errno %v", errno)
}
return
}
func (d *i2cDevice) Close() (err error) {
return d.file.Close()
}
func (d *i2cDevice) ReadByte() (val uint8, err error) {
var data uint8
err = d.smbusAccess(I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, uintptr(unsafe.Pointer(&data)))
return data, err
}
func (d *i2cDevice) ReadByteData(reg uint8) (val uint8, err error) {
var data uint8
err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, uintptr(unsafe.Pointer(&data)))
return data, err
}
func (d *i2cDevice) ReadWordData(reg uint8) (val uint16, err error) {
var data uint16
err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA, uintptr(unsafe.Pointer(&data)))
return data, err
}
func (d *i2cDevice) ReadBlockData(reg uint8, buf []byte) (n int, err error) {
if d.funcs&I2C_FUNC_SMBUS_READ_BLOCK_DATA == 0 {
return 0, fmt.Errorf("SMBus block data reading not supported")
}
data := make([]byte, 32+1) // Max message + size as defined by SMBus standard
err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_I2C_BLOCK_DATA, uintptr(unsafe.Pointer(&data[0])))
copy(buf, data[1:])
return int(data[0]), err
}
func (d *i2cDevice) WriteByte(val uint8) (err error) {
err = d.smbusAccess(I2C_SMBUS_WRITE, val, I2C_SMBUS_BYTE, uintptr(0))
return err
}
func (d *i2cDevice) WriteByteData(reg uint8, val uint8) (err error) {
var data = val
err = d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, uintptr(unsafe.Pointer(&data)))
return err
}
func (d *i2cDevice) WriteWordData(reg uint8, val uint16) (err error) {
var data = val
err = d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA, uintptr(unsafe.Pointer(&data)))
return err
}
func (d *i2cDevice) WriteBlockData(reg uint8, data []byte) (err error) {
if d.funcs&I2C_FUNC_SMBUS_WRITE_BLOCK_DATA == 0 {
return fmt.Errorf("SMBus block data writing not supported")
}
if len(data) > 32 {
data = data[:32]
}
buf := make([]byte, len(data)+1)
copy(buf[:1], data)
buf[0] = uint8(len(data))
err = d.smbusAccess(I2C_SMBUS_WRITE, reg, I2C_SMBUS_I2C_BLOCK_DATA, uintptr(unsafe.Pointer(&buf[0])))
return err
}
// Read implements the io.ReadWriteCloser method by direct I2C read operations.
func (d *i2cDevice) Read(b []byte) (n int, err error) {
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) {
return d.file.Write(b)
}
func (d *i2cDevice) smbusAccess(readWrite byte, command byte, size uint32, data uintptr) error {
smbus := &i2cSmbusIoctlData{
readWrite: readWrite,
command: command,
size: size,
data: data,
}
_, _, errno := Syscall(
syscall.SYS_IOCTL,
d.file.Fd(),
I2C_SMBUS,
uintptr(unsafe.Pointer(smbus)),
)
if errno != 0 {
return fmt.Errorf("Failed with syscall.Errno %v", errno)
}
return nil
}