231 lines
5.5 KiB
Go
231 lines
5.5 KiB
Go
package sysfs
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
// From /usr/include/linux/i2c-dev.h:
|
|
// ioctl signals
|
|
I2C_SLAVE = 0x0703
|
|
I2C_FUNCS = 0x0705
|
|
I2C_SMBUS = 0x0720
|
|
// Read/write markers
|
|
I2C_SMBUS_READ = 1
|
|
I2C_SMBUS_WRITE = 0
|
|
|
|
// From /usr/include/linux/i2c.h:
|
|
// Adapter functionality
|
|
I2C_FUNC_SMBUS_READ_BYTE = 0x00020000
|
|
I2C_FUNC_SMBUS_WRITE_BYTE = 0x00040000
|
|
I2C_FUNC_SMBUS_READ_BYTE_DATA = 0x00080000
|
|
I2C_FUNC_SMBUS_WRITE_BYTE_DATA = 0x00100000
|
|
I2C_FUNC_SMBUS_READ_WORD_DATA = 0x00200000
|
|
I2C_FUNC_SMBUS_WRITE_WORD_DATA = 0x00400000
|
|
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_BROKEN = 6
|
|
I2C_SMBUS_BLOCK_PROC_CALL = 7 /* SMBus 2.0 */
|
|
I2C_SMBUS_I2C_BLOCK_DATA = 8 /* SMBus 2.0 */
|
|
)
|
|
|
|
type i2cSmbusIoctlData struct {
|
|
readWrite byte
|
|
command byte
|
|
size uint32
|
|
data uintptr
|
|
}
|
|
|
|
type I2cOperations interface {
|
|
io.ReadWriteCloser
|
|
ReadByte() (val byte, err error)
|
|
ReadByteData(reg uint8) (val uint8, err error)
|
|
ReadWordData(reg uint8) (val uint16, err error)
|
|
WriteByte(val byte) (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 byte, err error) {
|
|
if d.funcs&I2C_FUNC_SMBUS_READ_BYTE == 0 {
|
|
return 0, fmt.Errorf("SMBus read byte not supported")
|
|
}
|
|
|
|
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) {
|
|
if d.funcs&I2C_FUNC_SMBUS_READ_BYTE_DATA == 0 {
|
|
return 0, fmt.Errorf("SMBus read byte data not supported")
|
|
}
|
|
|
|
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) {
|
|
if d.funcs&I2C_FUNC_SMBUS_READ_WORD_DATA == 0 {
|
|
return 0, fmt.Errorf("SMBus read word data not supported")
|
|
}
|
|
|
|
var data uint16
|
|
err = d.smbusAccess(I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA, uintptr(unsafe.Pointer(&data)))
|
|
return data, err
|
|
}
|
|
|
|
func (d *i2cDevice) WriteByte(val byte) (err error) {
|
|
if d.funcs&I2C_FUNC_SMBUS_WRITE_BYTE == 0 {
|
|
return fmt.Errorf("SMBus write byte not supported")
|
|
}
|
|
|
|
err = d.smbusAccess(I2C_SMBUS_WRITE, val, I2C_SMBUS_BYTE, uintptr(0))
|
|
return err
|
|
}
|
|
|
|
func (d *i2cDevice) WriteByteData(reg uint8, val uint8) (err error) {
|
|
if d.funcs&I2C_FUNC_SMBUS_WRITE_BYTE_DATA == 0 {
|
|
return fmt.Errorf("SMBus write byte data not supported")
|
|
}
|
|
|
|
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) {
|
|
if d.funcs&I2C_FUNC_SMBUS_WRITE_WORD_DATA == 0 {
|
|
return fmt.Errorf("SMBus write word data not supported")
|
|
}
|
|
|
|
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 len(data) > 32 {
|
|
return fmt.Errorf("Writing blocks larger than 32 bytes (%v) not supported", len(data))
|
|
}
|
|
|
|
buf := make([]byte, len(data)+1)
|
|
copy(buf[1:], data)
|
|
buf[0] = reg
|
|
|
|
n, err := d.file.Write(buf)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if n != len(buf) {
|
|
return fmt.Errorf("Write to device truncated, %v of %v written", n, len(buf))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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
|
|
}
|