// idle_actions.c

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

boolean idle_bus() {                    // false if any reason to exit Idle
  return (!((power_reset) ||
    (reset_detected()) ||
    (ibr) ||
    ((proxy_root && Legacy_junior_request() && !arb_OK()) || Beta_port_request) ||
    (!proxy_root && (Legacy_junior_request() || arb_OK())) ||
    (immediate_request || (proxy_root && arb_OK()) || grant_self) ||
    (ping_response) ||
    (phy_response) ||
    (data_coming())
  ));
}

void idle_actions() {
  int i, j;
  boolean grant_only_at_async_start = FALSE;
  boolean exit_idle = FALSE;
  boolean check_arbrst = FALSE;         // TRUE after timeout to recover from lost arb reset token
  boolean unstick;              // TRUE to unstick after missing ACK

  int bip;                              // best isochronous request port number
  int bap;                              // best asynchronous request port number
  int best_port;                        // over all best request port number
  byte apm;                             // mask to select the in-phase async requests
  byte ipm;                             // mask to select the in-phase isoch requests
  boolean in_phase_isoch_request, in_phase_async_request;
  boolean i_advance_interval, a_advance_interval;
  BetaRequestCode best_req;
  ArbState received_grant;
  
  Beta_port_request = FALSE;
  unstick = FALSE;
  
  sent_async_start_token = sent_arb_reset = FALSE;     // to allow us to send another one now
  cur_speed = S100;             // Default in anticipation of no explicit receive speed code
  cur_format = LEGACY;          // and to ensure S100 "downshift" tests in TX don't accidentally
                                // trigger on the first packet out of Idle
 
  for (i = 0; i<NPORT; i++) {           // all ports default to sending the best arbitration request known
                                        // except for DS stuck ports
    if (Beta_mode[i] || !DS_stuck)
      portTarb(i, IDLE);
    if (collision[i]) {
      resolve_collision_request = TRUE;
      send_null_packet = TRUE;
    }
  }
  arb_timer = 0;
  arb_timer_max = FALSE;
  fork
    {
      do {
        // check the senior port
        if (gap_token(senior_port)) gap_repeat_actions(TRUE, senior_port);    
        if (send_async_start_token || arb_reset) 
          gap_repeat_actions(TRUE, NPORT);

        if (proxy_root) {  // introduce explicit grant if OK_to_grant, else implicit grant
          if (OK_to_grant && iso_cycle) received_grant = GRANT_ISOCH;
          else if (OK_to_grant && !iso_cycle) received_grant = GRANT;
          else received_grant = DATA_END;
        } else { // !proxy_root
          if (portRarb[senior_port] == GRANT) received_grant = GRANT;
          else if (portRarb[senior_port] == GRANT_ISOCH) received_grant = GRANT_ISOCH;
          else received_grant = IDLE;  // no grant
        }

        if (received_grant != IDLE || check_arbrst) { // can the node take action?
          // any suitable requests?
          bap = bip = NPORT;
          best_port = NPORT+1;          // NPORT means the link, 
                                        // NPORT+1 means no suitable request
          best_req = bestReq(&apm, &ipm, &bap, &bip, // find best request
                &in_phase_isoch_request, &in_phase_async_request,
                &i_advance_interval, &a_advance_interval);

          if ((received_grant == GRANT_ISOCH) && in_phase_isoch_request)
            best_port = bip;
          else if ((received_grant == GRANT) && in_phase_async_request)
            best_port = bap;
          else if ((received_grant == DATA_END) && (best_req.isoch == ISOCH_CURRENT)) {
            best_port = bip;
            received_grant = GRANT_ISOCH;  // upgrade to an explicit isoch grant
          }

          if ((breq!=NO_REQ) || Legacy_junior_request() || immediate_request)
            ;                           // Legacy and immediate gets serviced immediately
          else if (DS_stuck && ((own_req.async == BORDER) || unstick)) { // only at a senior border 
            // DS ports stuck somewhere (not necessarily on this node) 
            send_null_packet = TRUE;
            grant_self = TRUE;          // schedule TX of a null packet
            unstick = FALSE;
          } else if (best_port == NPORT) { // best is the local link
            grant_to_give = received_grant;
            grant_self = TRUE;
            if (!iso_cycle) isbr_OK = isbr;
          } else if (best_port < NPORT) {
            requesting_port = best_port;
            grant_to_give = received_grant;
            Beta_port_request = TRUE;   // schedule the grant
          } else {
            // here if no in phase requests to grant
            if (!proxy_root && (received_grant != IDLE)) {  // Explicit grant from senior going unused
              send_null_packet = TRUE;
              grant_self = TRUE;        // schedule TX of a null packet 
            } else { // proxy root or senior border with check_arbrst set
              if (B_bus || check_arbrst) {  // schedule tokens as necessary
                // end of isoch interval?
                if (iso_cycle && i_advance_interval && (received_grant == GRANT_ISOCH)) {
                  send_async_start_token = TRUE;
                }

                // send arb reset if in the async interval AND any of the following are TRUE:
                // (1) Normal end of fairness interval: At the end of a subaction, no asynchronous 
                //     requests remain for the current fairness interval on a B_bus and an arb reset
                //     token has not yet been sent.  Advance the phase and introduce a new token.
                //     Same condition as at the end of test_requests().
                // (2) Error recovery: An idle timeout has occured, all asynchronous requests
                //     agree on the current interval's phase, and the highest priority
                //     request is non-null and for the next fairness interval.  Advance the
                //     phase and introduce a new token to recover.
                // (3) Error recovery: An idle timeout has occured, there are no active in phase
                //     requests to grant, but the inbound requests do not agree on the current
                //     interval's phase.  Re-introduce the arb reset token without advancing
                //     the phase. 
                if ((B_bus && !iso_cycle && a_advance_interval && !did_arbrst && 
                      (received_grant == GRANT)) ||                  // Case (1)
                    (check_arbrst && a_advance_interval &&           // Case (2)
                      ((best_req.async & apm) == (odd_async_phase ? NEXT_EVEN & apm : 
                                                                  NEXT_ODD & apm))) ||
                     (check_arbrst && !a_advance_interval)) {        // Case (3)
                  if (a_advance_interval) odd_async_phase = !odd_async_phase;
                  arb_reset = TRUE;
                  did_arbrst = TRUE;
                }
                check_arbrst = FALSE;
              } else {                  // proxy root at hybrid bus
                if (grant_only_at_async_start) 
                  OK_to_grant = FALSE;  // only one chance
              } // end of hybrid bus actions
            }
          }
        } 
      } while (idle_bus() || (!B_bus && !DS_stuck && 
          !arb_timer_max && (arb_timer < Legacy_time(MIN_IDLE_TIME))));
        // preserve Legacy idle time where necessary
      exit_idle = TRUE;
    }
    {                                   // Boss proxy_root timer etc
      do {
        if (B_bus && !OK_to_grant) { // deal with possible missing end of subaction
          if (proxy_root && (arb_timer == (STORES_GAP_COUNT ? subaction_gap: BOSS_RESTART_TIME))) {
            send_async_start_token = TRUE;
            OK_to_grant = TRUE;
            defer_grant = FALSE;
            arb_timer_max = TRUE;        // effectively marks arb_timer as saturated
            arb_timer = 0;               // reset timer for next timeout
          }
        } else if (!B_bus && !arb_timer_max) {  // normal gap initiation in hybrid bus
          if (senior_border && !send_async_start_token && (arb_timer == subaction_gap) && DS_stuck) {
            // ACK missing when the packet was beta format
              if (link == Legacy_Link) PH_DATA_indication(PH_SUBACTION_GAP, 0, 0, 0);
              send_async_start_token = TRUE; // send indication on Beta ports and advance phase
              unstick = TRUE;
          }              
          if ((senior_border || (link == Legacy_Link)) && !DS_stuck) {
            // check if grant has been defered (at a Legacy cycle master) and reintroduce if
            // sufficient idle time has been met for the Link to request the bus
            if (senior_border && defer_grant && ((breq != NO_REQ) || (arb_timer == CM_MIN_IDLE_TIME))) {
              OK_to_grant = TRUE;
              defer_grant = FALSE;
            }
            // on legacy links, the arb timer will expire before we receive a corresponding token
            // but use the receipt of token (unless senior border) to advance the phase etc
            if (!send_async_start_token && (arb_timer == subaction_gap)) {
              if (link == Legacy_Link) {
	              PH_DATA_indication(PH_SUBACTION_GAP, 0, 0, 0);
	              if (bus_initialize_active) {        // End of self_ID process for whole bus?    
	                PH_EVENT_indication(PH_BUS_RESET_COMPLETE, 0, 0);
	                bus_initialize_active = FALSE;
	              }
	            }  
              if (senior_border) { 
                send_async_start_token = TRUE; // send indication on Beta ports and advance phase
                                               // and maybe end iso cycle
              }
            }
            if (!arb_reset && (arb_timer == arb_reset_gap)) { // End of fairness interval?
              if (link == Legacy_Link) 
                PH_DATA_indication(PH_ARBITRATION_RESET_GAP, 0, 0, 0); // Alert link
              if (senior_border) {
                arb_reset = TRUE;          // send indication on Beta ports and advance interval
              }
            }
            // next two groups only have effect at proxy root
            if (!grant_only_at_async_start && arb_timer == subaction_gap + arb_delay) {
              OK_to_grant = TRUE;        // Window for first fair request and priority requests
              defer_grant = FALSE;
              grant_only_at_async_start = TRUE;
            }
            if (arb_timer >= arb_reset_gap + arb_delay) {
              OK_to_grant = TRUE;        // Window for all requests (new fairness interval)
              defer_grant = FALSE;
              grant_only_at_async_start = FALSE;
              arb_timer_max = TRUE;      // effectively marks arb_timer as saturated
              arb_timer = 0;             // reset timer for next timeout
            }
          }
        } else  // normal ack missing and gap indications have been processed, now perform token recovery
          if ((proxy_root || senior_border) && 
              (arb_timer == (STORES_GAP_COUNT ? subaction_gap: BOSS_RESTART_TIME))) {
            check_arbrst = TRUE;       // recover from lost or corrupt arb reset token if necessary
            arb_timer_max = TRUE;      // effectively marks arb_timer as saturated
            arb_timer = 0;             // reset timer for next timeout
          }
      } while (!exit_idle);
    }
  join

  OK_to_grant = FALSE;
  defer_grant = FALSE;
  did_arbrst = FALSE;                   // only relevant to a B_bus
}

