Repository navigation
Expand file tree
/
Copy pathdevice.go
More file actions
401 lines (335 loc) · 11.4 KB
/
Copy pathdevice.go
File metadata and controls
401 lines (335 loc) · 11.4 KB
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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
package evdev
import (
"bytes"
"encoding/binary"
"fmt"
"os"
"runtime"
"syscall"
"unsafe"
)
// InputDevice represent a Linux kernel input device in userspace.
// It can be used to query and write device properties, read input events,
// or grab it for exclusive access.
type InputDevice struct {
file *os.File
driverVersion int32
}
// OpenWithFlags creates a new InputDevice from the given path. The input device
// is opened with the specified flags (O_RDONLY etc.).
// It is the responsibility of the user to provide sane flags and handle potential errors
// resulting from inappropriate flag combinations or permissions.
// Returns an error if the device node could not be opened or its properties failed to read.
func OpenWithFlags(path string, flags int) (*InputDevice, error) {
d := &InputDevice{}
var err error
d.file, err = os.OpenFile(path, flags, 0)
if err != nil {
return nil, err
}
d.driverVersion, err = ioctlEVIOCGVERSION(d.file.Fd())
if err != nil {
_ = d.file.Close()
return nil, fmt.Errorf("cannot get driver version: %v", err)
}
return d, nil
}
// Open creates a new InputDevice from the given path. The input device is
// opened with flag O_RDWR. Returns an error if the device node could not
// be opened or its properties failed to read.
func Open(path string) (*InputDevice, error) {
return OpenWithFlags(path, os.O_RDWR)
}
// OpenByNameWithFlags creates a new InputDevice from the device name as reported
// by the kernel. The input device is opened with the specified flags (O_RDONLY etc.).
// It is the responsibility of the user to provide sane flags and handle potential errors
// resulting from inappropriate flag combinations or permissions.
// Returns an error if the name does not exist, or the device node could
// not be opened or its properties failed to read.
func OpenByNameWithFlags(name string, flags int) (*InputDevice, error) {
devices, err := ListDevicePaths()
if err != nil {
return nil, err
}
for _, d := range devices {
if d.Name == name {
return OpenWithFlags(d.Path, flags)
}
}
return nil, fmt.Errorf("could not find input device with name %q", name)
}
// OpenByName creates a new InputDevice from the device name as reported by the kernel.
// The input device is opened with flag O_RDWR.
// Returns an error if the name does not exist, or the device node could
// not be opened or its properties failed to read.
func OpenByName(name string) (*InputDevice, error) {
return OpenByNameWithFlags(name, os.O_RDWR)
}
// Close releases the resources held by an InputDevice. After calling this
// function, the InputDevice is no longer operational.
func (d *InputDevice) Close() error {
return d.file.Close()
}
// Path returns the device's node path it was opened under.
func (d *InputDevice) Path() string {
return d.file.Name()
}
// DriverVersion returns the version of the Linux Evdev driver.
// The three ints returned by this function describe the major, minor and
// micro parts of the version code.
func (d *InputDevice) DriverVersion() (int, int, int) {
return int(d.driverVersion >> 16),
int((d.driverVersion >> 8) & 0xff),
int((d.driverVersion >> 0) & 0xff)
}
// Name returns the device's name as reported by the kernel.
func (d *InputDevice) Name() (string, error) {
return ioctlEVIOCGNAME(d.file.Fd())
}
// PhysicalLocation returns the device's physical location as reported by the kernel.
func (d *InputDevice) PhysicalLocation() (string, error) {
return ioctlEVIOCGPHYS(d.file.Fd())
}
// UniqueID returns the device's unique identifier as reported by the kernel.
func (d *InputDevice) UniqueID() (string, error) {
return ioctlEVIOCGUNIQ(d.file.Fd())
}
// InputID returns the device's vendor/product/busType/version information as reported by the kernel.
func (d *InputDevice) InputID() (InputID, error) {
return ioctlEVIOCGID(d.file.Fd())
}
// CapableTypes returns a slice of EvType that are the device supports
func (d *InputDevice) CapableTypes() []EvType {
var types []EvType
evBits, err := ioctlEVIOCGBIT(d.file.Fd(), 0)
if err != nil {
return []EvType{}
}
evBitmap := newBitmap(evBits)
for _, t := range evBitmap.setBits() {
types = append(types, EvType(t))
}
return types
}
// CapableEvents returns a slice of EvCode that are the device supports for given EvType
func (d *InputDevice) CapableEvents(t EvType) []EvCode {
var codes []EvCode
evBits, err := ioctlEVIOCGBIT(d.file.Fd(), int(t))
if err != nil {
return []EvCode{}
}
evBitmap := newBitmap(evBits)
for _, t := range evBitmap.setBits() {
codes = append(codes, EvCode(t))
}
return codes
}
// Properties returns a slice of EvProp that are the device supports
func (d *InputDevice) Properties() []EvProp {
var props []EvProp
propBits, err := ioctlEVIOCGPROP(d.file.Fd())
if err != nil {
return []EvProp{}
}
propBitmap := newBitmap(propBits)
for _, p := range propBitmap.setBits() {
props = append(props, EvProp(p))
}
return props
}
// State return a StateMap for the given type. The map will be empty if the requested type
// is not supported by the device.
func (d *InputDevice) State(t EvType) (StateMap, error) {
fd := d.file.Fd()
evBits, err := ioctlEVIOCGBIT(fd, 0)
if err != nil {
return nil, fmt.Errorf("cannot get evBits: %v", err)
}
evBitmap := newBitmap(evBits)
if !evBitmap.bitIsSet(int(t)) {
return StateMap{}, nil
}
codeBits, err := ioctlEVIOCGBIT(fd, int(t))
if err != nil {
return nil, fmt.Errorf("cannot get evBits: %v", err)
}
codeBitmap := newBitmap(codeBits)
var stateBits []byte
switch t {
case EV_KEY:
stateBits, err = ioctlEVIOCGKEY(fd)
case EV_SW:
stateBits, err = ioctlEVIOCGSW(fd)
case EV_LED:
stateBits, err = ioctlEVIOCGLED(fd)
case EV_SND:
stateBits, err = ioctlEVIOCGSND(fd)
default:
err = fmt.Errorf("unsupported evType %d", t)
}
if err != nil {
return nil, err
}
stateBitmap := newBitmap(stateBits)
st := StateMap{}
for _, code := range codeBitmap.setBits() {
st[EvCode(code)] = stateBitmap.bitIsSet(code)
}
return st, nil
}
// SetMask masks the events in mask for the event type of the mask. Other event
// types are unaffected. The filter is applied at the kernel level.
//
// If successful, SetMask overrides any previous mask set for the event type.
//
// Not every event type can be filtered (an example is EV_SYN). The kernel may
// fail to filter events, always check the error.
//
// Usage examples (error checking omitted for brevity):
//
// Produce no key-press events:
//
// evdev.SetMask(evdev.AllowMask(evdev.EV_KEY, nil))
//
// Only produce BTN_SOUTH and BTN_EAST (from EV_KEY) events.
//
// evdev.SetMask(evdev.AllowMask(evdev.EV_KEY, evdev.EvCode{
// evdev.BTN_SOUTH,
// evdev.BTN_EAST,
// }))
//
// Produce all EV_KEY events except for BTN_NORTH:
//
// evdev.SetMask(evdev.DenyMask(evdev.EV_KEY, evdev.EvCode{
// evdev.BTN_NORTH,
// }))
//
// Only allow EV_KEY (by silencing all other event types):
//
// for _, evtype := range dev.CapableTypes() {
// if evtype == evdev.EV_KEY || evtype == evdev.EV_SYN {
// continue // Don't silence EV_KEY, and EV_SYN can't be silenced.
// }
//
// mask := evdev.AllowMask(evtype, nil) // Allow nothing.
// if err := dev.SetMask(mask); err != nil { ... }
// }
//
// NOTE: Unsetting a filter is not supported. The effect can be achieved by
// passing evdev.DenyMask(evtype, nil), though it is not strictly the same due
// to the kernel potentially knowing about more events. It is recommended to
// close and re-open the [InputDevice] to reset.
func (d *InputDevice) SetMask(mask Mask) error {
// EV_SYN cannot be filtered, don't even try (see __evdev_is_filtered in
// drivers/input/evdev.c).
if mask.typ == EV_SYN {
return fmt.Errorf("the kernel does not allow masking EV_SYN")
}
cnt := mask.typ.count()
if cnt == 0 {
return fmt.Errorf("could not size mask for unknown event type %d", mask.typ)
}
// Calculate required bytes for bitmask. The bitmask must be long (uintptr)
// sized, even when the bitmap itself would require fewer bytes.
//
// EVIOCSMASK passes this size as codes_size to the bits_from_user() in the
// kernel, which requires `codes_size % sizeof(long) == 0`.
const (
bytesPerWord = unsafe.Sizeof(uintptr(0))
bitsPerWord = bytesPerWord * 8
)
words := (uintptr(cnt) + bitsPerWord - 1) / bitsPerWord
bits := newBitmap(make([]byte, words*bytesPerWord))
if mask.allow {
for _, code := range mask.codes {
bits.set(int(code))
}
} else {
bits.setAll()
for _, code := range mask.codes {
bits.clear(int(code))
}
}
// Ensure the GC does not collected the bitmask until _after_ the ioctl has
// returned. An alternative would be to make [CodesPtr] an [unsafe.Pointer],
// but that would not work well on 32-bit as the kernel still expects a 64-bit
// integer.
defer runtime.KeepAlive(bits.bits)
return ioctlEVIOCSMASK(d.file.Fd(), InputMask{
Type: uint32(mask.typ),
CodesSize: uint32(len(bits.bits)),
CodesPtr: uint64(uintptr(unsafe.Pointer(&bits.bits[0]))),
})
}
// AbsInfos returns the AbsInfo struct for all axis the device supports.
func (d *InputDevice) AbsInfos() (map[EvCode]AbsInfo, error) {
a := make(map[EvCode]AbsInfo)
absBits, err := ioctlEVIOCGBIT(d.file.Fd(), EV_ABS)
if err != nil {
return nil, fmt.Errorf("cannot get absBits: %v", err)
}
absBitmap := newBitmap(absBits)
for _, abs := range absBitmap.setBits() {
absInfo, err := ioctlEVIOCGABS(d.file.Fd(), abs)
if err == nil {
a[EvCode(abs)] = absInfo
}
}
return a, nil
}
// Grab grabs the device for exclusive access. No other process will receive
// input events until the device instance is active.
func (d *InputDevice) Grab() error {
return ioctlEVIOCGRAB(d.file.Fd(), 1)
}
// Ungrab releases a previously taken exclusive use with Grab().
func (d *InputDevice) Ungrab() error {
return ioctlEVIOCGRAB(d.file.Fd(), 0)
}
// Revoke revokes device access
func (d *InputDevice) Revoke() error {
return ioctlEVIOCREVOKE(d.file.Fd())
}
// NonBlock sets file descriptor into nonblocking mode.
// This way it is possible to interrupt ReadOne call by closing the device.
// Note: file.Fd() call will set file descriptor back to blocking mode so make sure your program
// is not using any other method than ReadOne after NonBlock call.
func (d *InputDevice) NonBlock() error {
return syscall.SetNonblock(int(d.file.Fd()), true)
}
// Read and returns a slice of InputEvents from the device.
// It blocks until events has been received or an error has occurred.
func (d *InputDevice) ReadSlice(eventSlice int) ([]InputEvent, error) {
buffer := make([]byte, eventsize*eventSlice)
bytesRead, err := d.file.Read(buffer)
if err != nil {
return nil, err
}
// Calculate how many complete events we actually got
count := bytesRead / eventsize
if count == 0 {
return nil, nil // no complete event in this read
}
// Create events slice dynamically
events := make([]InputEvent, count)
reader := bytes.NewReader(buffer[:bytesRead])
if err = binary.Read(reader, binary.LittleEndian, &events); err != nil {
return nil, err
}
return events, nil
}
// ReadOne reads one InputEvent from the device. It blocks until an event has
// been received or an error has occurred.
func (d *InputDevice) ReadOne() (*InputEvent, error) {
event := InputEvent{}
err := binary.Read(d.file, binary.LittleEndian, &event)
if err != nil {
return nil, err
}
return &event, nil
}
// WriteOne writes one InputEvent to the device.
// Useful for controlling LEDs of the device
func (d *InputDevice) WriteOne(event *InputEvent) error {
return binary.Write(d.file, binary.LittleEndian, event)
}