// port.c

#define unsubscripted

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

// Per port code
// Note that unsubscripted references to active, connected, disabled, suspend, resume,
// proxy, restore, bias, Beta_mode and Beta_mode_only_port are to be interpreted as references
// to active[i] etc, where i is the number of the particular port
// The EVT_X_Y constants are to be considered distinct for each port and distinct from the
// values used in the arbitration state machine. Thus the use of wait_event within the per port code
// depends only on signals sent within the port.

#define EVT_SENT_SPEED 0x00000001
#define EVT_SENT_ACK 0x00000002
#define EVT_RECEIVED_SPEED 0x00000004

timer bias_delay_timer;                 // used for timing out Bias delays
boolean connect_detect_valid;           // When true, indicates that the DC connect detect comparator 
              // will give a valid indication of the connection status
timer connect_timer;                    // Timer for connection status monitor
boolean connection_unreliable;          // PHY register bit, set true if the Beta mode speed negotiation  
            // fails or synchronization fails whilst establishing the operating mode and speed 
            // of a connection. Reset by software after appropriate higher level action has been taken.
boolean dc_connected;                   // latches PMD's connect_detect
            // TRUE if the port has detected a DC connection to the peer port. 
            // This is maintained as a read-only bit in the port register map.
boolean hard_disable;                   // port register map flag to turn a disabled port off completely
boolean int_enable;
boolean listening_for_speed;            // turns on/off the receive_speed_indication routine
speedCode max_port_speed;               // maximum speed at which a port is allowed to connect in Beta mode
const min_port_speed;                   // minimum speed at which a port is allowed to connect in Beta mode 
            // This per-port constant is the minimum speed at which a port is capable of 
            // connecting in Beta mode.
            // The speed is encoded as in max_port_speed. The port uses this register 
            // only when a new connection is established in Beta mode. An attempt by a  
            // peer port to negotiate Beta mode connection at a speed lower than this 
            // speed will be rejected.

boolean port_event;
const port_num;                         // number of this port
speedCode received_speed;               // Last speed received from peer PHY. Encoding as max_port_speed
boolean sd_detected;                    // latches the PMD's signal_detect
boolean send_speed;
boolean speed_ack;                      // speed_ack says that the other end received OUR speed
boolean sync_fail;                      // true if suspend initiator due to loss of sync
timer tone_test_timer;
boolean toner_active;                   // to avoid contention on drivers
boolean toning;                         // turns on/off the toner
boolean watchdog;  
boolean we_agree;                       // Indicates to the toner to signal the peer that speed is agreed 
                                        // (the transmitted ACK bit in a speed code should be set to 1)

// the following supplements the 1394a definition 
// enum speedCode                    {S100, S200, S400, S800, S1600, S3200};                              
// speed codes are encoded             000   001   010   011    100    101
// Speed codes exchanged 
// during speed negotiation 
// are encoded as                      001   010   011   100    101    110

void activate_connect_detect(int delay) {
  PMD_CONTROL_request(PMD_UNSELECT_PORT, 0);
  bport_on = dsport_on = FALSE;
  if (!Beta_mode && !Beta_mode_only_port) PMD_DSPORT_TPBIAS_request(GND); // Drive TpBias low
  if (delay != 0) {
    connect_timer = 0;
    while (connect_timer < delay)       // Enforce minimum hold time for TpBias low
      ;                                 // also ensures handshake times for both modes
  }
  if (!Beta_mode && !Beta_mode_only_port) {
    while (!(PMD_STATUS_request() & PMD_CONNECT_DETECT))             // Wait for connect_detect circuit to stabilize
      ;                                 // (this assumes sufficient hysteresis in analog circuit)
    PMD_DSPORT_TPBIAS_request(ZZ);                         // Release TpBias
  }
  connect_detect_valid = TRUE;          // Signal OK to connection_status()
}

void port_power_reset() {
  active = suspend_request = resume = do_standby = in_standby = loop_disabled =
  suspend_fault = resume_fault = standby_fault =  
  connection_unreliable = proxy = sync_fail = attach = untested = 
  force_disconnect = FALSE;

  // the variables disabled, max_port_speed 
  // are set to their implementation-dependent power-reset values

  activate_connect_detect(0);
  wait_time(2*DISCONNECTED_TONE_INTERVAL); // ensure that there is more than one tone
                  // interval since a signal was transmitted
                  // so that the peer port moves into disconnected
  while (power_reset)                   // wait for power reset to be released
    ;
}

