; *************************************************************************** ; 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) if 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, 100 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, 10 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<