File: nslu2_protocol.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 (457 lines) | stat: -rw-r--r-- 14,570 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
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
/*-
 * nslu2_protocol.h
 *  A definition of the protocol used by the NSLU2 in "upgrade mode".
 *
 *  The protocol consists of a sequence of packets which are sent and
 *  received over the lowest level ethernet protocol (which, it is important
 *  to note, is unreliable - the packet may need to be resent by the
 *  program).  The values in the packet are little endian - least significant
 *  byte first.
 *
 *  Each protocol packet is acknowledged by the NSLU2, sometimes by
 *  simply returning the original packet with the first couple of bytes
 *  modified to an error code, sometimes (in the case of HardwareInfo)
 *  by returning a new packet with the requested information.
 *
 * BYTES 0,1 TYPE
 * The first two bytes in the data packet describe the contents of
 * the rest of the packet and what to do with them.
 *
 * HardwareInfo   Return a description of hardware and firmware
 * UpgradeStart   Start an upgrade
 * UpgradeData    Download a block of new data
 * Reboot         Reboot the NSLU2
 * UpgradeVerify  Verify a block of data ('upgrade' data only)
 * ReprogramStart Start a complete reflash (Reboot too)
 *
 * BYTES 2,3  SEQUENCE
 * The next two bytes give a little endian sequence number, the protocol
 * requires packets of data (UpgradeData and UpgradeVerify) to have
 * sequence numbers incremented by one between each packet.  The initial
 * sequence number is set by the UpgradeStart or ReprogramStart packet.
 * Verification and Upgrade commands may not be intermixed - Verify must
 * follow upgrade - because the first UpgradeData erases the flash and
 * the first Verify resets the 'erase' state to 'erase on next UpgradeData'.
 *
 * BYTES 4,5 and 6,7 ADDRESS
 * Next comes the address.  This is relevant only for UpgradeData and
 * UpgradeVerify.  The address is an offset within a 1MByte block stored
 * as a byte offset within a 16 byte chunk plus a chunk offset.  Both
 * offsets are 16 bit values, therefore the scheme can only address
 * (about) 1MByte.  When the NSLU2 receives a 0 address it increments
 * it's internal 1MByte block counter.  This is set to (-1) by both
 * UpgradeStart and ReprogramStart and on the first UpgradeVerify.
 * Consequently the first UpgradeData or UpgradeVerify must have an
 * address of 0 (to cause the block number to be incremented to 0).
 *
 * BYTES 4,5: byte offset
 * BYTES 6,7: 16 byte chunk
 *
 * BYTES 8,9  LENGTH
 * The data length is stored next - this is the number of bytes of data
 * which follows.  The actual protocol code in the NSLU2 defines 600
 * bytes for this, however the buffer is allocated by the stock RedBoot
 * ethernet code and this seems to be set up to handle a maximum data
 * length of an ethernet packet (1500 bytes) correctly.
 *
 * Nevertheless of safety, and just in case something on the wire limits
 * the packet size further, this code sends 512 bytes of data at a time;
 * so the maximum data size (including the header) is 522 bytes.
 *
 *-----------------------------------------------------------------------------
 * Protocol definitions.
 *-----------------------------------------------------------------------------
 * In practice the RedBoot implementation does not re-allocate the buffer
 * before returning the packet, therefore whatever the value of the LENGTH
 * field in the packet, the packet must be long enough to also accomodate the
 * return data.  Also the sequence number is never changed, so it can be
 * used to check the response to a packet of any type.
 *
 * HardwareInfo
 * SEND:
 *  TYPE:	HarwareInfo
 *  SEQUENCE:	not required
 *  ADDRESS:	not required
 *  LENGTH:	not required
 *  DATA:	not required
 * RECEIVE:
 *  TYPE:	HardwareInfo
 *  SEQUENCE:	not set
 *  ADDRESS:	not set
 *  LENGTH:	56
 *  DATA:
 *  	The data is taken from the last 70 bytes of the RedBoot flash
 *  	segment, with the trailing and leading 7 bytes stripped and then
 *	further overwritten with the first 6 of the last 16 bytes of the
 *	flash.  Consequently the data block returned looks like this:
 *
 *	0..37  RedBoot 0x03FFC1..0x03FFF8
 *	38,39  Flash   0x7FFFF0, 0x7FFFF1	'product id'
 *	40,41  RedBoot 0x03FFE9, 0x03FFEA	'product id mask'
 *	42,43  Flash   0x7FFFF2, 0x7FFFF3	'protocol id'
 *	44,45  RedBoot 0x03FFED, 0x03FFEE	'protocol id mask'
 *	46..49 RedBoot 0x03FFEF, 0x03FFF2
 *	50,51  Flash   0x7FFFF4, 0x7FFFF5	'firmware version'
 *	52..55 RedBoot 0x03FFF5, 0x03FFF8
 *
 *	The values do not seem to be very useful:
 *
 *	product id	1
 *	protocol id	0
 *	FuncID(redboot)	3
 *	firmware	2329
 *
 * UpgradeStart
 * SEND:
 *  TYPE:	UpgradeStart
 *  SEQUENCE:	first sequence number
 *  ADDRESS:	not required
 *  LENGTH:	not required
 *  DATA:	not required
 * RECEIVE:
 *  TYPE:	UpgradeStart
 *  SEQUENCE:	not set
 *  ADDRESS:	not set
 *  LENGTH:	2
 *  DATA:	not set (!)
 *
 * ReprogramStart is identical.
 *
 * Reboot
 * SEND:
 *  TYPE:	Reboot
 *  SEQUENCE:	not required
 *  ADDRESS:	not required
 *  LENGTH:	not required
 *  DATA:	not required
 * RECEIVE:
 *  TYPE:	Reboot
 *  SEQUENCE:	not set
 *  ADDRESS:	not set
 *  LENGTH:	0 (!)
 *  DATA:	2 bytes set to 0
 */
