avr: fully implemented router functionality in network and router app.

This commit is contained in:
Martin Preuss
2025-07-07 16:05:53 +02:00
parent 691ee3c71b
commit cbd498150f
21 changed files with 769 additions and 66 deletions

View File

@@ -62,6 +62,7 @@
m_flashend.h m_flashend.h
m_flashready.h m_flashready.h
m_flashresponse.h m_flashresponse.h
m_range.h
</headers> </headers>
@@ -89,6 +90,7 @@
m_flashend.c m_flashend.c
m_flashready.c m_flashready.c
m_flashresponse.c m_flashresponse.c
m_range.c
</sources> </sources>

View File

@@ -28,6 +28,7 @@
#include "aqhome/msg/node/m_flashend.h" #include "aqhome/msg/node/m_flashend.h"
#include "aqhome/msg/node/m_flashready.h" #include "aqhome/msg/node/m_flashready.h"
#include "aqhome/msg/node/m_flashresponse.h" #include "aqhome/msg/node/m_flashresponse.h"
#include "aqhome/msg/node/m_range.h"
#include <gwenhywfar/text.h> #include <gwenhywfar/text.h>
@@ -212,6 +213,7 @@ const char *AQH_NodeMessage_MsgTypeToChar(uint8_t i)
case AQH_MSG_TYPE_CLAIM_ADDRESS: return "ClaimAddress"; case AQH_MSG_TYPE_CLAIM_ADDRESS: return "ClaimAddress";
case AQH_MSG_TYPE_DENY_ADDRESS: return "DenyAddress"; case AQH_MSG_TYPE_DENY_ADDRESS: return "DenyAddress";
case AQH_MSG_TYPE_ADDRESS_RANGE: return "Range"; case AQH_MSG_TYPE_ADDRESS_RANGE: return "Range";
case AQH_MSG_TYPE_REENUM: return "Reenum";
case AQH_MSG_TYPE_FLASH_START: return "FlashStart"; case AQH_MSG_TYPE_FLASH_START: return "FlashStart";
case AQH_MSG_TYPE_FLASH_END: return "FlashEnd"; case AQH_MSG_TYPE_FLASH_END: return "FlashEnd";
@@ -246,6 +248,8 @@ void AQH_NodeMessage_DumpSpecificToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *d
case AQH_MSG_TYPE_HAVE_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_HAVE_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_CLAIM_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_CLAIM_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_DENY_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_DENY_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_ADDRESS_RANGE: AQH_RangeMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_REENUM: AQH_RangeMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_FLASH_START: AQH_FlashStartMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_FLASH_START: AQH_FlashStartMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_FLASH_END: AQH_FlashEndMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_FLASH_END: AQH_FlashEndMessage_DumpToBuffer(msg, dbuf, sText); break;
@@ -266,7 +270,6 @@ void AQH_NodeMessage_DumpSpecificToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *d
case AQH_MSG_TYPE_DEBUG: case AQH_MSG_TYPE_DEBUG:
case AQH_MSG_TYPE_TWIBUSMEMBER: case AQH_MSG_TYPE_TWIBUSMEMBER:
case AQH_MSG_TYPE_ADDRESS_RANGE:
default: AQH_NodeMessage_DumpToBuffer(msg, dbuf, sText); break; default: AQH_NodeMessage_DumpToBuffer(msg, dbuf, sText); break;
} }
} }

View File

@@ -40,6 +40,7 @@
#define AQH_MSG_TYPE_CLAIM_ADDRESS 62 #define AQH_MSG_TYPE_CLAIM_ADDRESS 62
#define AQH_MSG_TYPE_DENY_ADDRESS 63 #define AQH_MSG_TYPE_DENY_ADDRESS 63
#define AQH_MSG_TYPE_ADDRESS_RANGE 64 #define AQH_MSG_TYPE_ADDRESS_RANGE 64
#define AQH_MSG_TYPE_REENUM 65
#define AQH_MSG_TYPE_FLASH_START 70 #define AQH_MSG_TYPE_FLASH_START 70
#define AQH_MSG_TYPE_FLASH_END 71 #define AQH_MSG_TYPE_FLASH_END 71

61
aqhome/msg/node/m_range.c Normal file
View File

@@ -0,0 +1,61 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "aqhome/msg/node/m_range.h"
#include "aqhome/msg/node/m_node.h"
#include <gwenhywfar/debug.h>
#define AQH_MSG_OFFS_RANGE_UID 0 /* 4 bytes */
#define AQH_MSG_OFFS_RANGE_BEGIN 4 /* 1 bytes */
#define AQH_MSG_OFFS_RANGE_END 5 /* 1 bytes */
uint32_t AQH_RangeMessage_GetUid(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint32At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_RANGE_UID, 0);
}
uint8_t AQH_RangeMessage_GetRangeBegin(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_RANGE_BEGIN, 0);
}
uint8_t AQH_RangeMessage_GetRangeEnd(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_RANGE_END, 0);
}
void AQH_RangeMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText)
{
GWEN_Buffer_AppendArgs(dbuf,
"0x%02x->0x%02x: RANGE(%s) %s (uid=0x%08x, begin=0x%x, end=0x%x)\n",
AQH_NodeMessage_GetSourceAddress(msg),
AQH_NodeMessage_GetDestAddress(msg),
AQH_NodeMessage_MsgTypeToChar(AQH_NodeMessage_GetMsgType(msg)),
sText,
(unsigned int) AQH_RangeMessage_GetUid(msg),
AQH_RangeMessage_GetRangeBegin(msg),
AQH_RangeMessage_GetRangeEnd(msg));
}

30
aqhome/msg/node/m_range.h Normal file
View File

