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




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

.dseg

appHubDataBegin:
  appHubRangeBegin: .byte 1
  appHubRangeEnd:   .byte 1
appHubDataEnd:




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

.cseg

; ---------------------------------------------------------------------------
; @routine AppHub_Init @global

AppHub_Init:
  ldi xh, HIGH(appHubDataBegin)
  ldi xl, LOW(appHubDataBegin)
  clr r16
  ldi r17, (appHubDataEnd-appHubDataBegin)
  rcall Utils_FillSram

  ; set device address and interface number in all interfaces
  ldi r16, 0xf0                         ; hub address
  ldi r17, 1                            ; first interface number
  rcall appHubAllSetAddrIfaceNumAndRange  ; (R17, R19, R20, Y)
  
  ; TODO: read ranges from EEPROM
  ; TODO: send range msg to all interfaces
  ret
; @end



; ---------------------------------------------------------------------------
; @routine AppHub_Run @global
;
; Read messages from any interface, handle them and probably forward to the other
; interfaces.
; @return CFLAG set if something done, cleared otherwise
; @clobbers all

AppHub_Run:
  rjmp appHubCheckRecvdMsg
; @end



; ---------------------------------------------------------------------------
; @routine appHubGetDeviceByIfaceNum
;
; @param r16 interface number
; @clobbers r17, r19, r20, Y

appHubGetDeviceByIfaceNum:
  ldi r19, COM_PORTS
  ldi yl, LOW(com2w0_iface)             ; first interface
  ldi yh, HIGH(com2w0_iface)
appHubGetDeviceByIfaceNum_loop:
    ldd r17, Y+NET_IFACE_OFFS_IFACENUM
    cp r16, r17
    sec                                 ; mark "found"
    breq appHubGetDeviceByIfaceNum_ret
    ldi r20, COM2W_IFACE_SIZE
    add yl, r20
    adc yh, r20
    sub yh, r20
    dec r19
  brne appHubGetDeviceByIfaceNum_loop
  clc                                   ; mark "not found"
appHubGetDeviceByIfaceNum_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubAllSetAddrIfaceNumAndRange
;
; @clobbers R17, R18, R19, R20, Y

appHubAllSetAddrIfaceNumAndRange:
  ldi r19, COM_PORTS
  ldi yl, LOW(com2w0_iface)             ; first interface
  ldi yh, HIGH(com2w0_iface)
  ldi r18, 0x10
appHubAllSetAddrIfaceNumAndRange_loop:
    ; set address
    std Y+NET_IFACE_OFFS_ADDRESS, r16
    ; set interface number
    std Y+NET_IFACE_OFFS_IFACENUM, r17
    inc r17
    ; set default range (step of 16)
    std Y+NET_IFACE_OFFS_RANGE_BEGIN, r18
    subi r18, -15
    std Y+NET_IFACE_OFFS_RANGE_END, r18
    inc r18
    ; next interface
    ldi r20, COM2W_IFACE_SIZE
    add yl, r20
    adc yh, r20
    sub yh, r20
    dec r19
  brne appHubAllSetAddrIfaceNumAndRange_loop
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubSendRangeMsg
;
; @param R18 msg code
; @param Y pointer to interface data
; @return CFLAG set if message enqueued, cleared on error
; @clobbers (R16, R17, R18, R19, R20, R21, R24, R25, X)

appHubSendRangeMsg:
  bigcall NET_Buffer_Alloc                ; (R16, R17, X)
  brcc appHubSendRangeMsg_end
  push r16
    ldd r20, Y+NET_IFACE_OFFS_RANGE_BEGIN
    ldd r21, Y+NET_IFACE_OFFS_RANGE_END
    adiw xh:xl, 1
    bigcall NETMSG_Range_Write            ; (R16, R17, R18, R19, R20, R21)
    sbiw xh:xl, 1
  pop r16
  rcall appHubSendMsg                     ; (R16, R17, R18, R24, R25, X)
