; ***************************************************************************
; 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.   *
; ***************************************************************************



; ***************************************************************************
; defines

; ---------------------------------------------------------------------------
; network interfaces

.equ NETDEV0_IFACENUM = 1
.equ NETDEV1_IFACENUM = 2



; ***************************************************************************
; data

.dseg

appRouterDataBegin:
  appRouterRangeBegin: .byte 1
  appRouterRangeEnd:   .byte 1
appRouterDataEnd:




; ***************************************************************************
; code

.cseg

; ---------------------------------------------------------------------------
; @routine AppRouter_Init @global

AppRouter_Init:
  ldi xh, HIGH(appRouterDataBegin)
  ldi xl, LOW(appRouterDataBegin)
  clr r16
  ldi r17, (appRouterDataEnd-appRouterDataBegin)
  rcall Utils_FillSram

#ifndef APP_ROUTER_NO_ADDR_MGR
  ldi r16, 0xe0                                        ; default range from 0xe0-0xef (will be changed later)
  sts netInterfaceData+NET_IFACE_OFFS_ADDRESS, r16     ; use first address for router itself
  sts netInterfaceData2+NET_IFACE_OFFS_ADDRESS, r16    ; use same address for both interfaces to save on addresses
  inc r16
  sts appRouterRangeBegin, r16                         ; range from router+1
  ldi r16, 0xef
  sts appRouterRangeEnd, r16

  rcall appRouterReadConfFromEeprom                    ; try to read config from EEPROM
#endif

  ; set interface number for NETDEV0
  ldi r16, NETDEV0_IFACENUM
  sts netInterfaceData+NET_IFACE_OFFS_IFACENUM, r16
  ; set interface number for NETDEV1
  ldi r16, NETDEV1_IFACENUM
  sts netInterfaceData2+NET_IFACE_OFFS_IFACENUM, r16
  
#ifndef APP_ROUTER_NO_ADDR_MGR
  ldi r18, NETMSG_CMD_ADDRESS_RANGE
  rcall appRouterSendRangeMsgToDev1
#endif

  ret
; @end



; ---------------------------------------------------------------------------
; @routine AppRouter_EveryDay @global
;
; @clobbers R16, R17, X

AppRouter_EveryDay:
  ldi yl, LOW(netInterfaceData)
  ldi yh, HIGH(netInterfaceData)
  bigcall NET_Interface_ResetStats ; (R16, R17, X)

  ldi yl, LOW(netInterfaceData2)
  ldi yh, HIGH(netInterfaceData2)
  bigcall NET_Interface_ResetStats ; (R16, R17, X)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine AppRouter_Run @global
;
; Read messages from either interface and forward to the other one.

AppRouter_Run:
  rjmp appRouterCheckRecvdMsg
; @end



; ---------------------------------------------------------------------------
; @routine appRouterHandleMsgAnyDev @global
;
; @param X pointer to received message
; @return CFLAG set if msg handled, cleared otherwise
; @clobbers any, !X

appRouterHandleMsgAnyDev:
  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
  sbiw xh:xl, NETMSG_OFFS_CMD
  cpi r16, NETMSG_CMD_REBOOT_REQUEST
  breq appRouterHandleMsgAnyDev_handleRebootMsg
  cpi r16, NETMSG_CMD_PING
  breq appRouterHandleMsgAnyDev_handlePingMsg
  cpi r16, NETMSG_CMD_VALUE_SET
  breq appRouterHandleMsgAnyDev_handleSetValue
  rjmp appRouterHandleMsgAnyDev_clcRet
appRouterHandleMsgAnyDev_handleRebootMsg:
  rcall appRouterHandleRebootRequest
  ret
appRouterHandleMsgAnyDev_handlePingMsg:
  rcall appRouterHandlePingRequest
  clc
  ret
appRouterHandleMsgAnyDev_handleSetValue:
  rcall NETMSG_ValueRead                            ; (none)
  cpi r17, VALUE_ID_ROUTER_SETRANGE
  breq appRouterHandleMsgAnyDev_handleSetRange
  rjmp appRouterHandleMsgAnyDev_clcRet
appRouterHandleMsgAnyDev_handleSetRange:
#ifndef APP_ROUTER_NO_ADDR_MGR
  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)
#endif
  sec
  ret
appRouterHandleMsgAnyDev_clcRet:
  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:
#ifndef APP_ROUTER_NO_ADDR_MGR
  push xl
    push xh
      rcall appRouterHandleDev1Msg_savedX
    pop xh
  pop xl
  rjmp appRouterHandleDev1Msg_end
appRouterHandleDev1Msg_savedX:
  adiw xh:xl, NETMSG_OFFS_CMD
  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
  ldi r16, 0
  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
#endif
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, R24, R25, X, Y)
appRouterSendAddrMsgToDev1_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appRouterSendRangeMsgToDev1
;
; @param R18 msg code
; @return CFLAG set if message enqueued, cleared on error
; @clobbers (R16, R17, R18, R19, R20, R21, R24, R25, 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, R24, R25, 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)
; @return CFLAG set if message enqueued, cleared on error
; @clobbers Y (R16, R17, R18, R24, R25, 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)
  ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
  rcall NET_Interface_IncCounter16        ; (R24, R25)
  clc
appRouterSendMsgToDev1_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appRouterHandleRebootRequest
;
; Doesn't return if reboot msg is valid.
;
; @param X pointer to received message