void dc_connection_detector() { // Continuously running
  static boolean connection_in_progress; // TRUE when debouncing a new connection
  if (power_reset) {
    dc_connected = connection_in_progress = FALSE;
    while (power_reset)
      ;
    return;
  }
  if (!Beta_mode_only_port) {            // dc connection detection and debouncing is not implemented 
                                         // in a Beta_mode_only_port
	  // Note that an incoming tone may cause connect_detect
	  // to give a false "disconnect" indication
	  //  connection debounce from 1394a
	  if (connection_in_progress) {
	    if (!(PMD_STATUS_request() & PMD_CONNECT_DETECT))
	      connection_in_progress = FALSE;   // Lost attempted connection
	                                        // or possibly set false because a tone came in
	    else if (connect_timer >= CONNECT_TIMEOUT) {
	      connection_in_progress = FALSE;
	      dc_connected = TRUE;              // Confirmed connection
	    }
	  } else if ( !dc_connected             // Recognize connection if port or interrupts enabled
	      && (!disabled || int_enable)) {
	    if (PMD_STATUS_request() & PMD_CONNECT_DETECT) {               // Possible new connection?
	      connect_timer = 0;                // Start connect timer
	      connection_in_progress = TRUE;
	    }
	  } else if (connect_detect_valid && !(PMD_STATUS_request() & PMD_CONNECT_DETECT)) {
	      dc_connected = FALSE;             // Detect disconnect fairly instantaneously
	  }
	}
}

void send_tone() {
  if (bias) {                         // avoid toning into incoming TpBias
    wait_time(TONE_DURATION);
    return;
  }
  sending_tone = TRUE;
  PMD_CONTROL_request(PMD_TONE_ON, 0);
  wait_time(TONE_DURATION);
  PMD_CONTROL_request(PMD_TONE_OFF, 0);
  sending_tone = FALSE;
}

void signal_detect_monitor() {          // continuously running - latches signal_detect
  if (power_reset) {  
    sd_detected = FALSE;
    while (power_reset)
      ;
  } else if (PMD_STATUS_request() & PMD_SIGNAL_DETECT) sd_detected = TRUE;
}

boolean signal_detect_OK() {            // true if signal_detect set since last looked
  boolean x;
  x = sd_detected;
  wait_time(TONE_DURATION);             // to make sure that don't see the same tone twice
  if (x) 
    sd_detected = PMD_STATUS_request() & PMD_SIGNAL_DETECT; // clear the latch once seen a tone,
                                        // but don't bother if the next tone is already present
  return(x);
}

void receive_ok_monitor() {             // continuously running
  if (Beta_mode)
    receive_ok = !toning && sd_detected;  // always FALSE during toning
  else if (Beta_mode_only_port)
    receive_ok = FALSE;
  else receive_ok = bias;
  rx_ok = Beta_mode ? bport_sync_ok: bias;
}

void toner() {                          // continuously running
  boolean t_ack;
  int i, t_speed;                       // t_speed latches value of speed to send
  if (power_reset) {
    sending_tone = FALSE;
    PMD_CONTROL_request(PMD_TONE_OFF, 0);
    while (power_reset)
      ;
    return;
  }
  while (toning || send_speed) {
    toner_active = TRUE; 
    for (i = 0; i < TONE_INTERVAL; i++) {
    	if (!(toning || send_speed))                  // allow early exit, particularly to set
    	  break;                                      // toner_active to FALSE when toning stops
      if (i == 1)
        t_ack = we_agree && send_speed;             // latch latest status of we_agree just
                                                    // before the ack bit is sent
      if (i == 2)
        t_speed = send_speed ? port_speed + 1 : 0;  // latch latest speed just before
                                                    // the first speed bit is sent
      if ((i == 0) || // start bit
          (i == 1 && t_ack) || // ack bit
          (i >= 2 && i <= 4 && (t_speed & (1 << (i - 2))) != 0)) // speed bits
        send_tone();
      else
        wait_time(TONE_DURATION);

      wait_time(SPEEDCODE_BIT_INTERVAL); // inter-bit gap

      if (i == 7) {
        if (t_speed != 0)               // speed code bits sent
          signal(EVT_SENT_SPEED);
        if (t_ack)                      // ack bit sent
          signal(EVT_SENT_ACK);
      }
    }
  }
  toner_active = FALSE;
} 

