File: upslug2_progress.h

package info (click to toggle)
upslug2 11-5
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 612 kB
  • sloc: sh: 3,430; cpp: 2,142; makefile: 7
file content (366 lines) | stat: -rw-r--r-- 11,818 bytes parent folder | download | duplicates (4)
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/*-
 * upslug2_progress.h
 *
 *  A basic progress implementation which accumulates the information but does
 *  not output it to the user, provides virtual callbacks for a real implementation.
 *
 *  This header defines a template class which should be sub-classed to do
 *  something appropriate with the information.  The sub-class will normally
 *  implement Sent and Received by calling this the parent class (this class)
 *  implementation.
 */
#ifndef UPSLUG2_PROGRESS_H
#define UPSLUG2_PROGRESS_H 1

#include <cstring>

#include "nslu2_protocol.h"
#include "nslu2_upgrade.h"

namespace UpSlug2 {
	/* The basis of a progress bar implementation - simply keeps track of
	 * the last seen and sent addresses.
	 */
	class Progress : public NSLU2Upgrade::Progress {
	public:
		inline Progress() :
			lastType(NSLU2Protocol::InvalidType),
			addressOfLastSeen(-1), addressOfLastSent(-1)
			{
			}
		// virtual inline ~Progress() {}

		/* Return a value in the range 0..(scale-1) to indicate the
		 * proportion of packets sent (but not necessarily received
		 * by the NSLU2) or received and acknowledged.  The result
		 * is *rounded* to the given range and must not be scaled
		 * again - to get a value to a different scale just call this
		 * API a second time.
		 */
		inline unsigned int ProportionSent(unsigned int scale) const {
			if (addressOfLastSent > 0) {
				return Scale(addressOfLastSent,
						NSLU2Protocol::Ln2FlashSize, scale);
			} else
				return 0;
		}

		inline int ProportionReceived(int scale) const {
			if (addressOfLastSeen > 0)
				return Scale(addressOfLastSeen,
						NSLU2Protocol::Ln2FlashSize, scale);
			else
				return 0;
		}
				
	protected:
		int AddressOfLastSeen(void) const {
			return addressOfLastSeen;
		}

		inline int AddressOfLastSent(void) const {
			return addressOfLastSent;
		}

		static inline unsigned int Scale(unsigned long val, unsigned int shift,
				unsigned int scale) {
			while (shift > 0 && scale >= (1<<(32-shift)))
				val >>=1, --shift;
			/* This really is correctly rounded, because a result of
			 * scale-1 corresponds to 100% (i.e. 100% is scale-1,
			 * not scale...)  If you don't want to ever see the full
			 * scale result (i.e. the UI doesn't want to output the
			 * 100% setting until after everything has been confirmed
			 * as written) simply pass in range-1 instead of range.
			 */
			return (val * scale) >> shift;
		}

	protected:
		/* Sent is called whenever a packet is sent with the sequence
		 * number, address (in flash) of the data and length of the
		 * data.  If retransmission is necessary the sequence number will
		 * be re-used, the last sent valid address (and the highest) is
		 * always that in the last 'Sent' callback - i.e. lower sequence
		 * numbers/addresses invalidate earlier sends.
		 */
		virtual void Sent(NSLU2Protocol::Type type,
				int sequence, int address, int length) {
			if (type == NSLU2Protocol::UpgradeVerify &&
				lastType == NSLU2Protocol::UpgradeData) {
				/* Reset for the verify step. */
				addressOfLastSeen = -1;
			}

			lastType = type;
			addressOfLastSent = address+length-1;
		}

		/* Timeout is called when a receive timeout occurs.  Nothing is
		 * implemented for this class.

		 virtual inline void Timeout(NSLU2Protocol::Type type, int sequence);

		 */

		/* Retransmit is called when a packet must be retransmitted, the
		 * sequence number is that of the first packet to be retransmitted.

		 virtual inline void Retransmit(NSLU2Protocol::Type type, int sequence);

		 */

