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

.equ APP_NETWORK_STATE_INITIALWAIT   = 0   ; initially wait
.equ APP_NETWORK_STATE_NEEDADDRESS   = 1   ; wait for range msgs after sending "NEEDADDRESS"
.equ APP_NETWORK_STATE_CLAIMADDRESS1 = 2   ; wait for "DENYADDRESS"
.equ APP_NETWORK_STATE_CLAIMADDRESS2 = 3   ; wait for "DENYADDRESS"
.equ APP_NETWORK_STATE_CLAIMADDRESS3 = 4   ; wait for "DENYADDRESS"
.equ APP_NETWORK_STATE_HAVEADDRESS1  = 5   ; just notify (and handle "DENYADDRESS")
.equ APP_NETWORK_STATE_HAVEADDRESS2  = 6   ; just notify (and handle "DENYADDRESS")
.equ APP_NETWORK_STATE_UP            = 7
.equ APP_NETWORK_STATE_COUNT         = 8

.equ APP_NETWORK_TIMER_100MS         = 50


.equ APP_NETWORK_ADDRESS_RANGE_BEGIN = 1
.equ APP_NETWORK_ADDRESS_RANGE_END   = 191

.equ APP_NETWORK_REBOOT_TIME_100MS   = 20



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

.dseg




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

.cseg



; ---------------------------------------------------------------------------
; @routine AppNetwork_Init @global
;

AppNetwork_Init:
  rcall appNetworkResetState
  sec
  ret
; @end




; ---------------------------------------------------------------------------
; @routine AppNetwork_Fini @global
;

AppNetwork_Fini:
  ret
; @end




; ---------------------------------------------------------------------------
; @routine AppNetwork_Every100ms @global
;

AppNetwork_Every100ms:
  ldd r16, Y+NET_IFACE_OFFS_STATETIMER
  tst r16
  brne AppNetwork_Every100ms_decAndJump
  ret
AppNetwork_Every100ms_decAndJump:
  dec r16
  std Y+NET_IFACE_OFFS_STATETIMER, r16
  brne AppNetwork_Every100ms_end
  ldd r16, Y+NET_IFACE_OFFS_STATUS
  cpi r16, APP_NETWORK_STATE_COUNT
  brcs AppNetwork_Every100ms_jump
AppNetwork_Every100ms_end:
  ret
AppNetwork_Every100ms_jump:
  ldi zl, LOW(appNetworkTimerTable)
  ldi zh, HIGH(appNetworkTimerTable)
  add zl, r16
  adc zh, r16
  sub zh, r16
  ijmp
; @end



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

AppNetwork_EveryDay:
  bigjmp NET_Interface_ResetStats ; (R16, R17, X)
; @end



; ---------------------------------------------------------------------------
; @routine AppNetwork_HandleMsg @global
;
; @param X pointer to received message

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
  ld r16, X
  sbiw xh:xl, NETMSG_OFFS_CMD
  cpi r16, NETMSG_CMD_REBOOT_REQUEST
  breq AppNetwork_HandleMsg_handleRebootMsg
  cpi r16, NETMSG_CMD_PING
  breq AppNetwork_HandleMsg_handlePingMsg
;  cpi r16, NETMSG_CMD_REENUM
;  breq AppNetwork_HandleMsg_handleReenumMsg
  cpi r16, NETMSG_CMD_NEED_ADDRESS
  brcs AppNetwork_HandleMsg_clcRet         ; lower than "HAVE_NEED"
  cpi r16, NETMSG_CMD_ADDRESS_RANGE
  breq AppNetwork_HandleMsg_handleRangeMsg
  brcc AppNetwork_HandleMsg_clcRet         ; higher or equal to "ADDR_RANGE"
  rjmp AppNetwork_HandleMsg_handleAddrMsg
;AppNetwork_HandleMsg_handleReenumMsg:
;  rjmp appNetworkHandleReeunumRequest
AppNetwork_HandleMsg_handleRangeMsg:
  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
  
