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
|