-
Notifications
You must be signed in to change notification settings - Fork 1
/
unp.go
178 lines (160 loc) · 2.86 KB
/
unp.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
package unp
import (
"bytes"
"errors"
"fmt"
"io"
"github.com/dyrkin/composer"
)
type CommandType byte
const (
C_POLL CommandType = iota
C_SREQ
C_AREQ
C_SRSP
C_RES0
C_RES1
C_RES2
C_RES3
)
type Subsystem byte
const (
S_RES0 Subsystem = iota
S_SYS
S_MAC
S_NWK
S_AF
S_ZDO
S_SAPI
S_UTIL
S_DBG
S_APP
S_OTA
S_ZNP
S_SPARE_12
S_UBL
S_RES14
S_APP_CNF
S_RES16
S_PROTOBUF
S_RES18 // RPC_SYS_PB_NWK_MGR
S_RES19 // RPC_SYS_PB_GW
S_RES20 // RPC_SYS_PB_OTA_MGR
S_GP
S_MAX
)
type Unp struct {
size uint8
Transceiver io.ReadWriter
incoming chan byte
errors chan error
}
type Frame struct {
CommandType CommandType
Subsystem Subsystem
Command byte
Payload []byte
}
const sof byte = 0xFE
func New(size uint8, transmitter io.ReadWriter) *Unp {
u := &Unp{size, transmitter, make(chan byte), make(chan error)}
go u.receiver()
return u
}
func (u *Unp) WriteFrame(frame *Frame) error {
rendered := u.RenderFrame(frame)
_, err := u.Transceiver.Write(rendered)
return err
}
func (u *Unp) RenderFrame(frame *Frame) []byte {
cmp := composer.New()
cmd0 := ((byte(frame.CommandType << 5)) & 0xE0) | (byte(frame.Subsystem) & 0x1F)
cmd1 := frame.Command
cmp.Byte(sof)
len := len(frame.Payload)
if u.size == 1 {
cmp.Uint8(uint8(len))
} else {
cmp.Uint16be(uint16(len))
}
cmp.Byte(cmd0).Byte(cmd1).Bytes(frame.Payload)
fcs := checksum(cmp.Make()[1:])
cmp.Byte(fcs)
return cmp.Make()
}
func (u *Unp) ReadFrame() (frame *Frame, err error) {
var b byte
var checksumBuffer bytes.Buffer
var read = func() {
select {
case b = <-u.incoming:
checksumBuffer.WriteByte(b)
case err = <-u.errors:
}
}
if read(); err != nil {
return
}
if b != sof {
return nil, errors.New("Invalid start of frame")
}
if read(); err != nil {
return
}
var payloadLength uint16
if u.size == 1 {
payloadLength = uint16(b)
} else {
b1 := uint16(b) << 8
if read(); err != nil {
return
}
payloadLength = b1 | uint16(b)
}
if read(); err != nil {
return
}
cmd0 := b
if read(); err != nil {
return
}
cmd1 := b
payload := make([]byte, payloadLength)
for i := 0; i < int(payloadLength); i++ {
if read(); err != nil {
return
}
payload[i] = b
}
checksumBytes := checksumBuffer.Bytes()
if read(); err != nil {
return
}
fcs := b
csum := checksum(checksumBytes[1:])
if fcs != csum {
err = fmt.Errorf("Invalid checksum. Expected: %b, actual: %b", fcs, csum)
return
}
commandType := (cmd0 & 0xE0) >> 5
subsystem := cmd0 & 0x1F
return &Frame{CommandType(commandType), Subsystem(subsystem), cmd1, payload}, nil
}
func checksum(buf []byte) byte {
fcs := byte(0)
for i := 0; i < len(buf); i++ {
fcs ^= buf[i]
}
return fcs
}
func (u *Unp) receiver() {
var buf [1]byte
for {
n, err := io.ReadFull(u.Transceiver, buf[:])
if n > 0 {
u.incoming <- buf[0]
} else if err != io.EOF {
u.errors <- err
}
}
}