#ifndef NSLU2_PROTOCOL_H
#define NSLU2_PROTOCOL_H 1

#include <cstring>

#define CHECK_ADDRESS 0
#if CHECK_ADDRESS
#include <stdexcept>
#endif

namespace NSLU2Protocol {
	/* CONSTANTS */
	typedef enum {
		HardwareInfo   = 0,
		UpgradeStart   = 1,
		UpgradeData    = 2,
		Reboot         = 3,
		UpgradeVerify  = 4,
		ReprogramStart = 5,
		InvalidType    = 0xffff,
	} Type;

	/* The following define the possible return codes - these are only
	 * set for UpgradeData and UpgradeVerify.  If an error is marked as
	 * fatal it will be necessary to restart (and re-erase).
	 */
	typedef enum {
		Ok            = 0, /* operation completed ok */
		ProtocolError = 5, /* operation not expected (UpgradeStart packet dropped) */
		SequenceError = 6, /* packet out of sequence (and ignored) */
		ProgramError  = 7, /* flash programming failed (fatal) */
		VerifyError   = 9, /* flash verification failed (fatal) */
	} ReturnCodeType;

	typedef enum {
		TypeOffset = 0,
		SequenceOffset = 2,
		AddressOffset = 4,
		LengthOffset = 8,
		DataOffset = 10,
		HeaderLength = DataOffset,
		SkipLength = 14+HeaderLength,/* actual packet header size */
		HardwareInfoLength = 56,
		MinDataLength = 2,           /* For the return code */
		/* By experiment 1504 produces a 'message too long' error from Linux,
		 * even though it should be fine.  1472 is chosen as the next lower
		 * multiple of 32 (and, in this case, 64).
		 */
		MaxDataLength = 1472,        /* 1540-14(eth hdr)-10(this header) - 1516 */
		MaxPacketLength = 1540-14,   /* at least 600 from the RedBoot code */
		BaseAddress = 0x60000,       /* skip RedBoot and SysConf */
		UpgradeProtocol = 0x8888,    /* defined in the RedBoot code */
		Ln2FlashSize = 23,           /* 8MByte Flash memory */
		FlashSize = (1<<Ln2FlashSize),
		/* The MaxPendingPackets figure is the number of packets which will
		 * be transmitted without receiving a response, this value should be
		 * 5, but experiment on a single system shows that even at 4 timeouts
		 * will occur (because of dropped packets).  It would seem that the
		 * RedBoot polling (not interrupt driven) implementation of the ethernet
		 * packet handling is unable to keep up, at least while upgrading
		 * (writing the flash).
		 */
		//MaxPendingPackets = 5,     /* Redboot can buffer 4 while one is in process */
		MaxPendingPackets = 1,       /* Determined by experiment to be the maximum */
		PacketArraySize = 8,         /* Next power of two for the array of data packets */
		PacketArrayMask = 7,         /* Mask a packet array index from an address */
	} Constant;

