Browse Source

Marlin Binary Protocol Mark II (#14817)

Chris Pepper 4 years ago
parent
commit
f499cecf0d

+ 36
- 0
Marlin/src/feature/binary_protocol.cpp View File

@@ -0,0 +1,36 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
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.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+#include "../inc/MarlinConfigPre.h"
24
+
25
+#if ENABLED(BINARY_FILE_TRANSFER)
26
+
27
+#include "../sd/cardreader.h"
28
+#include "binary_protocol.h"
29
+
30
+char* SDFileTransferProtocol::Packet::Open::data = nullptr;
31
+size_t SDFileTransferProtocol::data_waiting, SDFileTransferProtocol::transfer_timeout, SDFileTransferProtocol::idle_timeout;
32
+bool SDFileTransferProtocol::transfer_active, SDFileTransferProtocol::dummy_transfer, SDFileTransferProtocol::compression;
33
+
34
+BinaryStream binaryStream[NUM_SERIAL];
35
+
36
+#endif // BINARY_FILE_TRANSFER

+ 471
- 0
Marlin/src/feature/binary_protocol.h View File

@@ -0,0 +1,471 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
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.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+#pragma once
23
+
24
+#include "../inc/MarlinConfig.h"
25
+
26
+#define BINARY_STREAM_COMPRESSION
27
+
28
+#if ENABLED(BINARY_STREAM_COMPRESSION)
29
+  #include "../libs/heatshrink/heatshrink_decoder.h"
30
+#endif
31
+
32
+inline bool bs_serial_data_available(const uint8_t index) {
33
+  switch (index) {
34
+    case 0: return MYSERIAL0.available();
35
+    #if NUM_SERIAL > 1
36
+      case 1: return MYSERIAL1.available();
37
+    #endif
38
+  }
39
+  return false;
40
+}
41
+
42
+inline int bs_read_serial(const uint8_t index) {
43
+  switch (index) {
44
+    case 0: return MYSERIAL0.read();
45
+    #if NUM_SERIAL > 1
46
+      case 1: return MYSERIAL1.read();
47
+    #endif
48
+  }
49
+  return -1;
50
+}
51
+
52
+#if ENABLED(BINARY_STREAM_COMPRESSION)
53
+  static heatshrink_decoder hsd;
54
+  static uint8_t decode_buffer[512] = {};
55
+#endif
56
+
57
+class SDFileTransferProtocol  {
58
+private:
59
+  struct Packet {
60
+    struct [[gnu::packed]] Open {
61
+      static bool validate(char* buffer, size_t length) {
62
+        return (length > sizeof(Open) && buffer[length - 1] == '\0');
63
+      }
64
+      static Open& decode(char* buffer) {
65
+        data = &buffer[2];
66
+        return *reinterpret_cast<Open*>(buffer);
67
+      }
68
+      bool compression_enabled() { return compression & 0x1; }
69
+      bool dummy_transfer() { return dummy & 0x1; }
70
+      static char* filename() { return data; }
71
+      private:
72
+        uint8_t dummy, compression;
73
+        static char* data;  // variable length strings complicate things
74
+    };
75
+  };
76
+
77
+  static bool file_open(char* filename) {
78
+    if (!dummy_transfer) {
79
+      card.initsd();
80
+      card.openFile(filename, false);
81
+      if (!card.isFileOpen()) return false;
82
+    }
83
+    transfer_active = true;
84
+    data_waiting = 0;
85
+    #if ENABLED(BINARY_STREAM_COMPRESSION)
86
+      heatshrink_decoder_reset(&hsd);
87
+    #endif
88
+    return true;
89
+  }
90
+
91
+  static bool file_write(char* buffer, const size_t length) {
92
+    #if ENABLED(BINARY_STREAM_COMPRESSION)
93
+      if (compression) {
94
+        size_t total_processed = 0, processed_count = 0;
95
+        HSD_poll_res presult;
96
+
97
+        while (total_processed < length) {
98
+          heatshrink_decoder_sink(&hsd, reinterpret_cast<uint8_t*>(&buffer[total_processed]), length - total_processed, &processed_count);
99
+          total_processed += processed_count;
100
+          do {
101
+            presult = heatshrink_decoder_poll(&hsd, &decode_buffer[data_waiting], sizeof(decode_buffer) - data_waiting, &processed_count);
102
+            data_waiting += processed_count;
103
+            if (data_waiting == sizeof(decode_buffer)) {
104
+              if (!dummy_transfer)
105
+                if (card.write(decode_buffer, data_waiting) < 0) {
106
+                  return false;
107
+                }
108
+              data_waiting = 0;
109
+            }
110
+          } while (presult == HSDR_POLL_MORE);
111
+        }
112
+        return true;
113
+      }
114
+    #endif
115
+    return (dummy_transfer || card.write(buffer, length) >= 0);
116
+  }
117
+
118
+  static bool file_close() {
119
+    if (!dummy_transfer) {
120
+      #if ENABLED(BINARY_STREAM_COMPRESSION)
121
+        // flush any buffered data
122
+        if (data_waiting) {
123
+          if (card.write(decode_buffer, data_waiting) < 0) return false;
124
+          data_waiting = 0;
125
+        }
126
+      #endif
127
+      card.closefile();
128
+      card.release();
129
+    }
130
+    #if ENABLED(BINARY_STREAM_COMPRESSION)
131
+      heatshrink_decoder_finish(&hsd);
132
+    #endif
133
+    transfer_active = false;
134
+    return true;
135
+  }
136
+
137
+  static void transfer_abort() {
138
+    if (!dummy_transfer) {
139
+      card.closefile();
140
+      card.removeFile(card.filename);
141
+      card.release();
142
+      #if ENABLED(BINARY_STREAM_COMPRESSION)
143
+        heatshrink_decoder_finish(&hsd);
144
+      #endif
145
+    }
146
+    transfer_active = false;
147
+    return;
148
+  }
149
+
150
+  enum class FileTransfer : uint8_t { QUERY, OPEN, CLOSE, WRITE, ABORT };
151
+
152
+  static size_t data_waiting, transfer_timeout, idle_timeout;
153
+  static bool transfer_active, dummy_transfer, compression;
154
+
155
+public:
156
+
157
+  static void idle() {
158
+    // If a transfer is interrupted and a file is left open, abort it after TIMEOUT ms
159
+    const millis_t ms = millis();
160
+    if (transfer_active && ELAPSED(ms, idle_timeout)) {
161
+      idle_timeout = ms + IDLE_PERIOD;
162
+      if (ELAPSED(ms, transfer_timeout)) transfer_abort();
163
+    }
164
+  }
165
+
166
+  static void process(uint8_t packet_type, char* buffer, const uint16_t length) {
167
+    transfer_timeout = millis() + TIMEOUT;
168
+    switch (static_cast<FileTransfer>(packet_type)) {
169
+      case FileTransfer::QUERY:
170
+        SERIAL_ECHOPAIR("PFT:version:", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH);
171
+        #if ENABLED(BINARY_STREAM_COMPRESSION)
172
+          SERIAL_ECHOLNPAIR(":compresion:heatshrink,", HEATSHRINK_STATIC_WINDOW_BITS, ",", HEATSHRINK_STATIC_LOOKAHEAD_BITS);
173
+        #else
174
+          SERIAL_ECHOLNPGM(":compresion:none");
175
+        #endif
176
+        break;
177
+      case FileTransfer::OPEN:
178
+        if (transfer_active)
179
+          SERIAL_ECHOLNPGM("PFT:busy");
180
+        else {
181
+          if (Packet::Open::validate(buffer, length)) {
182
+            auto packet = Packet::Open::decode(buffer);
183
+            compression = packet.compression_enabled();
184
+            dummy_transfer = packet.dummy_transfer();
185
+            if (file_open(packet.filename())) {
186
+              SERIAL_ECHOLNPGM("PFT:success");
187
+              break;
188
+            }
189
+          }
190
+          SERIAL_ECHOLNPGM("PFT:fail");
191
+        }
192
+        break;
193
+      case FileTransfer::CLOSE:
194
+        if (transfer_active) {
195
+          if (file_close())
196
+            SERIAL_ECHOLNPGM("PFT:success");
197
+          else
198
+            SERIAL_ECHOLNPGM("PFT:ioerror");
199
+        }
200
+        else SERIAL_ECHOLNPGM("PFT:invalid");
201
+        break;
202
+      case FileTransfer::WRITE:
203
+        if (!transfer_active)
204
+          SERIAL_ECHOLNPGM("PFT:invalid");
205
+        else if (!file_write(buffer, length))
206
+          SERIAL_ECHOLNPGM("PFT:ioerror");
207
+        break;
208
+      case FileTransfer::ABORT:
209
+        transfer_abort();
210
+        SERIAL_ECHOLNPGM("PFT:success");
211
+        break;
212
+      default:
213
+        SERIAL_ECHOLNPGM("PTF:invalid");
214
+        break;
215
+    }
216
+  }
217
+
218
+  static const uint16_t VERSION_MAJOR = 0, VERSION_MINOR = 1, VERSION_PATCH = 0, TIMEOUT = 10000, IDLE_PERIOD = 1000;
219
+};
220
+
221
+class BinaryStream {
222
+public:
223
+  enum class Protocol : uint8_t { CONTROL, FILE_TRANSFER };
224
+
225
+  enum class ProtocolControl : uint8_t { SYNC = 1, CLOSE };
226
+
227
+  enum class StreamState : uint8_t { PACKET_RESET, PACKET_WAIT, PACKET_HEADER, PACKET_DATA, PACKET_FOOTER,
228
+                                     PACKET_PROCESS, PACKET_RESEND, PACKET_TIMEOUT, PACKET_ERROR };
229
+
230
+  struct Packet { // 10 byte protocol overhead, ascii with checksum and line number has a minimum of 7 increasing with line
231
+    struct [[gnu::packed]]  Header {
232
+      static constexpr uint16_t HEADER_TOKEN = 0xB5AD;
233
+      uint16_t token;       // packet start token
234
+      uint8_t sync;         // stream sync, resend id and packet loss detection
235
+      uint8_t meta;         // 4 bit protocol,
236
+                            // 4 bit packet type
237
+      uint16_t size;        // data length
238
+      uint16_t checksum;    // header checksum
239
+
240
+      uint8_t protocol() { return (meta >> 4) & 0xF; }
241
+      uint8_t type() { return meta & 0xF; }
242
+      void reset() { token = 0; sync = 0; meta = 0; size = 0; checksum = 0; }
243
+    };
244
+
245
+    struct [[gnu::packed]] Footer {
246
+      uint16_t checksum; // full packet checksum
247
+      void reset() { checksum = 0; }
248
+    };
249
+
250
+    uint8_t header_data[sizeof(Header)],
251
+            footer_data[sizeof(Footer)];
252
+    uint32_t bytes_received;
253
+    uint16_t checksum, header_checksum;
254
+    millis_t timeout;
255
+    char* buffer;
256
+
257
+    Header& header() { return *reinterpret_cast<Header*>(header_data); }
258
+    Footer& footer() { return *reinterpret_cast<Footer*>(footer_data); }
259
+    void reset() {
260
+      header().reset();
261
+      footer().reset();
262
+      bytes_received = 0;
263
+      checksum = 0;
264
+      header_checksum = 0;
265
+      timeout = millis() + PACKET_MAX_WAIT;
266
+      buffer = nullptr;
267
+    }
268
+  } packet{};
269
+
270
+  void reset() {
271
+    sync = 0;
272
+    packet_retries = 0;
273
+    buffer_next_index = 0;
274
+  }
275
+
276
+  // fletchers 16 checksum
277
+  uint32_t checksum(uint32_t cs, uint8_t value) {
278
+    uint16_t cs_low = (((cs & 0xFF) + value) % 255);
279
+    return ((((cs >> 8) + cs_low) % 255) << 8)  | cs_low;
280
+  }
281
+
282
+  // read the next byte from the data stream keeping track of
283
+  // whether the stream times out from data starvation
284
+  // takes the data variable by reference in order to return status
285
+  bool stream_read(uint8_t& data) {
286
+    if (stream_state != StreamState::PACKET_WAIT && ELAPSED(millis(), packet.timeout)) {
287
+      stream_state = StreamState::PACKET_TIMEOUT;
288
+      return false;
289
+    }
290
+    if (!bs_serial_data_available(card.transfer_port_index)) return false;
291
+    data = bs_read_serial(card.transfer_port_index);
292
+    packet.timeout = millis() + PACKET_MAX_WAIT;
293
+    return true;
294
+  }
295
+
296
+  template<const size_t buffer_size>
297
+  void receive(char (&buffer)[buffer_size]) {
298
+    uint8_t data = 0;
299
+    millis_t transfer_window = millis() + RX_TIMESLICE;
300
+
301
+    #if ENABLED(SDSUPPORT)
302
+      PORT_REDIRECT(card.transfer_port_index);
303
+    #endif
304
+
305
+    while (PENDING(millis(), transfer_window)) {
306
+      switch (stream_state) {
307
+         /**
308
+          * Data stream packet handling
309
+          */
310
+        case StreamState::PACKET_RESET:
311
+          packet.reset();
312
+          stream_state = StreamState::PACKET_WAIT;
313
+        case StreamState::PACKET_WAIT:
314
+          if (!stream_read(data)) { idle(); return; }  // no active packet so don't wait
315
+          packet.header_data[1] = data;
316
+          if (packet.header().token == Packet::Header::HEADER_TOKEN) {
317
+            packet.bytes_received = 2;
318
+            stream_state = StreamState::PACKET_HEADER;
319
+          }
320
+          else {
321
+            // stream corruption drop data
322
+            packet.header_data[0] = data;
323
+          }
324
+          break;
325
+        case StreamState::PACKET_HEADER:
326
+          if (!stream_read(data)) break;
327
+
328
+          packet.header_data[packet.bytes_received++] = data;
329
+          packet.checksum = checksum(packet.checksum, data);
330
+
331
+          // header checksum calculation can't contain the checksum
332
+          if (packet.bytes_received == sizeof(Packet::Header) - 2)
333
+            packet.header_checksum = packet.checksum;
334
+
335
+          if (packet.bytes_received == sizeof(Packet::Header)) {
336
+            if (packet.header().checksum == packet.header_checksum) {
337
+              // The SYNC control packet is a special case in that it doesn't require the stream sync to be correct
338
+              if (static_cast<Protocol>(packet.header().protocol()) == Protocol::CONTROL && static_cast<ProtocolControl>(packet.header().type()) == ProtocolControl::SYNC) {
339
+                  SERIAL_ECHOLNPAIR("ss", sync, ",", buffer_size, ",", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH);
340
+                  stream_state = StreamState::PACKET_RESET;
341
+                  break;
342
+              }
343
+              if (packet.header().sync == sync) {
344
+                buffer_next_index = 0;
345
+                packet.bytes_received = 0;
346
+                if (packet.header().size) {
347
+                  stream_state = StreamState::PACKET_DATA;
348
+                  packet.buffer = static_cast<char *>(&buffer[0]); // multipacket buffering not implemented, always allocate whole buffer to packet
349
+                }
350
+                else
351
+                  stream_state = StreamState::PACKET_PROCESS;
352
+              }
353
+              else if (packet.header().sync == sync - 1) {           // ok response must have been lost
354
+                SERIAL_ECHOLNPAIR("ok", packet.header().sync);  // transmit valid packet received and drop the payload
355
+                stream_state = StreamState::PACKET_RESET;
356
+              }
357
+              else if (packet_retries) {
358
+                stream_state = StreamState::PACKET_RESET; // could be packets already buffered on flow controlled connections, drop them without ack
359
+              }
360
+              else {
361
+                SERIAL_ECHO_MSG("Datastream packet out of order");
362
+                stream_state = StreamState::PACKET_RESEND;
363
+              }
364
+            }
365
+            else {
366
+              SERIAL_ECHO_START();
367
+              SERIAL_ECHOLNPAIR("Packet Header(", packet.header().sync, "?) Corrupt");
368
+              stream_state = StreamState::PACKET_RESEND;
369
+            }
370
+          }
371
+          break;
372
+        case StreamState::PACKET_DATA:
373
+          if (!stream_read(data)) break;
374
+
375
+          if (buffer_next_index < buffer_size)
376
+            packet.buffer[buffer_next_index] = data;
377
+          else {
378
+            SERIAL_ECHO_MSG("Datastream packet data buffer overrun");
379
+            stream_state = StreamState::PACKET_ERROR;
380
+            break;
381
+          }
382
+
383
+          packet.checksum = checksum(packet.checksum, data);
384
+          packet.bytes_received++;
385
+          buffer_next_index++;
386
+
387
+          if (packet.bytes_received == packet.header().size) {
388
+            stream_state = StreamState::PACKET_FOOTER;
389
+            packet.bytes_received = 0;
390
+          }
391
+          break;
392
+        case StreamState::PACKET_FOOTER:
393
+          if (!stream_read(data)) break;
394
+
395
+          packet.footer_data[packet.bytes_received++] = data;
396
+          if (packet.bytes_received == sizeof(Packet::Footer)) {
397
+            if (packet.footer().checksum == packet.checksum) {
398
+              stream_state = StreamState::PACKET_PROCESS;
399
+            }
400
+            else {
401
+              SERIAL_ECHO_START();
402
+              SERIAL_ECHOLNPAIR("Packet(", packet.header().sync, ") Payload Corrupt");
403
+              stream_state = StreamState::PACKET_RESEND;
404
+            }
405
+          }
406
+          break;
407
+        case StreamState::PACKET_PROCESS:
408
+          sync++;
409
+          packet_retries = 0;
410
+          bytes_received += packet.header().size;
411
+
412
+          SERIAL_ECHOLNPAIR("ok", packet.header().sync); // transmit valid packet received
413
+          dispatch();
414
+          stream_state = StreamState::PACKET_RESET;
415
+          break;
416
+        case StreamState::PACKET_RESEND:
417
+          if (packet_retries < MAX_RETRIES || MAX_RETRIES == 0) {
418
+            packet_retries++;
419
+            stream_state = StreamState::PACKET_RESET;
420
+            SERIAL_ECHO_START();
421
+            SERIAL_ECHOLNPAIR("Resend request ", int(packet_retries));
422
+            SERIAL_ECHOLNPAIR("rs", sync);
423
+          }
424
+          else
425
+            stream_state = StreamState::PACKET_ERROR;
426
+          break;
427
+        case StreamState::PACKET_TIMEOUT:
428
+          SERIAL_ECHO_MSG("Datastream timeout");
429
+          stream_state = StreamState::PACKET_RESEND;
430
+          break;
431
+        case StreamState::PACKET_ERROR:
432
+          SERIAL_ECHOLNPAIR("fe", packet.header().sync);
433
+          reset(); // reset everything, resync required
434
+          stream_state = StreamState::PACKET_RESET;
435
+          break;
436
+      }
437
+    }
438
+  }
439
+
440
+  void dispatch() {
441
+    switch(static_cast<Protocol>(packet.header().protocol())) {
442
+      case Protocol::CONTROL:
443
+        switch(static_cast<ProtocolControl>(packet.header().type())) {
444
+          case ProtocolControl::CLOSE: // revert back to ASCII mode
445
+            card.flag.binary_mode = false;
446
+            break;
447
+          default:
448
+            SERIAL_ECHO_MSG("Unknown BinaryProtocolControl Packet");
449
+        }
450
+        break;
451
+      case Protocol::FILE_TRANSFER:
452
+        SDFileTransferProtocol::process(packet.header().type(), packet.buffer, packet.header().size); // send user data to be processed
453
+      break;
454
+      default:
455
+        SERIAL_ECHO_MSG("Unsupported Binary Protocol");
456
+    }
457
+  }
458
+
459
+  void idle() {
460
+    // Some Protocols may need periodic updates without new data
461
+    SDFileTransferProtocol::idle();
462
+  }
463
+
464
+  static const uint16_t PACKET_MAX_WAIT = 500, RX_TIMESLICE = 20, MAX_RETRIES = 0, VERSION_MAJOR = 0, VERSION_MINOR = 1, VERSION_PATCH = 0;
465
+  uint8_t  packet_retries, sync;
466
+  uint16_t buffer_next_index;
467
+  uint32_t bytes_received;
468
+  StreamState stream_state = StreamState::PACKET_RESET;
469
+};
470
+
471
+extern BinaryStream binaryStream[NUM_SERIAL];

+ 7
- 252
Marlin/src/gcode/queue.cpp View File

@@ -39,6 +39,11 @@ GCodeQueue queue;
39 39
   #include "../feature/leds/printer_event_leds.h"
40 40
 #endif
41 41
 
42
+#if ENABLED(BINARY_FILE_TRANSFER)
43
+  #include "../feature/binary_protocol.h"
44
+#endif
45
+
46
+
42 47
 /**
43 48
  * GCode line number handling. Hosts may opt to include line numbers when
44 49
  * sending commands to Marlin, and lines will be checked for sequentiality.
@@ -285,256 +290,6 @@ inline int read_serial(const uint8_t index) {
285 290
   }
286 291
 }
287 292
 
288
-#if ENABLED(BINARY_FILE_TRANSFER)
289
-
290
-  inline bool serial_data_available(const uint8_t index) {
291
-    switch (index) {
292
-      case 0: return MYSERIAL0.available();
293
-      #if NUM_SERIAL > 1
294
-        case 1: return MYSERIAL1.available();
295
-      #endif
296
-      default: return false;
297
-    }
298
-  }
299
-
300
-  class BinaryStream {
301
-  public:
302
-    enum class StreamState : uint8_t {
303
-      STREAM_RESET,
304
-      PACKET_RESET,
305
-      STREAM_HEADER,
306
-      PACKET_HEADER,
307
-      PACKET_DATA,
308
-      PACKET_VALIDATE,
309
-      PACKET_RESEND,
310
-      PACKET_FLUSHRX,
311
-      PACKET_TIMEOUT,
312
-      STREAM_COMPLETE,
313
-      STREAM_FAILED,
314
-    };
315
-
316
-    #pragma pack(push, 1)
317
-
318
-      struct StreamHeader {
319
-        uint16_t token;
320
-        uint32_t filesize;
321
-      };
322
-      union {
323
-        uint8_t stream_header_bytes[sizeof(StreamHeader)];
324
-        StreamHeader stream_header;
325
-      };
326
-
327
-      struct Packet {
328
-        struct Header {
329
-          uint32_t id;
330
-          uint16_t size, checksum;
331
-        };
332
-        union {
333
-          uint8_t header_bytes[sizeof(Header)];
334
-          Header header;
335
-        };
336
-        uint32_t bytes_received;
337
-        uint16_t checksum;
338
-        millis_t timeout;
339
-      } packet{};
340
-
341
-    #pragma pack(pop)
342
-
343
-    void packet_reset() {
344
-      packet.header.id = 0;
345
-      packet.header.size = 0;
346
-      packet.header.checksum = 0;
347
-      packet.bytes_received = 0;
348
-      packet.checksum = 0x53A2;
349
-      packet.timeout = millis() + STREAM_MAX_WAIT;
350
-    }
351
-
352
-    void stream_reset() {
353
-      packets_received = 0;
354
-      bytes_received = 0;
355
-      packet_retries = 0;
356
-      buffer_next_index = 0;
357
-      stream_header.token = 0;
358
-      stream_header.filesize = 0;
359
-    }
360
-
361
-    uint32_t checksum(uint32_t seed, uint8_t value) {
362
-      return ((seed ^ value) ^ (seed << 8)) & 0xFFFF;
363
-    }
364
-
365
-    // read the next byte from the data stream keeping track of
366
-    // whether the stream times out from data starvation
367
-    // takes the data variable by reference in order to return status
368
-    bool stream_read(uint8_t& data) {
369
-      if (ELAPSED(millis(), packet.timeout)) {
370
-        stream_state = StreamState::PACKET_TIMEOUT;
371
-        return false;
372
-      }
373
-      if (!serial_data_available(card.transfer_port_index)) return false;
374
-      data = read_serial(card.transfer_port_index);
375
-      packet.timeout = millis() + STREAM_MAX_WAIT;
376
-      return true;
377
-    }
378
-
379
-    template<const size_t buffer_size>
380
-    void receive(char (&buffer)[buffer_size]) {
381
-      uint8_t data = 0;
382
-      millis_t transfer_timeout = millis() + RX_TIMESLICE;
383
-
384
-      #if ENABLED(SDSUPPORT)
385
-        PORT_REDIRECT(card.transfer_port_index);
386
-      #endif
387
-
388
-      while (PENDING(millis(), transfer_timeout)) {
389
-        switch (stream_state) {
390
-          case StreamState::STREAM_RESET:
391
-            stream_reset();
392
-          case StreamState::PACKET_RESET:
393
-            packet_reset();
394
-            stream_state = StreamState::PACKET_HEADER;
395
-            break;
396
-          case StreamState::STREAM_HEADER: // The filename could also be in this packet, rather than handling it in the gcode
397
-            for (size_t i = 0; i < sizeof(stream_header); ++i)
398
-              stream_header_bytes[i] = buffer[i];
399
-
400
-            if (stream_header.token == 0x1234) {
401
-              stream_state = StreamState::PACKET_RESET;
402
-              bytes_received = 0;
403
-              time_stream_start = millis();
404
-              // confirm active stream and the maximum block size supported
405
-              SERIAL_ECHO_START();
406
-              SERIAL_ECHOLNPAIR("Datastream initialized (", stream_header.filesize, " bytes expected)");
407
-              SERIAL_ECHOLNPAIR("so", buffer_size);
408
-            }
409
-            else {
410
-              SERIAL_ECHO_MSG("Datastream init error (invalid token)");
411
-              stream_state = StreamState::STREAM_FAILED;
412
-            }
413
-            buffer_next_index = 0;
414
-            break;
415
-          case StreamState::PACKET_HEADER:
416
-            if (!stream_read(data)) break;
417
-
418
-            packet.header_bytes[packet.bytes_received++] = data;
419
-            if (packet.bytes_received == sizeof(Packet::Header)) {
420
-              if (packet.header.id == packets_received) {
421
-                buffer_next_index = 0;
422
-                packet.bytes_received = 0;
423
-                stream_state = StreamState::PACKET_DATA;
424
-              }
425
-              else {
426
-                SERIAL_ECHO_MSG("Datastream packet out of order");
427
-                stream_state = StreamState::PACKET_FLUSHRX;
428
-              }
429
-            }
430
-            break;
431
-          case StreamState::PACKET_DATA:
432
-            if (!stream_read(data)) break;
433
-
434
-            if (buffer_next_index < buffer_size)
435
-              buffer[buffer_next_index] = data;
436
-            else {
437
-              SERIAL_ECHO_MSG("Datastream packet data buffer overrun");
438
-              stream_state = StreamState::STREAM_FAILED;
439
-              break;
440
-            }
441
-
442
-            packet.checksum = checksum(packet.checksum, data);
443
-            packet.bytes_received++;
444
-            buffer_next_index++;
445
-
446
-            if (packet.bytes_received == packet.header.size)
447
-              stream_state = StreamState::PACKET_VALIDATE;
448
-
449
-            break;
450
-          case StreamState::PACKET_VALIDATE:
451
-            if (packet.header.checksum == packet.checksum) {
452
-              packet_retries = 0;
453
-              packets_received++;
454
-              bytes_received += packet.header.size;
455
-
456
-              if (packet.header.id == 0)                   // id 0 is always the stream descriptor
457
-                stream_state = StreamState::STREAM_HEADER; // defer packet confirmation to STREAM_HEADER state
458
-              else {
459
-                if (bytes_received < stream_header.filesize) {
460
-                  stream_state = StreamState::PACKET_RESET;  // reset and receive next packet
461
-                  SERIAL_ECHOLNPAIR("ok", packet.header.id); // transmit confirm packet received and valid token
462
-                }
463
-                else
464
-                  stream_state = StreamState::STREAM_COMPLETE; // no more data required
465
-
466
-                if (card.write(buffer, buffer_next_index) < 0) {
467
-                  stream_state = StreamState::STREAM_FAILED;
468
-                  SERIAL_ECHO_MSG("SDCard IO Error");
469
-                  break;
470
-                };
471
-              }
472
-            }
473
-            else {
474
-              SERIAL_ECHO_START();
475
-              SERIAL_ECHOLNPAIR("Block(", packet.header.id, ") Corrupt");
476
-              stream_state = StreamState::PACKET_FLUSHRX;
477
-            }
478
-            break;
479
-          case StreamState::PACKET_RESEND:
480
-            if (packet_retries < MAX_RETRIES) {
481
-              packet_retries++;
482
-              stream_state = StreamState::PACKET_RESET;
483
-              SERIAL_ECHO_START();
484
-              SERIAL_ECHOLNPAIR("Resend request ", int(packet_retries));
485
-              SERIAL_ECHOLNPAIR("rs", packet.header.id); // transmit resend packet token
486
-            }
487
-            else {
488
-              stream_state = StreamState::STREAM_FAILED;
489
-            }
490
-            break;
491
-          case StreamState::PACKET_FLUSHRX:
492
-            if (ELAPSED(millis(), packet.timeout)) {
493
-              stream_state = StreamState::PACKET_RESEND;
494
-              break;
495
-            }
496
-            if (!serial_data_available(card.transfer_port_index)) break;
497
-            read_serial(card.transfer_port_index); // throw away data
498
-            packet.timeout = millis() + STREAM_MAX_WAIT;
499
-            break;
500
-          case StreamState::PACKET_TIMEOUT:
501
-            SERIAL_ECHO_START();
502
-            SERIAL_ECHOLNPGM("Datastream timeout");
503
-            stream_state = StreamState::PACKET_RESEND;
504
-            break;
505
-          case StreamState::STREAM_COMPLETE:
506
-            stream_state = StreamState::STREAM_RESET;
507
-            card.flag.binary_mode = false;
508
-            SERIAL_ECHO_START();
509
-            SERIAL_ECHO(card.filename);
510
-            SERIAL_ECHOLNPAIR(" transfer completed @ ", ((bytes_received / (millis() - time_stream_start) * 1000) / 1024), "KiB/s");
511
-            SERIAL_ECHOLNPGM("sc"); // transmit stream complete token
512
-            card.closefile();
513
-            return;
514
-          case StreamState::STREAM_FAILED:
515
-            stream_state = StreamState::STREAM_RESET;
516
-            card.flag.binary_mode = false;
517
-            card.closefile();
518
-            card.removeFile(card.filename);
519
-            SERIAL_ECHO_START();
520
-            SERIAL_ECHOLNPGM("File transfer failed");
521
-            SERIAL_ECHOLNPGM("sf"); // transmit stream failed token
522
-            return;
523
-        }
524
-      }
525
-    }
526
-
527
-    static const uint16_t STREAM_MAX_WAIT = 500, RX_TIMESLICE = 20, MAX_RETRIES = 3;
528
-    uint8_t  packet_retries;
529
-    uint16_t buffer_next_index;
530
-    uint32_t packets_received,  bytes_received;
531
-    millis_t time_stream_start;
532
-    StreamState stream_state = StreamState::STREAM_RESET;
533
-
534
-  } binaryStream{};
535
-
536
-#endif // BINARY_FILE_TRANSFER
537
-
538 293
 void GCodeQueue::gcode_line_error(PGM_P const err, const int8_t port) {
539 294
   PORT_REDIRECT(port);
540 295
   SERIAL_ERROR_START();
@@ -564,13 +319,13 @@ void GCodeQueue::get_serial_commands() {
564 319
             ;
565 320
 
566 321
   #if ENABLED(BINARY_FILE_TRANSFER)
567
-    if (card.flag.saving && card.flag.binary_mode) {
322
+    if (card.flag.binary_mode) {
568 323
       /**
569 324
        * For binary stream file transfer, use serial_line_buffer as the working
570 325
        * receive buffer (which limits the packet size to MAX_CMD_SIZE).
571 326
        * The receive buffer also limits the packet size for reliable transmission.
572 327
        */
573
-      binaryStream.receive(serial_line_buffer[card.transfer_port_index]);
328
+      binaryStream[card.transfer_port_index].receive(serial_line_buffer[card.transfer_port_index]);
574 329
       return;
575 330
     }