void receive_speed_indication() {       // continuously running
  boolean found;
  int count, i;
  int rs;                               // accumulate the received speed
  while (listening_for_speed  && !power_reset) {
    rs = 0;
    // first find the gap
    count = (TONE_GAP - 1)*4;           // maximum gap (specified in samples separated by
                                        // TONE_DURATION that can occur within a valid speed 
                                        // code including possible future codings)
    while (count >= 0) {
      if (signal_detect_OK()) {         // saw a bit, so start looking for the gap again
        count = (TONE_GAP - 1)*4;
      } else count = count - 1;
    }
    count = (TONE_INTERVAL - TONE_GAP + 1)*4; // the maximum time (specified in
                // samples separated by TONE_DURATION) for a start
                // bit to appear after a previous gap was detected
    found = FALSE;
    while (count > 0 && !found) {
      count = count - 1;
      found = signal_detect_OK();       // seen the start bit for a new speed code              
    }
    wait_time(SPEEDCODE_BIT_INTERVAL + TONE_DURATION/2); // for centering
    if (found) {
      speed_ack = signal_detect_OK();   // next bit is the ack bit
      for (i = 0; i < 6; i++) {         // now look for six speed code bits
        wait_time(SPEEDCODE_BIT_INTERVAL); // 1394b only uses first three, but this allows
        if (signal_detect_OK()) rs = rs | (1 << i); // negotiation with other standards
      }
      if (rs > 0) {
        received_speed = rs - 1;          // decode
        signal(EVT_RECEIVED_SPEED);
      }
    }
  }
}

boolean start_resume_port() {
  while (suspend_in_progress())         // Let any other suspensions complete
    ;         // (node may resume those ports later)
  OK_to_detect_reset = resumption_done = FALSE;
  connect_detect_valid = FALSE;         // Bias renders connect detect circuit useless, 
  if (!resume && !boundary_node) resume_all_ports();
  else resume = TRUE;                   // Guarantee resume_in_progress() returns TRUE

  // initialize the arb/port interface so that the right thing is transmitted once training is done
  while (toner_active)                  // wait for toning to stop
     ;
  connect_timer = 0;
  portT.speed = DEFAULT;
  portT.tag = ARB_STATE;
  portT.arb = IDLE;
  if (Beta_mode)  {
    arbreqT.isoch = ISOCH_NONE;
    arbreqT.async = odd_async_phase ? NONE_ODD: NONE_EVEN;
    PMD_CONTROL_request(PMD_SELECT_BPORT, port_speed);
    bport_on = TRUE;                // start up and train the port
  } else  {
    PMD_CONTROL_request(PMD_SELECT_DS_PORT, 0);
    dsport_on = TRUE;
    PMD_DSPORT_TPBIAS_request(Bias_On);               // DS mode, Generate TpBias
  }

  // Need to wait till resumption is OK on all resuming ports
  while ((connect_timer < RECEIVE_OK_HANDSHAKE) && !resume_rx_OK())
    ;    // Beta_mode only requires DISCONNECTED_TONE_INTERVAL/4 + 
         // 4 * TONE_DURATION + RECEIVER_INIT_TIME + 
         // (SYNCHRONIZATION_LENGTH/(BASE_RATE*(1<<(port_speed-1))))
  return (rx_ok);  
}

boolean start_port() {                  // for restore and loop free build
  OK_to_detect_reset = resumption_done = FALSE;
  connect_detect_valid = FALSE;         // Bias renders connect detect circuit useless
  // initialize the arb/port interface so that the right thing is transmitted once
  // training is done
  while (toner_active)                  // wait for toning to stop
     ;
  connect_timer = 0;
  portT.speed = DEFAULT;
  arbreqT.isoch = ISOCH_NONE;
  arbreqT.async = odd_async_phase ? NONE_ODD: NONE_EVEN;
  portT.tag = ARB_STATE;
  portT.arb = IDLE;
  PMD_CONTROL_request(PMD_SELECT_BPORT, port_speed);
  bport_on = TRUE;// start up and train the port

  // strictly, the requirement is (DISCONNECTED_TONE_INTERVAL/4 + 4 * TONE_DURATION + 
  //      RECEIVER_INIT_TIME + (SYNCHRONIZATION_LENGTH/(BASE_RATE*(1<<(port_speed-1)))))  
  while ((connect_timer < DISCONNECTED_TONE_INTERVAL/2) && !bport_sync_ok)
    ;
  return (bport_sync_ok);  
}

boolean set_Beta() {                    // set Beta mode and exchange speed codes to establish 
  boolean speed_agreed, sent_ack;       // operating speed, returns FALSE if the speed negotiation failed
  int count, event;
  speedCode cable_speed;
  cable_speed = PMD_CABLE_SPEED_request();
  port_speed = (cable_speed < max_port_speed) ? cable_speed : max_port_speed; // our starting point
  if (port_speed < min_port_speed) return(FALSE);
  count = 10;                           // five tries
  speed_agreed = we_agree = sent_ack = FALSE;
  send_speed = TRUE;                    // start the autonomous speed sender going with the 
                                        // updated port_speed 
  listening_for_speed = TRUE;           // set the autonomous speed listener going;
  while (((count > 0) && !speed_agreed) || (speed_agreed && !sent_ack)) {
    event = wait_event(EVT_SENT_SPEED | EVT_RECEIVED_SPEED | EVT_SENT_ACK);
    if (event & EVT_RECEIVED_SPEED) { 
      if (received_speed < min_port_speed) {
        listening_for_speed = FALSE;
        send_speed = FALSE;
        return(FALSE);
      }
      if (received_speed == port_speed) {
        we_agree = TRUE;
        speed_agreed = speed_ack;       // all agreed when port has the acknowledge from other end 
      } else {
        if (received_speed < port_speed) {
          port_speed = received_speed;  // set Negotiated_speed in port register map to port_speed
          we_agree = TRUE;
          count = 10;                   // and try again with a lower speed
        } else count++;                 // received speed > port_speed, so allow another go, but
                                        // not too many times; also allows a future standard product to 
                                        // negotiate down to what can be achieved
      }
    }
    if (event & EVT_SENT_SPEED) count = count - 2;
    if (event & EVT_SENT_ACK) sent_ack = TRUE;
  }
  listening_for_speed = FALSE;          // turn off the autonomous listner (may take some time) 
  send_speed = FALSE;                   // turn off sending of speed code (may take some time)
  if (speed_agreed) {                   // and set Negotiated_speed in port register map to port_speed
    toning = FALSE;
    Beta_mode = TRUE;
  }
  return (speed_agreed);
}

