-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
i2c_device.go
212 lines (174 loc) · 4.99 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
package sysfs
import (
"fmt"
"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 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
}