	/* SEQUENCE NUMBER HANDLING CLASS */
	/* SequenceNumber holds a sequence number and manages the tricky logic
	 * of updating it correctly - the local end needs to know both the
	 * last sequence number *sent* and the last one acknowledged.  In this
	 * implementation the initial sequence number is always "1".
	 */
	class SequenceNumber {
	public:
		SequenceNumber(void) :
			lastSeen(0), lastSent(0)
		{}

		inline unsigned long LastSeen(void) const {
			return lastSeen;
		}

		inline unsigned long LastSent(void) const {
			return lastSent;
		}

		inline int Send(void) {
			return ++lastSent;
		}

		/* Seen returns 'false' if the value is detectably invalid -
		 * if it is numerically greater than the lastSeen value.  Note
		 * that each packet is 512 bytes, so 65536 sequence numbers
		 * only allow for 32MByte, thus the sequence number will wrap
		 * in the verify for a 16MByte image.
		 */
		inline bool Seen(int seen) {
			if (seen > (lastSent & 0xffff))
				seen |= (lastSeen & ~0xffff);
			else
				seen |= (lastSent & ~0xffff);
			if (seen > lastSeen) {
				if (seen <= lastSent)
					lastSeen = seen;
				else
					return false;
			}
			return true;
		}

		/* Resend resets the sequence number to 'lastSeen', so that the
		 * next sent sequence number will be for the packet after the
		 * last seen one.
		 */
		inline void Resend(void) {
			lastSent = lastSeen;
		}

	private:
		unsigned long lastSeen, lastSent;
	};

	/* PACKET CLASSES */
	/* Packets vary in size, so we need a template class. */
	template <int datalength> class Packet {
		/* THE BUFFER */
	private:
		unsigned char buffer[HeaderLength+
			(datalength < MinDataLength ? MinDataLength : datalength)];

		/* APIS */
	private:
		/* Buffer APIs */
		inline void Write16Bits(int where, int value) {
			buffer[where+0] = value;
			buffer[where+1] = value >> 8;
		}

		inline void WriteAddress(int address) {
			/* Write offset then chunk. */
			Write16Bits(AddressOffset, address & 0xf);
			Write16Bits(AddressOffset+2, address >> 4);
		}

		inline int ReadAddress(void) const {
			return Read16Bits(AddressOffset) + (Read16Bits(AddressOffset+2) << 4);
		}

		inline unsigned char *WriteData(void) {
			return buffer + DataOffset;
		}

		inline const unsigned char *ReadData(void) const {
			return buffer + DataOffset;
		}

	protected:
		inline int Read16Bits(int whence) const {
			return buffer[whence+0] + (buffer[whence+1] << 8);
		}


		/* CONSTRUCTOR */
	protected:
		/* This does not initialise the data! */
		void Init(Type type, int sequence, int address, int length) {
#if CHECK_ADDRESS
			/* It would seem that the NSLU2 RedBoot flash write
			 * code relies on the address being correctly aligned for
			 * the base type of the flash.  Since this is 16 bit and
			 * might be 32 bit this code sanity checks the address
			 * here.
			 */
			if (address & 3)
				throw std::logic_error("badly aligned flash address");
#endif
			Write16Bits(TypeOffset, type);
			Write16Bits(SequenceOffset, sequence);
			WriteAddress(address);
			Write16Bits(LengthOffset, length);
		}

		/* This is used where the sequence number is a pre-determined
		 * value (the data packets).
		 */
		inline Packet(Type type, int sequence, int address, int length) {
			Init(type, sequence, address, length);
		}

		/* This is used for packets to be sent where the sequence number is
		 * just the next in line.
		 */
		inline Packet(Type type, SequenceNumber &seq, int address, int length) {
			Init(type, seq.Send(), address, length);
		}

		/* This is used for the array of data packets, which are uninitialised
		 */
		inline Packet() {}

		/* PUBLIC APIs */
	public:
		inline Type TypeOf(void) const {
			return static_cast<Type>(Read16Bits(TypeOffset));
		}
		inline int DataLength(void) const {
			return Read16Bits(LengthOffset);
		}
		inline int PacketLength(void) const {
			int dataLength(DataLength());
			if (dataLength < MinDataLength)
				dataLength = MinDataLength;
			return HeaderLength + dataLength;
		}
		inline int Sequence(void) const {
			return Read16Bits(SequenceOffset);
		}
		inline const unsigned char *PacketBuffer(void) const {
			return buffer;
		}
		inline const unsigned char *Data(void) const {
			return buffer + DataOffset;
		}
		inline unsigned char *Data(void) {
			return buffer + DataOffset;
		}

	protected:
		inline unsigned char *PacketWriteBuffer(void) {
			return buffer;
		}
	};