@@ -0,0 +1,30 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQH_M_RANGE_H
#define AQH_M_RANGE_H
#include <aqhome/api.h>
#include <aqhome/ipc2/message.h>
#include <gwenhywfar/debug.h>
/* This message is used for message types ClaimAddr, DenyAddr, HaveAddr */
AQHOME_API uint32_t AQH_RangeMessage_GetUid(const AQH_MESSAGE *msg);
AQHOME_API uint8_t AQH_RangeMessage_GetAddr(const AQH_MESSAGE *msg);
AQHOME_API uint8_t AQH_RangeMessage_GetRangeBegin(const AQH_MESSAGE *msg);
AQHOME_API uint8_t AQH_RangeMessage_GetRangeEnd(const AQH_MESSAGE *msg);
AQHOME_API void AQH_RangeMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText);
#endif

View File

@@ -117,6 +117,13 @@ AppNetwork_EveryDay:
; @param X pointer to received message ; @param X pointer to received message
AppNetwork_HandleMsg: AppNetwork_HandleMsg:
push xl
push xh
rcall AppNetwork_HandleMsg_savedX
pop xh
pop xl
rjmp AppNetwork_HandleMsg_end
AppNetwork_HandleMsg_savedX:
adiw xh:xl, NETMSG_OFFS_CMD adiw xh:xl, NETMSG_OFFS_CMD
ld r16, X ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD sbiw xh:xl, NETMSG_OFFS_CMD
@@ -124,14 +131,21 @@ AppNetwork_HandleMsg:
breq AppNetwork_HandleMsg_handleRebootMsg breq AppNetwork_HandleMsg_handleRebootMsg
cpi r16, NETMSG_CMD_PING cpi r16, NETMSG_CMD_PING
breq AppNetwork_HandleMsg_handlePingMsg breq AppNetwork_HandleMsg_handlePingMsg
cpi r16, NETMSG_CMD_REENUM
breq AppNetwork_HandleMsg_handleReenumMsg
cpi r16, NETMSG_CMD_NEED_ADDRESS cpi r16, NETMSG_CMD_NEED_ADDRESS
brcs AppNetwork_HandleMsg_clcRet ; lower than "HAVE_NEED" brcs AppNetwork_HandleMsg_clcRet ; lower than "HAVE_NEED"
cpi r16, NETMSG_CMD_ADDRESS_RANGE cpi r16, NETMSG_CMD_ADDRESS_RANGE
breq AppNetwork_HandleMsg_handleRangeMsg breq AppNetwork_HandleMsg_handleRangeMsg
brcc AppNetwork_HandleMsg_clcRet ; higher or equal to "ADDR_RANGE" brcc AppNetwork_HandleMsg_clcRet ; higher or equal to "ADDR_RANGE"
rjmp AppNetwork_HandleMsg_handleAddrMsg rjmp AppNetwork_HandleMsg_handleAddrMsg
AppNetwork_HandleMsg_handleReenumMsg:
rjmp appNetworkHandleReeunumRequest
AppNetwork_HandleMsg_handleRangeMsg: AppNetwork_HandleMsg_handleRangeMsg:
; TODO bigcall NETMSG_Range_Read
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r20
std Y+NET_IFACE_OFFS_RANGE_END, r21
std Y+NET_IFACE_OFFS_ADDRESS, r20
rjmp AppNetwork_HandleMsg_clcRet rjmp AppNetwork_HandleMsg_clcRet
AppNetwork_HandleMsg_handleAddrMsg: AppNetwork_HandleMsg_handleAddrMsg:
@@ -145,21 +159,13 @@ AppNetwork_HandleMsg_handleAddrMsg:
sub zh, r16 sub zh, r16
ijmp ijmp
AppNetwork_HandleMsg_handleRebootMsg: AppNetwork_HandleMsg_handleRebootMsg:
push xl rcall appNetworkHandleRebootRequest
push xh
rcall appNetworkHandleRebootRequest
pop xh
pop xl
ret ret
AppNetwork_HandleMsg_handlePingMsg: AppNetwork_HandleMsg_handlePingMsg:
push xl rjmp appNetworkHandlePingRequest
push xh
rcall appNetworkHandlePingRequest
pop xh
pop xl
ret
AppNetwork_HandleMsg_clcRet: AppNetwork_HandleMsg_clcRet:
clc clc
AppNetwork_HandleMsg_end:
ret ret
; @end ; @end
@@ -171,12 +177,8 @@ AppNetwork_HandleMsg_clcRet:
; @param X pointer to received message ; @param X pointer to received message
appNetworkHandleRebootRequest: appNetworkHandleRebootRequest:
ld r16, X ; dest addr
cpi r16, 0xff ; don't reboot for broadcast address
breq appNetworkHandleRebootRequest_end
rcall NETMSG_RebootRequestRead rcall NETMSG_RebootRequestRead
brcc appNetworkHandleRebootRequest_end brcc appNetworkHandleRebootRequest_end ; uid doesn't match
; reboot ; reboot
cli cli
bigjmp BOOTLOADER_ADDR bigjmp BOOTLOADER_ADDR
@@ -207,6 +209,20 @@ appNetworkHandlePingRequest_end:
appNetworkHandleReeunumRequest:
rcall NETMSG_Range_Read
ldi r16, APP_NETWORK_STATE_INITIALWAIT
std Y+NET_IFACE_OFFS_STATUS, r16
ldd r16, Y+NET_IFACE_OFFS_ADDRESS ; use current address as timer
std Y+NET_IFACE_OFFS_STATETIMER, r16
ldi r16, APP_NETWORK_ADDRESS_RANGE_BEGIN
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r20
std Y+NET_IFACE_OFFS_RANGE_END, r21
ret
; @end
appNetworkTimerTable: appNetworkTimerTable:
rjmp appNetworkHandleStateInitialWait rjmp appNetworkHandleStateInitialWait
rjmp appNetworkHandleStateNeedAddress rjmp appNetworkHandleStateNeedAddress
@@ -253,12 +269,8 @@ appNetworkHandleStateHaveAddress2:
std Y+NET_IFACE_OFFS_STATUS, r16 std Y+NET_IFACE_OFFS_STATUS, r16
ldi r16, APP_NETWORK_TIMER_100MS ldi r16, APP_NETWORK_TIMER_100MS
std Y+NET_IFACE_OFFS_STATETIMER, r16 std Y+NET_IFACE_OFFS_STATETIMER, r16
ldd r16, Y+NET_IFACE_OFFS_RANGE_BEGIN ; set interface address
ldd r17, Y+NET_IFACE_OFFS_ADDRESS
cp r16, r17
breq appNetworkHandleStateHaveAddress2_end
; store new address in IFACE and in EEPROM ; store new address in IFACE and in EEPROM
std Y+NET_IFACE_OFFS_ADDRESS, r16 ldd r16, Y+NET_IFACE_OFFS_ADDRESS
push r15 push r15
in r15, SREG in r15, SREG
cli cli
@@ -284,7 +296,7 @@ appNetworkHandleStateUp:
; @clobbers R16, R19 (R17, R18, R20, R21, X) ; @clobbers R16, R19 (R17, R18, R20, R21, X)
appNetworkSendMsgNextState: appNetworkSendMsgNextState:
ldd r19, Y+NET_IFACE_OFFS_RANGE_BEGIN ldd r19, Y+NET_IFACE_OFFS_ADDRESS
rcall appNetworkSendAddrMsg ; (R16, R17, R18, R19, R20, R21, X) rcall appNetworkSendAddrMsg ; (R16, R17, R18, R19, R20, R21, X)
brcc appNetworkSendMsgNextState_retry brcc appNetworkSendMsgNextState_retry
ldd r16, Y+NET_IFACE_OFFS_STATUS ldd r16, Y+NET_IFACE_OFFS_STATUS
@@ -349,9 +361,9 @@ appNetworkHandleMsgClaimAddr:
cp r19, r16 cp r19, r16
brne appNetworkHandleMsgClaimAddr_end ; not our address brne appNetworkHandleMsgClaimAddr_end ; not our address
ldd r16, Y+NET_IFACE_OFFS_STATUS ldd r16, Y+NET_IFACE_OFFS_STATUS
cpi r16, APP_NETWORK_STATE_UP ; up? cpi r16, APP_NETWORK_STATE_CLAIMADDRESS1
brne appNetworkHandleMsgClaimAddr_end ; nope, ignore brcs appNetworkHandleMsgClaimAddr_end ; nope, ignore
; network is up, someone claimed our address, deny it ; network is somewhat up, someone claimed our address, deny it
ldi r18, NETMSG_CMD_DENY_ADDRESS ; deny our addr ldi r18, NETMSG_CMD_DENY_ADDRESS ; deny our addr
ldd r19, Y+NET_IFACE_OFFS_ADDRESS ldd r19, Y+NET_IFACE_OFFS_ADDRESS
rjmp appNetworkSendAddrMsg rjmp appNetworkSendAddrMsg
@@ -373,12 +385,12 @@ appNetworkHandleMsgDenyAddr:
cpi r16, APP_NETWORK_STATE_UP cpi r16, APP_NETWORK_STATE_UP
breq appNetworkHandleMsgDenyAddr_end ; ignore (our network stack is up) breq appNetworkHandleMsgDenyAddr_end ; ignore (our network stack is up)
; still setting up address, check whether the last one is denied now ; still setting up address, check whether the last one is denied now
ldd r16, Y+NET_IFACE_OFFS_RANGE_BEGIN ldd r16, Y+NET_IFACE_OFFS_ADDRESS
cp r19, r16 ; our claimed address? cp r19, r16 ; our claimed address?
brne appNetworkHandleMsgDenyAddr_end ; nope, jump brne appNetworkHandleMsgDenyAddr_end ; nope, jump
; try next address (if any left) ; try next address (if any left)
ldd r17, Y+NET_IFACE_OFFS_RANGE_END ldd r17, Y+NET_IFACE_OFFS_RANGE_END
inc r16 ; RANGE_BEGIN+1 inc r16 ; next address
cp r17, r16 ; smaller than or equal to RANGE_END? cp r17, r16 ; smaller than or equal to RANGE_END?
brcc appNetworkHandleMsgDenyAddr_claimNext brcc appNetworkHandleMsgDenyAddr_claimNext
; out of addresses, start completely new after some waiting time ; out of addresses, start completely new after some waiting time
@@ -388,7 +400,7 @@ appNetworkHandleMsgDenyAddr:
rjmp appNetworkHandleMsgDenyAddr_end rjmp appNetworkHandleMsgDenyAddr_end
appNetworkHandleMsgDenyAddr_claimNext: appNetworkHandleMsgDenyAddr_claimNext:
; send CLAIM_ADDR for next address (new state: APP_NETWORK_STATE_NEEDADDRESS+1) ; send CLAIM_ADDR for next address (new state: APP_NETWORK_STATE_NEEDADDRESS+1)
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r16 std Y+NET_IFACE_OFFS_ADDRESS, r16
ldi r16, APP_NETWORK_STATE_NEEDADDRESS ldi r16, APP_NETWORK_STATE_NEEDADDRESS
std Y+NET_IFACE_OFFS_STATUS, r16 std Y+NET_IFACE_OFFS_STATUS, r16
ldi r18, NETMSG_CMD_CLAIM_ADDRESS ldi r18, NETMSG_CMD_CLAIM_ADDRESS
@@ -439,6 +451,7 @@ appNetworkResetState:
std Y+NET_IFACE_OFFS_STATETIMER, r16 std Y+NET_IFACE_OFFS_STATETIMER, r16
ldi r16, APP_NETWORK_ADDRESS_RANGE_BEGIN ldi r16, APP_NETWORK_ADDRESS_RANGE_BEGIN
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r16 std Y+NET_IFACE_OFFS_RANGE_BEGIN, r16
std Y+NET_IFACE_OFFS_ADDRESS, r16
rcall appNetworkGetAddressFromEeprom ; R16=addr (R15, X) rcall appNetworkGetAddressFromEeprom ; R16=addr (R15, X)
tst r16 tst r16
breq appNetworkResetState_setRangeEnd breq appNetworkResetState_setRangeEnd

