forked from google/periph
/
i2creg.go
252 lines (236 loc) · 7.26 KB
/
i2creg.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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// Copyright 2017 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
// Package i2creg defines I²C bus registry to list buses present on the host.
package i2creg
import (
"errors"
"strconv"
"strings"
"sync"
"periph.io/x/periph/conn/i2c"
)
// Opener opens an handle to a bus.
//
// It is provided by the actual bus driver.
type Opener func() (i2c.BusCloser, error)
// Ref references an I²C bus.
//
// It is returned by All() to enumerate all registered buses.
type Ref struct {
// Name of the bus.
//
// It must not be a sole number. It must be unique across the host.
Name string
// Aliases are the alternative names that can be used to reference this bus.
Aliases []string
// Number of the bus or -1 if the bus doesn't have any "native" number.
//
// Buses provided by the CPU normally have a 0 based number. Buses provided
// via an addon (like over USB) generally are not numbered.
Number int
// Open is the factory to open an handle to this I²C bus.
Open Opener
}
// Open opens an I²C bus by its name, an alias or its number and returns an
// handle to it.
//
// Specify the empty string "" to get the first available bus. This is the
// recommended default value unless an application knows the exact bus to use.
//
// Each bus can register multiple aliases, each leading to the same bus handle.
//
// "Bus number" is a generic concept that is highly dependent on the platform
// and OS. On some platform, the first bus may have the number 0, 1 or higher.
// Bus numbers are not necessarily continuous and may not start at 0. It was
// observed that the bus number as reported by the OS may change across OS
// revisions.
//
// When the I²C bus is provided by an off board plug and play bus like USB via
// a FT232H USB device, there can be no associated number.
func Open(name string) (i2c.BusCloser, error) {
var r *Ref
var err error
func() {
mu.Lock()
defer mu.Unlock()
if len(byName) == 0 {
err = errors.New("i2creg: no bus found; did you forget to call Init()?")
return
}
if len(name) == 0 {
r = getDefault()
return
}
// Try by name, by alias, by number.
if r = byName[name]; r == nil {
if r = byAlias[name]; r == nil {
if i, err2 := strconv.Atoi(name); err2 == nil {
r = byNumber[i]
}
}
}
}()
if err != nil {
return nil, err
}
if r == nil {
return nil, errors.New("i2creg: can't open unknown bus: " + strconv.Quote(name))
}
return r.Open()
}
// All returns a copy of all the registered references to all know I²C buses
// available on this host.
//
// The list is sorted by the bus name.
func All() []*Ref {
mu.Lock()
defer mu.Unlock()
out := make([]*Ref, 0, len(byName))
for _, v := range byName {
r := &Ref{Name: v.Name, Aliases: make([]string, len(v.Aliases)), Number: v.Number, Open: v.Open}
copy(r.Aliases, v.Aliases)
out = insertRef(out, r)
}
return out
}
// Register registers an I²C bus.
//
// Registering the same bus name twice is an error, e.g. o.Name(). o.Number()
// can be -1 to signify that the bus doesn't have an inherent "bus number". A
// good example is a bus provided over a FT232H device connected on an USB bus.
// In this case, the bus name should be created from the serial number of the
// device for unique identification.
func Register(name string, aliases []string, number int, o Opener) error {
if len(name) == 0 {
return errors.New("i2creg: can't register a bus with no name")
}
if o == nil {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with nil Opener")
}
if number < -1 {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with invalid bus number " + strconv.Itoa(number))
}
if _, err := strconv.Atoi(name); err == nil {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with name being only a number")
}
if strings.Contains(name, ":") {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with name containing ':'")
}
for _, alias := range aliases {
if len(alias) == 0 {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an empty alias")
}
if name == alias {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an alias the same as the bus name")
}
if _, err := strconv.Atoi(alias); err == nil {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an alias that is a number: " + strconv.Quote(alias))
}
if strings.Contains(alias, ":") {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an alias containing ':': " + strconv.Quote(alias))
}
}
mu.Lock()
defer mu.Unlock()
if _, ok := byName[name]; ok {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice")
}
if _, ok := byAlias[name]; ok {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice; it is already an alias")
}
if number != -1 {
if _, ok := byNumber[number]; ok {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + "; bus number " + strconv.Itoa(number) + " is already registered")
}
}
for _, alias := range aliases {
if _, ok := byName[alias]; ok {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already a bus")
}
if _, ok := byAlias[alias]; ok {
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already an alias")
}
}
r := &Ref{Name: name, Aliases: make([]string, len(aliases)), Number: number, Open: o}
copy(r.Aliases, aliases)
byName[name] = r
if number != -1 {
byNumber[number] = r
}
for _, alias := range aliases {
byAlias[alias] = r
}
return nil
}
// Unregister removes a previously registered I²C bus.
//
// This can happen when an I²C bus is exposed via an USB device and the device
// is unplugged.
func Unregister(name string) error {
mu.Lock()
defer mu.Unlock()
r := byName[name]
if r == nil {
return errors.New("i2creg: can't unregister unknown bus name " + strconv.Quote(name))
}
delete(byName, name)
delete(byNumber, r.Number)
for _, alias := range r.Aliases {
delete(byAlias, alias)
}
return nil
}
//
var (
mu sync.Mutex
byName = map[string]*Ref{}
// Caches
byNumber = map[int]*Ref{}
byAlias = map[string]*Ref{}
)
// getDefault returns the Ref that should be used as the default bus.
func getDefault() *Ref {
var o *Ref
if len(byNumber) == 0 {
// Fallback to use byName using a lexical sort.
name := ""
for n, o2 := range byName {
if len(name) == 0 || n < name {
o = o2
name = n
}
}
return o
}
number := int((^uint(0)) >> 1)
for n, o2 := range byNumber {
if number > n {
number = n
o = o2
}
}
return o
}
func insertRef(l []*Ref, r *Ref) []*Ref {
n := r.Name
i := search(len(l), func(i int) bool { return l[i].Name > n })
l = append(l, nil)
copy(l[i+1:], l[i:])
l[i] = r
return l
}
// search implements the same algorithm as sort.Search().
//
// It was extracted to to not depend on sort, which depends on reflect.
func search(n int, f func(int) bool) int {
lo := 0
for hi := n; lo < hi; {
if i := int(uint(lo+hi) >> 1); !f(i) {
lo = i + 1
} else {
hi = i
}
}
return lo
}