appHubSendRangeMsg_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubSendDenyAddrR19
;
; @param R19 address to send
; @clobbers R16, R17, R18, R19, R20, R21, X, Y

appHubSendDenyAddrR19:
  ldi r18, NETMSG_CMD_DENY_ADDRESS      ; deny addr
  rjmp appHubSendAddrMsg                ; (R16, R17, R18, R19, R20, R21, X, Y)
; @end



; ---------------------------------------------------------------------------
; @routine appHubSendAddrMsg
;
; @param R18 command
; @param R19 address to send
; @clobbers R16 (R17, R18, R19, R20, R21, X, Y)

appHubSendAddrMsg:
  bigcall NET_Buffer_Alloc       ; (R16, R17, X)
  brcc appHubSendAddrMsg_end
  push r16
    adiw xh:xl, 1
    bigcall NETMSG_Address_Write ; (R16, R17, R18, R19, R20, R21)
    sbiw xh:xl, 1
  pop r16
  rcall appHubSendMsg            ; (R16, R17, R18, R24, R25, X)
appHubSendAddrMsg_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubSendValueResponse
;
; @param  R17     value id
; @param  R19:R18 value
; @param  R21:R20 denom (e.g. 100, meaning value must be divided by 100)
; @param  R23     command
; @param  R25:R24 ref msg id
; @return CFLAG on success, cleared on error
; @clobbers r22 (r16, r17, r18, r19, r20, r21, r23, r24, r25, X)

appHubSendValueResponse:
  push r17
    rcall NET_Buffer_Alloc               ; (R16, R17, X)
  pop r17
  brcc appHubSendValueResponse_end       ; jmp on error
  push r16                               ; buffer num
    ldi r16, 0xff                        ; DEST addr
    clr r22                              ; value type
    adiw xh:xl, 1
    rcall NETMSG_ValueWriteResponse      ; (R16, R17, R18, R19, R20, R21, R23, R24, R25)
    sbiw xh:xl, 1
  pop r16                                ; buffer num
  rcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
appHubSendValueResponse_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubSendMsg
;
; @param R16 num of allocated buffer
; @param Y pointer to interface data
; @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 R16 (R17, R18, R24, R25, X)

appHubSendMsg:
  bigcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
  brcs appHubSendMsg_end
  bigcall NET_Buffer_ReleaseByNum         ; (R16, X)
  ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
  rcall NET_Interface_IncCounter16        ; (R24, R25)
  clc
appHubSendMsg_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubAllResetStats
;
; @clobbers r19, r20, Y (r16, r17, X)

appHubAllResetStats:
  ldi r19, COM_PORTS
  ldi yl, LOW(com2w0_iface)             ; first interface
  ldi yh, HIGH(com2w0_iface)
appHubAllResetStats_loop:
    bigcall NET_Interface_ResetStats ; (R16, R17, X)

    ldi r20, COM2W_IFACE_SIZE
    add yl, r20
    adc yh, r20
    sub yh, r20
    dec r19
  brne appHubAllResetStats_loop
  ret
; @end



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

AppHub_EveryDay:
  rcall appHubAllResetStats
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubSendMsgToAllOthers
;
; @param R16 buffer number
; @param X buffer pointer
; @clobbers r17, r19, r20, r24, r25 (r16, r18, X)

appHubSendMsgToAllOthers:
  ld r24, X
  andi r24, 0x0f                              ; get sender interface num
  ldi r19, COM_PORTS                          ; number of ports
  ldi yl, LOW(com2w0_iface)                   ; first interface
  ldi yh, HIGH(com2w0_iface)