View File

@@ -56,6 +56,8 @@ AppRouter_Init:
ldi r16, 0xef ldi r16, 0xef
sts appRouterRangeEnd, r16 sts appRouterRangeEnd, r16
rcall appRouterReadConfFromEeprom ; try to read config from EEPROM
; set interface number for NETDEV0 ; set interface number for NETDEV0
ldi r16, NETDEV0_IFACENUM ldi r16, NETDEV0_IFACENUM
sts netInterfaceData+NET_IFACE_OFFS_IFACENUM, r16 sts netInterfaceData+NET_IFACE_OFFS_IFACENUM, r16
@@ -97,35 +99,207 @@ AppRouter_Run:
; --------------------------------------------------------------------------- ; ---------------------------------------------------------------------------
; @routine AppRouter_HandleMsg @global ; @routine appRouterHandleMsgAnyDev @global
; ;
; @param X pointer to received message ; @param X pointer to received message
; @return CFLAG set if msg handled, cleared otherwise
; @clobbers any, !X
AppRouter_HandleMsg: appRouterHandleMsgAnyDev:
adiw xh:xl, NETMSG_OFFS_CMD ; maybe move ping/reboot handling to all/main.asm? push xl
push xh
rcall appRouterHandleMsgAnyDev_savedX
pop xh
pop xl
rjmp appRouterHandleMsgAnyDev_end
appRouterHandleMsgAnyDev_savedX:
adiw xh:xl, NETMSG_OFFS_CMD ; maybe move ping/reboot handling to all/main.asm?
ld r16, X ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD sbiw xh:xl, NETMSG_OFFS_CMD
cpi r16, NETMSG_CMD_REBOOT_REQUEST cpi r16, NETMSG_CMD_REBOOT_REQUEST
breq AppRouter_HandleMsg_handleRebootMsg breq appRouterHandleMsgAnyDev_handleRebootMsg
cpi r16, NETMSG_CMD_PING cpi r16, NETMSG_CMD_PING
breq AppRouter_HandleMsg_handlePingMsg breq appRouterHandleMsgAnyDev_handlePingMsg
rjmp AppRouter_HandleMsg_clcRet cpi r16, NETMSG_CMD_VALUE_SET
AppRouter_HandleMsg_handleRebootMsg: breq appRouterHandleMsgAnyDev_handleSetValue
push xl rjmp appRouterHandleMsgAnyDev_clcRet
push xh appRouterHandleMsgAnyDev_handleRebootMsg:
rcall appRouterHandleRebootRequest rcall appRouterHandleRebootRequest
pop xh
pop xl
ret ret
AppRouter_HandleMsg_handlePingMsg: appRouterHandleMsgAnyDev_handlePingMsg:
push xl rcall appRouterHandlePingRequest
push xh
rcall appRouterHandlePingRequest
pop xh
pop xl
ret ret
AppRouter_HandleMsg_clcRet: appRouterHandleMsgAnyDev_handleSetValue:
rcall NETMSG_ValueRead ; (none)
cpi r17, VALUE_ID_ROUTER_SETRANGE
breq appRouterHandleMsgAnyDev_handleSetRange
rjmp appRouterHandleMsgAnyDev_clcRet
appRouterHandleMsgAnyDev_handleSetRange:
sts netInterfaceData+NET_IFACE_OFFS_ADDRESS, r18 ; use first address for router itself
sts netInterfaceData2+NET_IFACE_OFFS_ADDRESS, r18 ; use same address for both interfaces to save on addresses
inc r18 ; start range after router
sts appRouterRangeBegin, r18
sts appRouterRangeEnd, r19
; send ACK
ldi r23, NETMSG_CMD_VALUE_SET_ACK
rcall Main_SendValueResponse ; (clobbers all, including Y)
; let subnodes re-eunumerate
ldi r18, NETMSG_CMD_REENUM
rcall appRouterSendRangeMsgToDev1 ; (R16, R17, R18, R19, R20, R21, X, Y)
rcall appRouterWriteConfToEeprom ; (r16, r17, X)
sec
ret
appRouterHandleMsgAnyDev_clcRet:
clc clc
appRouterHandleMsgAnyDev_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterHandleDev1Msg @global
;
; Handle messages from controlled subnet.
;
; @param X pointer to received message
; @return CFLAG set if msg handled, cleared otherwise
; @clobbers any, !X
appRouterHandleDev1Msg:
push xl
push xh
rcall appRouterHandleDev1Msg_savedX
pop xh
pop xl
rjmp appRouterHandleDev1Msg_end
appRouterHandleDev1Msg_savedX:
adiw xh:xl, NETMSG_OFFS_CMD ; maybe move ping/reboot handling to all/main.asm?
ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD
cpi r16, NETMSG_CMD_NEED_ADDRESS
breq appRouterHandleDev1Msg_handleNeedAddr
cpi r16, NETMSG_CMD_CLAIM_ADDRESS
breq appRouterHandleDev1Msg_handleClaimAddr
; add more here
rjmp appRouterHandleDev1Msg_clcRet
appRouterHandleDev1Msg_handleNeedAddr:
ldi r18, NETMSG_CMD_ADDRESS_RANGE
rcall appRouterSendRangeMsgToDev1 ; (R16, R17, R18, R19, R20, R21, X, Y)
sec
rjmp appRouterHandleDev1Msg_end
appRouterHandleDev1Msg_handleClaimAddr:
rcall NETMSG_Address_Read ; R18=cmd, R19=addr(R18, R19)
rcall appRouterIsR19InRange
brcs appRouterHandleDev1Msg_end
; is not in subnet range, deny
rcall appRouterSendDenyAddrR19ToDev1 ; (R16, R17, R18, R19, R20, R21, X, Y)
sec
rjmp appRouterHandleDev1Msg_end
appRouterHandleDev1Msg_clcRet:
clc
appRouterHandleDev1Msg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterIsR19InRange
;
; @param R19 address to check against range
; @clobbers R16
appRouterIsR19InRange:
lds r16, appRouterRangeBegin
cp r19, r16
brcs appRouterIsR19InRangeClcRet
lds r16, appRouterRangeEnd
cp r16, r19
brcs appRouterIsR19InRangeClcRet
sec
rjmp appRouterIsR19InRange_end
appRouterIsR19InRangeClcRet:
clc
appRouterIsR19InRange_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterSendDenyAddrR19ToDev1
;
; @param R19 address to send
; @clobbers R16, R17, R18, R19, R20, R21, X, Y
appRouterSendDenyAddrR19ToDev1:
ldi r18, NETMSG_CMD_DENY_ADDRESS ; deny addr
rjmp appRouterSendAddrMsgToDev1 ; (R16, R17, R18, R19, R20, R21, X, Y)
; @end
; ---------------------------------------------------------------------------
; @routine appRouterSendAddrMsgToDev1
;
; @param R18 command
; @param R19 address to send
; @clobbers R16 (R17, R18, R19, R20, R21, X, Y)
appRouterSendAddrMsgToDev1:
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc appRouterSendAddrMsgToDev1_end
push r16
adiw xh:xl, 1
bigcall NETMSG_Address_Write ; (R16, R17, R18, R19, R20, R21)
sbiw xh:xl, 1
pop r16
rcall appRouterSendMsgToDev1 ; (R16, R17, R18, X, Y)
appRouterSendAddrMsgToDev1_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterSendRangeMsgToDev1
;
; @param R18 msg code
; @clobbers (R16, R17, R18, R19, R20, R21, X, Y)
appRouterSendRangeMsgToDev1:
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc appRouterSendRangeMsgToDev1_end
push r16
lds r20, appRouterRangeBegin
lds r21, appRouterRangeEnd
adiw xh:xl, 1
bigcall NETMSG_Range_Write ; (R16, R17, R18, R19, R20, R21)
sbiw xh:xl, 1
pop r16
rcall appRouterSendMsgToDev1 ; (R16, R17, R18, X, Y)
appRouterSendRangeMsgToDev1_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterSendMsgToDev1
;
; @param R16 num of allocated buffer
; @param X msg to send (points to start of allocated buffer, e.g. buffer header)
; @clobbers Y (R16, R17, R18, X)
appRouterSendMsgToDev1:
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
bigcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
brcs appRouterSendMsgToDev1_end
bigcall NET_Buffer_ReleaseByNum ; (R16, X)
clc
appRouterSendMsgToDev1_end:
ret ret
; @end ; @end
@@ -134,13 +308,11 @@ AppRouter_HandleMsg_clcRet:
; --------------------------------------------------------------------------- ; ---------------------------------------------------------------------------
; @routine appRouterHandleRebootRequest ; @routine appRouterHandleRebootRequest
; ;
; Doesn't return if reboot msg is valid.
;
; @param X pointer to received message ; @param X pointer to received message
appRouterHandleRebootRequest: appRouterHandleRebootRequest:
ld r16, X ; dest addr
cpi r16, 0xff ; don't reboot for broadcast address
breq appRouterHandleRebootRequest_end
rcall NETMSG_RebootRequestRead rcall NETMSG_RebootRequestRead
brcc appRouterHandleRebootRequest_end brcc appRouterHandleRebootRequest_end
; reboot ; reboot
@@ -152,6 +324,11 @@ appRouterHandleRebootRequest_end:
; ---------------------------------------------------------------------------
; @routine appRouterHandlePingRequest
;
; @param X pointer to received message
appRouterHandlePingRequest: appRouterHandlePingRequest:
adiw xh:xl, NETMSG_OFFS_SRCADDR adiw xh:xl, NETMSG_OFFS_SRCADDR
ld r17, X ld r17, X
@@ -180,14 +357,14 @@ appRouterHandlePingRequest_end:
appRouterCheckRecvdMsg: appRouterCheckRecvdMsg:
rcall NET_PeekNextIncomingMsgNum ; check read queue (bufNum->r16) rcall NET_PeekNextIncomingMsgNum ; check read queue (bufNum->r16)
brcc appRouterCheckRecvdMsg_end ; no msg, jmp brcc appRouterCheckRecvdMsg_end ; no msg, jmp
rcall NET_Buffer_Locate ; (R17) rcall NET_Buffer_Locate ; (R17)
rcall appRouterHandleRouterMsgWithHdr ; filter out router msgs
brcs appRouterCheckRecvdMsg_removeMsg ; handled by router code, don't forward
; let system handle incoming messages ; let system handle incoming messages
push r16 adiw xh:xl, 1
adiw xh:xl, 1 rcall appRouterLetSysHandleMsg
rcall appRouterLetSysHandleMsg sbiw xh:xl, 1
sbiw xh:xl, 1
pop r16
; forward to other interface ; forward to other interface
ld r17, X ld r17, X
@@ -197,11 +374,43 @@ appRouterCheckRecvdMsg:
brcc appRouterCheckRecvdMsg_end ; could not add, jmp brcc appRouterCheckRecvdMsg_end ; could not add, jmp
rcall NET_GetNextIncomingMsgNum ; take off the queue rcall NET_GetNextIncomingMsgNum ; take off the queue
rjmp appRouterCheckRecvdMsg rjmp appRouterCheckRecvdMsg
appRouterCheckRecvdMsg_removeMsg:
rcall NET_GetNextIncomingMsgNum ; take off the queue
rcall NET_Buffer_ReleaseByNum ; (R16, X)
sec
appRouterCheckRecvdMsg_end: appRouterCheckRecvdMsg_end:
ret ret
; @end ; @end
; ---------------------------------------------------------------------------
; @routine appRouterHandleRouterMsgWithHdr
;
; @param X pointer to msg
; @return CFLAG set if msg handled, cleared otherwise
appRouterHandleRouterMsgWithHdr:
ld r17, X
andi r17, (1<<NET_IFACE_BUFFER_IFACENUM1_BIT) | (1<<NET_IFACE_BUFFER_IFACENUM0_BIT)
adiw xh:xl, 1
cpi r17, NETDEV1_IFACENUM
brne appRouterHandleRouterMsgWithHdr_any
rcall appRouterHandleDev1Msg ; handle messages from controlled subnet
brcs appRouterHandleRouterMsgWithHdr_msgHandled
appRouterHandleRouterMsgWithHdr_any:
rcall appRouterHandleMsgAnyDev ; handle any msg
brcs appRouterHandleRouterMsgWithHdr_msgHandled
sbiw xh:xl, 1 ; CFLAG cleared
rjmp appRouterHandleRouterMsgWithHdr_end
appRouterHandleRouterMsgWithHdr_msgHandled:
sbiw xh:xl, 1
sec
appRouterHandleRouterMsgWithHdr_end:
ret
; @end
; --------------------------------------------------------------------------- ; ---------------------------------------------------------------------------
; @routine appRouterLetSysHandleMsg ; @routine appRouterLetSysHandleMsg
@@ -276,5 +485,62 @@ appRouterAddMsgToInterface_end:
; ---------------------------------------------------------------------------
; @routine appRouterWriteConfToEeprom
;
; @clobbers R16, X (R17)
appRouterWriteConfToEeprom:
; write range begin
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
lds r16, appRouterRangeBegin
rcall Eeprom_WriteByteIfChanged ; (R17)
brcc appRouterWriteConfToEeprom_end
; write range end
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_END)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_END)
lds r16, appRouterRangeEnd
rcall Eeprom_WriteByteIfChanged ; (R17)
appRouterWriteConfToEeprom_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterReadConfFromEeprom
;
; @clobbers R16, X (R17)
appRouterReadConfFromEeprom:
; read range begin
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
rcall Eeprom_ReadByte
brcc appRouterReadConfFromEeprom_end
cpi r16, 0xff
breq appRouterReadConfFromEeprom_okay
cpi r16, 2 ; range should at least start at 2 to assign 1 to router
brcs appRouterReadConfFromEeprom_okay
sts appRouterRangeBegin, r16
dec r16
sts netInterfaceData+NET_IFACE_OFFS_ADDRESS, r16 ; use addr rangeBegin-1 for router itself
sts netInterfaceData2+NET_IFACE_OFFS_ADDRESS, r16 ; use same address for both interfaces to save on addresses
; read range end
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_END)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_END)
rcall Eeprom_ReadByte
brcc appRouterReadConfFromEeprom_end
cpi r16, 0xff
breq appRouterReadConfFromEeprom_okay
sts appRouterRangeEnd, r16
appRouterReadConfFromEeprom_okay:
sec
appRouterReadConfFromEeprom_end:
ret

