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 }