-
Notifications
You must be signed in to change notification settings - Fork 0
/
devfs.go
177 lines (157 loc) · 4.5 KB
/
devfs.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
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux
package spi
import (
"fmt"
"os"
"syscall"
"unsafe"
// "golang.org/x/exp/io/spi/driver"
"github.com/the-sibyl/piSPI/driver"
)
const (
devfs_MAGIC = 107
devfs_NRBITS = 8
devfs_TYPEBITS = 8
devfs_SIZEBITS = 13
devfs_DIRBITS = 3
devfs_NRSHIFT = 0
devfs_TYPESHIFT = devfs_NRSHIFT + devfs_NRBITS
devfs_SIZESHIFT = devfs_TYPESHIFT + devfs_TYPEBITS
devfs_DIRSHIFT = devfs_SIZESHIFT + devfs_SIZEBITS
devfs_READ = 4
devfs_WRITE = 2
)
type payload struct {
tx uint64
rx uint64
length uint32
speed uint32
delay uint16
bits uint8
csChange uint8
txNBits uint8
rxNBits uint8
pad uint16
}
// Devfs is an SPI driver that works against the devfs.
// You need to have loaded the "spidev" Linux module to use this driver.
type Devfs struct {
// Dev is the device to be opened.
// Device name is usually in the /dev/spidev<bus>.<chip> format.
// Required.
Dev string
// Mode is the SPI mode. SPI mode is a combination of polarity and phases.
// CPOL is the high order bit, CPHA is the low order. Pre-computed mode
// values are Mode0, Mode1, Mode2 and Mode3. The value of the mode argument
// can be overriden by the device's driver.
// Required.
Mode Mode
// MaxSpeed is the max clock speed (Hz) and can be overriden by the device's driver.
// Required.
MaxSpeed int64
}
// Open opens the provided device with the specified options
// and returns a connection.
func (d *Devfs) Open() (driver.Conn, error) {
f, err := os.OpenFile(d.Dev, os.O_RDWR, os.ModeDevice)
if err != nil {
return nil, err
}
conn := &devfsConn{f: f}
if err := conn.Configure(driver.Mode, int(d.Mode)); err != nil {
conn.Close()
return nil, err
}
if err := conn.Configure(driver.MaxSpeed, int(d.MaxSpeed)); err != nil {
conn.Close()
return nil, err
}
return conn, nil
}
type devfsConn struct {
f *os.File
mode uint8
speed uint32
bits uint8
delay uint16
csChange uint8
}
func (c *devfsConn) Configure(k, v int) error {
switch k {
case driver.Mode:
m := uint8(v)
if err := c.ioctl(requestCode(devfs_WRITE, devfs_MAGIC, 1, 1), uintptr(unsafe.Pointer(&m))); err != nil {
return fmt.Errorf("error setting mode to %v: %v", m, err)
}
c.mode = m
case driver.Bits:
b := uint8(v)
if err := c.ioctl(requestCode(devfs_WRITE, devfs_MAGIC, 3, 1), uintptr(unsafe.Pointer(&b))); err != nil {
return fmt.Errorf("error setting bits per word to %v: %v", b, err)
}
c.bits = b
case driver.MaxSpeed:
s := uint32(v)
if err := c.ioctl(requestCode(devfs_WRITE, devfs_MAGIC, 4, 4), uintptr(unsafe.Pointer(&s))); err != nil {
return fmt.Errorf("error setting speed to %v: %v", s, err)
}
c.speed = s
case driver.Order:
o := uint8(v)
if err := c.ioctl(requestCode(devfs_WRITE, devfs_MAGIC, 2, 1), uintptr(unsafe.Pointer(&o))); err != nil {
return fmt.Errorf("error setting bit order to %v: %v", o, err)
}
case driver.Delay:
c.delay = uint16(v)
case driver.CSChange:
c.csChange = uint8(v)
default:
return fmt.Errorf("unknown key: %v", k)
}
return nil
}
func (c *devfsConn) Tx(w, r []byte) error {
if r == nil {
r = make([]byte, len(w))
}
// TODO(jbd): len(w) == len(r)?
// TODO(jbd): Allow nil w.
p := payload{
tx: uint64(uintptr(unsafe.Pointer(&w[0]))),
rx: uint64(uintptr(unsafe.Pointer(&r[0]))),
length: uint32(len(w)),
speed: c.speed,
delay: c.delay,
bits: c.bits,
csChange: c.csChange,
}
// TODO(jbd): Read from the device and fill rx.
return c.ioctl(msgRequestCode(1), uintptr(unsafe.Pointer(&p)))
}
func (c *devfsConn) Close() error {
return c.f.Close()
}
// requestCode returns the device specific request code for the specified direction,
// type, number and size to be used in the ioctl call.
func requestCode(dir, typ, nr, size uintptr) uintptr {
return (dir << devfs_DIRSHIFT) | (typ << devfs_TYPESHIFT) | (nr << devfs_NRSHIFT) | (size << devfs_SIZESHIFT)
}
// msgRequestCode returns the device specific value for the SPI
// message payload to be used in the ioctl call.
// n represents the number of messages.
func msgRequestCode(n uint32) uintptr {
return uintptr(0x40006B00 + (n * 0x200000))
}
// ioctl makes an IOCTL on the open device file descriptor.
func (c *devfsConn) ioctl(a1, a2 uintptr) error {
_, _, errno := syscall.Syscall(
syscall.SYS_IOCTL, c.f.Fd(), a1, a2,
)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
}