// decode_phy_packet.c

#include "1394.h"
#include "data_structures.h"
#include "phy_services.h"
#include "arb.h"
#include "shared.h"

PHY_PKT phy_resp_pkt;                   // Create remote confirmation or reply packet here
byte read_phy_reg(int page, int port_num, int reg, boolean base_reg); // Not defined in C code
                                        // Return current value of specified PHY register

void remote_access(int page, int port_num, int reg, int ext_type) {  
  // Current value of remotely read register
  phy_resp_pkt.dataQuadlet = 0;
  phy_resp_pkt.phy_ID = physical_ID;
  phy_resp_pkt.ext_type = (ext_type == 1) ? 3 : 7;
  phy_resp_pkt.page = page;
  phy_resp_pkt.port_num = port_num;
  phy_resp_pkt.reg = reg;
  phy_resp_pkt.data = read_phy_reg(page, port_num, reg, ext_type == 1);
  phy_response = TRUE;
}

void remote_command(int cmnd, int e_cmnd, int port_num) { // Conditionally execute requested command
  int i, num_connected_ports = 0;
  int standby_port = NPORT;
  phy_resp_pkt.dataQuadlet = 0;
  phy_resp_pkt.phy_ID = physical_ID;
  phy_resp_pkt.ext_type = 0x0A;
  phy_resp_pkt.port_num = port_num;
  phy_resp_pkt.ok = TRUE;
  if (port_num >= NPORT) phy_resp_pkt.ok = FALSE; // port out of range?
  else if (cmnd == 1)                   // Disable port
    if (port_num == receive_port)       // What? Disable the port that received the packet?
      phy_resp_pkt.ok = FALSE;          // No, node is not going to lock itself out...
    else if (active[port_num])          // Active, DISABLE_NOTIFY to peer PHY first
      disable_request[port_num] = TRUE;
    else                                // Otherwise (inactive), just mark disabled
      disabled[port_num] = TRUE;
  else if (cmnd == 2)                   // Suspend port
    if (!active[port_num] || resume_in_progress() || suspend_in_progress())
      phy_resp_pkt.ok = FALSE;
    else
      suspend_request[port_num] = TRUE;
  else if (cmnd == 4)                   // Clear fault conditions
    resume_fault[port_num] = suspend_fault[port_num] = 
      standby_fault[port_num] = FALSE;
  else if (cmnd == 5)                   // Enable port
    disabled[port_num] = FALSE;
  else if (cmnd == 6)                   // Resume port
    if (active[port_num] || !connected[port_num] || disabled[port_num]
        || resume_in_progress() || suspend_in_progress())
      phy_resp_pkt.ok = FALSE;
    else
      resume[port_num] = TRUE;
  else if (cmnd == 7) {                 // Extended command
    if (e_cmnd == 1) {                  // Standby the one active port
      for (i = 0; i < NPORT; i++) {
        if (connected[i]) num_connected_ports++;
        if (active[i]) standby_port = i;    // find an active port
      }
      if ((standby_port == NPORT) || (num_connected_ports > 1) || !Beta_mode[standby_port] || 
        root || (link == Legacy_Link)) // accept if Beta or no link  
        phy_resp_pkt.ok = FALSE;
      else
        do_standby[standby_port] = TRUE;
    }
    if (e_cmnd == 2)                    // Restore port
      if (active[port_num] || !connected[port_num] || disabled[port_num])
        phy_resp_pkt.ok = FALSE;
      else
        restore[port_num] = TRUE;
  }
  if (!((cmnd == 7) && (e_cmnd == 1))) { // fields ignored on standby command
    phy_resp_pkt.standby_fault = standby_fault[port_num];
    phy_resp_pkt.fault = resume_fault[port_num] || suspend_fault[port_num];
    phy_resp_pkt.connected = connected[port_num];
    phy_resp_pkt.bias = receive_ok[port_num];
    phy_resp_pkt.disabled = disabled[port_num];
  }
  phy_resp_pkt.cmnd = cmnd;
  phy_resp_pkt.e_cmnd = e_cmnd;
  phy_response = TRUE;
}