appHubSendMsgToAllOthers_loop:
    ldd r25, Y+NET_IFACE_OFFS_IFACENUM
    cp r24, r25                               ; same interface?
    breq appHubSendMsgToAllOthers_next
    ; current iface is not source, send and inc ref counter
    bigcall NET_Interface_AddOutgoingMsgNum   ; (R17, R18, X)
    brcs appHubSendMsgToAllOthers_added
    ; inc error counter
    push r24
      mov r17, r16                            ; save r16
      ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
      bigcall NET_Interface_IncCounter16      ; (R24, R25)
      mov r16, r17                            ; restore r16
    pop r24
    rjmp appHubSendMsgToAllOthers_next
appHubSendMsgToAllOthers_added:
    mov r17, r16                              ; save r16
    bigcall NET_Buffer_IncRef                 ; (r16)
    mov r16, r17                              ; restore r16
appHubSendMsgToAllOthers_next:
    ldi r20, COM2W_IFACE_SIZE
    add yl, r20
    adc yh, r20
    sub yh, r20
    dec r19
  brne appHubSendMsgToAllOthers_loop
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubHandleMsgLocally
;
; @param X pointer to received buffer (points to header byte)
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !X

appHubHandleMsgLocally:
  push xl
    push xh
      rcall appHubHandleMsgLocally_savedX
    pop xh
  pop xl
  rjmp appHubHandleMsgLocally_ret
appHubHandleMsgLocally_savedX:
  ; get message type
  adiw xh:xl, NETMSG_OFFS_CMD+1    ; account for header byte
  ld r16, X
  sbiw xh:xl, NETMSG_OFFS_CMD+1
  cpi r16, NETMSG_CMD_PING
  breq appHubHandleMsgLocally_handlePingMsg
  cpi r16, NETMSG_CMD_REBOOT_REQUEST
  breq appHubHandleMsgLocally_handleRebootMsg
  cpi r16, NETMSG_CMD_VALUE_SET
  breq appHubHandleMsgLocally_handleSetValue
  rjmp appHubHandleMsgLocally_ret
appHubHandleMsgLocally_handlePingMsg:
  rjmp appHubHandlePingMsg
appHubHandleMsgLocally_handleRebootMsg:
  rjmp appHubHandleRebootMsg
appHubHandleMsgLocally_handleSetValue:
  rjmp appHubHandleSetValueMsg
appHubHandleMsgLocally_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubHandlePingMsg
;
; @param X pointer to received message
; @clobbers any, !X, !Y

appHubHandlePingMsg:
  adiw xh:xl, NETMSG_OFFS_DESTADDR+1
  ld r17, X
  sbiw xh:xl, NETMSG_OFFS_DESTADDR+1
  ldd r16, Y+NET_IFACE_OFFS_ADDRESS
  cp r16, r17
  breq appHubHandlePingMsg_forMe
  cpi r17, 0xff
  breq appHubHandlePingMsg_forMe
  clc
  rjmp appHubHandlePingMsg_end
appHubHandlePingMsg_forMe:
  adiw xh:xl, NETMSG_OFFS_SRCADDR+1
  ld r17, X
  sbiw xh:xl, NETMSG_OFFS_SRCADDR+1
  push r17
    bigcall NET_Buffer_Alloc               ; (R16, R17, X)
  pop r17
  brcc appHubHandlePingMsg_end             ; jmp on error
  push r16                                 ; buffer num
    mov r16, r17                           ; DEST addr
    adiw xh:xl, 1
    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)
appHubHandlePingMsg_end:
  ret
; @end



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

appHubHandleRebootMsg:
  rcall NETMSG_RebootRequestRead
  brcc appHubHandleRebootMsg_end
  ; reboot
  cli
  bigjmp BOOTLOADER_ADDR
appHubHandleRebootMsg_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubHandleSetValueMsg
;
; @param X pointer to received message
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !Y

appHubHandleSetValueMsg:
  adiw xh:xl, NETMSG_OFFS_DESTADDR+1
  ld r17, X
  sbiw xh:xl, NETMSG_OFFS_DESTADDR+1
  ldd r16, Y+NET_IFACE_OFFS_ADDRESS
  cp r16, r17
  breq appHubHandleSetValueMsg_forMe
  cpi r17, 0xff
  breq appHubHandleSetValueMsg_forMe
  rjmp appHubHandleSetValueMsg_ret