	/* Constructors for specific packet types. */
	class HardwareInfoPacket : public Packet<HardwareInfoLength> {
	public:
		/* This packet allows an arbitrary sequence number because it will
		 * typically be broadcast - this allows us to more reliably detect
		 * responses to our request.
		 */
		inline HardwareInfoPacket(int sequence) :
			Packet<HardwareInfoLength>(HardwareInfo, sequence, 0,
					HardwareInfoLength) {
		}
	};
	class RebootPacket : public Packet<0> {
	public:
		inline RebootPacket(int sequence) :
			Packet<0>(Reboot, sequence, 0, 0)
		{}
	};

	/* UpgradeStartPacket and ReprogramStartPacket are both instances of
	 * StartPacket and StartPacket contains the sequence number for the
	 * upgrade and verify exchange.
	 */
	class StartPacket : public Packet<0> {
	protected:
		inline StartPacket(Type type, SequenceNumber &seq) :
			Packet<0>(type, seq, 0, 0)
		{}
	};
	class UpgradeStartPacket : public StartPacket {
	public:
		inline UpgradeStartPacket(SequenceNumber &seq) :
			StartPacket(UpgradeStart, seq)
		{}
	};
	class ReprogramStartPacket : public StartPacket {
	public:
		inline ReprogramStartPacket(SequenceNumber &seq) :
			StartPacket(ReprogramStart, seq)
		{}
	};

	/* UpgradeDataPacket and VerifyDataPacket are implemented using a shared
	 * DataPacket.  In practice these are allocated in an uninitialised array.
	 */
	class DataPacket : public Packet<MaxDataLength> {
	public:
		/* The public initialiser. */
		void Init(Type type, int sequence, int address, int length,
				const void *data) {
			Packet<MaxDataLength>::Init(type, sequence, address, length);
			std::memcpy(Data(), data, length);
		}

		inline DataPacket(Type type, int sequence, int address,
				int length, const void *data) {
			Init(type, sequence, address, length, data);
		}

		/* The non-initialising version */
		inline DataPacket() {}
	};

	/* A general packet used to receive data. */
	class ReceivePacket : public Packet<MaxPacketLength> {
	public:
		inline ReceivePacket() :
			Packet<MaxPacketLength>(InvalidType, 0xffff, 0, 0xffff)
		{}

		/* The ReceivePacket has a writeable buffer for Receive! */
		inline unsigned char *PacketWriteBuffer(void) {
			return Packet<MaxPacketLength>::PacketWriteBuffer();
		}

		inline int PacketBufferSize(void) const {
			return MaxPacketLength;
		}

		inline ReturnCodeType ReturnCode(void) const {
			if (TypeOf() == UpgradeData || TypeOf() == UpgradeVerify)
				return static_cast<ReturnCodeType>(Read16Bits(DataOffset));
			return Ok;

		}
	};
};

#endif