void set_DS() {                         // set DS mode (implied by Beta_mode remaining false)
  toning = FALSE;
  connect_detect_valid = FALSE;
  connected = TRUE;
}

void connection_status() {              // continuously running code for each port
  int i;
  boolean crossover;     // When true, instructs the PMD to transmit on TPA and to receive 
                         // and perform signal detect on TPB (only required for UTP-5 ports)
  boolean know_still_Beta_mode;         // maintains knowledge of connection if suspended
  boolean new_connection_detected;
  boolean tried_bias;                   // indicates that the local port has tried sending TpBias just
              // in case the far end port was a suspended 1394a port initialized
              // to false on POR, which is the only time this can occur
  if (power_reset) {
    Beta_mode = bport_on = connected = connection_unreliable = crossover =
    dsport_on = listening_for_speed = receive_ok = rx_ok = send_speed = toning = 
    tried_bias = FALSE;
    PMD_CONTROL_request(PMD_UNSELECT_PORT, 0);
    PMD_CONTROL_request(PMD_NO_CROSSOVER, 0);
    while (power_reset)
      ;
    return;
  }

  if (!(PMD_STATUS_request() & PMD_LOCAL_PLUG_PRESENT) || 
       (disabled && (hard_disable || in_standby))) {  // give up if no plug or hard disabled
    toning = FALSE;                     // don't signal presence
    connected = FALSE;
    Beta_mode = FALSE;
    wait_time(2 * DISCONNECTED_TONE_INTERVAL); // ensure far end sees a disconnect, 
// only need to do the above line "first time around", but its not worth the extra flag to control this
    return;                             // give up, i.e. to start the routine again
  }

// note that the connected variable may be false even though a physical connection has been established

// connected     Beta_mode
//   0             0                operating mode not determined
//   0             1                Beta mode, may be disabled and connection not reported
//   1             0                DS mode
//   1             1                Beta mode

// first case:- port has not established whether there is a connection, nor the operating mode
// here if in P0, or P6 with the connected flag set to false

  if (!connected && !Beta_mode) {       // look for new connection and determine operating mode
    toning = TRUE;                      // set the autonomous toner going (for the peer's benefit)
    signal_detect_OK();                 // clear out any stale value (no need to wait)
    new_connection_detected = FALSE;
    while (!connected && !Beta_mode) {
      if ((hard_disable && disabled) || !(PMD_STATUS_request() & PMD_LOCAL_PLUG_PRESENT))
        return;                         // good to return immediately if this becomes 
                                        // TRUE anywhere from here in
      for (i = 0; i < NUM_OF_TRIES; i++) {
                                        // only needed if bi-lingual port
        if (bias) break;                // don't try to send tones if detect TpBias
                                        // so break out of the trying to tone loop
        if (!dc_connected) toning = TRUE; // might have been waiting for dc_connected
                                        // peer to wake us up
        if (dc_connected && !new_connection_detected) { 
              // try toning for at least two more times on a new connection
          new_connection_detected = TRUE;             
          i = NUM_OF_TRIES - 2;
        }
        tone_test_timer = 0;
        while ((tone_test_timer < DISCONNECTED_TONE_INTERVAL) && !sd_detected)
          ;

        if (signal_detect_OK()) {
          // heard tone, now debounce
          wait_time(CONNECT_TIMEOUT);   // interval to ignore bouncing
          if (set_Beta()) {             // try Beta mode
            if (disabled && !int_enable) return; // (compatible with 1394a)
            connected = TRUE;
            if (disabled && int_enable && !port_event) { 
            // notify if disabled, as resume notifies for an enabled port
              port_event = TRUE;
              PH_EVENT_indication(PH_INTERRUPT, 0, 0);
            }
            while (!untested_state)     // wait till in untested_state
              ;
            return;
          }
          // here if failed to negotiate speed, connection seems unreliable
          if ((PMD_STATUS_request() & PMD_AUTOCROSSOVER_SUPPORTED) == 0) { // (random backoff if it is)
            if (!connection_unreliable) { // already reported?
              connection_unreliable = TRUE;
              if (int_enable && !port_event) {
                port_event = TRUE;
                PH_EVENT_indication(PH_INTERRUPT, 0, 0);
              }
            }
            return;                     // to continue trying to connect
          }
        }
        // here if failed to hear a tone
        if (((PMD_STATUS_request() & PMD_AUTOCROSSOVER_SUPPORTED) != 0) && !dc_connected) {
          if (random_bool()) {          // try a crossover at random
            while (sending_tone)        // but delay if sending tone
              ;
            if (!sd_detected) {         // final check just in case don't need to 
              crossover = !crossover;
              PMD_CONTROL_request(crossover ? PMD_CROSSOVER : PMD_NO_CROSSOVER, 0);
            }
          }
        }
  // note that the time to do signal_detect_OK means that the listening loop
  // is guaranteed to be longer than the toning loop on the peer connection
      }                                 // end of trying to tone loop
// here if  (1) tried toning 4 times without detecting a tone;
//      (2) detected a connection and tried toning twice without detecting a tone;
//      (3) detected incoming TpBias
      if (dc_connected) {
        toning = FALSE; 
        if (bias) {   
          bias_delay_timer = 0;
          while ((bias_delay_timer < 16*RESET_DETECT) && bias)
            ;                           // longer than a 1394a or 1394b port will generate it
          if (bias) {                   // incoming TpBias persists 
            set_DS();                   // peer is a 1394-1995 port
            return;
          }
          tried_bias = FALSE;           // necessary to try bias after seeing bias, 
                  // as the peer port may be a 1394a port which was resuming 
                  // and then went into suspend with a resume fault,
          toning = TRUE;                // now need to try toning again in case node/port
                  // powered up just when a peer p1394b port was generating bias
        } else  if (!tried_bias) {          
          // Bias not present
          // try sending Bias just once, in case port has just powered up
          // whilst the connected 1394a PHY was in suspend
          tried_bias = TRUE;        
          while (sending_tone)          // but delay if sending tone
            ;
          connect_detect_valid = FALSE;
          PMD_DSPORT_TPBIAS_request(Bias_On);
          bias_delay_timer = 0;
          while ((bias_delay_timer < 12*RESET_DETECT) && !bias)
            ;
          if (bias) {
            set_DS();
            if (disabled && int_enable && !port_event) { 
            // notify if disabled, as resume notifies for an enabled port
              port_event = TRUE;
              PH_EVENT_indication(PH_INTERRUPT, 0, 0);
            }
            return;
          }
          activate_connect_detect(0);   // includes taking TpBias away
          // the DC connected peer port is disabled
          // or powered off, either way it'll wake us
        }                               // end of trying bias 
      }                                 // end of DC connected actions when toning has failed 
                                        // or bias detected
    }                                   // main loop whilst not connected
    return;
  }                                     // end of look for new connection and determine operating mode

// here if connected, or Beta mode

// check for continuous signaling or bias in relevant port states
  if (port_state == P1 || port_state == P2 || port_state == P3 || port_state == P4 ||
      port_state == P7 || port_state == P8 || port_state == P10 || port_state == P11) { 
    if (Beta_mode) {
      // turn on toning if going into a suspend-type state
      if (!rx_ok && !((port_state == P1) || (port_state == P10) || (port_state == P11)))
        toning = TRUE;                  // turn on toning as soon as possible
    }
    if (force_disconnect) {  // caused by failure to startup while in P10 or if a Legacy loop is detected
      activate_connect_detect(0);
      connected = force_disconnect = FALSE;
      wait_time(2 * DISCONNECTED_TONE_INTERVAL);  // ensure the far end sees a "disconnection"          
    }
    return;
  }

// here if P5, P6, P9 or P12 and connectivity established
// look for disconnect or report bias/continuous tone
  if (Beta_mode) {                      // Beta mode - send a tone at periodic intervals
    know_still_Beta_mode = FALSE;
    toning = TRUE;                      // turn on the autonomous toner
    signal_detect_OK();                 // flush any stale values (no need to wait)
    for (i = 0; i < RESUME_CHECKS; i++) {
      if (signal_detect_OK()) {
        if (port_state == P1 || port_state == P10 || port_state == P11) {
           toning = FALSE;              // port changed state, so stop toning
           return;                      // this will also turn on receive_OK
        } else {
           know_still_Beta_mode = TRUE;
           if (signal_detect_OK()) {       // still true - continuous tone
             if (!disabled) {              // in P5:suspended state
               toning = FALSE;             // turn off the autonomous toner
             }
             return;
           }
           break;                       // connected, but no continuous tone
        }
      }
    }
    // loss of sync in suspend forces disconnection, so reset Beta_mode as well
    Beta_mode = know_still_Beta_mode && connected;
    if (!Beta_mode)  {                  // new disconnection when in Beta mode
      connected = FALSE;
      toning = FALSE;                   // ensure the far end sees a "disconnection"          
      wait_time(2 * DISCONNECTED_TONE_INTERVAL);
    }
  } else {                              // DS mode
    if (!disabled) {                    // in P5:suspended state  
      if (receive_ok) return;
    }
    connected = dc_connected;           // see if still connected
  }
  if (!connected && !Beta_mode) {       // disconnection detected
    if (int_enable && !port_event) {
      port_event = TRUE;
      PH_EVENT_indication(PH_INTERRUPT, 0, 0);
    }
  }
}

