summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--_ont/ont-huawei-ma5671a-ymodem.md206
-rw-r--r--_sass/custom/custom.scss4
-rw-r--r--assets/js/rootLantiq.js141
-rw-r--r--assets/js/xymini.js147
4 files changed, 490 insertions, 8 deletions
diff --git a/_ont/ont-huawei-ma5671a-ymodem.md b/_ont/ont-huawei-ma5671a-ymodem.md
new file mode 100644
index 0000000..9756dd0
--- /dev/null
+++ b/_ont/ont-huawei-ma5671a-ymodem.md
@@ -0,0 +1,206 @@
+---
+title: Root Procedure for Huawei MA5671A (flash firmware)
+has_children: false
+parent: Huawei MA5671A
+layout: default
+---
+
+<div class="modal" data-jtd-modal="flash-modal" data-jtd-modal-backdrop="static" id="flash-modal">
+ <div class="modal-content">
+ <div class="modal-header">
+ <span class="close">&times;</span>
+ <h2>Flash firmware</h2>
+ </div>
+ <form id="flash-form" class="p-4" novalidate>
+ <div class="form-floating mb-3">
+ <input type="file" class="form-control" placeholder="Flash MTD" name="flash-mtd" id="flash-mtd" required>
+ <label for="flash-mtd">Flash MTD</label>
+ </div>
+ <div class="mb-3 form-check">
+ <input type="checkbox" class="form-check-input" id="baud-rate-oc" name="baud-rate-oc">
+ <label class="form-check-label" for="baud-rate-oc">230400 baud rate, do not enable unless told to do so</label>
+ </div>
+ <div class="form-check">
+ <input class="form-check-input" type="radio" name="image" id="image0" value="image0">
+ <label class="form-check-label" for="image0">
+ Image 0
+ </label>
+ </div>
+ <div class="form-check">
+ <input class="form-check-input" type="radio" name="image" id="image1" value="image1">
+ <label class="form-check-label" for="image1">
+ Image 1
+ </label>
+ </div>
+ <div class="mb-3">
+ <input type="submit" class="btn btn-primary" value="Flash!">
+ </div>
+ <progress id="flash-progress" value="0" max="100"></progress>
+ <p id="flash-text-step"></p>
+ </form>
+ </div>
+</div>
+
+<button id="flash-start-button" class="btn btn-blue" data-jtd-toogle="modal" data-jtd-target="#flash-modal">Start flash!</button>
+
+<script type="text/javascript" src="/assets/js/xymini.js"></script>
+<script type="text/javascript" src="/assets/js/rootLantiq.js"></script>
+<script type="text/javascript" src="/assets/js/serialUtil.js"></script>
+<script>
+ const acontroller = new AbortController();
+ const cs = acontroller.signal;
+ let flashModal = document.getElementById("flash-modal");
+ let flashForm = document.getElementById("flash-form");
+ let flashProgress = document.getElementById("flash-progress");
+ let flashTextStep = document.getElementById("flash-text-step");
+ flashModal.addEventListener('modal-jtd-close', async function(event) {
+ acontroller.abort();
+ });
+ flashModal.addEventListener('modal-jtd-open', async function(event) {
+ flash({signal: cs});
+ });
+ function initTextStep() {
+ flashTextStep.textContent = "";
+ flashTextStep.classList.remove('success');
+ flashTextStep.classList.remove('error');
+ }
+ function pause(message) {
+ flashTextStep.textContent = message;
+ }
+ function loading(message) {
+ flashTextStep.textContent = message;
+ }
+ function showError(message) {
+ flashTextStep.textContent = message;
+ flashTextStep.classList.add('error');
+ flashTextStep.classList.remove('success');
+ }
+ function showSuccess(message) {
+ flashTextStep.textContent = message;
+ flashTextStep.classList.add('success');
+ flashTextStep.classList.remove('error');
+ }
+ async function flash({ signal } = {}) {
+ initTextStep();
+ let port;
+ try {
+ port = await navigator.serial.requestPort();
+ } catch (err) {
+ showError(`Error: ${err.message}`);
+ console.log(`Error: ${err.message}\n`);
+ return;
+ }
+ if (!port) {
+ showError('Error: port not open');
+ console.log('Error: port not open\n');
+ return;
+ }
+ flashForm.addEventListener('submit', async function(event) {
+ [...flashForm.elements].map(function(e){return e.parentNode}).forEach(function(e){e.classList.toggle('was-validated', true)});
+ if (!flashForm.checkValidity()) {
+ event.preventDefault();
+ } else {
+ event.preventDefault();
+ var fomrdata = new FormData(flashForm);
+ var file = fomrdata.get('flash-mtd');
+ var image = fomrdata.get('image');
+ var data = new Uint8Array(await file.arrayBuffer());
+ console.log(data);
+
+ /* Unlock U-Boot if needed and stop booting */
+ let result = await lantiqRootUboot(port, "Huawei MA5671A",
+ (msg) => {
+ loading(msg);
+ console.log(msg);
+ },
+ (err) => {
+ showError(err);
+ console.log(err);
+ }
+ );
+
+ if (!result) {
+ return;
+ }
+
+ let baudrate = 115200;
+ if(fomrdata.has('baud-rate-oc')) {
+ let newBaudrate = 230400;
+ loading(`Changing baudrate to: ${newBaudrate}`);
+
+ result = await changeBaudrate(port, newBaudrate, baudrate,
+ (err) => {
+ showError(err);
+ console.log(err);
+ }
+ );
+
+ if (result) {
+ baudrate = newBaudrate;
+ } else {
+ return;
+ }
+ }
+
+ loading("Start sending image to the SFP...");
+ result = await sendImageMtd(port, data, baudrate,
+ (err) => {
+ showError(err);
+ console.log(err);
+ },
+ (byteTransfered) => {
+ const perc = (byteTransfered/data.length) * 100;
+ const percTrunc = Math.trunc(perc*100)/100; /* Two decimal trunc */
+ flashProgress.value = perc;
+ loading(`Image transfer: ${percTrunc}% complete`)
+ }
+ );
+
+ if (!result) {
+ return;
+ }
+
+ result = await waitEndImageLoad(port, baudrate,
+ (err) => {
+ showError(err);
+ console.log(err);
+ }
+ );
+
+ if (!result) {
+ return;
+ }
+
+ if(fomrdata.has('baud-rate-oc')) {
+ let newBaudrate = 115200;
+ loading(`Restore baudrate to: ${newBaudrate}`);
+
+ result = await changeBaudrate(port, newBaudrate, baudrate,
+ (err) => {
+ showError(err);
+ console.log(err);
+ }
+ );
+
+ if (result) {
+ baudrate = newBaudrate;
+ } else {
+ return;
+ }
+ }
+
+ loading("Transfer complete, image flash in progress. DO NOT REMOVE the SFP!");
+ result = await flashImageMtd(port, image, baudrate,
+ (err) => {
+ showError(err);
+ console.log(err);
+ }
+ );
+
+ if (result) {
+ showSuccess("Flash completed, now you can remove SFP");
+ }
+ }
+ });
+ };
+</script>
diff --git a/_sass/custom/custom.scss b/_sass/custom/custom.scss
index 9651d11..f8bf607 100644
--- a/_sass/custom/custom.scss
+++ b/_sass/custom/custom.scss
@@ -231,4 +231,8 @@ figure {
stroke-dashoffset: 0;
}
}
+}
+
+progress {
+ width: 100%;
} \ No newline at end of file
diff --git a/assets/js/rootLantiq.js b/assets/js/rootLantiq.js
index ff0f478..570133c 100644
--- a/assets/js/rootLantiq.js
+++ b/assets/js/rootLantiq.js
@@ -1,20 +1,28 @@
-async function waitUbootStop(writer, reader, sfpModel, outputMsgCallback) {
- const interval = setInterval(function() {
- writer.write(String.fromCharCode(3));
- }, 10);
+const LOAD_ADDR = "80800000"
+const IMAGE0_ADDR = "C0000 740000";
+const IMAGE1_ADDR = "800000 800000";
+async function detectUboot(reader) {
while (true) {
const { value, done } = await reader.read();
if (value.startsWith('U-Boot')) {
- outputMsgCallback(`Root in progress: Trigger characters received. DO NOT TOUCH THE ${sfpModel} UNTIL THE PROCEDURE IS COMPLETED!`);
- await delay(5000);
- clearInterval(interval);
- break;
+ return;
}
}
}
+async function waitUbootStop(writer, reader, sfpModel, outputMsgCallback) {
+ const interval = setInterval(function() {
+ writer.write(String.fromCharCode(3));
+ }, 10);
+
+ await detectUboot(reader);
+ outputMsgCallback(`Root in progress: Trigger characters received. DO NOT TOUCH THE ${sfpModel} UNTIL THE PROCEDURE IS COMPLETED!`);
+ await delay(5000);
+ clearInterval(interval);
+}
+
async function checkUbootUnlocked(reader) {
while (true) {
try {
@@ -131,3 +139,120 @@ async function unlockHuaweiShell(port, outputMsgCallback, outputErrorCallback, b
return false;
}
}
+
+async function changeBaudrate(port, newBaudrate, currBaudrate, outputErrorCallback) {
+ let reader,writer, readableStreamClosed, writerStreamClosed;
+
+ try {
+ ({ reader, writer, readableStreamClosed, writerStreamClosed } = await openPortLineBreak(port, currBaudrate));
+ await writer.write(`setenv baudrate ${newBaudrate}\n`);
+ await delay(1000);
+ await closePortLineBreak(port, reader, writer, readableStreamClosed, writerStreamClosed);
+ ({ reader, writer, readableStreamClosed, writerStreamClosed } = await openPortLineBreak(port, newBaudrate));
+
+ const interval = setInterval(function() {
+ writer.write(String.fromCharCode(13));
+ }, 10);
+
+ while (true) {
+ const { value, done } = await reader.read();
+
+ if (value.startsWith('FALCON')) {
+ clearInterval(interval);
+ break;
+ }
+ }
+
+ await closePortLineBreak(port, reader, writer, readableStreamClosed, writerStreamClosed);
+ return true;
+ } catch (err) {
+ outputErrorCallback(`Error: ${err.message}`);
+ await closePortLineBreak(port, reader, writer, readableStreamClosed, writerStreamClosed);
+ return false;
+ }
+}
+
+async function sendImageMtd(port, data, baudRate, outputErrorCallback, progressCallback) {
+ let reader,writer, readableStreamClosed, writerStreamClosed;
+
+ try {
+ ({ reader, writer, readableStreamClosed, writerStreamClosed } = await openPortLineBreak(port, baudRate));
+ await writer.write(`loady 0x${LOAD_ADDR}\n`);
+ await delay(1000);
+ await closePortLineBreak(port, reader, writer, readableStreamClosed, writerStreamClosed); /* XYMini needs reopen the port */
+ } catch (err) {
+ outputErrorCallback(`Error: ${err.message}`);
+ await closePortLineBreak(port, reader, writer, readableStreamClosed, writerStreamClosed);
+ return false;
+ }
+
+ try {
+ await port.open({ baudRate: baudRate });
+ reader = port.readable.getReader();
+ writer = port.writable.getWriter();
+
+ await sendXYMini(reader, writer, data, baudRate,
+ (byteTransfered) => {
+ progressCallback(byteTransfered);
+ }
+ );
+ await reader.cancel();
+ await writer.close();
+ await port.close();
+ return true;
+ } catch (err) {
+ await reader.cancel();
+ await writer.close();
+ await port.close();
+ outputErrorCallback(`Error: ${err.message}`);
+ return false;
+ }
+}
+
+async function waitEndImageLoad(port, baudRate, outputErrorCallback) {
+ let reader, writer, readableStreamClosed, writerStreamClosed;
+
+ try {
+ ({ reader, writer, readableStreamClosed, writerStreamClosed } = await openPortLineBreak(port, baudRate));
+
+ while (true) {
+ const { value, done } = await reader.read();
+
+ if (value.includes('Total Size')) {
+ break;
+ }
+ }
+
+ await(1000);
+ await closePortLineBreak(port, reader, writer, readableStreamClosed, writerStreamClosed);
+ return true;
+ } catch (err) {
+ outputErrorCallback(`Error: ${err.message}`);
+ await closePortLineBreak(port, reader, writer, readableStreamClosed, writerStreamClosed);
+ return false;
+ }
+}
+
+async function flashImageMtd(port, image, baudRate, outputErrorCallback) {
+ let reader, writer, readableStreamClosed, writerStreamClosed;
+
+ try {
+ ({ reader, writer, readableStreamClosed, writerStreamClosed } = await openPortLineBreak(port, baudRate));
+ if (image == "image0") {
+ await writer.write(`sf probe 0 && sf erase ${IMAGE0_ADDR} && sf write ${LOAD_ADDR} ${IMAGE0_ADDR} && setenv committed_image 0 && setenv image0_is_valid 1 && saveenv && reset\n`);
+ } else {
+ await writer.write(`sf probe 0 && sf erase ${IMAGE1_ADDR} && sf write ${LOAD_ADDR} ${IMAGE1_ADDR} && setenv committed_image 1 && setenv image1_is_valid 1 && saveenv && reset\n`);
+ }
+
+ await delay(1000);
+
+ /* Wait to avoid the user from disconnecting the SFP while the image is being flashed */
+ await detectUboot(reader);
+ await closePortLineBreak(port, reader, writer, readableStreamClosed, writerStreamClosed);
+ return true;
+ } catch (err) {
+ outputErrorCallback(`Error: ${err.message}`);
+ await closePortLineBreak(port, reader, writer, readableStreamClosed, writerStreamClosed);
+ return false;
+ }
+}
diff --git a/assets/js/xymini.js b/assets/js/xymini.js
new file mode 100644
index 0000000..a1fd1ca
--- /dev/null
+++ b/assets/js/xymini.js
@@ -0,0 +1,147 @@
+/* XYMini Sender - Minimal implementation of file transfer through serial
+ * Copyright (C) Ernesto Castellotti <mail@ernestocastellotti.it>
+ * SPDX-License-Identifier: MPL-2.0-no-copyleft-exception
+ *
+ * Warning: This does not comply with XMODEM and YMODEM standards
+*/
+
+const STX = 0x02;
+const ACK = 0x06;
+const NAK = 0x15;
+const EOF = 0x04;
+const XYMINI_1K_MAGIC = 0x43;
+const PAYLOAD_LEN = 1024;
+const BLOCK_LEN = PAYLOAD_LEN + 5;
+const CRC_POLY = 0x1021;
+
+function uint16 (n) {
+ return n & 0xFFFF;
+}
+
+function updateCrc(crcIn, incr) {
+ const xor = uint16(crcIn >> 15);
+ let result = uint16(crcIn << 1);
+
+ if (incr) {
+ result = uint16(result + 1);
+ }
+
+ if (xor) {
+ result = uint16(result ^= CRC_POLY);
+ }
+
+ return result;
+}
+
+function crc16(data) {
+ let crc;
+
+ for (let i = 0; i < data.length; i++) {
+ for (let j = 0x80; j; j >>= 1) {
+ crc = updateCrc(crc, data[i] & j);
+ }
+ }
+
+ for (let n = 0; n < 16; n++) {
+ crc = updateCrc(crc, 0);
+ }
+
+ return crc;
+}
+
+async function detectXYMini(reader) {
+ const textDecoder = new TextDecoder();
+
+ while (true) {
+ const { value, done } = await reader.read();
+
+ if (value[0] == XYMINI_1K_MAGIC) {
+ console.log("XYMini: detected");
+ break;
+ }
+ }
+}
+
+function generateXYMiniBlock(blockId, payload) {
+ let buf = new Uint8Array(BLOCK_LEN);
+ let i = 0;
+
+ buf[i++] = STX;
+ buf[i++] = blockId;
+ buf[i++] = 0xFF - blockId;
+
+ if (payload.length > PAYLOAD_LEN) {
+ throw new Error("Payload too large to be transmitted in one block");
+ }
+
+ for (let j = 0; j < payload.length; j++) {
+ buf[i++] = payload[j];
+ }
+
+ while (i < BLOCK_LEN - 2) {
+ buf[i++] = 0xFF;
+ }
+
+ let crcBuf = buf.slice(3, PAYLOAD_LEN + 3)
+ let crc = crc16(crcBuf);
+
+ buf[i++] = (crc >> 8) & 0xFF;
+ buf[i++] = crc & 0xFF;
+
+ return buf;
+}
+
+async function sendXYMini(portReader, portWriter, data, baudRate = 115200, progressCallback) {
+ let blockId = 1;
+ let size = data.length;
+ let i = 0;
+ let nakN = 0;
+ let wrongCharN = 0;
+
+ await detectXYMini(portReader);
+
+ while(true) {
+ const payloadSize = Math.min(PAYLOAD_LEN, size);
+
+ if (size) {
+ const payload = data.slice(i, payloadSize + i);
+
+ const block = generateXYMiniBlock(blockId, payload);
+ await portWriter.write(block);
+ } else {
+ portWriter.write(new Uint8Array([EOF]));
+ }
+
+ const { value, done } = await portReader.read();
+
+ if (value[0] == ACK) {
+ if (!size) {
+ console.log("XYMini: End of transmission");
+
+ return;
+ }
+
+ blockId++;
+ size -= payloadSize;
+ i += payloadSize;
+ nakN = 0;
+ wrongCharN = 0;
+ progressCallback(data.length - size);
+ } else if (value[0] == NAK) {
+ if (nakN >= 10) {
+ throw new Error("Received 10 NAK, receiver is rejecting file transmission");
+ }
+
+ console.log("XYMini: NAK");
+ nakN++;
+ } else {
+ if (wrongCharN >= 30) {
+ throw new Error("Received 30 wrong characters, the receiver is rejecting the transmission or the connection is too noisy");
+ }
+
+ console.log("XYMini: wrong character");
+ console.log(value);
+ wrongCharN++;
+ }
+ }
+}