appHubHandleSetValueMsg_forMe:
  rcall NETMSG_ValueRead                            ; (none)
  cpi r17, VALUE_ID_HUB_SETRANGE1
  brcs appHubHandleSetValueMsg_ret
  cpi r17, VALUE_ID_HUB_SETRANGE8+1
  brcc appHubHandleSetValueMsg_ret
appHubHandleSetValueMsg_setRange:
  push yl
    push yh
      subi r17, (VALUE_ID_HUB_SETRANGE1-1)
      push r18
        push r19
          ; send ACK back the same interface the request came from
          push r17                                       ; interface number
            ldi r23, NETMSG_CMD_VALUE_SET_ACK
            rcall appHubSendValueResponse                ; r22 (r16, r17, r18, r19, r20, r21, r23, r24, r25, X)
          pop r16                                        ; pop interface number to r16 (from r17)
          rcall appHubGetDeviceByIfaceNum                ; Y=interface to modify (r17, r19, r20)
        pop r19
      pop r18
      ; modify interface
      std Y+NET_IFACE_OFFS_RANGE_BEGIN, r18
      std Y+NET_IFACE_OFFS_RANGE_END, r19   
      ; TODO: store new config
      
      ; let subnodes of modified interface re-eunumerate
      ldi r18, NETMSG_CMD_REENUM
      rcall appHubSendRangeMsg                           ; (R16, R17, R18, R19, R20, R21, X)
    pop yh
  pop yl  

appHubHandleSetValueMsg_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubHandleRoutingMsg
;
; @param X pointer to received buffer (points to header byte)
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !X

appHubHandleRoutingMsg:
  push xl
    push xh
      rcall appHubHandleRoutingMsg_savedX
    pop xh
  pop xl
  rjmp appHubHandleRoutingMsg_ret
appHubHandleRoutingMsg_savedX:
  ; get message type
  adiw xh:xl, NETMSG_OFFS_CMD+1    ; account for header byte
  ld r16, X
  sbiw xh:xl, NETMSG_OFFS_CMD+1
  cpi r16, NETMSG_CMD_NEED_ADDRESS
  breq appHubHandleRoutingMsg_handleNeedAddress
  cpi r16, NETMSG_CMD_CLAIM_ADDRESS
  breq appHubHandleRoutingMsg_handleClaimAddress
  clc
  rjmp appHubHandleRoutingMsg_ret
appHubHandleRoutingMsg_handleNeedAddress:
  rcall appHubHandleNeedAddressMsg
  rjmp appHubHandleRoutingMsg_secRet
appHubHandleRoutingMsg_handleClaimAddress:
  rcall appHubHandleClaimAddressMsg
appHubHandleRoutingMsg_secRet:
  sec
appHubHandleRoutingMsg_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubHandleNeedAddressMsg
;
; @param X pointer to received message
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !Y

appHubHandleNeedAddressMsg:
  ldi r18, NETMSG_CMD_ADDRESS_RANGE
  rcall appHubSendRangeMsg                           ; (R16, R17, R18, R19, R20, R21, X)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubHandleClaimAddressMsg
;
; @param X pointer to received message
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !Y

appHubHandleClaimAddressMsg:
  rcall NETMSG_Address_Read            ; R18=cmd, R19=addr(R18, R19)
  rcall appHubIsR19InRange
  brcs appHubHandleClaimAddressMsg_end
  ; is not in subnet range, deny
  rcall appHubSendDenyAddrR19          ; (R16, R17, R18, R19, R20, R21, X)
appHubHandleClaimAddressMsg_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubIsR19InRange
;
; @param R19 address to check against range
; @param Y pointer to IFACE data
; @clobbers R16