void disabled_actions() {
   if (int_enable && !port_event) {
      port_event = TRUE;
      PH_EVENT_indication(PH_INTERRUPT, 0, 0);
   }
   if (disable_request) 
     disable_request = signaled = FALSE;
   disabled = TRUE;
   activate_connect_detect(0);          // Enable the connect detect circuit
   if (in_standby)                      // entered here from standby
     while (connected)                  // wait for background processing to succeed
       ;                                // in forcing a disconnect
   in_standby = FALSE; 
}

void resume_actions() {
  if (start_resume_port()) {
    restore_port();                     // restore port on nephew
    while (restore_in_progress)
      ;
    // ok to continue with resume if receive_ok is present in DS ports,
    // but for Beta ports bport_sync_ok should also be set
    if ((int_enable || watchdog) && !port_event) {
      port_event = TRUE;
      PH_EVENT_indication(PH_INTERRUPT, 0, 0);
    }

    while (rx_ok && !resumption_done) { // Defer bus reset until ready
      if (bus_initialize_active)        // Do nothing if reset commences
        ;
      else if ((Beta_mode || (connect_timer >= PORT_ENABLE_TIME)) && !OK_to_detect_reset)
        OK_to_detect_reset = TRUE; 
        // Now safe to detect reset on all resuming ports
      else if (boundary_node && (connect_timer >= 3 * RESET_DETECT))
        isbr = resumption_done = TRUE;   // NOW, short reset, if node can arb
      else if (connect_timer >= 7 * RESET_DETECT)
        ibr = resumption_done = TRUE;   // Sigh! Have to use long reset
    }  
    while (rx_ok && !bus_initialize_active) // wait for bus reset to start
         ; 
  }

  resume_fault = !rx_ok;
  if (Beta_mode && !bport_sync_ok) sync_fail = TRUE;
  // Resume attempt failed if sync lost (for Beta port) or receive_ok is
  // de-asserted
  resume = FALSE;                       // Resume attempt complete
  if (!resume_in_progress())            // Last resuming port does cleanup
    OK_to_detect_reset = resumption_done = FALSE;
}


