|
@@ -16,15 +16,17 @@ const PROTOCOL_NAME = 'storage-access'
|
|
|
|
|
|
|
|
const CMD_ERR_MASK = 0x80
|
|
const CMD_ERR_MASK = 0x80
|
|
|
const CMD_CONTROL_FLAG = 0x40
|
|
const CMD_CONTROL_FLAG = 0x40
|
|
|
|
|
+const CMD_SPECIAL_OP_MASK = 0x3F
|
|
|
const CMD_WRITE_MASK = 0x08
|
|
const CMD_WRITE_MASK = 0x08
|
|
|
const CMD_ADDRESS_MODE_MASK = 0x07
|
|
const CMD_ADDRESS_MODE_MASK = 0x07
|
|
|
const CMD_RESERVED_MASK = 0x30
|
|
const CMD_RESERVED_MASK = 0x30
|
|
|
-const CMD_CONTROL = 0x4F
|
|
|
|
|
-const CONTROL_RESPONSE_HEADER_LENGTH = 3
|
|
|
|
|
|
|
+const CMD_CONTROL = CMD_CONTROL_FLAG
|
|
|
|
|
+const CONTROL_RESPONSE_HEADER_LENGTH = 1
|
|
|
const ADDRESS16_BYTE_LENGTH = 2
|
|
const ADDRESS16_BYTE_LENGTH = 2
|
|
|
const ADDRESS32_BYTE_LENGTH = 4
|
|
const ADDRESS32_BYTE_LENGTH = 4
|
|
|
|
|
|
|
|
const AREA = {
|
|
const AREA = {
|
|
|
|
|
+ CODEINFO: 0x00,
|
|
|
ADDR32: 0x07,
|
|
ADDR32: 0x07,
|
|
|
DATA: 0x01,
|
|
DATA: 0x01,
|
|
|
IDATA: 0x02,
|
|
IDATA: 0x02,
|
|
@@ -33,22 +35,11 @@ const AREA = {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const CONTROL_OP = {
|
|
const CONTROL_OP = {
|
|
|
- RESET: 0x01,
|
|
|
|
|
- START: 0x02,
|
|
|
|
|
- STOP: 0x03,
|
|
|
|
|
- SET_CONTROL_REF: 0x04,
|
|
|
|
|
- READ_CODE_INFO_DESCRIPTOR: 0x05
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const CONTROL_STATUS_MESSAGES = {
|
|
|
|
|
- 0x00: '成功',
|
|
|
|
|
- 0x01: '不支持的指令',
|
|
|
|
|
- 0x02: '参数无效',
|
|
|
|
|
- 0x03: '设备忙',
|
|
|
|
|
- 0x04: '执行失败'
|
|
|
|
|
|
|
+ RESET: 0x01
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const AREA_NAMES = {
|
|
const AREA_NAMES = {
|
|
|
|
|
+ [AREA.CODEINFO]: 'CODEINFO',
|
|
|
[AREA.ADDR32]: 'ADDR32',
|
|
[AREA.ADDR32]: 'ADDR32',
|
|
|
[AREA.DATA]: 'DATA',
|
|
[AREA.DATA]: 'DATA',
|
|
|
[AREA.IDATA]: 'IDATA',
|
|
[AREA.IDATA]: 'IDATA',
|
|
@@ -57,6 +48,8 @@ const AREA_NAMES = {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const AREA_BY_NAME = {
|
|
const AREA_BY_NAME = {
|
|
|
|
|
+ CODEINFO: AREA.CODEINFO,
|
|
|
|
|
+ CODE_INFO: AREA.CODEINFO,
|
|
|
ADDR32: AREA.ADDR32,
|
|
ADDR32: AREA.ADDR32,
|
|
|
ADDRESS32: AREA.ADDR32,
|
|
ADDRESS32: AREA.ADDR32,
|
|
|
DATA: AREA.DATA,
|
|
DATA: AREA.DATA,
|
|
@@ -95,7 +88,9 @@ const READ_RESPONSE_OVERHEAD_32 = 1 + ADDRESS32_BYTE_LENGTH + 2 + 2
|
|
|
const WRITE_RESPONSE_LENGTH_16 = 1 + ADDRESS16_BYTE_LENGTH + 2 + 2
|
|
const WRITE_RESPONSE_LENGTH_16 = 1 + ADDRESS16_BYTE_LENGTH + 2 + 2
|
|
|
const WRITE_RESPONSE_LENGTH_32 = 1 + ADDRESS32_BYTE_LENGTH + 2 + 2
|
|
const WRITE_RESPONSE_LENGTH_32 = 1 + ADDRESS32_BYTE_LENGTH + 2 + 2
|
|
|
const EXCEPTION_RESPONSE_LENGTH = 4
|
|
const EXCEPTION_RESPONSE_LENGTH = 4
|
|
|
-const CODE_INFO_DESCRIPTOR_BYTE_LENGTH = 11
|
|
|
|
|
|
|
+const CODE_INFO_DESCRIPTOR_ADDRESS = 0
|
|
|
|
|
+const CODE_INFO_DESCRIPTOR_BYTE_LENGTH = 9
|
|
|
|
|
+const CODE_INFO_DESCRIPTOR_RESPONSE_LENGTH = 1 + CODE_INFO_DESCRIPTOR_BYTE_LENGTH + 2
|
|
|
const MEMORY_ENDIAN_MARK_BIG = 0x55AA
|
|
const MEMORY_ENDIAN_MARK_BIG = 0x55AA
|
|
|
const MEMORY_ENDIAN_MARK_LITTLE = 0xAA55
|
|
const MEMORY_ENDIAN_MARK_LITTLE = 0xAA55
|
|
|
const MEMORY_ENDIAN = {
|
|
const MEMORY_ENDIAN = {
|
|
@@ -106,9 +101,10 @@ const MEMORY_ENDIAN = {
|
|
|
const STORAGE_CRC_OPTIONS = {
|
|
const STORAGE_CRC_OPTIONS = {
|
|
|
byteOrder: BYTE_ORDER_HIGH
|
|
byteOrder: BYTE_ORDER_HIGH
|
|
|
}
|
|
}
|
|
|
-const VALID_AREAS = [AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA, AREA.CODE]
|
|
|
|
|
-const MEMORY_AREAS = [AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA, AREA.CODE]
|
|
|
|
|
-const RESERVED_AREAS = [0x00, 0x05, 0x06]
|
|
|
|
|
|
|
+const VALID_AREAS = [AREA.CODEINFO, AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA, AREA.CODE]
|
|
|
|
|
+const MEMORY_AREAS = [AREA.CODEINFO, AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA, AREA.CODE]
|
|
|
|
|
+const WRITABLE_AREAS = [AREA.ADDR32, AREA.DATA, AREA.IDATA, AREA.XDATA]
|
|
|
|
|
+const RESERVED_AREAS = [0x05, 0x06]
|
|
|
|
|
|
|
|
function toByte(value, label) {
|
|
function toByte(value, label) {
|
|
|
if (!Number.isInteger(value) || value < 0 || value > 0xFF) {
|
|
if (!Number.isInteger(value) || value < 0 || value > 0xFF) {
|
|
@@ -126,14 +122,6 @@ function toWord(value, label) {
|
|
|
return value
|
|
return value
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function toInt16Word(value, label) {
|
|
|
|
|
- if (!Number.isInteger(value) || value < -0x8000 || value > 0x7FFF) {
|
|
|
|
|
- throw new Error(`${label}必须在 -32768 至 32767 之间`)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return value & 0xFFFF
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
function toUint32(value, label) {
|
|
function toUint32(value, label) {
|
|
|
if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
|
|
if (!Number.isInteger(value) || value < 0 || value > MAX_UINT32) {
|
|
|
throw new Error(`${label}必须在 0x00000000 至 0xFFFFFFFF 之间`)
|
|
throw new Error(`${label}必须在 0x00000000 至 0xFFFFFFFF 之间`)
|
|
@@ -150,7 +138,7 @@ function normalizeArea(value) {
|
|
|
|
|
|
|
|
const area = toByte(Number(value), '存储区域')
|
|
const area = toByte(Number(value), '存储区域')
|
|
|
if (VALID_AREAS.indexOf(area) < 0) {
|
|
if (VALID_AREAS.indexOf(area) < 0) {
|
|
|
- throw new Error('存储区域必须为 addr32/data/idata/xdata/code')
|
|
|
|
|
|
|
+ throw new Error('存储区域必须为 codeinfo/data/idata/xdata/code/addr32')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return area
|
|
return area
|
|
@@ -169,6 +157,8 @@ function getMemoryHeaderLength(area) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function getReadRequestLength(area) {
|
|
function getReadRequestLength(area) {
|
|
|
|
|
+ if (Number(area) === AREA.CODEINFO) return 3
|
|
|
|
|
+
|
|
|
return getMemoryHeaderLength(area) + 2
|
|
return getMemoryHeaderLength(area) + 2
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -177,6 +167,8 @@ function getWriteRequestOverhead(area) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function getReadResponseOverhead(area) {
|
|
function getReadResponseOverhead(area) {
|
|
|
|
|
+ if (Number(area) === AREA.CODEINFO) return 1 + 2
|
|
|
|
|
+
|
|
|
return getMemoryHeaderLength(area) + 2
|
|
return getMemoryHeaderLength(area) + 2
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -184,10 +176,14 @@ function getWriteResponseLength(area) {
|
|
|
return getMemoryHeaderLength(area) + 2
|
|
return getMemoryHeaderLength(area) + 2
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function isCodeInfoDescriptorArea(area) {
|
|
|
|
|
+ return Number(area) === AREA.CODEINFO
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function normalizeMemoryArea(value) {
|
|
function normalizeMemoryArea(value) {
|
|
|
const area = normalizeArea(value)
|
|
const area = normalizeArea(value)
|
|
|
if (MEMORY_AREAS.indexOf(area) < 0) {
|
|
if (MEMORY_AREAS.indexOf(area) < 0) {
|
|
|
- throw new Error('存储读写区域必须为 addr32/data/idata/xdata/code')
|
|
|
|
|
|
|
+ throw new Error('存储访问区域必须为 codeinfo/data/idata/xdata/code/addr32')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return area
|
|
return area
|
|
@@ -294,19 +290,32 @@ function getMaxWriteByteLength(maxFrameBytes = DEFAULT_MAX_FRAME_BYTES, area = A
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function buildCommand(area, isWrite = false) {
|
|
function buildCommand(area, isWrite = false) {
|
|
|
- const normalizedArea = isWrite ? normalizeMemoryArea(area) : normalizeArea(area)
|
|
|
|
|
|
|
+ const normalizedArea = normalizeMemoryArea(area)
|
|
|
|
|
|
|
|
if (RESERVED_AREAS.indexOf(normalizedArea) >= 0) {
|
|
if (RESERVED_AREAS.indexOf(normalizedArea) >= 0) {
|
|
|
- throw new Error('存储访问区域号 0x00/0x05/0x06 暂时保留')
|
|
|
|
|
|
|
+ throw new Error('存储访问区域号 0x05/0x06 暂时保留')
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isWrite && WRITABLE_AREAS.indexOf(normalizedArea) < 0) {
|
|
|
|
|
+ throw new Error(`${AREA_NAMES[normalizedArea] || '该'} 区不可写`)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return (isWrite ? CMD_WRITE_MASK : 0x00) | normalizedArea
|
|
return (isWrite ? CMD_WRITE_MASK : 0x00) | normalizedArea
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function buildSpecialCommand(operation) {
|
|
|
|
|
+ const op = toByte(Number(operation), '特殊指令') & CMD_SPECIAL_OP_MASK
|
|
|
|
|
+ if (op <= 0) {
|
|
|
|
|
+ throw new Error('特殊指令必须在 0x01 至 0x3F 之间')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return CMD_CONTROL_FLAG | op
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function decodeCommand(command) {
|
|
function decodeCommand(command) {
|
|
|
const cmd = toByte(Number(command), '命令字')
|
|
const cmd = toByte(Number(command), '命令字')
|
|
|
const sourceCommand = cmd & ~CMD_ERR_MASK
|
|
const sourceCommand = cmd & ~CMD_ERR_MASK
|
|
|
- const isControl = sourceCommand === CMD_CONTROL
|
|
|
|
|
|
|
+ const isControl = !!(sourceCommand & CMD_CONTROL_FLAG)
|
|
|
|
|
+ const operation = isControl ? (sourceCommand & CMD_SPECIAL_OP_MASK) : 0
|
|
|
const area = isControl ? CMD_CONTROL : (sourceCommand & CMD_ADDRESS_MODE_MASK)
|
|
const area = isControl ? CMD_CONTROL : (sourceCommand & CMD_ADDRESS_MODE_MASK)
|
|
|
const reservedBits = sourceCommand & CMD_RESERVED_MASK
|
|
const reservedBits = sourceCommand & CMD_RESERVED_MASK
|
|
|
|
|
|
|
@@ -315,10 +324,12 @@ function decodeCommand(command) {
|
|
|
area,
|
|
area,
|
|
|
command: cmd,
|
|
command: cmd,
|
|
|
hasError: !!(cmd & CMD_ERR_MASK),
|
|
hasError: !!(cmd & CMD_ERR_MASK),
|
|
|
|
|
+ hasInvalidSpecialOperation: isControl && operation === 0,
|
|
|
hasReservedBits: !isControl && reservedBits !== 0,
|
|
hasReservedBits: !isControl && reservedBits !== 0,
|
|
|
isAddress32: !isControl && isAddress32Area(area),
|
|
isAddress32: !isControl && isAddress32Area(area),
|
|
|
isControl,
|
|
isControl,
|
|
|
isWrite: !isControl && !!(sourceCommand & CMD_WRITE_MASK),
|
|
isWrite: !isControl && !!(sourceCommand & CMD_WRITE_MASK),
|
|
|
|
|
+ operation,
|
|
|
sourceCommand
|
|
sourceCommand
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -339,6 +350,10 @@ function appendStorageCrc(bytes) {
|
|
|
|
|
|
|
|
function buildReadFrame(area, address, byteLength, options = {}) {
|
|
function buildReadFrame(area, address, byteLength, options = {}) {
|
|
|
const normalizedArea = normalizeMemoryArea(area)
|
|
const normalizedArea = normalizeMemoryArea(area)
|
|
|
|
|
+ if (isCodeInfoDescriptorArea(normalizedArea)) {
|
|
|
|
|
+ return buildCodeInfoDescriptorFrame()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
|
const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
const startAddress = addressBytes === ADDRESS32_BYTE_LENGTH
|
|
|
? toUint32(Number(address), '内存地址')
|
|
? toUint32(Number(address), '内存地址')
|
|
@@ -354,8 +369,8 @@ function buildReadFrame(area, address, byteLength, options = {}) {
|
|
|
|
|
|
|
|
function buildWriteFrame(area, address, bytes, options = {}) {
|
|
function buildWriteFrame(area, address, bytes, options = {}) {
|
|
|
const normalizedArea = normalizeMemoryArea(area)
|
|
const normalizedArea = normalizeMemoryArea(area)
|
|
|
- if (normalizedArea === AREA.CODE) {
|
|
|
|
|
- throw new Error('code 区暂不支持写入')
|
|
|
|
|
|
|
+ if (WRITABLE_AREAS.indexOf(normalizedArea) < 0) {
|
|
|
|
|
+ throw new Error(`${AREA_NAMES[normalizedArea] || '该'} 区不可写`)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
const addressBytes = getAddressFieldByteLength(normalizedArea)
|
|
@@ -373,18 +388,31 @@ function buildWriteFrame(area, address, bytes, options = {}) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function buildControlFrame(operation, dataBytes = []) {
|
|
function buildControlFrame(operation, dataBytes = []) {
|
|
|
- const op = toByte(Number(operation), '特殊指令')
|
|
|
|
|
|
|
+ const command = buildSpecialCommand(operation)
|
|
|
const payload = toByteArray(dataBytes).map((byte) => toByte(Number(byte), '指令数据'))
|
|
const payload = toByteArray(dataBytes).map((byte) => toByte(Number(byte), '指令数据'))
|
|
|
|
|
|
|
|
- return appendStorageCrc([CMD_CONTROL, op].concat(payload))
|
|
|
|
|
|
|
+ return appendStorageCrc([command].concat(payload))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function buildControlReferenceFrame(referenceValue) {
|
|
|
|
|
- return buildControlFrame(CONTROL_OP.SET_CONTROL_REF, splitWord(toInt16Word(Number(referenceValue), '控制参考值')))
|
|
|
|
|
|
|
+function buildCodeInfoDescriptorFrame() {
|
|
|
|
|
+ return appendStorageCrc([buildCommand(AREA.CODEINFO, false)])
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function buildCodeInfoDescriptorFrame() {
|
|
|
|
|
- return buildControlFrame(CONTROL_OP.READ_CODE_INFO_DESCRIPTOR)
|
|
|
|
|
|
|
+function parseCodeInfoDescriptorBytes(bytes) {
|
|
|
|
|
+ const dataBytes = toByteArray(bytes)
|
|
|
|
|
+ if (dataBytes.length < CODE_INFO_DESCRIPTOR_BYTE_LENGTH) {
|
|
|
|
|
+ throw new Error('CodeInfo 描述符长度无效')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ codeInfoAddress: readDword(dataBytes, 0),
|
|
|
|
|
+ codeInfoAddressWidth: dataBytes[6] & 0xFF,
|
|
|
|
|
+ codeInfoByteLength: readWord(dataBytes, 4),
|
|
|
|
|
+ codeInfoDescriptorBytes: dataBytes.slice(0, CODE_INFO_DESCRIPTOR_BYTE_LENGTH),
|
|
|
|
|
+ codeInfoMaxPacketLength: readWord(dataBytes, 7),
|
|
|
|
|
+ codeInfoMemoryEndian: MEMORY_ENDIAN.BIG,
|
|
|
|
|
+ codeInfoMemoryEndianMark: 0
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function formatHex(bytes) {
|
|
function formatHex(bytes) {
|
|
@@ -393,7 +421,7 @@ function formatHex(bytes) {
|
|
|
|
|
|
|
|
function parseStorageAccessResponse(bytes) {
|
|
function parseStorageAccessResponse(bytes) {
|
|
|
const frame = toByteArray(bytes)
|
|
const frame = toByteArray(bytes)
|
|
|
- if (frame.length < EXCEPTION_RESPONSE_LENGTH || !hasValidStorageCrc(frame)) return null
|
|
|
|
|
|
|
+ if (frame.length < 3 || !hasValidStorageCrc(frame)) return null
|
|
|
|
|
|
|
|
const command = frame[0] & 0xFF
|
|
const command = frame[0] & 0xFF
|
|
|
const decoded = decodeCommand(command)
|
|
const decoded = decodeCommand(command)
|
|
@@ -416,6 +444,32 @@ function parseStorageAccessResponse(bytes) {
|
|
|
|
|
|
|
|
if (decoded.hasReservedBits) return null
|
|
if (decoded.hasReservedBits) return null
|
|
|
if (!AREA_NAMES[decoded.area]) return null
|
|
if (!AREA_NAMES[decoded.area]) return null
|
|
|
|
|
+
|
|
|
|
|
+ if (isCodeInfoDescriptorArea(decoded.area)) {
|
|
|
|
|
+ if (decoded.isWrite || frame.length !== CODE_INFO_DESCRIPTOR_RESPONSE_LENGTH) return null
|
|
|
|
|
+
|
|
|
|
|
+ const dataBytes = frame.slice(1, 1 + CODE_INFO_DESCRIPTOR_BYTE_LENGTH)
|
|
|
|
|
+ const response = {
|
|
|
|
|
+ address: CODE_INFO_DESCRIPTOR_ADDRESS,
|
|
|
|
|
+ addressWidth: 0,
|
|
|
|
|
+ area: decoded.area,
|
|
|
|
|
+ areaName: AREA_NAMES[decoded.area] || 'UNKNOWN',
|
|
|
|
|
+ byteLength: CODE_INFO_DESCRIPTOR_BYTE_LENGTH,
|
|
|
|
|
+ command,
|
|
|
|
|
+ dataBytes,
|
|
|
|
|
+ isException: false,
|
|
|
|
|
+ isAddress32: false,
|
|
|
|
|
+ isWrite: false,
|
|
|
|
|
+ protocol: PROTOCOL_NAME,
|
|
|
|
|
+ words: bytesToWords(dataBytes.length % 2 === 0 ? dataBytes : dataBytes.concat(0))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...response,
|
|
|
|
|
+ ...parseCodeInfoDescriptorBytes(dataBytes)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
const addressBytes = decoded.addressBytes
|
|
const addressBytes = decoded.addressBytes
|
|
|
const headerLength = getMemoryHeaderLength(decoded.area)
|
|
const headerLength = getMemoryHeaderLength(decoded.area)
|
|
|
|
|
|
|
@@ -447,7 +501,7 @@ function parseStorageAccessResponse(bytes) {
|
|
|
|
|
|
|
|
const dataBytes = frame.slice(dataStart, dataEnd)
|
|
const dataBytes = frame.slice(dataStart, dataEnd)
|
|
|
|
|
|
|
|
- return {
|
|
|
|
|
|
|
+ const response = {
|
|
|
address,
|
|
address,
|
|
|
addressWidth: decoded.isAddress32 ? 32 : 16,
|
|
addressWidth: decoded.isAddress32 ? 32 : 16,
|
|
|
area: decoded.area,
|
|
area: decoded.area,
|
|
@@ -461,9 +515,13 @@ function parseStorageAccessResponse(bytes) {
|
|
|
protocol: PROTOCOL_NAME,
|
|
protocol: PROTOCOL_NAME,
|
|
|
words: bytesToWords(dataBytes.length % 2 === 0 ? dataBytes : dataBytes.concat(0))
|
|
words: bytesToWords(dataBytes.length % 2 === 0 ? dataBytes : dataBytes.concat(0))
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return response
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function parseStorageControlResponse(frame, decoded) {
|
|
function parseStorageControlResponse(frame, decoded) {
|
|
|
|
|
+ if (decoded.hasInvalidSpecialOperation) return null
|
|
|
|
|
+
|
|
|
if (decoded.hasError) {
|
|
if (decoded.hasError) {
|
|
|
if (frame.length !== EXCEPTION_RESPONSE_LENGTH) return null
|
|
if (frame.length !== EXCEPTION_RESPONSE_LENGTH) return null
|
|
|
|
|
|
|
@@ -475,6 +533,7 @@ function parseStorageControlResponse(frame, decoded) {
|
|
|
isControl: true,
|
|
isControl: true,
|
|
|
isException: true,
|
|
isException: true,
|
|
|
isWrite: false,
|
|
isWrite: false,
|
|
|
|
|
+ operation: decoded.operation,
|
|
|
protocol: PROTOCOL_NAME,
|
|
protocol: PROTOCOL_NAME,
|
|
|
sourceCommand: decoded.sourceCommand
|
|
sourceCommand: decoded.sourceCommand
|
|
|
}
|
|
}
|
|
@@ -482,8 +541,6 @@ function parseStorageControlResponse(frame, decoded) {
|
|
|
|
|
|
|
|
if (frame.length < CONTROL_RESPONSE_HEADER_LENGTH + 2) return null
|
|
if (frame.length < CONTROL_RESPONSE_HEADER_LENGTH + 2) return null
|
|
|
|
|
|
|
|
- const operation = frame[1] & 0xFF
|
|
|
|
|
- const status = frame[2] & 0xFF
|
|
|
|
|
const dataStart = CONTROL_RESPONSE_HEADER_LENGTH
|
|
const dataStart = CONTROL_RESPONSE_HEADER_LENGTH
|
|
|
const dataEnd = frame.length - 2
|
|
const dataEnd = frame.length - 2
|
|
|
const byteLength = Math.max(0, dataEnd - dataStart)
|
|
const byteLength = Math.max(0, dataEnd - dataStart)
|
|
@@ -495,44 +552,46 @@ function parseStorageControlResponse(frame, decoded) {
|
|
|
areaName: 'CONTROL',
|
|
areaName: 'CONTROL',
|
|
|
byteLength,
|
|
byteLength,
|
|
|
command: frame[0] & 0xFF,
|
|
command: frame[0] & 0xFF,
|
|
|
- controlStatus: status,
|
|
|
|
|
- controlStatusText: CONTROL_STATUS_MESSAGES[status] || '未知状态',
|
|
|
|
|
dataBytes,
|
|
dataBytes,
|
|
|
isControl: true,
|
|
isControl: true,
|
|
|
isException: false,
|
|
isException: false,
|
|
|
isWrite: false,
|
|
isWrite: false,
|
|
|
- operation,
|
|
|
|
|
- protocol: PROTOCOL_NAME,
|
|
|
|
|
- ...(operation === CONTROL_OP.READ_CODE_INFO_DESCRIPTOR && dataBytes.length >= CODE_INFO_DESCRIPTOR_BYTE_LENGTH
|
|
|
|
|
- ? (() => {
|
|
|
|
|
- const endian = parseMemoryEndianMark(dataBytes, 7)
|
|
|
|
|
-
|
|
|
|
|
- return {
|
|
|
|
|
- codeInfoAddress: readDword(dataBytes, 0),
|
|
|
|
|
- codeInfoByteLength: readWord(dataBytes, 4),
|
|
|
|
|
- codeInfoAddressWidth: dataBytes[6] & 0xFF,
|
|
|
|
|
- codeInfoDescriptorBytes: dataBytes.slice(0, CODE_INFO_DESCRIPTOR_BYTE_LENGTH),
|
|
|
|
|
- codeInfoMaxPacketLength: readWord(dataBytes, 9),
|
|
|
|
|
- codeInfoMemoryEndian: endian.memoryEndian,
|
|
|
|
|
- codeInfoMemoryEndianMark: endian.marker
|
|
|
|
|
- }
|
|
|
|
|
- })()
|
|
|
|
|
- : {})
|
|
|
|
|
|
|
+ operation: decoded.operation,
|
|
|
|
|
+ protocol: PROTOCOL_NAME
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function parseStorageAccessRequest(bytes) {
|
|
function parseStorageAccessRequest(bytes) {
|
|
|
const frame = toByteArray(bytes)
|
|
const frame = toByteArray(bytes)
|
|
|
- if (frame.length < 4 || !hasValidStorageCrc(frame)) return null
|
|
|
|
|
|
|
+ if (frame.length < 3 || !hasValidStorageCrc(frame)) return null
|
|
|
|
|
|
|
|
const command = frame[0] & 0xFF
|
|
const command = frame[0] & 0xFF
|
|
|
|
|
|
|
|
const decoded = decodeCommand(command)
|
|
const decoded = decodeCommand(command)
|
|
|
- if (decoded.isControl) return parseStorageControlRequest(frame)
|
|
|
|
|
- if (frame.length < READ_REQUEST_LENGTH_16) return null
|
|
|
|
|
|
|
+ if (decoded.isControl) return parseStorageControlRequest(frame, decoded)
|
|
|
if (decoded.hasError) return null
|
|
if (decoded.hasError) return null
|
|
|
if (decoded.hasReservedBits) return null
|
|
if (decoded.hasReservedBits) return null
|
|
|
if (!AREA_NAMES[decoded.area]) return null
|
|
if (!AREA_NAMES[decoded.area]) return null
|
|
|
|
|
+ if (isCodeInfoDescriptorArea(decoded.area)) {
|
|
|
|
|
+ if (decoded.isWrite || frame.length !== getReadRequestLength(decoded.area)) return null
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ address: CODE_INFO_DESCRIPTOR_ADDRESS,
|
|
|
|
|
+ addressWidth: 0,
|
|
|
|
|
+ area: decoded.area,
|
|
|
|
|
+ areaName: AREA_NAMES[decoded.area] || 'UNKNOWN',
|
|
|
|
|
+ byteLength: CODE_INFO_DESCRIPTOR_BYTE_LENGTH,
|
|
|
|
|
+ command,
|
|
|
|
|
+ dataBytes: [],
|
|
|
|
|
+ isAddress32: false,
|
|
|
|
|
+ isWrite: false,
|
|
|
|
|
+ kind: 'raw-hex',
|
|
|
|
|
+ operation: 'read',
|
|
|
|
|
+ protocol: PROTOCOL_NAME,
|
|
|
|
|
+ quantity: CODE_INFO_DESCRIPTOR_BYTE_LENGTH
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (frame.length < READ_REQUEST_LENGTH_16) return null
|
|
|
|
|
|
|
|
const addressBytes = decoded.addressBytes
|
|
const addressBytes = decoded.addressBytes
|
|
|
const headerLength = getMemoryHeaderLength(decoded.area)
|
|
const headerLength = getMemoryHeaderLength(decoded.area)
|
|
@@ -563,11 +622,10 @@ function parseStorageAccessRequest(bytes) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function parseStorageControlRequest(frame) {
|
|
|
|
|
- if (frame.length < 4) return null
|
|
|
|
|
|
|
+function parseStorageControlRequest(frame, decoded = decodeCommand(frame && frame[0])) {
|
|
|
|
|
+ if (frame.length < 3 || decoded.hasInvalidSpecialOperation) return null
|
|
|
|
|
|
|
|
- const operation = frame[1] & 0xFF
|
|
|
|
|
- const dataStart = 2
|
|
|
|
|
|
|
+ const dataStart = CONTROL_RESPONSE_HEADER_LENGTH
|
|
|
const dataEnd = frame.length - 2
|
|
const dataEnd = frame.length - 2
|
|
|
const byteLength = Math.max(0, dataEnd - dataStart)
|
|
const byteLength = Math.max(0, dataEnd - dataStart)
|
|
|
|
|
|
|
@@ -575,12 +633,12 @@ function parseStorageControlRequest(frame) {
|
|
|
area: CMD_CONTROL,
|
|
area: CMD_CONTROL,
|
|
|
areaName: 'CONTROL',
|
|
areaName: 'CONTROL',
|
|
|
byteLength,
|
|
byteLength,
|
|
|
- command: CMD_CONTROL,
|
|
|
|
|
|
|
+ command: frame[0] & 0xFF,
|
|
|
dataBytes: frame.slice(dataStart, dataEnd),
|
|
dataBytes: frame.slice(dataStart, dataEnd),
|
|
|
isControl: true,
|
|
isControl: true,
|
|
|
isWrite: false,
|
|
isWrite: false,
|
|
|
kind: 'storage-control',
|
|
kind: 'storage-control',
|
|
|
- operation,
|
|
|
|
|
|
|
+ operation: decoded.operation,
|
|
|
protocol: PROTOCOL_NAME
|
|
protocol: PROTOCOL_NAME
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -593,8 +651,6 @@ function getExpectedResponseLength(expected, responseCommand, responseBytes = []
|
|
|
|
|
|
|
|
if (expected.isControl) {
|
|
if (expected.isControl) {
|
|
|
if (responseBytes.length < CONTROL_RESPONSE_HEADER_LENGTH) return 0
|
|
if (responseBytes.length < CONTROL_RESPONSE_HEADER_LENGTH) return 0
|
|
|
- const status = responseBytes[2] & 0xFF
|
|
|
|
|
- if (status !== 0) return CONTROL_RESPONSE_HEADER_LENGTH + 2
|
|
|
|
|
|
|
|
|
|
return CONTROL_RESPONSE_HEADER_LENGTH + Number(expected.expectedByteLength || 0) + 2
|
|
return CONTROL_RESPONSE_HEADER_LENGTH + Number(expected.expectedByteLength || 0) + 2
|
|
|
}
|
|
}
|
|
@@ -603,6 +659,10 @@ function getExpectedResponseLength(expected, responseCommand, responseBytes = []
|
|
|
return getWriteResponseLength(expected.area)
|
|
return getWriteResponseLength(expected.area)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (isCodeInfoDescriptorArea(expected.area)) {
|
|
|
|
|
+ return CODE_INFO_DESCRIPTOR_RESPONSE_LENGTH
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
const headerLength = getMemoryHeaderLength(expected.area)
|
|
const headerLength = getMemoryHeaderLength(expected.area)
|
|
|
if (responseBytes.length < headerLength) return 0
|
|
if (responseBytes.length < headerLength) return 0
|
|
|
|
|
|
|
@@ -647,6 +707,9 @@ function getReadBufferHint(expected) {
|
|
|
if (expected.operation === 'write' || expected.isWrite) {
|
|
if (expected.operation === 'write' || expected.isWrite) {
|
|
|
return getWriteResponseLength(expected.area)
|
|
return getWriteResponseLength(expected.area)
|
|
|
}
|
|
}
|
|
|
|
|
+ if (isCodeInfoDescriptorArea(expected.area)) {
|
|
|
|
|
+ return CODE_INFO_DESCRIPTOR_RESPONSE_LENGTH
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return getReadResponseOverhead(expected.area) + Number(expected.byteLength || expected.quantity || 0)
|
|
return getReadResponseOverhead(expected.area) + Number(expected.byteLength || expected.quantity || 0)
|
|
|
}
|
|
}
|
|
@@ -763,11 +826,12 @@ function createExpected(area, address, byteLength, isWrite, kind, options = {})
|
|
|
|
|
|
|
|
function createControlExpected(operation, kind = 'storage-control', options = {}) {
|
|
function createControlExpected(operation, kind = 'storage-control', options = {}) {
|
|
|
const op = toByte(Number(operation), '特殊指令')
|
|
const op = toByte(Number(operation), '特殊指令')
|
|
|
|
|
+ const command = buildSpecialCommand(op)
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
area: CMD_CONTROL,
|
|
area: CMD_CONTROL,
|
|
|
byteLength: 0,
|
|
byteLength: 0,
|
|
|
- command: CMD_CONTROL,
|
|
|
|
|
|
|
+ command,
|
|
|
expectedByteLength: Number(options.expectedByteLength) || 0,
|
|
expectedByteLength: Number(options.expectedByteLength) || 0,
|
|
|
isControl: true,
|
|
isControl: true,
|
|
|
isWrite: false,
|
|
isWrite: false,
|
|
@@ -851,16 +915,17 @@ module.exports = {
|
|
|
ADDRESS32_BYTE_LENGTH,
|
|
ADDRESS32_BYTE_LENGTH,
|
|
|
CMD_CONTROL,
|
|
CMD_CONTROL,
|
|
|
CMD_CONTROL_FLAG,
|
|
CMD_CONTROL_FLAG,
|
|
|
|
|
+ CMD_SPECIAL_OP_MASK,
|
|
|
CMD_ADDRESS_MODE_MASK,
|
|
CMD_ADDRESS_MODE_MASK,
|
|
|
CMD_ERR_MASK,
|
|
CMD_ERR_MASK,
|
|
|
CMD_RESERVED_MASK,
|
|
CMD_RESERVED_MASK,
|
|
|
CMD_WRITE_MASK,
|
|
CMD_WRITE_MASK,
|
|
|
CONTROL_OP,
|
|
CONTROL_OP,
|
|
|
CONTROL_RESPONSE_HEADER_LENGTH,
|
|
CONTROL_RESPONSE_HEADER_LENGTH,
|
|
|
- CONTROL_STATUS_MESSAGES,
|
|
|
|
|
DEFAULT_MAX_FRAME_BYTES,
|
|
DEFAULT_MAX_FRAME_BYTES,
|
|
|
EXCEPTION_MESSAGES,
|
|
EXCEPTION_MESSAGES,
|
|
|
EXCEPTION_RESPONSE_LENGTH,
|
|
EXCEPTION_RESPONSE_LENGTH,
|
|
|
|
|
+ CODE_INFO_DESCRIPTOR_ADDRESS,
|
|
|
CODE_INFO_DESCRIPTOR_BYTE_LENGTH,
|
|
CODE_INFO_DESCRIPTOR_BYTE_LENGTH,
|
|
|
MAX_PAYLOAD_BYTES,
|
|
MAX_PAYLOAD_BYTES,
|
|
|
MEMORY_ENDIAN,
|
|
MEMORY_ENDIAN,
|
|
@@ -880,9 +945,9 @@ module.exports = {
|
|
|
appendStorageCrc,
|
|
appendStorageCrc,
|
|
|
buildCommand,
|
|
buildCommand,
|
|
|
buildCodeInfoDescriptorFrame,
|
|
buildCodeInfoDescriptorFrame,
|
|
|
- buildControlReferenceFrame,
|
|
|
|
|
buildControlFrame,
|
|
buildControlFrame,
|
|
|
buildReadFrame,
|
|
buildReadFrame,
|
|
|
|
|
+ buildSpecialCommand,
|
|
|
buildWriteFrame,
|
|
buildWriteFrame,
|
|
|
createControlExpected,
|
|
createControlExpected,
|
|
|
createExpected,
|
|
createExpected,
|
|
@@ -898,6 +963,7 @@ module.exports = {
|
|
|
normalizeArea,
|
|
normalizeArea,
|
|
|
normalizeMaxFrameBytes,
|
|
normalizeMaxFrameBytes,
|
|
|
normalizeMemoryArea,
|
|
normalizeMemoryArea,
|
|
|
|
|
+ parseCodeInfoDescriptorBytes,
|
|
|
resolveDescriptorMaxFrameBytes,
|
|
resolveDescriptorMaxFrameBytes,
|
|
|
response,
|
|
response,
|
|
|
splitDword,
|
|
splitDword,
|