forked from SAFTehnika/Xamarin-NRF-DFU
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathButonlessDFU.cs
165 lines (144 loc) · 7.12 KB
/
ButonlessDFU.cs
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
// Copyright (c) 2018 SAF Tehnika. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Plugin.BluetoothLE;
using System.Diagnostics;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Threading;
using System.Text;
using System.Linq;
namespace Plugin.XamarinNordicDFU
{
class DFUTimeout : Exception
{
}
partial class DFU
{
/// <summary>
/// Switch device in Secure DFU state
/// ! IMPORTANT, assumes that each device has different name !
/// </summary>
/// <param name="device"></param>
/// <returns>Device which is in buttonles state</returns>
private async Task<IDevice> ButtonlessDFUWithoutBondsToSecureDFU(IDevice device)
{
Debug.WriteLineIf(LogLevelDebug, String.Format("Start of Buttonless switching"));
//await RefreshGattAsync(device);
IGattCharacteristic buttonlessCharacteristic = null;
TaskCompletionSource<CharacteristicGattResult> notif = null;
device.Connect();
device = await device.ConnectWait().Timeout(DeviceConnectionTimeout);
buttonlessCharacteristic = await device.GetKnownCharacteristics(DfuService, DfuButtonlessCharacteristicWithoutBonds).Timeout(OperationTimeout);
await buttonlessCharacteristic.EnableNotifications(true).Timeout(OperationTimeout);
// Change device Name
// Advertisment name should not be longer than 20 symbols
var newFullName = device.Name + "DFU";
var newName = new string(newFullName.Take(20).ToArray());
byte[] name = Encoding.ASCII.GetBytes(newName);
int newNameLen = name.Length;
byte[] newNameCommand = new byte[newNameLen + 1 + 1];
newNameCommand[0] = CChangeAdvertisedName;
newNameCommand[1] = (byte)(uint)newNameLen;
name.CopyTo(newNameCommand, 2);
var nameChangeNotif = GetTimedNotification(buttonlessCharacteristic);
await buttonlessCharacteristic.Write(newNameCommand).Timeout(OperationTimeout);
var nameChangeResult = await nameChangeNotif.Task;
Debug.WriteLineIf(LogLevelDebug, String.Format("Device name change response {0}", nameChangeResult.Data != null ? BitConverter.ToString(nameChangeResult.Data) : "Empty"));
// Jump from the main application to Secure DFU bootloader (Secure DFU mode)
notif = GetTimedNotification(buttonlessCharacteristic);
await buttonlessCharacteristic.Write(CEnterDFU).Timeout(OperationTimeout);
var result = await notif.Task;
Debug.WriteLineIf(LogLevelDebug, String.Format("Restart response {0}", result.Data != null ? BitConverter.ToString(result.Data) : "Empty"));
await buttonlessCharacteristic.DisableNotifications();
/*
* The response received from the DFU device contains:
* +---------+--------+----------------------------------------------------+
* | byte no | value | description |
* +---------+--------+----------------------------------------------------+
* | 0 | 0x20 | Response code |
* | 1 | 0x01 | The Op Code of a request that this response is for |
* | 2 | STATUS | Status code |
* +---------+--------+----------------------------------------------------+
*/
int status = result.Data[2];
if (status != ButtonlessSwitchSuccessCode)
{
throw new Exception("Init status not correct " + status);
}
bool alreadyRestarted = false;
IDisposable dispose = null;
dispose = device.WhenStatusChanged().Subscribe(res =>
{
if(res == ConnectionStatus.Disconnected || res == ConnectionStatus.Disconnecting)
{
alreadyRestarted = true;
}
Debug.WriteLine("###################### STAT {0}", res);
});
dispose?.Dispose();
if (!alreadyRestarted)
{
await device.WhenDisconnected().Take(1).Timeout(DeviceRestartTimeout);
device.CancelConnection();
}
IDevice newDevice = await ScanDFUDevice(device, newName);
Debug.WriteLineIf(LogLevelDebug, String.Format("End of Buttonless switching"));
return newDevice;
}
private async Task<IDevice> ScanDFUDevice(IDevice device, string newName)
{
IDevice newDevice = null;
IDisposable scanner = null;
bool DeviceFound = false;
object locked = new object();
var tcs = new TaskCompletionSource<bool>();
Task task = Task.Delay(DeviceRestartTimeout).ContinueWith(t => {
lock (locked)
{
if (!DeviceFound)
{
scanner?.Dispose();
tcs.TrySetException(new TimeoutException());
}
}
});
// Scan new Device which is (MAC adress + 1)
scanner = CrossBleAdapter.Current.Scan().Subscribe(scanResult =>
{
Debug.WriteLineIf(LogLevelDebug, String.Format("Scanned device: Name: |{0}:{1}| UUID: {2} preuid: {3}, compareRes: {4}",device.Name, scanResult.Device.Name, scanResult.Device.Uuid,device.Uuid, device.Uuid.CompareTo(scanResult.Device.Uuid)));
// If name is the same but id is different, should be our device
if (newName == scanResult.Device.Name)
{
//if (device.Uuid.CompareTo(scanResult.Device.Uuid) != 0)
//{
Debug.WriteLineIf(LogLevelDebug, String.Format("Device found: Name: {0} UUID: {1}", scanResult.Device.Name, scanResult.Device.Uuid));
lock (locked)
{
DeviceFound = true;
}
scanner?.Dispose();
newDevice = scanResult.Device;
tcs.TrySetResult(true);
//}
}
else if (device.Name == scanResult.Device.Name)
{
if (device.Uuid.CompareTo(scanResult.Device.Uuid) != 0)
{
Debug.WriteLineIf(LogLevelDebug, String.Format("Device found: Name: {0} UUID: {1}", scanResult.Device.Name, scanResult.Device.Uuid));
lock (locked)
{
DeviceFound = true;
}
scanner?.Dispose();
newDevice = scanResult.Device;
tcs.TrySetResult(true);
}
}
});
await tcs.Task;
return newDevice;
}
}
}