void suspend_initiator_actions() {
  connect_timer = 0;                    // Used to debounce receive_ok or for receive_ok handshake
  if (!suspend_request) {               // Unexpected loss of rx_ok?
    if (Beta_mode) sync_fail = !bport_sync_ok; // suspend initiator because of loss of sync?
    suspend_request = TRUE;             // Ensure suspend_in_progress() returns TRUE
    if (port_num != senior_port)        // Yes, senior still connected?
      isbr = TRUE;                      // Arbitrate for short reset
    else
      ibr = TRUE;                       // Transition to R0 for reset
    activate_connect_detect(0);
    while (connected && connect_timer < CONNECT_TIMEOUT / 2)
      ;                                 // see if rx_ok lost because of physical disconnection
  }
  else {                                // Instructed to suspend
    signaled = FALSE;
    while ((connect_timer < RECEIVE_OK_HANDSHAKE) && rx_ok)
      ;                                 // Wait for suspend target to deassert rx_ok
    suspend_fault = rx_ok;              // Suspend handshake refused by target?
    activate_connect_detect(RECEIVE_OK_HANDSHAKE); // Also guarantees handshake timing
  }
}

void suspend_target_actions() {
  if (resume_in_progress())             // Other ports resuming?
    resume = TRUE;                      // OK, do suspend handshake but resume afterwards
  suspend_request = TRUE;               // Ensure suspend_in_progress() returns TRUE
  if (portRarb == DISABLE_NOTIFY) {     // Is our peer PHY going away?
    immediate_phy_request = TRUE;       // Topology change! Reset on other (active) ports
    isbr = isbr_OK = TRUE;
  } else if (portRarb == SUSPEND && !resume) { // Don't propagate if resume in progress
    suspend_all_active_ports();
    immediate_phy_request = TRUE;       // Invoke transmitter to propagate TX_SUSPEND
    isbr = isbr_OK = TRUE;              // Alert link that node is now isolated
  }
  if (!Beta_mode) 
    while ((portRarb == DISABLE_NOTIFY) || (portRarb == SUSPEND))
      ;                                 // Let signals complete before rx_ok handshake
  activate_connect_detect(RECEIVE_OK_HANDSHAKE);
}

