UNPKG

inventoresed

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

165 lines (139 loc) 4.33 kB
import { ZWaveError, ZWaveErrorCodes } from "@zwave-js/core/safe"; import { FLASH_MAX_PAGE_SIZE, NVM3_MIN_PAGE_SIZE, NVM3_PAGE_COUNTER_MASK, NVM3_PAGE_COUNTER_SIZE, NVM3_PAGE_HEADER_SIZE, NVM3_PAGE_MAGIC, PageStatus, PageWriteSize, } from "./consts"; import { NVM3Object, readObjects } from "./object"; import { computeBergerCode, validateBergerCode } from "./utils"; export interface NVM3PageHeader { offset: number; version: number; eraseCount: number; status: PageStatus; encrypted: boolean; pageSize: number; writeSize: PageWriteSize; memoryMapped: boolean; deviceFamily: number; } export interface NVM3Page { header: NVM3PageHeader; objects: NVM3Object[]; } // The page size field has a value from 0 to 7 describing page sizes from 512 to 65536 bytes export function pageSizeToBits(pageSize: number): number { return Math.ceil(Math.log2(pageSize) - Math.log2(NVM3_MIN_PAGE_SIZE)); } export function pageSizeFromBits(bits: number): number { return NVM3_MIN_PAGE_SIZE * Math.pow(2, bits); } export function readPage( buffer: Buffer, offset: number, ): { page: NVM3Page; bytesRead: number } { const version = buffer.readUInt16LE(offset); const magic = buffer.readUInt16LE(offset + 2); if (magic !== NVM3_PAGE_MAGIC) { throw new ZWaveError( "Not a valid NVM3 page!", ZWaveErrorCodes.NVM_InvalidFormat, ); } if (version !== 0x01) { throw new ZWaveError( `Unsupported NVM3 page version: ${version}`, ZWaveErrorCodes.NVM_NotSupported, ); } // The erase counter is saved twice, once normally, once inverted let eraseCount = buffer.readUInt32LE(offset + 4); const eraseCountCode = eraseCount >>> NVM3_PAGE_COUNTER_SIZE; eraseCount &= NVM3_PAGE_COUNTER_MASK; validateBergerCode(eraseCount, eraseCountCode, NVM3_PAGE_COUNTER_SIZE); let eraseCountInv = buffer.readUInt32LE(offset + 8); const eraseCountInvCode = eraseCountInv >>> NVM3_PAGE_COUNTER_SIZE; eraseCountInv &= NVM3_PAGE_COUNTER_MASK; validateBergerCode( eraseCountInv, eraseCountInvCode, NVM3_PAGE_COUNTER_SIZE, ); if (eraseCount !== (~eraseCountInv & NVM3_PAGE_COUNTER_MASK)) { throw new ZWaveError( "Invalid erase count!", ZWaveErrorCodes.NVM_InvalidFormat, ); } // Page status const status = buffer.readUInt32LE(offset + 12); const devInfo = buffer.readUInt16LE(offset + 16); const deviceFamily = devInfo & 0x7ff; const writeSize = (devInfo >> 11) & 0b1; const memoryMapped = !!((devInfo >> 12) & 0b1); const pageSize = pageSizeFromBits((devInfo >> 13) & 0b111); // Application NVM pages seem to get written with a page size of 0xffff const actualPageSize = Math.min(pageSize, FLASH_MAX_PAGE_SIZE); if (buffer.length < offset + actualPageSize) { throw new ZWaveError( "Incomplete page in buffer!", ZWaveErrorCodes.NVM_InvalidFormat, ); } const formatInfo = buffer.readUInt16LE(offset + 18); const encrypted = !(formatInfo & 0b1); const header: NVM3PageHeader = { offset, version, eraseCount, status, encrypted, pageSize, writeSize, memoryMapped, deviceFamily, }; const bytesRead = actualPageSize; const data = buffer.slice(offset + 20, offset + bytesRead); const { objects } = readObjects(data); return { page: { header, objects }, bytesRead, }; } export function writePageHeader( header: Omit<NVM3PageHeader, "offset">, ): Buffer { const ret = Buffer.alloc(NVM3_PAGE_HEADER_SIZE); ret.writeUInt16LE(header.version, 0); ret.writeUInt16LE(NVM3_PAGE_MAGIC, 2); let eraseCount = header.eraseCount & NVM3_PAGE_COUNTER_MASK; const eraseCountCode = computeBergerCode( eraseCount, NVM3_PAGE_COUNTER_SIZE, ); eraseCount |= eraseCountCode << NVM3_PAGE_COUNTER_SIZE; ret.writeInt32LE(eraseCount, 4); let eraseCountInv = ~header.eraseCount & NVM3_PAGE_COUNTER_MASK; const eraseCountInvCode = computeBergerCode( eraseCountInv, NVM3_PAGE_COUNTER_SIZE, ); eraseCountInv |= eraseCountInvCode << NVM3_PAGE_COUNTER_SIZE; ret.writeInt32LE(eraseCountInv, 8); ret.writeUInt32LE(header.status, 12); const devInfo = (header.deviceFamily & 0x7ff) | ((header.writeSize & 0b1) << 11) | ((header.memoryMapped ? 1 : 0) << 12) | (pageSizeToBits(header.pageSize) << 13); ret.writeUInt16LE(devInfo, 16); const formatInfo = header.encrypted ? 0xfffe : 0xffff; ret.writeUInt16LE(formatInfo, 18); return ret; }