AppNetwork_HandleMsg_handleAddrMsg:
  bigcall NETMSG_Address_Read                ; R18=cmd, R19=addr(R18, R19)
  mov r16, r18
  subi r16, NETMSG_CMD_NEED_ADDRESS
  ldi zl, LOW(appNetworkMsgTable)
  ldi zh, HIGH(appNetworkMsgTable)
  add zl, r16
  adc zh, r16
  sub zh, r16
  ijmp
AppNetwork_HandleMsg_handleRebootMsg:
  rcall appNetworkHandleRebootRequest
  ret
AppNetwork_HandleMsg_handlePingMsg:
#ifdef MODULES_LED_SIMPLE
  bigcall Led1_SetIdTiming                  ; (R18, R19, R20)
#endif
  rjmp appNetworkHandlePingRequest
AppNetwork_HandleMsg_clcRet:
  clc
AppNetwork_HandleMsg_end:
  ret
; @end



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

appNetworkHandleRebootRequest:
  rcall NETMSG_RebootRequestRead
  brcc appNetworkHandleRebootRequest_end  ; uid doesn't match
  ; reboot
  cli
  bigjmp BOOTLOADER_ADDR
appNetworkHandleRebootRequest_end:
  ret
; @end



appNetworkHandlePingRequest:
  ld r17, X
  lds r16, (netInterfaceData+NET_IFACE_OFFS_ADDRESS)
  cp r16, r17
  breq appNetworkHandlePingRequest_forMe
  cpi r17, 0xff
  breq appNetworkHandlePingRequest_forMe
  clc
  rjmp appNetworkHandlePingRequest_end
appNetworkHandlePingRequest_forMe:
  adiw xh:xl, NETMSG_OFFS_SRCADDR
  ld r17, X
  push r17
    bigcall NET_Buffer_Alloc               ; (R16, R17, X)
  pop r17
  brcc appNetworkHandlePingRequest_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)
appNetworkHandlePingRequest_end:
  ret



#if 0
appNetworkHandleReeunumRequest:
  push xl
    push xh
      bigcall DevUid_ReadFromEeprom        ; r21:r20:r19:r18=uid (r16, X)
    pop xh
  pop xl
  rcall NETMSG_Range_Read                     ; r20=range begin, r21=range end (none)
  ldi r16, APP_NETWORK_STATE_INITIALWAIT
  std Y+NET_IFACE_OFFS_STATUS, r16
  cpi r18, 20
  brcc appNetworkHandleReeunumRequest_setWait
  subi r18, -20                               ; minimum 2s
appNetworkHandleReeunumRequest_setWait:
  std Y+NET_IFACE_OFFS_STATETIMER, r18        ; use lowest byte of uid as wait time
  std Y+NET_IFACE_OFFS_RANGE_BEGIN, r20
  std Y+NET_IFACE_OFFS_RANGE_END, r21
  ret
; @end
#endif



appNetworkTimerTable:
  rjmp appNetworkHandleStateInitialWait
  rjmp appNetworkHandleStateNeedAddress
  rjmp appNetworkHandleStateClaimAddress1
  rjmp appNetworkHandleStateClaimAddress2
  rjmp appNetworkHandleStateClaimAddress3
  rjmp appNetworkHandleStateHaveAddress1
  rjmp appNetworkHandleStateHaveAddress2
  rjmp appNetworkHandleStateUp


appNetworkHandleStateInitialWait:
  ldi r18, NETMSG_CMD_NEED_ADDRESS
  rjmp appNetworkSendMsgNextState


appNetworkHandleStateNeedAddress:
  ldi r18, NETMSG_CMD_CLAIM_ADDRESS
  rjmp appNetworkSendMsgNextState


appNetworkHandleStateClaimAddress1:
  ldi r18, NETMSG_CMD_CLAIM_ADDRESS
  rjmp appNetworkSendMsgNextState


appNetworkHandleStateClaimAddress2:
  ldi r18, NETMSG_CMD_CLAIM_ADDRESS
  rjmp appNetworkSendMsgNextState