View File

@@ -139,8 +139,19 @@ AppStats_OnEveryMinute_store:
ret ret
AppStats_OnEveryMinute_sendDevice: AppStats_OnEveryMinute_sendDevice:
rjmp AppNetwork_SendDevice push yl
push yh
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
rcall AppNetwork_SendDevice
#ifdef APP_STATS_NETDEV2
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
rcall AppNetwork_SendDevice
#endif
pop yh
pop yl
ret
AppStats_OnEveryMinute_sendPacketsIn: AppStats_OnEveryMinute_sendPacketsIn:
ldi r16, 0 ldi r16, 0
rjmp appStatsSendDeviceStat rjmp appStatsSendDeviceStat

View File

@@ -30,6 +30,8 @@
list_t.asm list_t.asm
tree.asm tree.asm
tree_t.asm tree_t.asm
eeprom-r.asm
eeprom-w.asm
</extradist> </extradist>
</gwbuild> </gwbuild>

86
avr/common/eeprom-r.asm Normal file
View File

@@ -0,0 +1,86 @@
; ***************************************************************************
; copyright : (C) 2025 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. *
; ***************************************************************************
#ifndef AQH_AVR_COMMON_EEPROM_R_H
#define AQH_AVR_COMMON_EEPROM_R_H
; ---------------------------------------------------------------------------
; @routine Eeprom_ReadByte
;
; Read a byte from EEPROM (see example in ATtiny24/44/84 manual p.19).
;
; @param X EEPROM Address to read from
; @return CFLAG set if address okay, cleared if out of range
; @return R16 byte read
; @return X EEPROM Address incremented
; @clobbers none
Eeprom_ReadByte:
rcall Eeprom_CheckAddr
brcs Eeprom_ReadByte_addrOk
ret
Eeprom_ReadByte_addrOk:
; call routine with IRQs disabled
push r15
inr r15, SREG
cli
rcall Eeprom_ReadByte_noirq
outr SREG, r15
pop r15
sec
ret
Eeprom_ReadByte_noirq:
; wait until EEPROM ready
Eeprom_ReadByte_waitLoop:
.ifdef EEPE
sbic EECR, EEPE ; wait for previous write to complete (if any)
.else
sbic EECR, EEWE ; wait for previous write to complete (if any)
.endif
rjmp Eeprom_ReadByte_waitLoop
; read from EEPROM
out EEARH, xh ; set EEPROM address
out EEARL, xl
sbi EECR, EERE ; start EEPROM read by writing EERE
in r16, EEDR ; read data from data register
ret
; @end
; ---------------------------------------------------------------------------
; @routine Eeprom_CheckAddr
;
;
; @param X EEPROM Address to validate
; @return CFLAG set if address okay, cleared if out of range
; @clobbers r16
Eeprom_CheckAddr:
mov r16, xl
subi r16, LOW(EEPROMEND)
mov r16, xh
sbci r16, HIGH(EEPROMEND)
brcc Eeprom_CheckAddr_ok
clc
rjmp Eeprom_CheckAddr_end
Eeprom_CheckAddr_ok:
sec
Eeprom_CheckAddr_end:
ret
; @end
#endif ; AQH_AVR_COMMON_EEPROM_R_H