void suspended_actions() {
  suspend_request = FALSE;
  if (int_enable && !port_event) {
    port_event = TRUE;
    PH_EVENT_indication(PH_INTERRUPT, 0, 0);
  }
  if (sync_fail) connected = sync_fail = FALSE; // force disconnection on sync fail
  if (resume_fault) {                   // here because failed to receive Bias or to synchronize
    while (!rx_ok && (connect_timer < 12 * RESET_DETECT))
      ; // wait longer to see if the resume fault condition clears; wait while 
        // no bias (DS mode) or no Beta-mode signal, or Beta-mode and no sync
    if (!rx_ok) activate_connect_detect(0);
  }
}

void restore_actions() {
  restore = TRUE;  // ensure that node and connection_status know we're restoring
  if (!proxy) {  // Inform nephew link immediately when a restore begins
    PH_EVENT_indication(PH_RESTORE_NO_RESET, 0, 0);
    waitPH_EVENT_response();
    cancel_requests();
  }
  if (!start_port()) {                  // Resume attempt failed if failed to synchronize
    restore = FALSE;
    force_disconnect = TRUE;
    while (connected)
       ;
    if (proxy) isbr = TRUE;             // bus reset on disconnection, arbitrated for uncle,
    else ibr = TRUE;                    // immediate for nephew
    proxy = FALSE;                      // uncle no longer proxies for nephew
    return;                             // restore attempt failed
  }
  restore_request = TRUE;               // arbitrate for the bus
  if (!proxy) immediate_restore_request = TRUE; // on nephew
  while (restore)                       // node level action clears this
    ;
  // Connection restored to active state
  if ((int_enable || watchdog) && !port_event) {
    port_event = TRUE;
    PH_EVENT_indication(PH_INTERRUPT, 0, 0);
  }
  in_standby = FALSE;
}

void standby_initiator_actions() {
  in_standby = TRUE;                    // for the purpose of port status accounting
  connect_timer = 0;                    // Used to debounce rx_ok or for rx_ok handshake
  signaled = FALSE;
  while ((connect_timer < RECEIVE_OK_HANDSHAKE) && rx_ok)
    ;                                   // Wait for suspend target to deassert rx_ok
  standby_fault = rx_ok;           // Standby handshake refused by target?
  activate_connect_detect(RECEIVE_OK_HANDSHAKE); // Also guarantees handshake timing
}

void standby_target_actions() {
  in_standby = TRUE;                    // for the purpose of port status accounting
  proxy = TRUE;                         // tell the arb state machine to proxy for this port
  activate_connect_detect(RECEIVE_OK_HANDSHAKE);
}

void standby_actions() {
  do_standby = FALSE;
  reset_notify = FALSE;                 // gone into standby since the last reset (note, now a per-port bit)
  if (int_enable && !port_event) {
    port_event = TRUE;
    PH_EVENT_indication(PH_INTERRUPT, 0, 0);
  }
  while (!((restore || (!standby_fault && receive_ok)) || // exit condition to restore
         disabled)) {                                     // exit condition to disabled
    if (!connected) {
      if (proxy) isbr = TRUE;             // bus reset on disconnection, arbitrated for uncle,
      else ibr = TRUE;                    // immediate for nephew
      break;
    }
  }
}