appNetworkHandleStateClaimAddress3:
  ldi r18, NETMSG_CMD_HAVE_ADDRESS
  rjmp appNetworkSendMsgNextState


appNetworkHandleStateHaveAddress1:
  ldi r18, NETMSG_CMD_HAVE_ADDRESS
  rjmp appNetworkSendMsgNextState


appNetworkHandleStateHaveAddress2:
  ldi r16, APP_NETWORK_STATE_UP
  std Y+NET_IFACE_OFFS_STATUS, r16
  ldi r16, APP_NETWORK_TIMER_100MS
  std Y+NET_IFACE_OFFS_STATETIMER, r16
  ; store new address in IFACE and in EEPROM
  ldd r16, Y+NET_IFACE_OFFS_ADDRESS
  ldi xl, LOW(EEPROM_OFFS_COMADDR)
  ldi xh, HIGH(EEPROM_OFFS_COMADDR)
  bigcall Eeprom_WriteByteIfChanged   ; write address to EEPROM (R17)
appNetworkHandleStateHaveAddress2_end:
  ret


appNetworkHandleStateUp:
  ldi r16, 200                           ; come again after 20s (nothing to do for now, maybe add some cleanup later)
  std Y+NET_IFACE_OFFS_STATETIMER, r16
  ret


; ---------------------------------------------------------------------------
; @routine appNetworkSendMsgNextState
;
; @param R18 msg type to send
; @clobbers R16, R19 (R17, R18, R20, R21, X)

appNetworkSendMsgNextState:
  ldd r19, Y+NET_IFACE_OFFS_ADDRESS
  rcall appNetworkSendAddrMsg               ; (R16, R17, R18, R19, R20, R21, X)
  brcc appNetworkSendMsgNextState_retry
  ldd r16, Y+NET_IFACE_OFFS_STATUS
  inc r16
  std Y+NET_IFACE_OFFS_STATUS, r16
  ldi r16, APP_NETWORK_TIMER_100MS
  std Y+NET_IFACE_OFFS_STATETIMER, r16
  ret
appNetworkSendMsgNextState_retry:
  ldi r16, 1
  std Y+NET_IFACE_OFFS_STATETIMER, r16
  ret
; @end






appNetworkMsgTable:
  rjmp appNetworkHandleMsgNeedAddr
  rjmp appNetworkHandleMsgHaveAddr
  rjmp appNetworkHandleMsgClaimAddr
  rjmp appNetworkHandleMsgDenyAddr



; ---------------------------------------------------------------------------
; @routine appNetworkHandleMsgNeedAddr
;
; @param R18 command
; @param R19 address to send

appNetworkHandleMsgNeedAddr:
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appNetworkHandleMsgHaveAddr
;
; @param R18 command
; @param R19 address to send

appNetworkHandleMsgHaveAddr:
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appNetworkHandleMsgClaimAddr
;
; @param R18 command
; @param R19 address from CLAIM_ADDRESS message

appNetworkHandleMsgClaimAddr:
  ldd r16, Y+NET_IFACE_OFFS_ADDRESS
  cp r19, r16
  brne appNetworkHandleMsgClaimAddr_end ; not our address
  ldd r16, Y+NET_IFACE_OFFS_STATUS
  cpi r16, APP_NETWORK_STATE_CLAIMADDRESS1
  brcs appNetworkHandleMsgClaimAddr_end    ; nope, ignore
  ; network is somewhat up, someone claimed our address, deny it
  ldi r18, NETMSG_CMD_DENY_ADDRESS      ; deny our addr
  ldd r19, Y+NET_IFACE_OFFS_ADDRESS
  rjmp appNetworkSendAddrMsg
appNetworkHandleMsgClaimAddr_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appNetworkHandleMsgDenyAddr
;
; @param R18 command
; @param R19 address from DENY_ADDR message