100
avr/common/eeprom-w.asm Normal file
View File

@@ -0,0 +1,100 @@
; ***************************************************************************
; copyright : (C) 2025 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. *
; ***************************************************************************
#ifndef AQH_AVR_COMMON_EEPROM_W_H
#define AQH_AVR_COMMON_EEPROM_W_H
; ---------------------------------------------------------------------------
; Utils_WriteEepromIncr
;
; Write a byte to EEPROM (see example in ATtiny24/44/84 manual p.18).
;
; @param R16 byte to write
; @param X EEPROM Address to write to
; @return CFLAG set if data written, cleared on error
; @clobbers R17
Eeprom_WriteByte:
mov r17, r16
rcall Eeprom_CheckAddr ; (r16)
mov r16, r17
brcs Eeprom_WriteByte_addrOk
ret
Eeprom_WriteByte_addrOk:
; call routine with IRQs disabled
push r15
inr r15, SREG
cli
rcall Eeprom_WriteByte_noirq
outr SREG, r15
pop r15
sec
ret
Eeprom_WriteByte_noirq:
; wait for EEPROM to be ready
Eeprom_WriteByte_waitLoop:
.ifdef EEPE
sbic EECR, EEPE ; wait for previous write to complete (if any)
.else
sbic EECR, EEWE ; wait for previous write to complete (if any)
.endif
rjmp Eeprom_WriteByte_waitLoop
; write data
.ifdef EEPM1
ldi r17, (0<<EEPM1) | (0<<EEPM0) ; set programming mode
.endif
out EECR, r17
out EEARH, xh ; set EEPROM address
out EEARL, xl
out EEDR, r16 ; write data to data register
.ifdef EEMPE
sbi EECR, EEMPE ; write logical one to EEMPE
.else
sbi EECR, EEMWE ; write logical one to EEMWE
.endif
.ifdef EEPE
sbi EECR, EEPE ; start EEPROM write by setting EEPE
.else
sbi EECR, EEWE ; start EEPROM write by setting EEWE
.endif
ret
; @end
; ---------------------------------------------------------------------------
; @routine Eeprom_WriteByteIfChanged @global
;
; Only write EEPROM byte if changed from current value (save on write cycles)
;
; @param r16 byte to write
; @param X eeprom address to write to
; @clobbers r17
Eeprom_WriteByteIfChanged:
mov r17, r16
rcall Eeprom_ReadByte
brcc Eeprom_WriteByteIfChanged_end
cp r16, r17
sec
breq Eeprom_WriteByteIfChanged_end
mov r16, r17
rcall Eeprom_WriteByte ; (R17)
Eeprom_WriteByteIfChanged_end:
ret
; @end
#endif ; AQH_AVR_COMMON_EEPROM_W_H