		/* Received is called after a packet is received and it is passed
		 * the highest sequence number received (not necessarily that of
		 * the received packet) - this is a low water mark on the flash upgrade
		 * or verify, this packet and all earlier ones (therefore all earlier
		 * addresses) have been handled on the NSLU2.
		 */
		virtual void Received(NSLU2Protocol::Type type, int sequence,
				int address, int length) {
			if (type == lastType) { /* else an old packet */
				addressOfLastSeen = address+length-1;
			}
		}

	private:
		NSLU2Protocol::Type lastType; /* type of last received packet */

		int addressOfLastSent; /* highest address sent */
		int addressOfLastSeen; /* highest address seen */
	};

	/* This is an implementation of Progress which stores enough information to
	 * display a progress bar.  Points on the progress bar indicate the state
	 * of the corresponding address in the flash.
	 *
	 * Every point on the progress bar will be in one of the enumerated states
	 * (see Status below).  What is more each state is assocated with a single
	 * range of addresses, later states may overlap (and override) states which
	 * occur earlier in the enumeration.  Consequently the progress bar can be
	 * built by asking for the low and high water mark of each state and drawing
	 * each in turn.
	 *
	 * The 'Changed' API is called when the status changes in some way - the
	 * sub-class must determine whether anything has changed enough to cause
	 * a redraw to be required.
	 */
	class ProgressBar : public Progress {
	protected:
		/* Basic typedef to return information about the state of a
		 * given address in the flash.
		 */
		typedef enum {
			Init,       /* .: address has original flash contents */
			Erase,      /* !: address is being erased */
			Erased,     /* -: address has been erased */
			Upgrade,    /* u: address is being upgraded (packet sent). */
			Upgraded,   /* U: address has been upgraded (response received). */
			Verify,     /* v: address is being verified. */
			Verified,   /* V: address has been verified (reprogramming complete). */
			Timedout,   /* *: timeout on a sent packet for this address. */
			NumberOfStates
		} Status;
		
		/* reprogram says whether this is a full reprogram (the entire
		 * flash will be erased) or not (the leading, RedBoot, SysConf
		 * partitions are not erased).
		 * resolution should be about 6 for a command line (character)
		 * progress bar and 8 for a GUI (pixel) progress bar.
		 */
		ProgressBar(bool r) :
			reprogram(r), timeout(false), retransmit(false), status(Init) {
		}

		/* lowWaterMark..(highWaterMark-1) bytes are in state 'st',
		 * unless they are also marked in a (numerically) higher state.
		 */
		void AddressByStatus(Status st, int &lowWaterMark, int &highWaterMark) {
			/* These initial settings cover the majority of cases
			 * correctly.
			 */
			lowWaterMark = reprogram ? 0 : NSLU2Protocol::BaseAddress;
			highWaterMark = status >= st ? NSLU2Protocol::FlashSize-1 : 0;
			switch (st) {
			case Init:
				/* Everything has an initial value... */
				lowWaterMark = 0;
				break;
			case Erase:
			case Erased:
				/* Set correctly above. */
				break;
			case Upgrade: case Verify:
				if (status == st)
					highWaterMark = AddressOfLastSent();
				break;
			case Upgraded: case Verified:
				/* The status class member is set to Upgrade or
				 * Verify, never to Upgraded or Verified.
				 */
				if (status == st-1)
					highWaterMark = AddressOfLastSeen();
				break;
			case Timedout:
				/* status is never set to timeout, but if there
				 * is a timeout then the 'uncertain' addresses
				 * are the ones which have been sent but not
				 * received.
				 */
				if (timeout || retransmit) {
					lowWaterMark = AddressOfLastSeen();
					highWaterMark = AddressOfLastSent();
				}
				break;
			}
		}

		/* The following must be implemented in a sub-class to do the actual
		 * display.
		 */
		virtual void Changed(void) = 0;

		virtual void Sent(NSLU2Protocol::Type type,
				int sequence, int address, int length) {
			int old(AddressOfLastSent());

			Progress::Sent(type, sequence, address, length);

			bool changed(old != AddressOfLastSent());
			if (status == Init && type == NSLU2Protocol::UpgradeData)
				changed = true, status = Erase;

			if (changed)
				Changed();
		}