void decode_phy_packet(PHY_PKT phy_pkt) {
  int i;
  boolean LTP_packet;
  LTP_packet = FALSE;
  if (phy_pkt.phy_pktType == 0b10) {    // self_ID packet?
    self_ID_pkt = phy_pkt;    
    if (bus_initialize_active) { 
      // note, can't do this processing in self_ID receive_actions,
      // as self_ID's from the parental side are received in normal arbitration
      // look for PHY speed to set max_Legacy_path_speed
      B_node_root = received_speed_signal; // finally set by the last self_ID received
      if (!Beta_mode[receive_port] || !received_speed_signal) { 
        // originated from node with Legacy link or DS node
        B_bus = FALSE;    
        switch (self_ID_pkt.sp) {
          case 0b00: break;
          case 0b01: max_Legacy_path_speed = 
                (max_Legacy_path_speed == S100) ? S200: max_Legacy_path_speed;
            break;
          default: max_Legacy_path_speed = S400;
            break;
        }
        
        // Always assume the latest advertised border is the proxy_root.  proxy_root may be
        // set true later if this PHY still has to send it's own self_ID
        proxy_root = FALSE;
        senior_port = receive_port;   // remember the path to the latest candidate proxy_root
        // Always assume the latest advertised border within the local Beta cloud is the
        // senior border.  senior_border may be set true later if this PHY still has to send
        // it's own self_ID.
        if (Beta_mode[receive_port]) {
          senior_border = FALSE;
        }
      }
    }
  } else if (phy_pkt.phy_pktType == 0b01) { // Link-on packet?
    if (phy_pkt.phy_ID == physical_ID)
      PH_EVENT_indication(PH_LINK_ON, 0, 0);   
      // LinkOn asserted only if either LPS or LCtrl FALSE
  } else if (phy_pkt.R != 0 || phy_pkt.T != 0) { // PHY configuration packet?
    if (phy_pkt.R == 1)                  // Set force_root if address matches
      force_root = (phy_pkt.root_ID == physical_ID);
    if (phy_pkt.T == 1) {               // Set gap_count regardless of physical ID
      gap_count = phy_pkt.gap_count;
      gap_count_reset_disable = TRUE;
    }
  } else if (phy_pkt.ext_type == 0)     // Ping packet?
    ping_response = (phy_pkt.phy_ID == physical_ID);
  else if ((phy_pkt.ext_type == 1 || phy_pkt.ext_type == 5) && phy_pkt.phy_ID == physical_ID)
    remote_access(phy_pkt.page, phy_pkt.port_num, phy_pkt.reg, phy_pkt.ext_type);
  else if (phy_pkt.ext_type == 8 && phy_pkt.phy_ID == physical_ID)
    remote_command(phy_pkt.cmnd, phy_pkt.e_cmnd, phy_pkt.port_num);
  else if (phy_pkt.ext_type == 0xF) {    // Resume packet?
    for (i = 0; i < NPORT; i++)
      if (!active[i] && !disabled[i] && connected[i])
        resume[i] = TRUE;               // Resume all suspended/suspending ports
  } else if (phy_pkt.ext_type == 0xE) {   // LTP packet?
    HR_mode = phy_pkt.mode;
    HR_G  = phy_pkt.G;
    HR_test_value = phy_pkt.test_value;
    need_new_LTP = FALSE;
    test_interval_timer = 0;
    LTP_packet = TRUE;
  } 
  if (!LTP_packet) HR_mode = LTP_TEST;  // reset on all PHY packets except LTP packets
}

void phy_response_actions() {
  receive_port = NPORT;                 // local node is transmitting (no port has this number)
  if ((breq == FAIR_REQ) || (breq == PRIORITY_REQ)) {
    breq = NO_REQ;                      // Cancel the request
    PH_ARB_confirmation(PH_LOST, 0, 0); // And let the link know
  }
  PH_DATA_indication(PH_DATA_PREFIX, 0, 0, 0); // Send notification of bus activity
  start_tx_packet(S100, LEGACY);        // send data prefix and 98.304 Mbit/sec speed code
  PH_DATA_indication(PH_DATA_START, S100, 0, LEGACY); // cc the link (always)
  tx_quadlet(phy_resp_pkt.dataQuadlet);
  tx_quadlet(~phy_resp_pkt.dataQuadlet);
  
// not able to call boss_end_packet_actions here, as then the node would have to allow
// for concatenating another packet, and there's complications with the possible
// isbr below, also cannot mark a PHY response packet from a suspend or disable 
// as being the end of subaction, so give an implicit grant and return to Idle

  PH_DATA_indication(PH_DATA_END, 0, 0, 0);
  stop_tx_packet(DATA_END, NPORT);
  if ((phy_resp_pkt.ext_type == 0x0A) && (phy_resp_pkt.ok == 1)) {
    if (disable_request[phy_resp_pkt.port_num] || phy_resp_pkt.cmnd == 2) {
      isbr = isbr_OK = TRUE;            // Bus reset active ports if disable or suspend
      immediate_phy_request = TRUE;         // To keep control to send the SUSPEND or DISABLE
    }
    if ((phy_resp_pkt.cmnd == 7) && (phy_resp_pkt.e_cmnd == 1))
      immediate_phy_request = TRUE;         // To keep control to send the STANDBY 
  } 
  phy_response = FALSE;
}