View File

@@ -89,10 +89,6 @@ mainAppsOnPacketReceived:
bigcall AppNetwork_HandleMsg bigcall AppNetwork_HandleMsg
#endif #endif
#ifdef APPS_ROUTER
bigcall AppRouter_HandleMsg
#endif
#ifdef APPS_MA_LIGHT #ifdef APPS_MA_LIGHT
bigcall AppMotionLight_OnPacketReceived bigcall AppMotionLight_OnPacketReceived
#endif #endif

View File

@@ -57,6 +57,12 @@
.equ TLV_OFFS_LEN = 0
.equ TLV_OFFS_TYPE = 1
.equ TLV_OFFS_VALUE = 2
; --------------------------------------------------------------------------- ; ---------------------------------------------------------------------------
; EEPROM positions ; EEPROM positions
@@ -76,5 +82,18 @@
.equ EEPROM_OFFS_MAL_CONF_SRC2_VALUEID = 19 ; 1 byte .equ EEPROM_OFFS_MAL_CONF_SRC2_VALUEID = 19 ; 1 byte
.equ EEPROM_OFFS_MAL_CONF_RGBWVALUE = 20 ; 4 bytes .equ EEPROM_OFFS_MAL_CONF_RGBWVALUE = 20 ; 4 bytes
; next is 24 .equ EEPROM_OFFS_ROUTER_RANGE_BEGIN = 24 ; 1 byte
.equ EEPROM_OFFS_ROUTER_RANGE_END = 25 ; 1 byte
; next is 26
.equ EEPROM_OFFS_TLV = 32 ; begin TLV area here
.equ EEPROM_TLV_ROUTER_RANGE = 0x10 ; 2 bytes (range begin, range end)

