435 lines
11 KiB
NASM
435 lines
11 KiB
NASM
; ***************************************************************************
|
|
; copyright : (C) 2023 by Martin Preuss
|
|
; email : martin@libchipcard.de
|
|
;
|
|
; ***************************************************************************
|
|
; * This file is part of the project "AqHome". *
|
|
; * Please see toplevel file COPYING of that project for license details. *
|
|
; ***************************************************************************
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; This sub-module contains code for cooperative address management.
|
|
; Because the network of nodes doen't have a central control unit the nodes
|
|
; need to assign addresses to themselves with help from the community of existing
|
|
; nodes.
|
|
;
|
|
; Protocol A: Full Address Assignment Protocol
|
|
; --------------------------------------------
|
|
; This protocol is used when a node has no currently or previously assigned address.
|
|
; In this case we need to find a free address which no other node uses.
|
|
;
|
|
; 1) a node needs an address. It sends the packet "NEED_ADDRESS"
|
|
; 2) every node which already has an address sees this message and answers with
|
|
; a "HAVE_ADDRESS" packet. The nodes must not answer all at once to avoid
|
|
; congestions. Instead, the time it takes for a node to respond depends on
|
|
; its own address (e.g. a node with address 10 will wait for 10+3s before answering)
|
|
; 3) the initial node collects all "HAVE_ADDRESS" packets and puts the addresses
|
|
; received this way into a bitfield in which for every address received a bit is
|
|
; set
|
|
; 4) after about 130s all nodes in the address range 1-127 should have answered.
|
|
; So after this time the initial node looks at the bitfield of received addresses
|
|
; and selects the first unused address (corresponding bit cleared in bitfield).
|
|
; For this address the initial node sends a "CLAIM_ADDRESS" packet, three times
|
|
; with about 30s between the packets.
|
|
; 5) if the to-be-claimed address is already in use by another node, that node will
|
|
; send a "DENY_ADDRESS" packet. This is the second line of defense against address
|
|
; collisions and should very rarely occur.
|
|
; 6) after no node denied the to-be-claimed address the initial node finally takes the
|
|
; address and sends a "HAVE_ADDRESS" packet.
|
|
; 7) this concludes the address assignemt protocol
|
|
;
|
|
; Protocol B: Shortened Address Assignment Protocol
|
|
; -------------------------------------------------
|
|
; This protocol is used upon boot when a node previously got an address assigned to it.
|
|
; In this case the address is probably not used by an other node so we can shorten the
|
|
; procedure.
|
|
;
|
|
; 1) a node sends the packet CLAIM_ADDRESS with the previously assigned address
|
|
; 2) is any other node objects to this node using that address (e.g. because the other
|
|
; node now uses that address) that node will send a DENY_ADDRESS packet.
|
|
; 3) if no DENY_ADDRESS has been received within 10s the address can be used.
|
|
; 4) Otherwise the node changes into protocol A (see above).
|
|
|
|
|
|
#ifdef MODULES_COM_WITH_ADDR_PROTO
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; defs
|
|
|
|
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; data
|
|
|
|
.dseg
|
|
|
|
|
|
cproAddressDataBegin:
|
|
cproAddrRangeBegin: .byte 1
|
|
cproAddrRangeEnd: .byte 1
|
|
cproUsedAddresses: .byte 16 ; one bit per address known to b in use
|
|
cproAddressDataEnd:
|
|
|
|
cproAddresModeTimer: .byte 2 ; timer must not be zeroed, this is done by the timer module!!
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; code
|
|
|
|
.cseg
|
|
|
|
|
|
|
|
CPRO_Address_Init:
|
|
ldi xh, HIGH(cproAddressDataBegin)
|
|
ldi xl, LOW(cproAddressDataBegin)
|
|
clr r16
|
|
ldi r17, (cproAddressDataEnd-cproAddressDataBegin)
|
|
rcall Utils_FillSram
|
|
|
|
; setup timer for address setup (after 10s)
|
|
ldi r16, CPRO_MODE_NOADDRESS
|
|
sts cproMode, r16
|
|
ldi r18, 10
|
|
ldi r19, 0
|
|
rcall cproAddressSetTimer
|
|
ret
|
|
|
|
|
|
|
|
CPRO_StartReclaimAddrProcedure:
|
|
ldi xl, LOW(EEPROM_OFFS_COMADDR)
|
|
ldi xh, HIGH(EEPROM_OFFS_COMADDR)
|
|
in r15, SREG
|
|
push r15
|
|
cli
|
|
rcall Utils_ReadEepromIncr ; (R16)
|
|
tst r16
|
|
breq CPRO_StartReclaimAddrProcedure_l1
|
|
cpi r16, 0xff
|
|
breq CPRO_StartReclaimAddrProcedure_l1
|
|
sts cproAddrRangeBegin, r16 ; currently claimed address
|
|
ldi r16, CPRO_MODE_SEND_RECLAIM_ADDR
|
|
sts cproMode, r16
|
|
rcall cproAddressSetTimer1s ; use singleshot timer, send after 1s
|
|
pop r15
|
|
out SREG, r15
|
|
sec
|
|
ret
|
|
CPRO_StartReclaimAddrProcedure_l1:
|
|
rcall CPRO_StartGetAddrProcedure
|
|
pop r15
|
|
out SREG, r15
|
|
ret
|
|
|
|
|
|
|
|
CPRO_StartGetAddrProcedure:
|
|
; reset bitfield of used addresses
|
|
ldi xh, HIGH(cproUsedAddresses)
|
|
ldi xl, LOW(cproUsedAddresses)
|
|
clr r16
|
|
ldi r17, 16
|
|
rcall Utils_FillSram
|
|
; preset range
|
|
ldi r16, 1
|
|
sts cproAddrRangeBegin, r16
|
|
ldi r16, 126
|
|
sts cproAddrRangeEnd, r16
|
|
; setup singleshot timer to later send "NEED_ADDRESS" packet
|
|
ldi r16, CPRO_MODE_SEND_NEED_ADDR
|
|
sts cproMode, r16
|
|
rcall cproAddressSetTimer1s
|
|
sec
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
; REGS: r18, r19
|
|
;
|
|
cproAddressSetTimer1s:
|
|
ldi r18, 1
|
|
ldi r19, 0
|
|
rjmp cproAddressSetTimer
|
|
|
|
|
|
|
|
; IN:
|
|
; - r18: timer value (low)
|
|
; - r19: timer value (high)
|
|
; REGS: none
|
|
|
|
cproAddressSetTimer:
|
|
push r15
|
|
in r15, SREG
|
|
cli
|
|
sts cproAddresModeTimer, r18
|
|
sts cproAddresModeTimer+1, r19
|
|
out SREG, r15
|
|
pop r15
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
cproGetFirstFreeAddr:
|
|
rjmp cproGetFreeAddr
|
|
|
|
|
|
|
|
cproGetNextFreeAddr:
|
|
lds r16, cproAddrRangeBegin
|
|
inc r16
|
|
sts cproAddrRangeBegin, r16
|
|
rjmp cproGetFreeAddr
|
|
|
|
|
|
|
|
cproGetFreeAddr:
|
|
lds r16, cproAddrRangeBegin
|
|
lds r17, cproAddrRangeEnd
|
|
cp r16, r17
|
|
brge cproGetFreeAddr_error
|
|
rcall cproFindFreeAddr
|
|
brcc cproGetFreeAddr_error
|
|
sec
|
|
ret
|
|
cproGetFreeAddr_error:
|
|
clc
|
|
ret
|
|
|
|
|
|
|
|
cproHandleAddrRange: ; not handled for now
|
|
; TODO
|
|
clc
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; cproFindFreeAddr
|
|
;
|
|
; find a free address in the bitfield cproUsedAddresses
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; OUT:
|
|
; - CFLAG: set if handled, cleared otherwise
|
|
; - R16: free address (if CFLAG set)
|
|
; USED: R16, R17, R18, R19, R20, R21 X, (R1, R2, Z)
|
|
|
|
cproFindFreeAddr:
|
|
ldi xl, LOW(cproUsedAddresses)
|
|
ldi xh, HIGH(cproUsedAddresses)
|
|
lds r16, cproAddrRangeBegin
|
|
dec r16
|
|
rcall cproGetPosAndMaskInBitField ; r1=bit pos, r2=mask (r1, r2, r17, Z)
|
|
clr r17
|
|
add xl, r1
|
|
adc xh, r17 ; X: pointer to byte
|
|
mov r17, r2 ; mask
|
|
lds r18, cproAddrRangeBegin
|
|
dec r18
|
|
lds r19, cproAddrRangeEnd
|
|
inc r19 ; to make comparision easier
|
|
ldi r20, 8
|
|
cproFindFreeAddr_byteLoop:
|
|
ld r16, X+
|
|
cpi r16, 0xff
|
|
breq cproFindFreeAddr_nextByte ; byte full, skip
|
|
cproFindFreeAddr_bitLoop:
|
|
mov r21, r16
|
|
and r21, r17
|
|
brne cproFindFreeAddr_nextBit
|
|
; found a clear bit, return
|
|
inc r18
|
|
sts cproAddrRangeBegin, r18
|
|
mov r16, r18
|
|
sec
|
|
ret
|
|
cproFindFreeAddr_nextBit:
|
|
inc r18 ; next address
|
|
lsl r17 ; shift mask to the left
|
|
brcc cproFindFreeAddr_bitLoop
|
|
dec r18 ; take back inc
|
|
cproFindFreeAddr_nextByte:
|
|
add r18, r20
|
|
andi r18, 0xf8 ; clear lower 3 bits
|
|
cp r18, r19 ; compare to end address+1
|
|
brcc cproFindFreeAddr_allFull
|
|
ldi r17, 0x01 ; mask
|
|
rjmp cproFindFreeAddr_byteLoop
|
|
cproFindFreeAddr_allFull:
|
|
clc
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; cproSetBitInBitfield
|
|
;
|
|
; IN:
|
|
; - R16 : bit number to set (0-127)
|
|
; OUT:
|
|
; - nothing
|
|
; USED: R16, R17, X (r1, r2, Z)
|
|
|
|
cproSetBitInBitfield:
|
|
; set bit corresponding to given address in bitfield of used addresses
|
|
rcall cproGetPosAndMaskInBitField ; get offset into R1, mask into R2 (r1, r2, r17, Z)
|
|
ldi xl, LOW(cproUsedAddresses)
|
|
ldi xh, HIGH(cproUsedAddresses)
|
|
clr r17
|
|
add xl, r1
|
|
adc xh, r17
|
|
ld r16, X
|
|
or r16, r2
|
|
st X, r16
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Get offset and mask for a given bit in a bitfield
|
|
; IN:
|
|
; - R16: bit to request position for
|
|
; OUT:
|
|
; - R1: offset into the bitfield to the byte containing the given bit
|
|
; - R2: mask for given id (apply to r1)
|
|
; USED REGISTERS: r1, r2, r17, Z
|
|
|
|
cproGetPosAndMaskInBitField:
|
|
mov r1, r16 ; divide by 8 to get the offset to the byte containing the given module id
|
|
lsr r1
|
|
lsr r1
|
|
lsr r1 ; r1=offset of the byte holding the given bit
|
|
mov r2, r16 ; get bit mask for bit position in table byte
|
|
ldi r17, 7 ; keep lower 3 bits
|
|
and r2, r17
|
|
ldi zh, HIGH(cproModuleBitNumToMaskMap*2)
|
|
ldi zl, LOW(cproModuleBitNumToMaskMap*2)
|
|
add zl, r2
|
|
brcc cproGetPosAndMaskInBitField_noOverflow
|
|
inc zh
|
|
cproGetPosAndMaskInBitField_noOverflow:
|
|
lpm r2, z ; r2=mask for bit in byte from bitfield
|
|
ret
|
|
|
|
|
|
cproModuleBitNumToMaskMap:
|
|
.db 1, 2, 4, 8, 16, 32, 64, 128
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Send a NEEDADDRESS packet.
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGS: R18 (R3, R4, R15, R16, R17, R20, R21, X, Y (R18, R19)
|
|
|
|
CPRO_SendNeedAddress:
|
|
ldi r18, CPRO_CMD_NEED_ADDRESS
|
|
rjmp cproSendAddressPacket
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Send a HAVE_ADDRESS packet.
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; - R19: address to send
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGS: R18 (R3, R4, R15, R16, R17, R18, R19, R20, R21, X, Y)
|
|
|
|
CPRO_SendHaveAddress:
|
|
ldi r18, CPRO_CMD_HAVE_ADDRESS
|
|
rjmp cproSendAddressPacket
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Send a CLAIM_ADDRESS packet.
|
|
;
|
|
; IN:
|
|
; - R19: claimed address
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGS: R18 (R3, R4, R15, R16, R17, R18, R19, R20, R21, X, Y)
|
|
|
|
CPRO_SendClaimAddress:
|
|
ldi r18, CPRO_CMD_CLAIM_ADDRESS
|
|
rjmp cproSendAddressPacket
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Send a DENY_ADDRESS packet.
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGS: R18, R19 (R3, R4, R15, R16, R17, R20, R21, X, Y)
|
|
|
|
CPRO_SendDenyAddress:
|
|
ldi r18, CPRO_CMD_DENY_ADDRESS
|
|
lds r19, com2Address
|
|
rjmp cproSendAddressPacket
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; cproSendAddressPacket
|
|
; Send a NEED/HAVE/CLAIM ADDRESS packet.
|
|
;
|
|
; IN:
|
|
; - R18: command (either CPRO_CMD_NEED_ADDRESS, CPRO_CMD_HAVE_ADDRESS or CPRO_CMD_CLAIM_ADDRESS)
|
|
; - R19: address to send (claim, have)
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGS: R16, R17, R20, R21, X, Y (R3, R4, R15, R16, R17, R18, R19, R21, X)
|
|
|
|
cproSendAddressPacket:
|
|
ldi xl, LOW(com2SendBuffer)
|
|
ldi xh, HIGH(com2SendBuffer)
|
|
mov r6, r19
|
|
|
|
ldi r16, 0xff
|
|
ldi r17, COM2_PAYLOAD_FLAGS_UID | (1<<COM2_PAYLOAD_FLAGS_SHIFT_NUM)
|
|
rcall COM2_BeginMsgWithVariablePayload ; (R3, R4, R16, R17, R18, R19, R20, R21, X)
|
|
st X+, r6 ; address
|
|
ldi xl, LOW(com2SendBuffer)
|
|
ldi xh, HIGH(com2SendBuffer)
|
|
rcall com2CalcAndAddChecksumByte ; (R16, R17, R18, R19, X)
|
|
rjmp COM2_SendPacket
|
|
|
|
|
|
|
|
.include "modules/comproto/addr1.asm"
|
|
.include "modules/comproto/addr2.asm"
|
|
|
|
|
|
#endif ; MODULES_COM_WITH_ADDR_PROTO
|
|
|
|
|