void untested_actions() {
  untested_state = TRUE;
  if (!force_disconnect && start_port()) { 
    // port is currently sending _NONE arb requests

    if (all_ports_suspended) {          // new connection on a suspended node
      resume_all_ports();
      while (resume_in_progress())
        ;
    }

    // the following needs to be done if this is a new connection
    restore_port();                     // if not a proxying port, then restore the standby
    while (restore_in_progress())       // port first (if any) and wait for it to restore
      ;
    if (NPORT==1) {
      portT.arb = ATTACH_REQUEST;  // single port PHY's can blindly request an attach
      portT.speed = DEFAULT;
      portT.tag = ARB_STATE;
      while (bport_sync_ok && !attach) {
        if (bus_initialize_active)  // Don't allow attach to start in the 
          ;                         // middle of an existing reset
        else if ((portRarb == BUS_RESET) || (portRarb == ATTACH_REQUEST))
          isbr = attach = TRUE;  // Peer port initiated, arm reset_detected and
                                 // attempt a short reset
      }
      while (bport_sync_ok && !bus_initialize_active)  // wait for bus reset to start
        ;
    }
    else {  // not a single port phy
      portT.arb = SPEED;            // start LTS with normal packet start at the port speed
      portT.speed = DEFAULT;
      portT.pkt = BETA;
      portT.tag = ARB_STATE;
      wait_symbol_time(port_speed);       // send speed signal (single SPEEDb)
      portT.arb = DATA_PREFIX;
      wait_symbol_time(port_speed);       // send DATA_PREFIX
      if (border_node)
        // strictly the following test is required to remain true until tree_ID completes on all Legacy ports
        while (bus_initialize_active) 
          ;
      portT.data = HR_mode << 7 | HR_G << 6 | HR_test_value; // Loop test symbol
      portT.tag = DATA;                   // send the LTS continuously

      // now make sure that this won't create a bus topology loop
      while (bport_sync_ok && !((portRarb == DATA_BYTE) || (portRarb == ATTACH_REQUEST)))
        ;  // wait for LTS coming in (ignoring the DP which will precede it and set in-packet context) or
           // the ATTACH_REQUEST which can be immediately sent by a single-port PHY
      untested = bport_sync_ok;  // Mark port as ready for testing
      while (bport_sync_ok && untested && !(port_under_test && found_loop)) {
        if (port_under_test && send_attach) { // second phase - attach the port then activate it
          portT.arb = ATTACH_REQUEST;     // note, can only get here if in control of the bus
          portT.speed = DEFAULT;
          portT.tag = ARB_STATE;

          // wait for reset, attach_request, or timer to expire

          if (isolated_node) {
            attach = TRUE;  // release bus and prepare to attach with next bus reset
            // Note that a bus reset is not scheduled.  Instead, wait
            // as long as possible for reset_detected to hear an inbound
            // reset on this port
            while (bport_sync_ok && !bus_initialize_active) {
              if (portRarb == ATTACH_REQUEST)
                isbr = TRUE;  // Peer port initiated and a short reset can be attempted
              if (max_occupancy_timer >= 7*RESET_DETECT)
                ibr = TRUE;   // Peer port failed to initiate, so need to use long reset
            }
            untested = FALSE;
          } else {
            while (bport_sync_ok && !attach) {
              if (portRarb == BUS_RESET) 
                isbr = attach = TRUE;  // Peer port initiated, signal node controller
                                       // to release bus and attempt a short reset
              else if (max_occupancy_timer >= MAX_OCCUPANCY_TIME)
                ibr = attach = TRUE;   // Peer port failed to initiate, so need to use long reset
              else if (ibr)
                attach = TRUE;         // Another port's untested peer has gotten impatient
                                       // and initiated a long bus reset in order to attach,
                                       // so release the bus and use reset for attach
            }
            while (bport_sync_ok && !bus_initialize_active)  // wait for bus reset to start
              ;
            untested = FALSE;               // as its now tested!!!
          }

        } else {

          portT.data = HR_mode << 7 | HR_G << 6 | HR_test_value; // Loop test symbol
          portT.tag = DATA;                 // send the LTS continuously

          if (portRarb == ATTACH_REQUEST) { // takes the port out of packet context
            while (bport_sync_ok && bus_initialize_active)    // Defer bus reset until any 
              ;                                               // current reset completes
            if (bport_sync_ok)         // If still synchronized, mark port to be attached.
              isbr = attach = TRUE;    // Note that although a short bus reset is scheduled,
                                       // it will have no effect if the node has scheduled
                                       // an ATTACH_REQUEST of it's own on another port and
                                       // has retained control of the bus (isolated nodes
                                       // are the exception).  In such a case, the node
                                       // awaits an ATTACH_REQUEST, BUS_RESET, or timeout
                                       // on the test_port, or for this port to get
                                       // impatient and set the ibr flag below.
            while (bport_sync_ok && !bus_initialize_active)  // wait for bus reset to start
              if (portRarb == BUS_RESET)        // Peer port has gotten impatient. Signal
                ibr = TRUE;                     // port_under_test, if any, to release bus
            untested = FALSE;               // as its now tested!!!
          }
        }
      }                                     // end of loop while untested
    }  // end of if NPORT==1 else ... statement
  }  // end of if start_port()
  loop_disabled = (port_under_test && found_loop);
  if (!bport_sync_ok) loop_disabled = TRUE;
  untested = FALSE;               // as its now tested!!!
  attach   = FALSE;               // Attach attempt complete
  untested_state = FALSE;         // will always take one of the transitions immediately
}
    
void loop_disabled_actions() {
  if (int_enable && !port_event) {
    port_event = TRUE;
    PH_EVENT_indication(PH_INTERRUPT, 0, 0);
  }
  activate_connect_detect(0);
  while (rx_ok)
    ; // Wait til peer becomes inactive
}