576 331
   #endif

+ 1
- 4
Marlin/src/gcode/sdcard/M28_M29.cpp View File

@@ -48,10 +48,7 @@ void GcodeSuite::M28() {
48 48
 
49 49
     // Binary transfer mode
50 50
     if ((card.flag.binary_mode = binary_mode)) {
51
-      SERIAL_ECHO_START();
52
-      SERIAL_ECHO(" preparing to receive: ");
53
-      SERIAL_ECHOLN(p);
54
-      card.openFile(p, false);
51
+      SERIAL_ECHO_MSG("Switching to Binary Protocol");
55 52
       #if NUM_SERIAL > 1
56 53
         card.transfer_port_index = queue.port[queue.index_r];
57 54
       #endif

+ 14
- 0
Marlin/src/libs/heatshrink/LICENSE View File

@@ -0,0 +1,14 @@
1
+Copyright (c) 2013-2015, Scott Vokes <vokes.s@gmail.com>
2
+All rights reserved.
3
+ 
4
+Permission to use, copy, modify, and/or distribute this software for any
5
+purpose with or without fee is hereby granted, provided that the above
6
+copyright notice and this permission notice appear in all copies.
7
+ 
8
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

+ 20
- 0
Marlin/src/libs/heatshrink/heatshrink_common.h View File

@@ -0,0 +1,20 @@
1
+/**
2
+ * libs/heatshrink/heatshrink_common.h
3
+ */
4
+#pragma once
5
+
6
+#define HEATSHRINK_AUTHOR "Scott Vokes <vokes.s@gmail.com>"
7
+#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink"
8
+
9
+/* Version 0.4.1 */
10
+#define HEATSHRINK_VERSION_MAJOR 0
11
+#define HEATSHRINK_VERSION_MINOR 4
12
+#define HEATSHRINK_VERSION_PATCH 1
13
+
14
+#define HEATSHRINK_MIN_WINDOW_BITS 4
15
+#define HEATSHRINK_MAX_WINDOW_BITS 15
16
+
17
+#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3
18
+
19
+#define HEATSHRINK_LITERAL_MARKER 0x01
20
+#define HEATSHRINK_BACKREF_MARKER 0x00

+ 26
- 0
Marlin/src/libs/heatshrink/heatshrink_config.h View File

@@ -0,0 +1,26 @@
1
+/**
2
+ * libs/heatshrink/heatshrink_config.h
3
+ */
4
+#pragma once
5
+
6
+// Should functionality assuming dynamic allocation be used?
7
+#ifndef HEATSHRINK_DYNAMIC_ALLOC
8
+  //#define HEATSHRINK_DYNAMIC_ALLOC 1
9
+#endif
10
+
11
+#if HEATSHRINK_DYNAMIC_ALLOC
12
+  // Optional replacement of malloc/free
13
+  #define HEATSHRINK_MALLOC(SZ) malloc(SZ)
14
+  #define HEATSHRINK_FREE(P, SZ) free(P)
15
+#else
16
+  // Required parameters for static configuration
17
+  #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32
18
+  #define HEATSHRINK_STATIC_WINDOW_BITS 8
19
+  #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4
20
+#endif
21
+
22
+// Turn on logging for debugging
23
+#define HEATSHRINK_DEBUGGING_LOGS 0
24
+
25
+// Use indexing for faster compression. (This requires additional space.)
26
+#define HEATSHRINK_USE_INDEX 1

+ 355
- 0
Marlin/src/libs/heatshrink/heatshrink_decoder.cpp View File

@@ -0,0 +1,355 @@
1
+/**
2
+ * libs/heatshrink/heatshrink_decoder.cpp
3
+ */
4
+#include <stdlib.h>
5
+#include <string.h>
6
+#include "heatshrink_decoder.h"
7
+
8
+#pragma GCC optimize ("O3")
9
+
10
+/* States for the polling state machine. */
11
+typedef enum {
12
+  HSDS_TAG_BIT,               /* tag bit */
13
+  HSDS_YIELD_LITERAL,         /* ready to yield literal byte */
14
+  HSDS_BACKREF_INDEX_MSB,     /* most significant byte of index */
15
+  HSDS_BACKREF_INDEX_LSB,     /* least significant byte of index */
16
+  HSDS_BACKREF_COUNT_MSB,     /* most significant byte of count */
17
+  HSDS_BACKREF_COUNT_LSB,     /* least significant byte of count */
18
+  HSDS_YIELD_BACKREF          /* ready to yield back-reference */
19
+} HSD_state;
20
+
21
+#if HEATSHRINK_DEBUGGING_LOGS
22
+  #include <stdio.h>
23
+  #include <ctype.h>
24
+  #include <assert.h>
25
+  #define LOG(...) fprintf(stderr, __VA_ARGS__)
26
+  #define ASSERT(X) assert(X)
27
+  static const char *state_names[] = {
28
+    "tag_bit",
29
+    "yield_literal",
30
+    "backref_index_msb",
31
+    "backref_index_lsb",
32
+    "backref_count_msb",
33
+    "backref_count_lsb",
34
+    "yield_backref"
35
+  };
36
+#else
37
+  #define LOG(...) /* no-op */
38
+  #define ASSERT(X) /* no-op */
39
+#endif
40
+
41
+typedef struct {
42
+  uint8_t *buf;               /* output buffer */
43
+  size_t buf_size;            /* buffer size */
44
+  size_t *output_size;        /* bytes pushed to buffer, so far */
45
+} output_info;
46
+
47
+#define NO_BITS ((uint16_t)-1)
48
+
49
+/* Forward references. */
50
+static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count);
51
+static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte);
52
+
53
+#if HEATSHRINK_DYNAMIC_ALLOC
54
+heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, uint8_t window_sz2, uint8_t lookahead_sz2) {
55
+  if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) ||
56
+      (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) ||
57
+      (input_buffer_size == 0) ||
58
+      (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) ||
59
+      (lookahead_sz2 >= window_sz2)) {
60
+      return nullptr;
61
+  }
62
+  size_t buffers_sz = (1 << window_sz2) + input_buffer_size;
63
+  size_t sz = sizeof(heatshrink_decoder) + buffers_sz;
64
+  heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz);
65
+  if (hsd == nullptr) return nullptr;
66
+  hsd->input_buffer_size = input_buffer_size;
67
+  hsd->window_sz2 = window_sz2;
68
+  hsd->lookahead_sz2 = lookahead_sz2;
69
+  heatshrink_decoder_reset(hsd);
70
+  LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n",
71
+      sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size);
72
+  return hsd;
73
+}
74
+
75
+void heatshrink_decoder_free(heatshrink_decoder *hsd) {
76
+  size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size;
77
+  size_t sz = sizeof(heatshrink_decoder) + buffers_sz;
78
+  HEATSHRINK_FREE(hsd, sz);
79
+  (void)sz;   /* may not be used by free */
80
+}
81
+#endif
82
+
83
+void heatshrink_decoder_reset(heatshrink_decoder *hsd) {
84
+  size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd);
85
+  size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd);
86
+  memset(hsd->buffers, 0, buf_sz + input_sz);
87
+  hsd->state = HSDS_TAG_BIT;
88
+  hsd->input_size = 0;
89
+  hsd->input_index = 0;
90
+  hsd->bit_index = 0x00;
91
+  hsd->current_byte = 0x00;
92
+  hsd->output_count = 0;
93
+  hsd->output_index = 0;
94
+  hsd->head_index = 0;
95
+}
96
+
97
+/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */
98
+HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd,
99
+  uint8_t *in_buf, size_t size, size_t *input_size) {
100
+  if (hsd == nullptr || in_buf == nullptr || input_size == nullptr)
101
+    return HSDR_SINK_ERROR_NULL;
102
+
103
+  size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size;
104
+  if (rem == 0) {
105
+    *input_size = 0;
106
+    return HSDR_SINK_FULL;
107
+  }
108
+
109
+  size = rem < size ? rem : size;
110
+  LOG("-- sinking %zd bytes\n", size);
111
+  /* copy into input buffer (at head of buffers) */
112
+  memcpy(&hsd->buffers[hsd->input_size], in_buf, size);
113
+  hsd->input_size += size;
114
+  *input_size = size;
115
+  return HSDR_SINK_OK;
116
+}
117
+
118
+
119
+/*****************
120
+ * Decompression *
121
+ *****************/
122
+
123
+#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD))
124
+#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD))
125
+
126
+// States
127
+static HSD_state st_tag_bit(heatshrink_decoder *hsd);
128
+static HSD_state st_yield_literal(heatshrink_decoder *hsd, output_info *oi);
129
+static HSD_state st_backref_index_msb(heatshrink_decoder *hsd);
130
+static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd);
131
+static HSD_state st_backref_count_msb(heatshrink_decoder *hsd);
132
+static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd);
133
+static HSD_state st_yield_backref(heatshrink_decoder *hsd, output_info *oi);
134
+
135
+HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, uint8_t *out_buf, size_t out_buf_size, size_t *output_size) {
136
+  if (hsd == nullptr || out_buf == nullptr || output_size == nullptr)
137
+    return HSDR_POLL_ERROR_NULL;
138
+
139
+  *output_size = 0;
140
+
141
+  output_info oi;
142
+  oi.buf = out_buf;
143
+  oi.buf_size = out_buf_size;
144
+  oi.output_size = output_size;
145
+
146
+  while (1) {
147
+    LOG("-- poll, state is %d (%s), input_size %d\n", hsd->state, state_names[hsd->state], hsd->input_size);
148
+    uint8_t in_state = hsd->state;
149
+    switch (in_state) {
150
+      case HSDS_TAG_BIT:
151
+        hsd->state = st_tag_bit(hsd);
152
+        break;
153
+      case HSDS_YIELD_LITERAL:
154
+        hsd->state = st_yield_literal(hsd, &oi);
155
+        break;
156
+      case HSDS_BACKREF_INDEX_MSB:
157
+        hsd->state = st_backref_index_msb(hsd);
158
+        break;
159
+      case HSDS_BACKREF_INDEX_LSB:
160
+        hsd->state = st_backref_index_lsb(hsd);
161
+        break;
162
+      case HSDS_BACKREF_COUNT_MSB:
163
+        hsd->state = st_backref_count_msb(hsd);
164
+        break;
165
+      case HSDS_BACKREF_COUNT_LSB:
166
+        hsd->state = st_backref_count_lsb(hsd);
167
+        break;
168
+      case HSDS_YIELD_BACKREF:
169
+        hsd->state = st_yield_backref(hsd, &oi);
170
+        break;
171
+      default:
172
+        return HSDR_POLL_ERROR_UNKNOWN;
173
+    }
174
+
175
+    // If the current state cannot advance, check if input or output
176
+    // buffer are exhausted.
177
+    if (hsd->state == in_state)
178
+      return (*output_size == out_buf_size) ? HSDR_POLL_MORE : HSDR_POLL_EMPTY;
179
+  }
180
+}
181
+
182
+static HSD_state st_tag_bit(heatshrink_decoder *hsd) {
183
+  uint32_t bits = get_bits(hsd, 1);  // get tag bit
184
+  if (bits == NO_BITS)
185
+    return HSDS_TAG_BIT;
186
+  else if (bits)
187
+    return HSDS_YIELD_LITERAL;
188
+  else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8)
189
+    return HSDS_BACKREF_INDEX_MSB;
190
+  else {
191
+    hsd->output_index = 0;
192
+    return HSDS_BACKREF_INDEX_LSB;
193
+  }
194
+}
195
+
196
+static HSD_state st_yield_literal(heatshrink_decoder *hsd, output_info *oi) {
197
+  /* Emit a repeated section from the window buffer, and add it (again)
198
+   * to the window buffer. (Note that the repetition can include
199
+   * itself.)*/
200
+  if (*oi->output_size < oi->buf_size) {
201
+    uint16_t byte = get_bits(hsd, 8);
202
+    if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */
203
+    uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
204
+    uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd))  - 1;
205
+    uint8_t c = byte & 0xFF;
206
+    LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.');
207
+    buf[hsd->head_index++ & mask] = c;
208
+    push_byte(hsd, oi, c);
209
+    return HSDS_TAG_BIT;
210
+  }
211
+  return HSDS_YIELD_LITERAL;
212
+}
213
+
214
+static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) {
215
+  uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
216
+  ASSERT(bit_ct > 8);
217
+  uint16_t bits = get_bits(hsd, bit_ct - 8);
218
+  LOG("-- backref index (msb), got 0x%04x (+1)\n", bits);
219
+  if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; }
220
+  hsd->output_index = bits << 8;
221
+  return HSDS_BACKREF_INDEX_LSB;
222
+}
223
+
224
+static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) {
225
+  uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
226
+  uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8);
227
+  LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits);
228
+  if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; }
229
+  hsd->output_index |= bits;
230
+  hsd->output_index++;
231
+  uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
232
+  hsd->output_count = 0;
233
+  return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB;
234
+}
235
+
236
+static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) {
237
+  uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
238
+  ASSERT(br_bit_ct > 8);
239
+  uint16_t bits = get_bits(hsd, br_bit_ct - 8);
240
+  LOG("-- backref count (msb), got 0x%04x (+1)\n", bits);
241
+  if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; }
242
+  hsd->output_count = bits << 8;
243
+  return HSDS_BACKREF_COUNT_LSB;
244
+}
245
+
246
+static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) {
247
+  uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
248
+  uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8);
249
+  LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits);
250
+  if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; }
251
+  hsd->output_count |= bits;
252
+  hsd->output_count++;
253
+  return HSDS_YIELD_BACKREF;
254
+}
255
+
256
+static HSD_state st_yield_backref(heatshrink_decoder *hsd, output_info *oi) {
257
+  size_t count = oi->buf_size - *oi->output_size;
258
+  if (count > 0) {
259
+    size_t i = 0;
260
+    if (hsd->output_count < count) count = hsd->output_count;
261
+    uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
262
+    uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1;
263
+    uint16_t neg_offset = hsd->output_index;
264
+    LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset);
265
+    ASSERT(neg_offset <= mask + 1);
266
+    ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd)));
267
+
268
+    for (i = 0; i < count; i++) {
269
+      uint8_t c = buf[(hsd->head_index - neg_offset) & mask];
270
+      push_byte(hsd, oi, c);
271
+      buf[hsd->head_index & mask] = c;
272
+      hsd->head_index++;
273
+      LOG("  -- ++ 0x%02x\n", c);
274
+    }
275
+    hsd->output_count -= count;
276
+    if (hsd->output_count == 0) { return HSDS_TAG_BIT; }
277
+  }
278
+  return HSDS_YIELD_BACKREF;
279
+}
280
+
281
+/* Get the next COUNT bits from the input buffer, saving incremental progress.
282
+ * Returns NO_BITS on end of input, or if more than 15 bits are requested. */
283
+static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) {
284
+  uint16_t accumulator = 0;
285
+  int i = 0;
286
+  if (count > 15) return NO_BITS;
287
+  LOG("-- popping %u bit(s)\n", count);
288
+
289
+  /* If we aren't able to get COUNT bits, suspend immediately, because we
290
+   * don't track how many bits of COUNT we've accumulated before suspend. */
291
+  if (hsd->input_size == 0 && hsd->bit_index < (1 << (count - 1))) return NO_BITS;
292
+
293
+  for (i = 0; i < count; i++) {
294
+    if (hsd->bit_index == 0x00) {
295
+      if (hsd->input_size == 0) {
296
+        LOG("  -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", accumulator, accumulator);
297
+        return NO_BITS;
298
+      }
299
+      hsd->current_byte = hsd->buffers[hsd->input_index++];
300
+      LOG("  -- pulled byte 0x%02x\n", hsd->current_byte);
301
+      if (hsd->input_index == hsd->input_size) {
302
+        hsd->input_index = 0; /* input is exhausted */
303
+        hsd->input_size = 0;
304
+      }
305
+      hsd->bit_index = 0x80;
306
+    }
307
+    accumulator <<= 1;
308
+    if (hsd->current_byte & hsd->bit_index) {
309
+      accumulator |= 0x01;
310
+      if (0) {
311
+        LOG("  -- got 1, accumulator 0x%04x, bit_index 0x%02x\n",
312
+        accumulator, hsd->bit_index);
313
+      }
314
+    }
315
+    else if (0) {
316
+      LOG("  -- got 0, accumulator 0x%04x, bit_index 0x%02x\n",
317
+      accumulator, hsd->bit_index);
318
+    }
319
+    hsd->bit_index >>= 1;
320
+  }
321
+
322
+  if (count > 1) LOG("  -- accumulated %08x\n", accumulator);
323
+  return accumulator;
324
+}
325
+
326
+HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) {
327
+  if (hsd == nullptr) { return HSDR_FINISH_ERROR_NULL; }
328
+  switch (hsd->state) {
329
+    case HSDS_TAG_BIT:
330
+      return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
331
+
332
+    /* If we want to finish with no input, but are in these states, it's
333
+     * because the 0-bit padding to the last byte looks like a backref
334
+     * marker bit followed by all 0s for index and count bits. */
335
+    case HSDS_BACKREF_INDEX_LSB:
336
+    case HSDS_BACKREF_INDEX_MSB:
337
+    case HSDS_BACKREF_COUNT_LSB:
338
+    case HSDS_BACKREF_COUNT_MSB:
339
+      return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
340
+
341
+    /* If the output stream is padded with 0xFFs (possibly due to being in
342
+     * flash memory), also explicitly check the input size rather than
343
+     * uselessly returning MORE but yielding 0 bytes when polling. */
344
+    case HSDS_YIELD_LITERAL:
345
+      return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
346
+
347
+    default: return HSDR_FINISH_MORE;
348
+  }
349
+}
350
+
351
+static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) {
352
+  LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.');
353
+  oi->buf[(*oi->output_size)++] = byte;
354
+  (void)hsd;
355
+}

