Renderer, ...: use PixelRect::GetCenter()
[xcsoar.git] / src / Device / Driver / Volkslogger / Protocol.cpp
blob343783b1d51791b5f6cffb8cefeddda9625bf55a
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "Protocol.hpp"
25 #include "Util/CRC.hpp"
26 #include "Device/Port/Port.hpp"
27 #include "Operation/Operation.hpp"
28 #include "Time/TimeoutClock.hpp"
30 #include <string.h>
32 bool
33 Volkslogger::Reset(Port &port, OperationEnvironment &env, unsigned n)
35 static constexpr unsigned delay = 2;
37 while (n-- > 0) {
38 if (!port.Write(CAN))
39 return false;
41 env.Sleep(delay);
44 return true;
47 bool
48 Volkslogger::Handshake(Port &port, OperationEnvironment &env,
49 unsigned timeout_ms)
51 TimeoutClock timeout(timeout_ms);
53 while (true) { // Solange R's aussenden, bis ein L zurückkommt
54 if (!port.Write('R'))
55 return false;
57 int remaining = timeout.GetRemainingSigned();
58 if (remaining < 0)
59 return false;
61 if (remaining > 500)
62 remaining = 500;
64 Port::WaitResult result =
65 port.WaitForChar('L', env, remaining);
66 if (result == Port::WaitResult::READY)
67 break;
69 if (result != Port::WaitResult::TIMEOUT)
70 return false;
72 /* timeout, try again */
75 unsigned count = 1;
76 while (true) { // Auf 4 hintereinanderfolgende L's warten
77 int remaining = timeout.GetRemainingSigned();
78 if (remaining < 0)
79 return false;
81 if (port.WaitForChar('L', env, remaining) != Port::WaitResult::READY)
82 return false;
84 count++;
85 if (count >= 4)
86 return true;
90 bool
91 Volkslogger::Connect(Port &port, OperationEnvironment &env,
92 unsigned timeout_ms)
94 return Reset(port, env, 10) && Handshake(port, env, timeout_ms);
97 bool
98 Volkslogger::ConnectAndFlush(Port &port, OperationEnvironment &env,
99 unsigned timeout_ms)
101 port.Flush();
103 return Connect(port, env, timeout_ms) && port.FullFlush(env, 50, 300);
106 static bool
107 SendWithCRC(Port &port, const void *data, size_t length,
108 OperationEnvironment &env)
110 if (!port.FullWrite(data, length, env, 2000))
111 return false;
113 uint16_t crc16 = UpdateCRC16CCITT(data, length, 0);
114 return port.Write(crc16 >> 8) && port.Write(crc16 & 0xff);
117 bool
118 Volkslogger::SendCommand(Port &port, OperationEnvironment &env,
119 Command cmd, uint8_t param1, uint8_t param2)
121 static constexpr unsigned delay = 2;
123 /* flush buffers */
124 if (!port.FullFlush(env, 20, 100))
125 return false;
127 /* reset command interpreter */
128 if (!Reset(port, env, 6))
129 return false;
131 /* send command packet */
133 const uint8_t cmdarray[8] = {
134 (uint8_t)cmd, param1, param2,
135 0, 0, 0, 0, 0,
138 if (!port.Write(ENQ))
139 return false;
141 env.Sleep(delay);
143 if (!SendWithCRC(port, cmdarray, sizeof(cmdarray), env))
144 return false;
146 /* wait for confirmation */
148 return port.WaitRead(env, 4000) == Port::WaitResult::READY &&
149 port.GetChar() == 0;
152 gcc_const
153 static int
154 GetBaudRateIndex(unsigned baud_rate)
156 switch(baud_rate) {
157 case 9600:
158 return 1;
160 case 19200:
161 return 2;
163 case 38400:
164 return 3;
166 case 57600:
167 return 4;
169 case 115200:
170 return 5;
172 default:
173 return -1;
177 bool
178 Volkslogger::SendCommandSwitchBaudRate(Port &port, OperationEnvironment &env,
179 Command cmd, uint8_t param1,
180 unsigned baud_rate)
182 int baud_rate_index = GetBaudRateIndex(baud_rate);
183 if (baud_rate_index < 0)
184 return false;
186 if (!SendCommand(port, env, cmd, param1, baud_rate_index))
187 return false;
189 return port.SetBaudrate(baud_rate);
192 bool
193 Volkslogger::WaitForACK(Port &port, OperationEnvironment &env)
195 return port.WaitForChar(ACK, env, 30000) == Port::WaitResult::READY;
199 Volkslogger::ReadBulk(Port &port, OperationEnvironment &env,
200 void *buffer, size_t max_length,
201 unsigned timeout_firstchar_ms)
203 unsigned nbytes = 0;
204 bool dle_r = false;
205 uint16_t crc16 = 0;
206 bool start = false, ende = false;
208 memset(buffer, 0xff, max_length);
210 uint8_t *p = (uint8_t *)buffer;
212 constexpr unsigned TIMEOUT_NORMAL_MS = 2000;
214 * We need to wait longer for the first char to
215 * give the logger time to calculate security
216 * when downloading a log-file.
217 * Therefore timeout_firstchar is configurable.
218 * If the timeout parameter is not specified or 0,
219 * set standard timeout
221 if (timeout_firstchar_ms == 0)
222 timeout_firstchar_ms = TIMEOUT_NORMAL_MS;
224 while (!ende) {
225 // Zeichen anfordern und darauf warten
227 if (!port.Write(ACK))
228 return -1;
230 // Set longer timeout on first char
231 unsigned timeout = start ? TIMEOUT_NORMAL_MS : timeout_firstchar_ms;
232 if (port.WaitRead(env, timeout) != Port::WaitResult::READY)
233 return -1;
235 int ch = port.GetChar();
236 if (ch < 0)
237 return -1;
239 // dabei ist Benutzerabbruch jederzeit möglich
240 if (env.IsCancelled()) {
241 env.Sleep(10);
242 port.Write(CAN);
243 port.Write(CAN);
244 port.Write(CAN);
245 return -1;
248 // oder aber das empfangene Zeichen wird ausgewertet
249 switch (ch) {
250 case DLE:
251 if (!dle_r) { //!DLE, DLE -> Achtung!
252 dle_r = true;
254 else { // DLE, DLE -> DLE-Zeichen
255 dle_r = false;
256 if (start) {
257 if(nbytes < max_length)
258 *p++ = ch;
259 nbytes++;
260 crc16 = UpdateCRC16CCITT(ch, crc16);
263 break;
264 case ETX:
265 if (!dle_r) { //!DLE, ETX -> Zeichen
266 if (start) {
267 if(nbytes < max_length) {
268 *p++ = ch;
270 nbytes++;
271 crc16 = UpdateCRC16CCITT(ch, crc16);
274 else {
275 if (start) {
276 ende = true; // DLE, ETX -> Blockende
277 dle_r = false;
280 break;
281 case STX:
282 if (!dle_r) { //!DLE, STX -> Zeichen
283 if (start) {
284 if(nbytes < max_length)
285 *p++ = ch;
286 nbytes++;
287 crc16 = UpdateCRC16CCITT(ch, crc16);
290 else {
291 start = true; // DLE, STX -> Blockstart
292 dle_r = false;
293 crc16 = 0;
295 break;
296 default:
297 if (start) {
298 if(nbytes < max_length)
299 *p++ = ch;
300 nbytes++;
301 crc16 = UpdateCRC16CCITT(ch, crc16);
303 break;
307 env.Sleep(100);
309 if (crc16 != 0)
310 return -1;
312 if (nbytes < 2)
313 return 0;
315 // CRC am Ende abschneiden
316 return nbytes - 2;
319 bool
320 Volkslogger::WriteBulk(Port &port, OperationEnvironment &env,
321 const void *buffer, unsigned length)
323 const unsigned delay = 1;
325 env.SetProgressRange(length);
327 uint16_t crc16 = 0;
328 const uint8_t *p = (const uint8_t *)buffer, *end = p + length;
329 while (p < end) {
330 unsigned n = end - p;
331 if (n > 400)
332 n = 400;
334 n = port.Write(p, n);
335 if (n == 0)
336 return false;
338 crc16 = UpdateCRC16CCITT(p, n, crc16);
339 p += n;
341 env.SetProgressPosition(p - (const uint8_t *)buffer);
343 /* throttle sending a bit, or the Volkslogger's receive buffer
344 will overrun */
345 env.Sleep(delay * 100);
348 return port.Write(crc16 >> 8) && port.Write(crc16 & 0xff);
352 Volkslogger::SendCommandReadBulk(Port &port, OperationEnvironment &env,
353 Command cmd,
354 void *buffer, size_t max_length,
355 const unsigned timeout_firstchar_ms)
357 return SendCommand(port, env, cmd)
358 ? ReadBulk(port, env, buffer, max_length, timeout_firstchar_ms)
359 : -1;
363 Volkslogger::SendCommandReadBulk(Port &port, unsigned baud_rate,
364 OperationEnvironment &env,
365 Command cmd, uint8_t param1,
366 void *buffer, size_t max_length,
367 const unsigned timeout_firstchar_ms)
369 unsigned old_baud_rate = port.GetBaudrate();
371 if (old_baud_rate != 0) {
372 if (!SendCommandSwitchBaudRate(port, env, cmd, param1, baud_rate))
373 return -1;
375 /* after switching baud rates, this sleep time is necessary; it has
376 been verified experimentally */
377 env.Sleep(300);
378 } else {
379 /* port does not support baud rate switching, use plain
380 SendCommand() without new baud rate */
382 if (!SendCommand(port, env, cmd, param1))
383 return -1;
386 int nbytes = ReadBulk(port, env, buffer, max_length, timeout_firstchar_ms);
388 if (old_baud_rate != 0)
389 port.SetBaudrate(old_baud_rate);
391 return nbytes;
394 bool
395 Volkslogger::SendCommandWriteBulk(Port &port, OperationEnvironment &env,
396 Command cmd,
397 const void *data, size_t size)
399 if (!SendCommand(port, env, cmd, 0, 0) || !WaitForACK(port, env))
400 return false;
402 env.Sleep(100);
404 return WriteBulk(port, env, data, size) && WaitForACK(port, env);
407 size_t
408 Volkslogger::ReadFlight(Port &port, unsigned databaud,
409 OperationEnvironment &env,
410 unsigned flightnr, bool secmode,
411 void *buffer, size_t buffersize)
413 const Volkslogger::Command cmd = secmode
414 ? Volkslogger::cmd_GFS
415 : Volkslogger::cmd_GFL;
418 * It is necessary to wait long for the first reply from
419 * the Logger in ReadBulk.
420 * Since the VL needs time to calculate the Security of
421 * the log before it responds.
423 const unsigned timeout_firstchar_ms = 300000;
425 // Download binary log data supports BulkBaudrate
426 int groesse = SendCommandReadBulk(port, databaud, env, cmd,
427 flightnr, buffer, buffersize,
428 timeout_firstchar_ms);
429 if (groesse <= 0)
430 return 0;
432 // read signature
433 env.Sleep(300);
436 * Testing has shown that downloading the Signature does not support
437 * BulkRate. It has to be done with standard IO Rate (9600)
439 int sgr = SendCommandReadBulk(port, env, Volkslogger::cmd_SIG,
440 (uint8_t *)buffer + groesse,
441 buffersize - groesse);
442 if (sgr <= 0)
443 return 0;
445 return groesse + sgr;