diff --git a/bun.lockb b/bun.lockb index 17c1778..4792ed3 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 7968bfd..edbd78d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "node": ">=20.11.0" }, "dependencies": { + "@commaai/qdl": "git+https://github.com/commaai/qdl.js.git", "@fontsource-variable/inter": "^5.0.18", "@fontsource-variable/jetbrains-mono": "^5.0.21", "comlink": "^4.4.1", diff --git a/src/QDL/firehose.js b/src/QDL/firehose.js deleted file mode 100644 index 42d22f2..0000000 --- a/src/QDL/firehose.js +++ /dev/null @@ -1,293 +0,0 @@ -import { xmlParser } from "./xmlParser" -import { concatUint8Array, containsBytes, compareStringToBytes, sleep, readBlobAsBuffer } from "./utils" -import * as Sparse from "./sparse" - - -class response { - constructor(resp=false, data=new Uint8Array(), error="", log=[]) { - this.resp = resp; - this.data = data; - this.error = error; - this.log = log; - } -} - - -class cfg { - constructor() { - this.ZLPAwareHost = 1; - this.SkipStorageInit = 0; - this.SkipWrite = 0; - this.MaxPayloadSizeToTargetInBytes = 1048576; - this.MaxPayloadSizeFromTargetInBytes = 4096; - this.MaxXMLSizeInBytes = 4096; - this.bit64 = true; - this.SECTOR_SIZE_IN_BYTES = 4096; - this.MemoryName = "UFS"; - this.maxlun = 6; - } -} - -export class Firehose { - constructor(cdc) { - this.cdc = cdc; - this.xml = new xmlParser(); - this.cfg = new cfg(); - this.luns = []; - } - - getStatus(resp) { - if ("value" in resp) { - let value = resp["value"]; - return (value === "ACK" || value === "true"); - } - return true; - } - - async xmlSend(data, wait=true) { - let dataToSend = new TextEncoder().encode(data).slice(0, this.cfg.MaxXMLSizeInBytes); - await this.cdc?.write(dataToSend, null, wait); - - let rData = new Uint8Array(); - let counter = 0; - let timeout = 3; - while (!(containsBytes(" timeout) { - break; - } - } - rData = concatUint8Array([rData, tmp]); - } - - const resp = this.xml.getReponse(rData); - const status = this.getStatus(resp); - if ("rawmode" in resp) { - if (resp["rawmode"] == "false") { - let log = this.xml.getLog(rData); - return new response(status, rData, "", log) - } - } else { - if (status) { - if (containsBytes("log value=", rData)) { - let log = this.xml.getLog(rData); - return new response(status, rData, "", log); - } - return new response(status, rData); - } - } - return new response(true, rData); - } - - getLuns() { - return Array.from({length: this.cfg.maxlun}, (x, i) => i) - } - - async configure() { - const connectCmd = `` + - `` + - `` - - await this.xmlSend(connectCmd, false); - this.luns = this.getLuns(); - return true; - } - - async cmdReadBuffer(physicalPartitionNumber, startSector, numPartitionSectors) { - const data = `\n` - - let rsp = await this.xmlSend(data); - let resData = new Uint8Array(); - if (!rsp.resp) { - return rsp; - } else { - let bytesToRead = this.cfg.SECTOR_SIZE_IN_BYTES * numPartitionSectors; - while (bytesToRead > 0) { - let tmp = await this.cdc.read(Math.min(this.cdc.maxSize, bytesToRead)); - const size = tmp.length; - bytesToRead -= size; - resData = concatUint8Array([resData, tmp]); - } - - const wd = await this.waitForData(); - const info = this.xml.getLog(wd); - rsp = this.xml.getReponse(wd); - if ("value" in rsp) { - if (rsp["value"] !== "ACK") { - return new response(false, resData, info); - } else if ("rawmode" in rsp) { - if (rsp["rawmode"] === "false") { - return new response(true, resData); - } - } - } else { - console.error("Failed read buffer"); - return new response(false, resData, rsp[2]); - } - } - let resp = rsp["value"] === "ACK"; - return response(resp, resData, rsp[2]); - } - - async waitForData() { - let tmp = new Uint8Array(); - let timeout = 0; - - while (!containsBytes("response value", tmp)) { - let res = await this.cdc.read(); - if (compareStringToBytes("", res)) { - timeout += 1; - if (timeout === 4) { - break; - } - await sleep(20); - } - tmp = concatUint8Array([tmp, res]); - } - return tmp; - } - - async cmdProgram(physicalPartitionNumber, startSector, blob, onProgress=()=>{}) { - let total = blob.size; - let sparseformat = false; - - let sparseHeader = await Sparse.parseFileHeader(blob.slice(0, Sparse.FILE_HEADER_SIZE)); - if (sparseHeader !== null) { - sparseformat = true; - total = await Sparse.getSparseRealSize(blob, sparseHeader); - } - - let numPartitionSectors = Math.floor(total / this.cfg.SECTOR_SIZE_IN_BYTES); - if (total % this.cfg.SECTOR_SIZE_IN_BYTES !== 0) { - numPartitionSectors += 1; - } - - const data = `\n` + - `\n`; - let i = 0; - let bytesWritten = 0; - let rsp = await this.xmlSend(data); - - if (rsp.resp) { - for await (let split of Sparse.splitBlob(blob)) { - let offset = 0; - let bytesToWriteSplit = split.size; - - while (bytesToWriteSplit > 0) { - const wlen = Math.min(bytesToWriteSplit, this.cfg.MaxPayloadSizeToTargetInBytes); - let wdata = new Uint8Array(await readBlobAsBuffer(split.slice(offset, offset + wlen))); - if (wlen % this.cfg.SECTOR_SIZE_IN_BYTES !== 0) { - let fillLen = (Math.floor(wlen/this.cfg.SECTOR_SIZE_IN_BYTES) * this.cfg.SECTOR_SIZE_IN_BYTES) + - this.cfg.SECTOR_SIZE_IN_BYTES; - const fillArray = new Uint8Array(fillLen-wlen).fill(0x00); - wdata = concatUint8Array([wdata, fillArray]); - } - await this.cdc.write(wdata); - await this.cdc.write(new Uint8Array(0), null, true); - offset += wlen; - bytesWritten += wlen; - bytesToWriteSplit -= wlen; - - // Need this for sparse image when the data.length < MaxPayloadSizeToTargetInBytes - // Add ~2.4s to total flash time - if (sparseformat && bytesWritten < total) { - await this.cdc.write(new Uint8Array(0), null, true); - } - - if (i % 10 === 0) { - onProgress(bytesWritten/total); - } - i += 1; - } - } - - const wd = await this.waitForData(); - const response = this.xml.getReponse(wd); - if ("value" in response) { - if (response["value"] !== "ACK") { - return false; - } - } else { - return false; - } - } - - onProgress(1.0); - return true; - } - - async cmdErase(physicalPartitionNumber, startSector, numPartitionSectors) { - const data = `\n` + - `\n`; - let rsp = await this.xmlSend(data) - let bytesToWrite = this.cfg.SECTOR_SIZE_IN_BYTES * numPartitionSectors; - let empty = new Uint8Array(this.cfg.MaxPayloadSizeToTargetInBytes).fill(0); - - if (rsp.resp) { - while (bytesToWrite > 0) { - let wlen = Math.min(bytesToWrite, this.cfg.MaxPayloadSizeToTargetInBytes); - await this.cdc.write(empty.slice(0, wlen)); - bytesToWrite -= wlen; - await this.cdc.write(new Uint8Array(0)); - } - - const res = await this.waitForData(); - const response = this.xml.getReponse(res); - if ("value" in response) { - if (response["value"] !== "ACK") { - throw "Failed to erase: NAK"; - } - } else { - throw "Failed to erase no return value"; - } - } - return true; - } - - async cmdSetBootLunId(lun) { - const data = `\n` - const val = await this.xmlSend(data); - if (val.resp) { - console.log(`Successfully set bootID to lun ${lun}`); - return true; - } else { - throw `Firehose - Failed to set boot lun ${lun}`; - } - } - - async cmdReset() { - let data = ''; - let val = await this.xmlSend(data); - if (val.resp) { - console.log("Reset succeeded"); - // Drain log buffer - try { - await this.waitForData(); - } catch { - // Ignore any errors - } - return true; - } else { - throw "Firehose - Reset failed"; - } - } -} diff --git a/src/QDL/gpt.js b/src/QDL/gpt.js deleted file mode 100644 index 09a0640..0000000 --- a/src/QDL/gpt.js +++ /dev/null @@ -1,255 +0,0 @@ -import { containsBytes, bytes2Number } from "./utils" -import { buf as crc32 } from "crc-32" - -export const AB_FLAG_OFFSET = 6; -export const AB_PARTITION_ATTR_SLOT_ACTIVE = (0x1 << 2); -export const PART_ATT_PRIORITY_BIT = BigInt(48) -export const PART_ATT_ACTIVE_BIT = BigInt(50) -export const PART_ATT_ACTIVE_VAL = BigInt(0x1) << PART_ATT_ACTIVE_BIT - -const efiType = { - 0x00000000 : "EFI_UNUSED", - 0xEBD0A0A2 : "EFI_BASIC_DATA", -} - - -class structHelper { - constructor(data, pos = 0) { - this.pos = pos; - this.data = data; - } - - qword(littleEndian=true) { - const view = new DataView(this.data.slice(this.pos, this.pos+=8).buffer, 0); - return Number(view.getBigUint64(0, littleEndian)); - } - - dword(littleEndian=true) { - let view = new DataView(this.data.slice(this.pos, this.pos+=4).buffer, 0); - return view.getUint32(0, littleEndian); - } - - bytes(rlen=1) { - const dat = this.data.slice(this.pos, this.pos+=rlen); - return dat; - } - - toString(rlen=1) { - const dat = this.data.slice(this.pos, this.pos+=rlen); - return dat; - } -} - - -class gptHeader { - constructor(data) { - let sh = new structHelper(data); - this.signature = sh.bytes(8); - this.revision = sh.dword(); - this.headerSize = sh.dword(); - this.crc32 = sh.dword(); - this.reserved = sh.dword(); - this.currentLba = sh.qword(); - this.backupLba = sh.qword(); - this.firstUsableLba = sh.qword(); - this.lastUsableLba = sh.qword(); - this.diskGuid = sh.bytes(16); - this.partEntryStartLba = sh.qword(); - this.numPartEntries = sh.dword(); - this.partEntrySize = sh.dword(); - this.crc32PartEntries = sh.dword(); - } -} - - -export class gptPartition { - constructor(data) { - let sh = new structHelper(data) - this.type = sh.bytes(16); - this.unique = sh.bytes(16); - this.firstLba = sh.qword(); - this.lastLba = sh.qword(); - this.flags = sh.qword(); - this.name = sh.toString(72); - } - - create() { - let buffer = new ArrayBuffer(16 + 16 + 8 + 8 + 8 + 72); - let view = new DataView(buffer); - let offset = 0; - for (let i = 0; i < this.type.length; i++) { - view.setUint8(offset++, this.type[i], true); - } - for (let i = 0; i < this.unique.length; i++) { - view.setUint8(offset++, this.unique[i], true); - } - let tmp = [BigInt(this.firstLba), BigInt(this.lastLba), BigInt(this.flags)]; - for (let i = 0; i < 3; i++) { - view.setBigUint64(offset, tmp[i], true); - offset += 8; - } - for (let i = 0; i < 72; i++) { - view.setUint8(offset++, this.name[i]); - } - return new Uint8Array(view.buffer); - } -} - - -class partf { - firstLba = 0; - lastLba = 0; - flags = 0; - sector = 0; - sectors = 0; - entryOffset = 0; - type = null; - name = ""; - unique = new Uint8Array(); -} - - -export class gpt { - constructor() { - this.header = null; - this.sectorSize = null; - this.partentries = {}; - } - - parseHeader(gptData, sectorSize=512) { - return new gptHeader(gptData.slice(sectorSize, sectorSize + 0x5C)); - } - - parse(gptData, sectorSize=512) { - this.header = new gptHeader(gptData.slice(sectorSize, sectorSize + 0x5C)); - this.sectorSize = sectorSize; - - if (!containsBytes("EFI PART", this.header.signature)) { - return false; - } - - if (this.header.revision != 0x10000) { - console.error("Unknown GPT revision."); - return false; - } - - // mbr (even for backup gpt header to ensure offset consistency) + gpt header + part_table - const start = 2 * sectorSize; - - const entrySize = this.header.partEntrySize; - this.partentries = {}; - const numPartEntries = this.header.numPartEntries; - for (let idx = 0; idx < numPartEntries; idx++) { - const data = gptData.slice(start + (idx * entrySize), start + (idx * entrySize) + entrySize); - if (new DataView(data.slice(16,32).buffer, 0).getUint32(0, true) == 0) { - break; - } - - let partentry = new gptPartition(data); - let pa = new partf(); - const guid1 = new DataView(partentry.unique.slice(0, 0x4).buffer, 0).getUint32(0, true); - const guid2 = new DataView(partentry.unique.slice(0x4, 0x6).buffer, 0).getUint16(0, true); - const guid3 = new DataView(partentry.unique.slice(0x6, 0x8).buffer, 0).getUint16(0, true); - const guid4 = new DataView(partentry.unique.slice(0x8, 0xA).buffer, 0).getUint16(0, true); - const guid5 = Array.from(partentry.unique.subarray(0xA, 0x10)) - .map(byte => byte.toString(16).padStart(2, '0')) - .join(''); - pa.unique =`${guid1.toString(16).padStart(8, '0')}- - ${guid2.toString(16).padStart(4, '0')}- - ${guid3.toString(16).padStart(4, '0')}- - ${guid4.toString(16).padStart(4, '0')}- - ${guid5}`; - pa.sector = partentry.firstLba; - pa.sectors = partentry.lastLba - partentry.firstLba + 1; - pa.flags = partentry.flags; - pa.entryOffset = start + (idx * entrySize); - const typeOfPartentry = new DataView(partentry.type.slice(0, 0x4).buffer, 0).getUint32(0, true); - if (typeOfPartentry in efiType) { - pa.type = efiType[typeOfPartentry]; - } else { - pa.type = typeOfPartentry.toString(16); - } - let nullIndex = Array.from(partentry.name).findIndex((element, index) => index % 2 === 0 && element === 0); - let nameWithoutNull = partentry.name.slice(0, nullIndex); - let decodedName = new TextDecoder('utf-16').decode(nameWithoutNull); - pa.name = decodedName; - if (pa.type == "EFI_UNUSED") { - continue; - } - this.partentries[pa.name] = pa; - } - return true; - } - - fixGptCrc(data) { - const headerOffset = this.sectorSize; - const partentryOffset = 2 * this.sectorSize; - const partentrySize = this.header.numPartEntries * this.header.partEntrySize; - const partdata = Uint8Array.from(data.slice(partentryOffset, partentryOffset + partentrySize)); - let headerdata = Uint8Array.from(data.slice(headerOffset, headerOffset + this.header.headerSize)); - - let view = new DataView(new ArrayBuffer(4)); - view.setInt32(0, crc32(partdata), true); - headerdata.set(new Uint8Array(view.buffer), 0x58); - view.setInt32(0, 0, true); - headerdata.set(new Uint8Array(view.buffer) , 0x10); - view.setInt32(0, crc32(headerdata), true); - headerdata.set(new Uint8Array(view.buffer), 0x10); - - data.set(headerdata, headerOffset); - return data; - } -} - - -// 0x003a for inactive and 0x006f for active boot partitions. This follows fastboot standard -export function setPartitionFlags(flags, active, isBoot) { - let newFlags = BigInt(flags); - if (active) { - if (isBoot) { - newFlags = BigInt(0x006f) << PART_ATT_PRIORITY_BIT; - } else { - newFlags |= PART_ATT_ACTIVE_VAL; - } - } else { - if (isBoot) { - newFlags = BigInt(0x003a) << PART_ATT_PRIORITY_BIT; - } else { - newFlags &= ~PART_ATT_ACTIVE_VAL; - } - } - return Number(newFlags); -} - - -function checkHeaderCrc(gptData, guidGpt) { - const headerOffset = guidGpt.sectorSize; - const headerSize = guidGpt.header.headerSize; - const testGptData = guidGpt.fixGptCrc(gptData).buffer; - const testHeader = new Uint8Array(testGptData.slice(headerOffset, headerOffset + headerSize)); - - const headerCrc = guidGpt.header.crc32; - const testHeaderCrc = bytes2Number(testHeader.slice(0x10, 0x10 + 4)); - const partTableCrc = guidGpt.header.crc32PartEntries; - const testPartTableCrc = bytes2Number(testHeader.slice(0x58, 0x58 + 4)); - - return [(headerCrc !== testHeaderCrc) || (partTableCrc !== testPartTableCrc), partTableCrc]; -} - - -export function ensureGptHdrConsistency(gptData, backupGptData, guidGpt, backupGuidGpt) { - const partTableOffset = guidGpt.sectorSize * 2; - - const [primCorrupted, primPartTableCrc] = checkHeaderCrc(gptData, guidGpt); - const [backupCorrupted, backupPartTableCrc] = checkHeaderCrc(backupGptData, backupGuidGpt); - - const headerConsistency = primPartTableCrc === backupPartTableCrc; - if (primCorrupted || !headerConsistency) { - if (backupCorrupted) { - throw "Both primary and backup gpt headers are corrupted, cannot recover"; - } - gptData.set(backupGptData.slice(partTableOffset), partTableOffset); - gptData = guidGpt.fixGptCrc(gptData); - } - return gptData; -} diff --git a/src/QDL/qdl.js b/src/QDL/qdl.js deleted file mode 100644 index 80cee25..0000000 --- a/src/QDL/qdl.js +++ /dev/null @@ -1,318 +0,0 @@ -import * as gpt from "./gpt" -import { usbClass } from "./usblib" -import { Sahara } from "./sahara" -import { Firehose } from "./firehose" -import { concatUint8Array, runWithTimeout, containsBytes } from "./utils" - - -export class qdlDevice { - constructor() { - this.mode = ""; - this.cdc = new usbClass(); - this.sahara = new Sahara(this.cdc); - this.firehose = new Firehose(this.cdc); - this._connectResolve = null; - this._connectReject = null; - } - - async waitForConnect() { - return await new Promise((resolve, reject) => { - this._connectResolve = resolve; - this._connectReject = reject; - }); - } - - async connectToSahara() { - while (!this.cdc.connected) { - await this.cdc?.connect(); - if (this.cdc.connected) { - console.log("QDL device detected"); - let resp = await runWithTimeout(this.sahara?.connect(), 10000); - if ("mode" in resp) { - this.mode = resp["mode"]; - console.log("Mode detected:", this.mode); - return resp; - } - } - } - return {"mode" : "error"}; - } - - async connect() { - try { - let resp = await this.connectToSahara(); - let mode = resp["mode"]; - if (mode === "sahara") { - await this.sahara?.uploadLoader(); - } else if (mode === "error") { - throw "Error connecting to Sahara"; - } - await this.firehose?.configure(); - this.mode = "firehose"; - } catch (error) { - if (this._connectReject !== null) { - this._connectReject(error); - this._connectResolve = null; - this._connectReject = null; - } - } - - if (this._connectResolve !== null) { - this._connectResolve(undefined); - this._connectResolve = null; - this._connectReject = null; - } - return true; - } - - async getGpt(lun, startSector=1) { - let resp; - resp = await this.firehose.cmdReadBuffer(lun, 0, 1); - if (!resp.resp) { - console.error(resp.error); - return [null, null]; - } - let data = concatUint8Array([resp.data, (await this.firehose.cmdReadBuffer(lun, startSector, 1)).data]); - let guidGpt = new gpt.gpt(); - const header = guidGpt.parseHeader(data, this.firehose.cfg.SECTOR_SIZE_IN_BYTES); - if (containsBytes("EFI PART", header.signature)) { - const partTableSize = header.numPartEntries * header.partEntrySize; - const sectors = Math.floor(partTableSize / this.firehose.cfg.SECTOR_SIZE_IN_BYTES); - data = concatUint8Array([data, (await this.firehose.cmdReadBuffer(lun, header.partEntryStartLba, sectors)).data]); - guidGpt.parse(data, this.firehose.cfg.SECTOR_SIZE_IN_BYTES); - return [guidGpt, data]; - } else { - throw "Error reading gpt header"; - } - } - - async detectPartition(partitionName, sendFull=false) { - const luns = this.firehose.luns; - for (const lun of luns) { - const [guidGpt, data] = await this.getGpt(lun); - if (guidGpt === null) { - break; - } else { - if (partitionName in guidGpt.partentries) { - return sendFull ? [true, lun, data, guidGpt] : [true, lun, guidGpt.partentries[partitionName]]; - } - } - } - return [false]; - } - - async flashBlob(partitionName, blob, onProgress=()=>{}) { - let startSector = 0; - let dp = await this.detectPartition(partitionName); - const found = dp[0]; - if (found) { - let lun = dp[1]; - const imgSize = blob.size; - let imgSectors = Math.floor(imgSize / this.firehose.cfg.SECTOR_SIZE_IN_BYTES); - if (imgSize % this.firehose.cfg.SECTOR_SIZE_IN_BYTES !== 0) { - imgSectors += 1; - } - if (partitionName.toLowerCase() !== "gpt") { - const partition = dp[2]; - if (imgSectors > partition.sectors) { - console.error("partition has fewer sectors compared to the flashing image"); - return false; - } - startSector = partition.sector; - console.log(`Flashing ${partitionName}...`); - if (await this.firehose.cmdProgram(lun, startSector, blob, (progress) => onProgress(progress))) { - console.log(`partition ${partitionName}: startSector ${partition.sector}, sectors ${partition.sectors}`); - } else { - throw `Error while writing ${partitionName}`; - } - } - } else { - throw `Can't find partition ${partitionName}`; - } - return true; - } - - async erase(partitionName) { - const luns = this.firehose.luns; - for (const lun of luns) { - let [guidGpt] = await this.getGpt(lun); - if (partitionName in guidGpt.partentries) { - const partition = guidGpt.partentries[partitionName]; - console.log(`Erasing ${partitionName}...`); - await this.firehose.cmdErase(lun, partition.sector, partition.sectors); - console.log(`Erased ${partitionName} starting at sector ${partition.sector} with sectors ${partition.sectors}`); - } else { - continue; - } - } - return true; - } - - async getDevicePartitionsInfo() { - const slots = []; - const partitions = []; - const luns = this.firehose.luns; - for (const lun of luns) { - let [guidGpt] = await this.getGpt(lun); - if (guidGpt === null) { - throw "Error while reading device partitions"; - } - for (let partition in guidGpt.partentries) { - let slot = partition.slice(-2); - if (slot === "_a" || slot === "_b") { - partition = partition.substring(0, partition.length-2); - if (!slots.includes(slot)) { - slots.push(slot); - } - } - if (!partitions.includes(partition)) { - partitions.push(partition); - } - } - } - return [slots.length, partitions]; - } - - async getActiveSlot() { - const luns = this.firehose.luns; - for (const lun of luns) { - const [guidGpt] = await this.getGpt(lun); - if (guidGpt === null) { - throw "Cannot get active slot." - } - for (const partitionName in guidGpt.partentries) { - const slot = partitionName.slice(-2); - // backup gpt header is more reliable, since it would always has the non-corrupted gpt header - const [backupGuidGpt] = await this.getGpt(lun, guidGpt.header.backupLba); - const partition = backupGuidGpt.partentries[partitionName]; - const active = (((BigInt(partition.flags) >> (BigInt(gpt.AB_FLAG_OFFSET) * BigInt(8)))) - & BigInt(gpt.AB_PARTITION_ATTR_SLOT_ACTIVE)) === BigInt(gpt.AB_PARTITION_ATTR_SLOT_ACTIVE); - if (slot == "_a" && active) { - return "a"; - } else if (slot == "_b" && active) { - return "b"; - } - } - } - throw "Can't detect slot A or B"; - } - - patchNewGptData(gptDataA, gptDataB, guidGpt, partA, partB, slot_a_status, slot_b_status, isBoot) { - const partEntrySize = guidGpt.header.partEntrySize; - - const sdataA = gptDataA.slice(partA.entryOffset, partA.entryOffset+partEntrySize); - const sdataB = gptDataB.slice(partB.entryOffset, partB.entryOffset+partEntrySize); - - const partEntryA = new gpt.gptPartition(sdataA); - const partEntryB = new gpt.gptPartition(sdataB); - - partEntryA.flags = gpt.setPartitionFlags(partEntryA.flags, slot_a_status, isBoot); - partEntryB.flags = gpt.setPartitionFlags(partEntryB.flags, slot_b_status, isBoot); - const tmp = partEntryB.type; - partEntryB.type = partEntryA.type; - partEntryA.type = tmp; - const pDataA = partEntryA.create(), pDataB = partEntryB.create(); - - return [pDataA, partA.entryOffset, pDataB, partB.entryOffset]; - } - - async setActiveSlot(slot) { - slot = slot.toLowerCase(); - const luns = this.firehose.luns - let slot_a_status, slot_b_status; - - if (slot == "a") { - slot_a_status = true; - } else if (slot == "b") { - slot_a_status = false; - } - slot_b_status = !slot_a_status; - - for (const lunA of luns) { - let checkGptHeader = false; - let sameLun = false; - let hasPartitionA = false; - let [guidGptA, gptDataA] = await this.getGpt(lunA); - let [backupGuidGptA, backupGptDataA] = await this.getGpt(lunA, guidGptA.header.backupLba); - let lunB, gptDataB, guidGptB, backupGptDataB, backupGuidGptB; - - if (guidGptA === null) { - throw "Error while getting gpt header data"; - } - for (const partitionNameA in guidGptA.partentries) { - let slotSuffix = partitionNameA.toLowerCase().slice(-2); - if (slotSuffix !== "_a") { - continue; - } - const partitionNameB = partitionNameA.slice(0, partitionNameA.length-1) + "b"; - let sts; - if (!checkGptHeader) { - hasPartitionA = true; - if (partitionNameB in guidGptA.partentries) { - lunB = lunA; - sameLun = true; - gptDataB = gptDataA; - guidGptB = guidGptA; - backupGptDataB = backupGptDataA; - backupGuidGptB = backupGuidGptA; - } else { - const resp = await this.detectPartition(partitionNameB, true); - sts = resp[0]; - if (!sts) { - throw `Cannot find partition ${partitionNameB}`; - } - [sts, lunB, gptDataB, guidGptB] = resp; - [backupGuidGptB, backupGptDataB] = await this.getGpt(lunB, guidGptB.header.backupLba); - } - } - - if (!checkGptHeader && partitionNameA.slice(0, 3) !== "xbl") { // xbl partitions aren't affected by failure of changing slot, saves time - gptDataA = gpt.ensureGptHdrConsistency(gptDataA, backupGptDataA, guidGptA, backupGuidGptA); - if (!sameLun) { - gptDataB = gpt.ensureGptHdrConsistency(gptDataB, backupGptDataB, guidGptB, backupGuidGptB); - } - checkGptHeader = true; - } - - const partA = guidGptA.partentries[partitionNameA]; - const partB = guidGptB.partentries[partitionNameB]; - - let isBoot = false; - if (partitionNameA === "boot_a") { - isBoot = true; - } - const [pDataA, pOffsetA, pDataB, pOffsetB] = this.patchNewGptData( - gptDataA, gptDataB, guidGptA, partA, partB, slot_a_status, slot_b_status, isBoot - ); - - gptDataA.set(pDataA, pOffsetA) - guidGptA.fixGptCrc(gptDataA); - if (lunA === lunB) { - gptDataB = gptDataA; - } - gptDataB.set(pDataB, pOffsetB) - guidGptB.fixGptCrc(gptDataB); - } - - if (!hasPartitionA) { - continue; - } - const writeOffset = this.firehose.cfg.SECTOR_SIZE_IN_BYTES; - const gptBlobA = new Blob([gptDataA.slice(writeOffset)]); - await this.firehose.cmdProgram(lunA, 1, gptBlobA); - if (!sameLun) { - const gptBlobB = new Blob([gptDataB.slice(writeOffset)]); - await this.firehose.cmdProgram(lunB, 1, gptBlobB); - } - } - const activeBootLunId = (slot === "a") ? 1 : 2; - await this.firehose.cmdSetBootLunId(activeBootLunId); - console.log(`Successfully set slot ${slot} active`); - return true; - } - - async reset() { - await this.firehose.cmdReset(); - return true; - } -} diff --git a/src/QDL/sahara.js b/src/QDL/sahara.js deleted file mode 100644 index 6af7542..0000000 --- a/src/QDL/sahara.js +++ /dev/null @@ -1,258 +0,0 @@ -import { CommandHandler, cmd_t, sahara_mode_t, status_t, exec_cmd_t } from "./saharaDefs" -import { concatUint8Array, packGenerator, readBlobAsBuffer } from "./utils"; -import config from "../config" - - -export class Sahara { - constructor(cdc) { - this.cdc = cdc; - this.ch = new CommandHandler(); - this.programmer = "6000000000010000_f8ab20526358c4fa_fhprg.bin"; - this.id = null; - this.serial = ""; - this.mode = ""; - this.rootDir = null; - } - - async connect() { - const v = await this.cdc?.read(0xC * 0x4); - if (v.length > 1) { - if (v[0] == 0x01) { - let pkt = this.ch.pkt_cmd_hdr(v); - if (pkt.cmd === cmd_t.SAHARA_HELLO_REQ) { - const rsp = this.ch.pkt_hello_req(v); - return { "mode" : "sahara", "cmd" : cmd_t.SAHARA_HELLO_REQ, "data" : rsp }; - } - } - } - throw "Sahara - Unable to connect to Sahara"; - } - - async cmdHello(mode, version=2, version_min=1, max_cmd_len=0) { - const cmd = cmd_t.SAHARA_HELLO_RSP; - const len = 0x30; - const elements = [cmd, len, version, version_min, max_cmd_len, mode, 1, 2, 3, 4, 5, 6]; - const responseData = packGenerator(elements); - await this.cdc?.write(responseData); - return true; - } - - async cmdModeSwitch(mode) { - const elements = [cmd_t.SAHARA_SWITCH_MODE, 0xC, mode]; - let data = packGenerator(elements); - await this.cdc?.write(data); - return true; - } - - async getResponse() { - try { - let data = await this.cdc?.read(); - let data_text = new TextDecoder('utf-8').decode(data.data); - if (data.length == 0) { - return {}; - } else if (data_text.includes("= 0) { - let resp = await this.getResponse(); - let cmd; - if ("cmd" in resp) { - cmd = resp["cmd"]; - } else { - throw "Sahara - Timeout while uploading loader. Wrong loader?"; - } - if (cmd == cmd_t.SAHARA_64BIT_MEMORY_READ_DATA) { - let pkt = resp["data"]; - this.id = pkt.image_id; - if (this.id >= 0xC) { - this.mode = "firehose"; - if (loop == 0) { - console.log("Firehose mode detected, uploading..."); - } - } else { - throw "Sahara - Unknown sahara id"; - } - - loop += 1; - let dataOffset = pkt.data_offset; - let dataLen = pkt.data_len; - if (dataOffset + dataLen > programmer.length) { - const fillerArray = new Uint8Array(dataOffset+dataLen-programmer.length).fill(0xff); - programmer = concatUint8Array([programmer, fillerArray]); - } - let dataToSend = programmer.slice(dataOffset, dataOffset+dataLen); - await this.cdc?.write(dataToSend); - datalen -= dataLen; - } else if (cmd == cmd_t.SAHARA_END_TRANSFER) { - let pkt = resp["data"]; - if (pkt.image_tx_status == status_t.SAHARA_STATUS_SUCCESS) { - if (await this.cmdDone()) { - console.log("Loader successfully uploaded"); - } else { - throw "Sahara - Failed to upload loader"; - } - return this.mode; - } - } - } - return this.mode; - } - - async cmdDone() { - const toSendData = packGenerator([cmd_t.SAHARA_DONE_REQ, 0x8]); - if (await this.cdc.write(toSendData)) { - let res = await this.getResponse(); - if ("cmd" in res) { - let cmd = res["cmd"]; - if (cmd == cmd_t.SAHARA_DONE_RSP) { - return true; - } else if (cmd == cmd_t.SAHARA_END_TRANSFER) { - if ("data" in res) { - let pkt = res["data"]; - if (pkt.image_tx_status == status_t.SAHARA_NAK_INVALID_CMD) { - console.error("Invalid transfer command received"); - return false; - } - } - } else { - throw "Sahara - Received invalid response"; - } - } - } - return false; - } -} diff --git a/src/QDL/saharaDefs.js b/src/QDL/saharaDefs.js deleted file mode 100644 index 7258f74..0000000 --- a/src/QDL/saharaDefs.js +++ /dev/null @@ -1,98 +0,0 @@ -import { structHelper_io } from "./utils" - - -export const cmd_t = { - SAHARA_HELLO_REQ : 0x1, - SAHARA_HELLO_RSP : 0x2, - SAHARA_READ_DATA : 0x3, - SAHARA_END_TRANSFER : 0x4, - SAHARA_DONE_REQ : 0x5, - SAHARA_DONE_RSP : 0x6, - SAHARA_RESET_RSP : 0x8, - SAHARA_CMD_READY : 0xB, - SAHARA_SWITCH_MODE : 0xC, - SAHARA_EXECUTE_REQ : 0xD, - SAHARA_EXECUTE_RSP : 0xE, - SAHARA_EXECUTE_DATA : 0xF, - SAHARA_64BIT_MEMORY_READ_DATA : 0x12, -} - -export const exec_cmd_t = { - SAHARA_EXEC_CMD_SERIAL_NUM_READ : 0x01 -} - -export const sahara_mode_t = { - SAHARA_MODE_IMAGE_TX_PENDING : 0x0, - SAHARA_MODE_COMMAND : 0x3 -} - -export const status_t = { - SAHARA_STATUS_SUCCESS : 0x00, // Invalid command received in current state - SAHARA_NAK_INVALID_CMD : 0x01, // Protocol mismatch between host and targe -} - - -export class CommandHandler { - pkt_cmd_hdr(data) { - let st = new structHelper_io(data); - return { cmd : st.dword(), len : st.dword() } - } - - pkt_hello_req(data) { - let st = new structHelper_io(data); - return { - cmd : st.dword(), - len : st.dword(), - version : st.dword(), - version_supported : st.dword(), - cmd_packet_length : st.dword(), - mode : st.dword(), - reserved1 : st.dword(), - reserved2 : st.dword(), - reserved3 : st.dword(), - reserved4 : st.dword(), - reserved5 : st.dword(), - reserved6 : st.dword(), - } - } - - pkt_image_end(data) { - let st = new structHelper_io(data); - return { - cmd : st.dword(), - len : st.dword(), - image_id : st.dword(), - image_tx_status : st.dword(), - } - } - - pkt_done(data) { - let st = new structHelper_io(data); - return { - cmd : st.dword(), - len : st.dword(), - image_tx_status : st.dword() - } - } - - pkt_read_data_64(data) { - let st = new structHelper_io(data); - return { - cmd : st.dword(), - len : st.dword(), - image_id : Number(st.qword()), - data_offset : Number(st.qword()), - data_len : Number(st.qword()), - } - } - - pkt_execute_rsp_cmd(data) { - let st = new structHelper_io(data); - return { - cmd : st.dword(), - len : st.dword(), - client_cmd : st.dword(), - data_len : st.dword(), - } - } -} diff --git a/src/QDL/sparse.js b/src/QDL/sparse.js deleted file mode 100644 index c0577eb..0000000 --- a/src/QDL/sparse.js +++ /dev/null @@ -1,260 +0,0 @@ -import { readBlobAsBuffer } from "./utils"; - -const FILE_MAGIC = 0xed26ff3a; -export const FILE_HEADER_SIZE = 28; -const CHUNK_HEADER_SIZE = 12; - -const ChunkType = { - Raw : 0xCAC1, - Fill : 0xCAC2, - Skip : 0xCAC3, - Crc32 : 0xCAC4, -} - - -class QCSparse { - constructor(blob, header) { - this.blob = blob; - this.blockSize = header.blockSize; - this.totalChunks = header.totalChunks; - this.blobOffset = 0; - } - - async getChunkSize() { - const chunkHeader = await parseChunkHeader(this.blob.slice(this.blobOffset, this.blobOffset + CHUNK_HEADER_SIZE)); - const chunkType = chunkHeader.type; - const blocks = chunkHeader.blocks; - const dataSize = chunkHeader.dataBytes; - this.blobOffset += CHUNK_HEADER_SIZE + dataSize; - - if (chunkType == ChunkType.Raw) { - if (dataSize != (blocks * this.blockSize)) { - throw "Sparse - Chunk input size does not match output size"; - } else { - return dataSize; - } - } else if (chunkType == ChunkType.Fill) { - if (dataSize != 4) { - throw "Sparse - Fill chunk should have 4 bytes"; - } else { - return blocks * this.blockSize; - } - } else if (chunkType == ChunkType.Skip) { - return blocks * this.blockSize; - } else if (chunkType == ChunkType.Crc32) { - if (dataSize != 4) { - throw "Sparse - CRC32 chunk should have 4 bytes"; - } else { - return 0; - } - } else { - throw "Sparse - Unknown chunk type"; - } - } - - async getSize() { - this.blobOffset = FILE_HEADER_SIZE; - let length = 0, chunk = 0; - while (chunk < this.totalChunks) { - let tlen = await this.getChunkSize(); - length += tlen; - chunk += 1; - } - this.blobOffset = FILE_HEADER_SIZE; - return length; - } -} - - -export async function getSparseRealSize(blob, header) { - const sparseImage = new QCSparse(blob, header); - return await sparseImage.getSize(); -} - - -async function parseChunkHeader(blobChunkHeader) { - let chunkHeader = await readBlobAsBuffer(blobChunkHeader); - let view = new DataView(chunkHeader); - return { - type : view.getUint16(0, true), - blocks : view.getUint32(4, true), - dataBytes : view.getUint32(8, true) - CHUNK_HEADER_SIZE, - data : null, - } -} - -export async function parseFileHeader(blobHeader) { - let header = await readBlobAsBuffer(blobHeader); - let view = new DataView(header); - - let magic = view.getUint32(0, true); - let majorVersion = view.getUint16(4, true); - let minorVersion = view.getUint16(6, true); - let fileHeadrSize = view.getUint16(8, true); - let chunkHeaderSize = view.getUint16(10, true); - let blockSize = view.getUint32(12, true); - let totalBlocks = view.getUint32(16, true); - let totalChunks = view.getUint32(20, true); - let crc32 = view.getUint32(24, true); - - if (magic != FILE_MAGIC) { - return null; - } - if (fileHeadrSize != FILE_HEADER_SIZE) { - console.error(`The file header size was expected to be 28, but is ${fileHeadrSize}.`); - return null; - } - if (chunkHeaderSize != CHUNK_HEADER_SIZE) { - console.error(`The chunk header size was expected to be 12, but is ${chunkHeaderSize}.`); - return null; - } - - return { - magic : magic, - majorVersion : majorVersion, - minorVersion : minorVersion, - fileHeadrSize : fileHeadrSize, - chunkHeaderSize : chunkHeaderSize, - blockSize : blockSize, - totalBlocks : totalBlocks, - totalChunks : totalChunks, - crc32 : crc32, - } -} - -async function populate(chunks, blockSize) { - const nBlocks = calcChunksBlocks(chunks); - let ret = new Uint8Array(nBlocks * blockSize); - let offset = 0; - - for (const chunk of chunks) { - const chunkType = chunk.type; - const blocks = chunk.blocks; - const dataSize = chunk.dataBytes; - const data = chunk.data; - - if (chunkType == ChunkType.Raw) { - let rawData = new Uint8Array(await readBlobAsBuffer(data)); - ret.set(rawData, offset); - offset += blocks * blockSize; - } else if (chunkType == ChunkType.Fill) { - const fillBin = new Uint8Array(await readBlobAsBuffer(data)); - const bufferSize = blocks * blockSize; - for (let i = 0; i < bufferSize; i += dataSize) { - ret.set(fillBin, offset); - offset += dataSize; - } - } else if (chunkType == ChunkType.Skip) { - let byteToSend = blocks * blockSize; - let skipData = new Uint8Array(byteToSend).fill(0); - ret.set(skipData, offset); - offset += byteToSend; - } else if (chunkType == ChunkType.Crc32) { - continue; - } else { - throw "Sparse - Unknown chunk type"; - } - } - return new Blob([ret.buffer]); -} - - -function calcChunksRealDataBytes(chunk, blockSize) { - switch (chunk.type) { - case ChunkType.Raw: - return chunk.dataBytes; - case ChunkType.Fill: - return chunk.blocks * blockSize; - case ChunkType.Skip: - return chunk.blocks * blockSize; - case ChunkType.Crc32: - return 0; - default: - throw "Sparse - Unknown chunk type"; - } -} - - -function calcChunksSize(chunks, blockSize) { - return chunks.map((chunk) => calcChunksRealDataBytes(chunk, blockSize)).reduce((total, c) => total + c, 0); -} - - -function calcChunksBlocks(chunks) { - return chunks.map((chunk) => chunk.blocks).reduce((total, c) => total + c, 0); -} - - -export async function* splitBlob(blob, splitSize = 1048576 /* maxPayloadSizeToTarget */) { - const safeToSend = splitSize; - - let header = await parseFileHeader(blob.slice(0, FILE_HEADER_SIZE)); - if (header === null) { - yield blob; - return; - } - - header.crc32 = 0; - blob = blob.slice(FILE_HEADER_SIZE); - let splitChunks = []; - for (let i = 0; i < header.totalChunks; i++) { - let originalChunk = await parseChunkHeader(blob.slice(0, CHUNK_HEADER_SIZE)); - originalChunk.data = blob.slice(CHUNK_HEADER_SIZE, CHUNK_HEADER_SIZE + originalChunk.dataBytes); - blob = blob.slice(CHUNK_HEADER_SIZE + originalChunk.dataBytes); - - let chunksToProcess = []; - let realBytesToWrite = calcChunksRealDataBytes(originalChunk, header.blockSize) - - const isChunkTypeSkip = originalChunk.type == ChunkType.Skip; - const isChunkTypeFill = originalChunk.type == ChunkType.Fill; - - if (realBytesToWrite > safeToSend) { - let bytesToWrite = isChunkTypeSkip ? 1 : originalChunk.dataBytes; - let originalChunkData = originalChunk.data; - - while (bytesToWrite > 0) { - const toSend = Math.min(safeToSend, bytesToWrite); - let tmpChunk; - - if (isChunkTypeFill || isChunkTypeSkip) { - while (realBytesToWrite > 0) { - const realSend = Math.min(safeToSend, realBytesToWrite); - tmpChunk = { - type : originalChunk.type, - blocks : realSend / header.blockSize, - dataBytes : isChunkTypeSkip ? 0 : toSend, - data : isChunkTypeSkip ? new Blob([]) : originalChunkData.slice(0, toSend), - } - chunksToProcess.push(tmpChunk); - realBytesToWrite -= realSend; - } - } else { - tmpChunk = { - type : originalChunk.type, - blocks : toSend / header.blockSize, - dataBytes : toSend, - data : originalChunkData.slice(0, toSend), - } - chunksToProcess.push(tmpChunk); - } - bytesToWrite -= toSend; - originalChunkData = originalChunkData?.slice(toSend); - } - } else { - chunksToProcess.push(originalChunk) - } - for (const chunk of chunksToProcess) { - const remainingBytes = splitSize - calcChunksSize(splitChunks); - const realChunkBytes = calcChunksRealDataBytes(chunk); - if (remainingBytes >= realChunkBytes) { - splitChunks.push(chunk); - } else { - yield await populate(splitChunks, header.blockSize); - splitChunks = [chunk]; - } - } - } - if (splitChunks.length > 0) { - yield await populate(splitChunks, header.blockSize); - } -} diff --git a/src/QDL/usblib.js b/src/QDL/usblib.js deleted file mode 100644 index dc431d0..0000000 --- a/src/QDL/usblib.js +++ /dev/null @@ -1,140 +0,0 @@ -import { concatUint8Array, sleep } from "./utils"; - -const vendorID = 0x05c6; -const productID = 0x9008; -const QDL_USB_CLASS = 0xff; -const BULK_TRANSFER_SIZE = 16384; - - -export class usbClass { - constructor() { - this.device = null; - this.epIn = null; - this.epOut = null; - this.maxSize = 512; - } - - get connected() { - return ( - this.device !== null && - this.device.opened && - this.device.configurations[0].interfaces[0].claimed - ); - } - - async _validateAndConnectDevice() { - let ife = this.device?.configurations[0].interfaces[0].alternates[0]; - if (ife.endpoints.length !== 2) { - throw "USB - Attempted to connect to null device"; - } - - this.epIn = null; - this.epOut = null; - - for (let endpoint of ife.endpoints) { - if (endpoint.type !== "bulk") { - throw "USB - Interface endpoint is not bulk"; - } - if (endpoint.direction === "in") { - if (this.epIn === null) { - this.epIn = endpoint; - } else { - throw "USB - Interface has multiple IN endpoints"; - } - } else if (endpoint.direction === "out") { - if (this.epOut === null) { - this.epOut = endpoint; - } else { - throw "USB - Interface has multiple OUT endpoints"; - } - } - this.maxSize = this.epIn.packetSize; - } - console.log("Endpoints: in =", this.epIn, ", out =", this.epOut); - - try { - await this.device?.open(); - await this.device?.selectConfiguration(1); - try { - await this.device?.claimInterface(0); - } catch(error) { - await this.device?.reset(); - await this.device?.forget(); - await this.device?.close(); - console.error(error); - } - } catch (error) { - throw `USB - ${error}`; - } - } - - async connect() { - this.device = await navigator.usb.requestDevice({ - filters: [ - { - vendorID : vendorID, - productID : productID, - classCode : QDL_USB_CLASS, - }, - ], - }); - console.log("Using USB device:", this.device); - - navigator.usb.addEventListener("connect", async (event) =>{ - console.log("USB device connect:", event.device); - this.device = event.device; - try { - await this._validateAndConnectDevice(); - } catch (error) { - console.log("Error while connecting to the device"); - throw error; - } - }); - await this._validateAndConnectDevice(); - } - - async read(resplen=null) { - let respData = new Uint8Array(); - let covered = 0; - if (resplen === null) { - resplen = this.epIn.packetSize; - } - - while (covered < resplen) { - let respPacket = await this.device?.transferIn(this.epIn?.endpointNumber, resplen); - respData = concatUint8Array([respData, new Uint8Array(respPacket.data.buffer)]); - resplen = respData.length; - covered += respData.length; - } - return respData; - } - - - async write(cmdPacket, pktSize=null, wait=true) { - if (cmdPacket.length === 0) { - try { - await this.device?.transferOut(this.epOut?.endpointNumber, cmdPacket); - } catch(error) { - await this.device?.transferOut(this.epOut?.endpointNumber, cmdPacket); - } - return true; - } - - let offset = 0; - if (pktSize === null) { - pktSize = BULK_TRANSFER_SIZE; - } - while (offset < cmdPacket.length) { - if (wait) { - await this.device?.transferOut(this.epOut?.endpointNumber, cmdPacket.slice(offset, offset + pktSize)); - } else { - // this is a hack, webusb doesn't have timed out catching - // this only happens in sahara.configure(). The loader receive the packet but doesn't respond back (same as edl repo). - this.device?.transferOut(this.epOut?.endpointNumber, cmdPacket.slice(offset, offset + pktSize)); - await sleep(80); - } - offset += pktSize; - } - return true; - } -} diff --git a/src/QDL/utils.js b/src/QDL/utils.js deleted file mode 100644 index daf8345..0000000 --- a/src/QDL/utils.js +++ /dev/null @@ -1,110 +0,0 @@ -export const sleep = ms => new Promise(r => setTimeout(r, ms)); - - -export class structHelper_io { - constructor(data, pos=0) { - this.pos = pos - this.data = data; - } - - dword(littleEndian=true) { - let view = new DataView(this.data.slice(this.pos, this.pos+4).buffer, 0); - this.pos += 4; - return view.getUint32(0, littleEndian); - } - - qword(littleEndian=true) { - let view = new DataView(this.data.slice(this.pos, this.pos+8).buffer, 0); - this.pos += 8; - return view.getBigUint64(0, littleEndian); - } -} - - -export function packGenerator(elements, littleEndian=true) { - let n = elements.length; - const buffer = new ArrayBuffer(n*4); - const view = new DataView(buffer); - for (let i = 0; i < n; i++) { - view.setUint32(i*4, elements[i], littleEndian); - } - return new Uint8Array(view.buffer); -} - - -export function concatUint8Array(arrays) { - let length = 0; - arrays.forEach(item => { - if (item !== null) { - length += item.length; - } - }); - let concatArray = new Uint8Array(length); - let offset = 0; - arrays.forEach( item => { - if (item !== null) { - concatArray.set(item, offset); - offset += item.length; - } - }); - return concatArray; -} - - -export function containsBytes(subString, array) { - let tArray = new TextDecoder().decode(array); - return tArray.includes(subString); -} - - -export function compareStringToBytes(compareString, array) { - let tArray = new TextDecoder().decode(array); - return compareString == tArray; -} - - -export function readBlobAsBuffer(blob) { - return new Promise((resolve, reject) => { - let reader = new FileReader(); - reader.onload = () => { - resolve(reader.result); - }; - reader.onerror = () => { - reject(reader.error); - }; - reader.readAsArrayBuffer(blob); - }); -} - - -export function bytes2Number(array) { - let view = new DataView(array.buffer, 0); - if (array.length !== 8 && array.length !== 4) { - throw "Only convert to 64 and 32 bit Number"; - } - return (array.length === 8) ? view.getBigUint64(0, true) : view.getUint32(0, true); -} - - -export function runWithTimeout(promise, timeout) { - return new Promise((resolve, reject) => { - let timedOut = false; - let tid = setTimeout(() => { - timedOut = true; - reject(new Error(`Timed out while trying to connect ${timeout}`)); - }, timeout); - promise - .then((val) => { - if (!timedOut) - resolve(val); - }) - .catch((err) => { - if (!timedOut) - reject(err); - }) - .finally(() => { - if (!timedOut) - clearTimeout(tid); - }); - }); -} diff --git a/src/QDL/xmlParser.js b/src/QDL/xmlParser.js deleted file mode 100644 index f5cd31b..0000000 --- a/src/QDL/xmlParser.js +++ /dev/null @@ -1,53 +0,0 @@ -export class xmlParser { - getReponse(input) { - let tInput = new TextDecoder().decode(input); - let lines = tInput.split(" { - obj[attr.name] = attr.value; - return obj; - }, content); - } - } - return content; - } - - - getLog(input) { - let tInput = new TextDecoder().decode(input); - let lines = tInput.split(" { - if (attr.name == "value") - obj.push(attr.value); - return obj; - }, data); - } - } - return data; - } -} diff --git a/src/utils/flash.js b/src/utils/flash.js index cdc6928..071df50 100644 --- a/src/utils/flash.js +++ b/src/utils/flash.js @@ -1,7 +1,6 @@ import { useEffect, useRef, useState } from 'react' - -import { concatUint8Array } from '../QDL/utils' -import { qdlDevice } from '../QDL/qdl' +import { qdlDevice } from '@commaai/qdl' +import { concatUint8Array } from '@commaai/qdl/utils' import * as Comlink from 'comlink' import config from '../config'