+ 96
- 0
Marlin/src/libs/heatshrink/heatshrink_decoder.h View File

@@ -0,0 +1,96 @@
1
+/**
2
+ * libs/heatshrink/heatshrink_decoder.h
3
+ */
4
+#pragma once
5
+
6
+#include <stdint.h>
7
+#include <stddef.h>
8
+#include "heatshrink_common.h"
9
+#include "heatshrink_config.h"
10
+
11
+typedef enum {
12
+  HSDR_SINK_OK,               /* data sunk, ready to poll */
13
+  HSDR_SINK_FULL,             /* out of space in internal buffer */
14
+  HSDR_SINK_ERROR_NULL=-1,    /* NULL argument */
15
+} HSD_sink_res;
16
+
17
+typedef enum {
18
+  HSDR_POLL_EMPTY,            /* input exhausted */
19
+  HSDR_POLL_MORE,             /* more data remaining, call again w/ fresh output buffer */
20
+  HSDR_POLL_ERROR_NULL=-1,    /* NULL arguments */
21
+  HSDR_POLL_ERROR_UNKNOWN=-2,
22
+} HSD_poll_res;
23
+
24
+typedef enum {
25
+  HSDR_FINISH_DONE,           /* output is done */
26
+  HSDR_FINISH_MORE,           /* more output remains */
27
+  HSDR_FINISH_ERROR_NULL=-1,  /* NULL arguments */
28
+} HSD_finish_res;
29
+
30
+#if HEATSHRINK_DYNAMIC_ALLOC
31
+#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \
32
+  ((BUF)->input_buffer_size)
33
+#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \
34
+  ((BUF)->window_sz2)
35
+#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
36
+  ((BUF)->lookahead_sz2)
37
+#else
38
+#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \
39
+  HEATSHRINK_STATIC_INPUT_BUFFER_SIZE
40
+#define HEATSHRINK_DECODER_WINDOW_BITS(_) \
41
+  (HEATSHRINK_STATIC_WINDOW_BITS)
42
+#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
43
+  (HEATSHRINK_STATIC_LOOKAHEAD_BITS)
44
+#endif
45
+
46
+typedef struct {
47
+  uint16_t input_size;        /* bytes in input buffer */
48
+  uint16_t input_index;       /* offset to next unprocessed input byte */
49
+  uint16_t output_count;      /* how many bytes to output */
50
+  uint16_t output_index;      /* index for bytes to output */
51
+  uint16_t head_index;        /* head of window buffer */
52
+  uint8_t state;              /* current state machine node */
53
+  uint8_t current_byte;       /* current byte of input */
54
+  uint8_t bit_index;          /* current bit index */
55
+
56
+#if HEATSHRINK_DYNAMIC_ALLOC
57
+  /* Fields that are only used if dynamically allocated. */
58
+  uint8_t window_sz2;         /* window buffer bits */
59
+  uint8_t lookahead_sz2;      /* lookahead bits */
60
+  uint16_t input_buffer_size; /* input buffer size */
61
+
62
+  /* Input buffer, then expansion window buffer */
63
+  uint8_t buffers[];
64
+#else
65
+  /* Input buffer, then expansion window buffer */
66
+  uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)];
67
+#endif
68
+} heatshrink_decoder;
69
+
70
+#if HEATSHRINK_DYNAMIC_ALLOC
71
+/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes,
72
+ * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead
73
+ * size of 2^lookahead_sz2. (The window buffer and lookahead sizes
74
+ * must match the settings used when the data was compressed.)
75
+ * Returns NULL on error. */
76
+heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2);
77
+
78
+/* Free a decoder. */
79
+void heatshrink_decoder_free(heatshrink_decoder *hsd);
80
+#endif
81
+
82
+/* Reset a decoder. */
83
+void heatshrink_decoder_reset(heatshrink_decoder *hsd);
84
+
85
+/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to
86
+ * indicate how many bytes were actually sunk (in case a buffer was filled). */
87
+HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, uint8_t *in_buf, size_t size, size_t *input_size);
88
+
89
+/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into
90
+ * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */
91
+HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, uint8_t *out_buf, size_t out_buf_size, size_t *output_size);
92
+
93
+/* Notify the dencoder that the input stream is finished.
94
+ * If the return value is HSDR_FINISH_MORE, there is still more output, so
95
+ * call heatshrink_decoder_poll and repeat. */
96
+HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd);

Loading…
Cancel
Save