appRouterHandleRebootRequest:
  rcall NETMSG_RebootRequestRead
  brcc appRouterHandleRebootRequest_end
  ; reboot
  cli
  bigjmp BOOTLOADER_ADDR
appRouterHandleRebootRequest_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appRouterHandlePingRequest
;
; @param X pointer to received message

appRouterHandlePingRequest:
  ld r17, X
  lds r16, (netInterfaceData+NET_IFACE_OFFS_ADDRESS)
  cp r16, r17
  breq appRouterHandlePingRequest_forMe
  cpi r17, 0xff
  breq appRouterHandlePingRequest_forMe
  clc
  rjmp appRouterHandlePingRequest_end
appRouterHandlePingRequest_forMe:
  adiw xh:xl, NETMSG_OFFS_SRCADDR
  ld r17, X
  sbiw xh:xl, NETMSG_OFFS_SRCADDR
  push r17
    bigcall NET_Buffer_Alloc               ; (R16, R17, X)
  pop r17
  brcc appRouterHandlePingRequest_end      ; jmp on error
  push r16                                 ; buffer num
    mov r16, r17                           ; DEST addr
    adiw xh:xl, 1
    ldi yl, LOW(netInterfaceData)
    ldi yh, HIGH(netInterfaceData)
    bigcall NETMSG_Pong_Write              ; (R16, R17, R18, R19, R20, X)
    sbiw xh:xl, 1
  pop r16                                  ; buffer num
  bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
appRouterHandlePingRequest_end:
  ret



; ---------------------------------------------------------------------------
; @routine appRouterCheckRecvdMsg
;
; Read messages from either interface and forward to the other one.
;
; @return CFLAG set if something done, cleared otherwise

appRouterCheckRecvdMsg:
  rcall NET_PeekNextIncomingMsgNum          ; check read queue (bufNum->r16)
  brcc appRouterCheckRecvdMsg_end           ; no msg, jmp
  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
  adiw xh:xl, 1
  rcall appRouterLetSysHandleMsg
  sbiw xh:xl, 1

  ; forward to other interface
  ld r17, X
  andi r17, (1<<NET_IFACE_BUFFER_IFACENUM1_BIT) | (1<<NET_IFACE_BUFFER_IFACENUM0_BIT)
  rcall appRouterReverseInterfaceNum                 ; (R16, R17)
  rcall appRouterAddMsgToInterface
  brcc appRouterCheckRecvdMsg_end                    ; could not add, jmp
  rcall NET_GetNextIncomingMsgNum                    ; take off the queue
  sec
  ret
appRouterCheckRecvdMsg_removeMsg:
  rcall NET_GetNextIncomingMsgNum                    ; take off the queue
  rcall NET_Buffer_ReleaseByNum                      ; (R16, X)
  sec
appRouterCheckRecvdMsg_end:
  ret
; @end


; ---------------------------------------------------------------------------
; @routine appRouterHandleRouterMsgWithHdr
;
; @param X pointer to msg
; @return CFLAG set if msg handled, cleared otherwise

appRouterHandleRouterMsgWithHdr:
  ld r17, X
  adiw xh:xl, 1
  andi r17, (1<<NET_IFACE_BUFFER_IFACENUM1_BIT) | (1<<NET_IFACE_BUFFER_IFACENUM0_BIT)
  cpi r17, NETDEV1_IFACENUM
  brne appRouterHandleRouterMsgWithHdr_any
#ifndef APP_ROUTER_NO_ADDR_MGR
  rcall appRouterHandleDev1Msg                       ; handle messages from controlled subnet
  brcs appRouterHandleRouterMsgWithHdr_msgHandled
#endif
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
;
; @param X pointer to msg to handle (point behind the buffer header!)
; @clobbers any, !X

appRouterLetSysHandleMsg:
  ld r16, X
  cpi r16, 0xff
  breq appRouterLetSysHandleMsg_forMe
  lds r17, netInterfaceData+NET_IFACE_OFFS_ADDRESS
  cp r16, r17
  brne appRouterLetSysHandleMsg_end
appRouterLetSysHandleMsg_forMe:
  push xl
    push xh
      rcall onMessageReceived
    pop xh
  pop xl
  push xl
    push xh
      rcall modulesOnPacketReceived
    pop xh
  pop xl
  push xl
    push xh
      rcall appsOnPacketReceived
    pop xh
  pop xl
appRouterLetSysHandleMsg_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appRouterReverseInterfaceNum
;
; @param r17 buffer num
; @return r17 reversed interface number
; @clobbers r16, r17

appRouterReverseInterfaceNum:
  ldi r16, (1<<NET_IFACE_BUFFER_IFACENUM1_BIT) | (1<<NET_IFACE_BUFFER_IFACENUM0_BIT)
  eor r17, r16
  and r17, r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appRouterAddMsgToInterface
; @param r16 buffer num
; @param r17 interface num

appRouterAddMsgToInterface:
  cpi r17, NETDEV0_IFACENUM
  brne appRouterAddMsgToInterface_notNetDev0
  ldi yl, LOW(netInterfaceData)
  ldi yh, HIGH(netInterfaceData)
  rjmp NET_Interface_AddOutgoingMsgNum     ; try to add msg to interface
appRouterAddMsgToInterface_notNetDev0:
  ldi yl, LOW(netInterfaceData2)
  ldi yh, HIGH(netInterfaceData2)
  rjmp NET_Interface_AddOutgoingMsgNum     ; try to add msg to interface
appRouterAddMsgToInterface_end:
  clc
  ret
; @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                ; not set, jmp
  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
; @end