View File

@@ -239,6 +239,8 @@
.include "modules/network/msg/reboot-d.asm" .include "modules/network/msg/reboot-d.asm"
.include "modules/network/msg/reboot-r.asm" .include "modules/network/msg/reboot-r.asm"
.include "modules/network/msg/pong-w.asm" .include "modules/network/msg/pong-w.asm"
.include "modules/network/msg/range-d.asm"
.include "modules/network/msg/range-r.asm"
#endif #endif
@@ -247,6 +249,11 @@
.include "modules/network/msg/reboot-d.asm" .include "modules/network/msg/reboot-d.asm"
.include "modules/network/msg/reboot-r.asm" .include "modules/network/msg/reboot-r.asm"
.include "modules/network/msg/pong-w.asm" .include "modules/network/msg/pong-w.asm"
.include "modules/network/msg/range-d.asm"
.include "modules/network/msg/range-r.asm"
.include "modules/network/msg/range-w.asm"
.include "common/eeprom-r.asm"
.include "common/eeprom-w.asm"
#endif #endif

View File

@@ -28,6 +28,7 @@
<value name="stats_missed_errors2" id="0xf1" type="sensor" dataType="uint16" denom="1" /> <value name="stats_missed_errors2" id="0xf1" type="sensor" dataType="uint16" denom="1" />
<value name="LEDTIMING" id="0x88" type="actor" dataType="uint16" /> <value name="LEDTIMING" id="0x88" type="actor" dataType="uint16" />
<value name="addressRange" id="0x89" type="actor" dataType="uint16" />
</values> </values>
</device> </device>

