Fix issue where DFU responses are missed causing DFU to time out intermittently

This commit is contained in:
Elara 2021-10-21 20:17:44 -07:00
parent d1a75f1c67
commit 1dde7f9b07

85
dfu.go
View File

@ -6,7 +6,6 @@ import (
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"errors" "errors"
"io"
"io/fs" "io/fs"
"io/ioutil" "io/ioutil"
"os" "os"
@ -191,6 +190,23 @@ func (dfu *DFU) sendProgress() {
} }
} }
// bufferPropsCh resends all messages on in to a new channel that is buffered with 5 elements
func bufferPropsCh(in chan *bluez.PropertyChanged) chan *bluez.PropertyChanged {
// Create new buffered channel
out := make(chan *bluez.PropertyChanged, 5)
go func() {
// Close channel when underlying channel closed
defer close(out)
// For every property change on in
for prop := range in {
// Write to out
out <- prop
}
}()
// Return new buffered channel
return out
}
// Start DFU process // Start DFU process
func (dfu *DFU) Start() error { func (dfu *DFU) Start() error {
if dfu.progress == nil { if dfu.progress == nil {
@ -209,10 +225,12 @@ func (dfu *DFU) Start() error {
} }
// Watch for property changes on control point // Watch for property changes on control point
dfu.ctrlRespCh, err = dfu.ctrlPointChar.WatchProperties() unbufferedCh, err := dfu.ctrlPointChar.WatchProperties()
if err != nil { if err != nil {
return err return err
} }
// Buffer properties channel so no changes are missed
dfu.ctrlRespCh = bufferPropsCh(unbufferedCh)
// Run step one // Run step one
err = dfu.stepOne() err = dfu.stepOne()
@ -279,24 +297,42 @@ func (dfu *DFU) Start() error {
return nil return nil
} }
// Reset reverts all values back to default to prepare for
// the next DFU.
func (dfu *DFU) Reset() {
dfu.bytesRecvd = 0
dfu.bytesSent = 0
dfu.fwSize = 0
dfu.fwSendDone = false
dfu.fwImage = nil
dfu.initPacket = nil
dfu.ctrlRespCh = nil
dfu.progress = nil
}
// on waits for the given command to be received on // on waits for the given command to be received on
// the control point characteristic, then runs the callback. // the control point characteristic, then runs the callback.
func (dfu *DFU) on(cmd []byte, onCmdCb func(data []byte) error) error { func (dfu *DFU) on(cmd []byte, onCmdCb func(data []byte) error) error {
select { // Use for loop in case of invalid property
case propChanged := <-dfu.ctrlRespCh: for {
if propChanged.Name != "Value" { select {
case propChanged := <-dfu.ctrlRespCh:
// If property was invalid
if propChanged.Name != "Value" {
// Keep waiting
continue
}
// Assert propery value as byte slice
data := propChanged.Value.([]byte)
// If command has prefix of given command
if bytes.HasPrefix(data, cmd) {
// Return callback with data after command
return onCmdCb(data[len(cmd):])
}
return ErrDFUInvalidResponse return ErrDFUInvalidResponse
case <-time.After(50 * time.Second):
return ErrDFUTimeout
} }
// Assert propery value as byte slice
data := propChanged.Value.([]byte)
// If command has prefix of given command
if bytes.HasPrefix(data, cmd) {
// Return callback with data after command
return onCmdCb(data[len(cmd):])
}
return ErrDFUInvalidResponse
case <-time.After(50 * time.Second):
return ErrDFUTimeout
} }
} }
@ -346,7 +382,13 @@ func (dfu *DFU) stepSeven() error {
// While send is not done // While send is not done
for !dfu.fwSendDone { for !dfu.fwSendDone {
for i := 0; i < DFUPktRecvInterval; i++ { for i := 0; i < DFUPktRecvInterval; i++ {
amtLeft := int(dfu.fwSize) - dfu.bytesSent amtLeft := dfu.fwSize - int64(dfu.bytesSent)
// If no bytes left to send, end transfer
if amtLeft == 0 {
dfu.sendProgress()
dfu.fwSendDone = true
return nil
}
var segment []byte var segment []byte
// If amount left is less than segment size // If amount left is less than segment size
if amtLeft < DFUSegmentSize { if amtLeft < DFUSegmentSize {
@ -357,13 +399,8 @@ func (dfu *DFU) stepSeven() error {
segment = make([]byte, DFUSegmentSize) segment = make([]byte, DFUSegmentSize)
} }
// Write firmware image into slice // Write firmware image into slice
n, err := dfu.fwImage.Read(segment) _, err := dfu.fwImage.Read(segment)
// If EOF, send is done if err != nil {
if err == io.EOF {
dfu.sendProgress()
dfu.fwSendDone = true
return nil
} else if err != nil {
return err return err
} }
// Write segment to packet characteristic // Write segment to packet characteristic
@ -372,7 +409,7 @@ func (dfu *DFU) stepSeven() error {
return err return err
} }
// Increment bytes sent by amount read // Increment bytes sent by amount read
dfu.bytesSent += n dfu.bytesSent += len(segment)
} }
// On 0x11, verify packet receipt size // On 0x11, verify packet receipt size
err := dfu.on(DFUNotifPktRecvd, func(data []byte) error { err := dfu.on(DFUNotifPktRecvd, func(data []byte) error {