		virtual void Timeout(NSLU2Protocol::Type type, int sequence) {
			if (!timeout) {
				timeout = true;
				Changed();
			}
		}

		virtual void Retransmit(NSLU2Protocol::Type type, int sequence,
				int sequenceError) {
			if (!retransmit) {
				retransmit = true;
				Changed();
			}
		}

		virtual void Received(NSLU2Protocol::Type type, int sequence,
				int address, int length) {
			int old(AddressOfLastSeen());

			Progress::Received(type, sequence, address, length);

			bool changed(old != AddressOfLastSeen());
			if (timeout || retransmit)
				changed = true, timeout = false, retransmit = false;
			if (type == NSLU2Protocol::UpgradeVerify) {
				if (status != Verify)
					changed = true, status = Verify;
			} else if (type == NSLU2Protocol::UpgradeData) {
				if (status != Upgrade)
					changed = true, status = Upgrade;
			} else
				return;

			if (changed)
				Changed();
		}

	private:
		Status status;        /* Overall status */
	protected:
		bool   reprogram;
		bool   timeout;       /* Timeout recorded */
		bool   retransmit;    /* Retransmit recorded */
	};


	/* This is an implementation of ProgressBar for the command line, the
	 * initialiser must be passed the actual of characters in the output
	 * display, the template parameter is the maximum this can be!
	 */
	template <int characters> class CharacterProgressBar : public ProgressBar {
	public:
		CharacterProgressBar(bool reprogram, int n, const char ind[NumberOfStates] = 0) :
			numberOfCharacters(n > characters || n < 1 ? characters : n),
			ProgressBar(reprogram) {
			if (ind)
				std::memcpy(indicators, ind, NumberOfStates);
			else
				std::memcpy(indicators, ".!-uUvV*", NumberOfStates);
			std::memset(display, 0, sizeof display);
		}

		/* Implement this to update the display, the display argument is
		 * the new character array (null terminated), the values are the
		 * indices of the first and last change.
		 */
		virtual void UpdateDisplay(const char *display,
				int firstChanged, int lastChanged) = 0;

	protected:
		/* Return the progress indicator character for a given state. */
		inline char Indicator(Status st) const {
			return indicators[st];
		}

		/* Callback from ancestor class to indicate a state change - this
		 * won't necessarily change the display.
		 */
		virtual void Changed(void) {
			char oldDisplay[characters];
			std::memcpy(oldDisplay, display, characters);
			for (Status st(Init); st < NumberOfStates; st = static_cast<Status>(st+1)) {
				int lowWaterMark, highWaterMark;
				AddressByStatus(st, lowWaterMark, highWaterMark);
				if (highWaterMark > lowWaterMark) {
					lowWaterMark = Scale(lowWaterMark,
						NSLU2Protocol::Ln2FlashSize, numberOfCharacters);
					/* For things like Upgrade and Verify use the basic
					 * scaling which returns 0..(numberOfCharacters-1)
					 * and evenly distributes the values 0..(flashsize-1)
					 * addresses between those values - flashsize/nochar
					 * in each slot.
					 *
					 * For Upgraded and Verified (etc) only fill value
					 * n when all the flashsize/nochar addresses for
					 * that slot have been filled.
					 */
					if (st & 1) /* 'in progress' status */
						highWaterMark = Scale(highWaterMark,
							NSLU2Protocol::Ln2FlashSize,
							numberOfCharacters);
					else        /* 'done' status */
						highWaterMark = Scale(highWaterMark+1,
							NSLU2Protocol::Ln2FlashSize,
							numberOfCharacters)-1;

					while (lowWaterMark <= highWaterMark)
						display[lowWaterMark++] = indicators[st];
				}
			}

			int firstChanged(characters), lastChanged(0);
			for (int i(0); i<numberOfCharacters; ++i)
				if (oldDisplay[i] != display[i]) {
					if (i < firstChanged)
						firstChanged = i;
					lastChanged = i;
				}
			if (firstChanged <= lastChanged || retransmit || timeout)
				UpdateDisplay(display, firstChanged, lastChanged);
		}

	private:
		int  numberOfCharacters;
		char indicators[NumberOfStates];
		char display[characters+1];
	};
};

#endif