appNetworkHandleMsgDenyAddr:
  ; check state
  ldd r16, Y+NET_IFACE_OFFS_STATUS
  cpi r16, APP_NETWORK_STATE_UP
  breq appNetworkHandleMsgDenyAddr_end  ; ignore (our network stack is up)
  ; still setting up address, check whether the last one is denied now
  ldd r16, Y+NET_IFACE_OFFS_ADDRESS
  cp r19, r16                           ; our claimed address?
  brne appNetworkHandleMsgDenyAddr_end  ; nope, jump
  ; try next address (if any left)
  ldd r17, Y+NET_IFACE_OFFS_RANGE_END
  inc r16                               ; next address
  cp r17, r16                           ; smaller than or equal to RANGE_END?
  brcc appNetworkHandleMsgDenyAddr_claimNext
  ; out of addresses, start completely new after some waiting time
  rcall appNetworkResetState
  ldi r16, 200                          ; wait for 20s before trying whole process again
  std Y+NET_IFACE_OFFS_STATETIMER, r16
  rjmp appNetworkHandleMsgDenyAddr_end
appNetworkHandleMsgDenyAddr_claimNext:
  ; send CLAIM_ADDR for next address (new state: APP_NETWORK_STATE_NEEDADDRESS+1)
  std Y+NET_IFACE_OFFS_ADDRESS, r16
  ldi r16, APP_NETWORK_STATE_NEEDADDRESS
  std Y+NET_IFACE_OFFS_STATUS, r16
  ldi r18, NETMSG_CMD_CLAIM_ADDRESS
  rjmp appNetworkSendMsgNextState
appNetworkHandleMsgDenyAddr_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appNetworkSendAddrMsg
;
; @param Y   pointer to device to write msg for
; @param R18 command
; @param R19 address to send
; @clobbers R16 (R17, R18, R19, R20, R21, X)

appNetworkSendAddrMsg:
  bigcall NET_Buffer_Alloc                ; (R16, R17, X)
  brcc appNetworkSendAddrMsg_end
  adiw xh:xl, 1
  push r16
    bigcall NETMSG_Address_Write          ; (R16, R17, R18, R19, R20, R21)
  pop r16
  sbiw xh:xl, 1
  bigcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
  brcs appNetworkSendAddrMsg_end
  bigcall NET_Buffer_ReleaseByNum         ; (R16, X)
  clc
appNetworkSendAddrMsg_end:
  ret
; @end




; ---------------------------------------------------------------------------
; @routine appNetworkResetState
;
; @param Y   pointer to device to write msg for
; @clobbers R16

appNetworkResetState:
  ldi r16, APP_NETWORK_STATE_INITIALWAIT
  std Y+NET_IFACE_OFFS_STATUS, r16
  ldi r16, APP_NETWORK_TIMER_100MS
  std Y+NET_IFACE_OFFS_STATETIMER, r16
  ldi r16, APP_NETWORK_ADDRESS_RANGE_BEGIN
  std Y+NET_IFACE_OFFS_RANGE_BEGIN, r16
  std Y+NET_IFACE_OFFS_ADDRESS, r16
  rcall appNetworkGetAddressFromEeprom       ; R16=addr (R15, X)
  tst r16
  breq appNetworkResetState_setRangeEnd
  cpi r16, 0xff
  breq appNetworkResetState_setRangeEnd
  std Y+NET_IFACE_OFFS_RANGE_BEGIN, r16
  std Y+NET_IFACE_OFFS_ADDRESS, r16          ; preset address from EEPROM
appNetworkResetState_setRangeEnd:
  ldi r16, APP_NETWORK_ADDRESS_RANGE_END     ; last possible address+1
  std Y+NET_IFACE_OFFS_RANGE_END, r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine appNetworkGetAddressFromEeprom
;
; @return R16 address from EEPROM
; @clobbers X

appNetworkGetAddressFromEeprom:
  ldi xl, LOW(EEPROM_OFFS_COMADDR)
  ldi xh, HIGH(EEPROM_OFFS_COMADDR)
  rcall Eeprom_ReadByte            ; r16=addr (none)
  ret
; @end


