forked from hybridgroup/gobot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
i2c_device.go
157 lines (127 loc) · 3.1 KB
/
i2c_device.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package sysfs
import (
"fmt"
"io"
"os"
"syscall"
"unsafe"
)
const (
I2C_SLAVE = 0x0703
I2C_SMBUS = 0x0720
I2C_SMBUS_WRITE = 0
I2C_SMBUS_READ = 1
I2C_SMBUS_I2C_BLOCK_DATA = 8
// Adapter functionality
I2C_FUNCS = 0x0705
I2C_FUNC_SMBUS_READ_BLOCK_DATA = 0x01000000
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA = 0x02000000
)
type i2cSmbusIoctlData struct {
readWrite byte
command byte
size uint32
data uintptr
}
type I2cDevice interface {
io.ReadWriteCloser
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 and device address
func NewI2cDevice(location string, address int) (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
}
err = d.SetAddress(address)
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) Read(b []byte) (n int, err error) {
if d.funcs&I2C_FUNC_SMBUS_READ_BLOCK_DATA == 0 {
// Adapter doesn't support SMBus block read
return d.file.Read(b)
}
data := make([]byte, len(b)+1)
data[0] = byte(len(b))
smbus := &i2cSmbusIoctlData{
readWrite: I2C_SMBUS_READ,
command: 0,
size: I2C_SMBUS_I2C_BLOCK_DATA,
data: uintptr(unsafe.Pointer(&data[0])),
}
_, _, errno := Syscall(
syscall.SYS_IOCTL,
d.file.Fd(),
I2C_SMBUS,
uintptr(unsafe.Pointer(smbus)),
)
if errno != 0 {
return n, fmt.Errorf("Read failed with syscall.Errno %v", errno)
}
copy(b, data[1:])
return int(data[0]), nil
}
func (d *i2cDevice) Write(b []byte) (n int, err error) {
if d.funcs&I2C_FUNC_SMBUS_WRITE_BLOCK_DATA == 0 {
// Adapter doesn't support SMBus block write
return d.file.Write(b)
}
// Command byte - a data byte which often selects a register on the device:
// https://www.kernel.org/doc/Documentation/i2c/smbus-protocol
command := byte(b[0])
buf := b[1:]
data := make([]byte, len(buf)+1)
data[0] = byte(len(buf))
copy(data[1:], buf)
smbus := &i2cSmbusIoctlData{
readWrite: I2C_SMBUS_WRITE,
command: command,
size: I2C_SMBUS_I2C_BLOCK_DATA,
data: uintptr(unsafe.Pointer(&data[0])),
}
_, _, errno := Syscall(
syscall.SYS_IOCTL,
d.file.Fd(),
I2C_SMBUS,
uintptr(unsafe.Pointer(smbus)),
)
if errno != 0 {
err = fmt.Errorf("Write failed with syscall.Errno %v", errno)
}
return len(b), err
}