View File

@@ -89,6 +89,7 @@
.equ VALUE_ID_DS18B20_TEMP = 0x06 .equ VALUE_ID_DS18B20_TEMP = 0x06
.equ VALUE_ID_LEDSIMPLE_TIMING = 0x88 .equ VALUE_ID_LEDSIMPLE_TIMING = 0x88
.equ VALUE_ID_ROUTER_SETRANGE = 0x89
; *************************************************************************** ; ***************************************************************************

View File

@@ -13,6 +13,9 @@
device-w.asm device-w.asm
memstats-w.asm memstats-w.asm
pong-w.asm pong-w.asm
range-d.asm
range-r.asm
range-w.asm
reboot-d.asm reboot-d.asm
reboot-r.asm reboot-r.asm
recvstats-w.asm recvstats-w.asm

View File

@@ -26,6 +26,7 @@
.equ NETMSG_CMD_CLAIM_ADDRESS = 62 .equ NETMSG_CMD_CLAIM_ADDRESS = 62
.equ NETMSG_CMD_DENY_ADDRESS = 63 .equ NETMSG_CMD_DENY_ADDRESS = 63
.equ NETMSG_CMD_ADDRESS_RANGE = 64 .equ NETMSG_CMD_ADDRESS_RANGE = 64
.equ NETMSG_CMD_REENUM = 65
.equ NETMSG_CMD_FLASH_START = 70 .equ NETMSG_CMD_FLASH_START = 70
.equ NETMSG_CMD_FLASH_END = 71 .equ NETMSG_CMD_FLASH_END = 71

View File

@@ -0,0 +1,18 @@
; ***************************************************************************
; copyright : (C) 2025 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. *
; ***************************************************************************
.equ NETMSG_RANGE_OFFS_UID = 4
.equ NETMSG_RANGE_OFFS_FROM = 8
.equ NETMSG_RANGE_OFFS_TO = 9

View File

@@ -0,0 +1,38 @@
; ***************************************************************************
; copyright : (C) 2025 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. *
; ***************************************************************************
; ***************************************************************************
; code
.cseg
; ---------------------------------------------------------------------------
; @routine NETMSG_RangeRead @global
; Read a RANGE message.
;
; @param X buffer to read from
; @return R18 command
; @return R20 range begin
; @return R21 range end
; @clobbers none
NETMSG_Range_Read:
adiw xh:xl, NETMSG_OFFS_CMD
ld r18, X ; command
adiw xh:xl, NETMSG_RANGE_OFFS_FROM-NETMSG_OFFS_CMD ; skip src addr and uid
ld r20, X+ ; range from
ld r21, X ; range to
sbiw xh:xl, NETMSG_RANGE_OFFS_TO ; back to msg begin
ret
; @end

View File

@@ -0,0 +1,44 @@
; ***************************************************************************
; copyright : (C) 2025 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. *
; ***************************************************************************
; ---------------------------------------------------------------------------
; @routine NETMSG_Range_Write
;
; @param Y pointer to device to write msg for
; @param X pointer to buffer to write to
; @param R18 command
; @param R20 range begin
; @param R21 range end
; @clobbers R16, R18 (R17, R19, R20, R21, Z)
NETMSG_Range_Write:
ldi r16, 0xff
st X+, r16 ; dest address
ldi r16, 8 ; msg code+src address+6 payload bytes
st X+, r16 ; msg len
st X+, r18 ; msg code
ldd r16, Y+NET_IFACE_OFFS_ADDRESS
st X+, r16 ; src address
push r20
push r21
rcall NETMSG_Common_AddUidToBuffer ; (R16, R18, R19, R20, R21)
pop r21
pop r20
st X+, r20 ; range begin
st X+, r21 ; range end
sbiw xh:xl, 10 ; go back to beginning of message (1 byte dst addr, 1 byte length, 8 bytes payload)
rcall NETMSG_CalcAndAddChecksumByte ; (R16, R17, R18, R19, R20, X)
sbiw xh:xl, 11 ; go back to beginning of message (1 byte dst addr, 1 byte length, 8 bytes payload, 1 byte crc)
ret
; @end