appHubIsR19InRange:
  ldd r16, Y+NET_IFACE_OFFS_RANGE_BEGIN
  cp r19, r16
  brcs appHubIsR19InRangeClcRet
  ldd r16, Y+NET_IFACE_OFFS_RANGE_END
  cp r16, r19
  brcs appHubIsR19InRangeClcRet
  sec
  rjmp appHubIsR19InRange_end
appHubIsR19InRangeClcRet:
  clc
appHubIsR19InRange_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubCheckRecvdMsg
;
; Read messages from any interface and forward to the other ones.
;
; @return CFLAG set if something done, cleared otherwise

appHubCheckRecvdMsg:
  rcall NET_PeekNextIncomingMsgNum       ; check read queue (R16=bufNum)
  brcc appHubCheckRecvdMsg_end           ; no msg, jmp
  rcall NET_Buffer_Locate                ; (R17)
  rcall appHubHandleRecvdMsg             ; (all, !X)
  rcall NET_GetNextIncomingMsgNum        ; take off the queue
  rcall NET_Buffer_ReleaseByNum          ; (R16, X)
  sec                                    ; we had a message, so something was done
appHubCheckRecvdMsg_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appHubHandleRecvdMsg
;
; @param R16 buffer number
; @param X pointer to received message
; @clobbers all, !X

appHubHandleRecvdMsg:
  mov r18, r16
  ld r16, X
  andi r16, 0x0f
  rcall appHubGetDeviceByIfaceNum  ; Y=source iface (r17, r19, r20)
  brcc appHubHandleRecvdMsg_ret
  ; filter out routing msgs
  push yl
    push yh
      push r18
        rcall appHubHandleRoutingMsg   ; (all, !X)
      pop r18
    pop yh
  pop yl
  brcs appHubHandleRecvdMsg_ret
  ; check for PING, SETVALUE etc
  push r18
    push yl
      push yh
        rcall appHubHandleMsgLocally   ; (all, !X)
      pop yh
    pop yl
    ; let other apps and modules handle message
    push xl
      push xh
        bigcall mainHandleMessages
      pop xh
    pop xl
  pop r18
  ; forward message to all other interfaces
  mov r16, r18                              ; buffer number
  push xl
    push xh
      rcall appHubSendMsgToAllOthers            ; (r17, r19, r20, r24, r25 (r16, r18, X)
    pop xh
  pop xl
appHubHandleRecvdMsg_ret:
  ret
; @end
















; ---------------------------------------------------------------------------
; @routine appHubWriteConfToEeprom
;
; @clobbers R16, X (R17)

appHubWriteConfToEeprom:
  ; write range begin
  ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
  ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
  lds r16, appHubRangeBegin
  rcall Eeprom_WriteByteIfChanged                ; (R17)
  brcc appHubWriteConfToEeprom_end

  ; write range end
  ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_END)
  ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_END)
  lds r16, appHubRangeEnd
  rcall Eeprom_WriteByteIfChanged                ; (R17)
appHubWriteConfToEeprom_end:
  ret
; @end


#if 0
; ---------------------------------------------------------------------------
; @routine appHubReadConfFromEeprom
;
; @clobbers R16, X (R17)

appHubReadConfFromEeprom:
  ; read range begin
  ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
  ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
  rcall Eeprom_ReadByte
  brcc appHubReadConfFromEeprom_end
  cpi r16, 0xff
  breq appHubReadConfFromEeprom_okay                ; not set, jmp
  cpi r16, 2                                           ; range should at least start at 2 to assign 1 to router
  brcs appHubReadConfFromEeprom_okay
  sts appHubRangeBegin, 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 appHubReadConfFromEeprom_end
  cpi r16, 0xff
  breq appHubReadConfFromEeprom_okay
  sts appHubRangeEnd, r16
appHubReadConfFromEeprom_okay:
  sec
appHubReadConfFromEeprom_end:
  ret
; @end
#endif



