// process_req.c

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

// common routine to cancel outstanding link requests, invoked
// whenever the link is notified of a restore or bus reset
void cancel_requests(){
  breq = NO_REQ;
  immediate_link_request = immediate_phy_request = immediate_restore_request = FALSE;
  current_request = FALSE;
  next_odd_request = FALSE;
  next_even_request = FALSE;
  isoch_odd_request = FALSE;
  isoch_even_request = FALSE;
  cycle_start_request = FALSE;
}

// continuously running routine to capture link requests and set appropriate shared variables
// all updates made in each cycle of this routine are assumed to be atomic
void capture_link_requests() {
  PH_arb_req_service phyArbReq;         // latest request from a link (if any)
  if (power_reset) {
    cancel_requests();                  // also on PHY/Link reset and disable
    link_CS_indications = FALSE;
    while (power_reset)
      ;
    // assume an indication after power reset  
    link = PH_LINK_TYPE_response();     // link type determined on power_reset
  }
  phyArbReq = waitPH_ARB_request();     // wait for next link request
  switch (phyArbReq.reqType) {
    case PH_IMMED_REQ:                // from Beta link or Legacy link
      immediate_link_request = TRUE;
      imm_speed = phyArbReq.speed;
      imm_link_format = phyArbReq.Beta_format ? BETA: UNSPECIFIED;
      imm_format = ((phyArbReq.Beta_format) || B_bus ||
                  (imm_speed > max_Legacy_path_speed))? BETA: LEGACY;
      break;
      
// The PHY can hold only one async request at a time (current or next_*)
    case PH_CURRENT:
      current_request = TRUE;
      next_odd_request = next_even_request = FALSE;
      a_speed = phyArbReq.speed;
      a_link_format = phyArbReq.Beta_format ? BETA: UNSPECIFIED;
      a_format = ((phyArbReq.Beta_format) || B_bus ||
                  (a_speed > max_Legacy_path_speed))? BETA: LEGACY; 
      break;
    case PH_NEXT_ODD:
      if (!odd_async_phase) {
        next_odd_request = TRUE;
        next_even_request = current_request = FALSE;
      } else {
        current_request = TRUE;
        next_odd_request = next_even_request = FALSE;
      }
      a_speed = phyArbReq.speed;
      a_link_format = phyArbReq.Beta_format ? BETA: UNSPECIFIED;
      a_format = ((phyArbReq.Beta_format) || B_bus ||
                  (a_speed > max_Legacy_path_speed))? BETA: LEGACY; 
      break;
    case PH_NEXT_EVEN:
      if (odd_async_phase) {
        next_even_request = TRUE;
        next_odd_request = current_request = FALSE;
      } else {
        current_request = TRUE;
        next_odd_request = next_even_request = FALSE;
      }
      a_speed = phyArbReq.speed;
      a_link_format = phyArbReq.Beta_format ? BETA: UNSPECIFIED;
      a_format = ((phyArbReq.Beta_format) || B_bus ||
                  (a_speed > max_Legacy_path_speed))? BETA: LEGACY;
      break;
    case PH_CYC_START_REQ:            // to send a cycle start packet, preceded by a cycle_start_token
      // Note that cycle start requests are the only bus requests from a beta link
      // that are queued after a bus reset or restore before the register 0 transfer
      cycle_start_request = TRUE;
      
      cyc_start_speed = phyArbReq.speed;
      cyc_start_link_format = phyArbReq.Beta_format ? BETA: UNSPECIFIED;
      cyc_start_format = ((phyArbReq.Beta_format) || B_bus ||
                          (cyc_start_speed > max_Legacy_path_speed))? BETA: LEGACY;
      break;
    case PH_ISOCH_REQ_EVEN:
      isoch_even_request = TRUE;
      isoch_odd_request = FALSE;
      i_speed = phyArbReq.speed;
      i_link_format = phyArbReq.Beta_format ? BETA: UNSPECIFIED;
      i_format = ((phyArbReq.Beta_format) || B_bus ||
                  (i_speed > max_Legacy_path_speed))? BETA: LEGACY;
      break;
    case PH_ISOCH_REQ_ODD:
      isoch_odd_request = TRUE;
      isoch_even_request = FALSE;
      i_speed = phyArbReq.speed;
      i_link_format = phyArbReq.Beta_format ? BETA: UNSPECIFIED;
      i_format = ((phyArbReq.Beta_format) || B_bus ||
                  (i_speed > max_Legacy_path_speed))? BETA: LEGACY;
      break;
    case PH_ISOCH_PHASE_ODD:   // link has received a cycle start packet
      odd_isoch_phase = TRUE;  // keeps link and PHY in phase in junior beta clouds
      iso_cycle = TRUE;        // in case cycle start token wasn't received
      accelerating = TRUE;
      break;
    case PH_ISOCH_PHASE_EVEN:   // link has received a cycle start packet
      odd_isoch_phase = FALSE;  // keeps link and PHY in phase in junior beta clouds
      iso_cycle = TRUE;         // in case cycle start token wasn't received
      accelerating = TRUE;
      break;
    case PH_RESET:              // reset PHY/Link interface
      cancel_requests();        // the link block tracks PHY reg 0 and bus phase
      link_CS_indications = FALSE;
      break;                    // and gives appropriate indications
    case PH_LPS_ACTIVE:
      LPS = TRUE;
      break;
    case PH_LPS_INACTIVE:
      LPS = FALSE;
      cancel_requests();
      link_CS_indications = FALSE;
      break;
    case PH_CYCLE_START_DUE:
      link_CS_indications = TRUE;
      accelerating = FALSE;
      break;
    // finally requests from a Legacy link
    case PH_CYCLE_START_SEEN:
      accelerating = TRUE;
      break;
    case PH_ISOCH_REQ:
      accelerating = TRUE;
      breq = ISOCH_REQ;
      req_speed = phyArbReq.speed;
      break;
    case PH_PRIORITY_REQ:
      breq = PRIORITY_REQ;
      req_speed = phyArbReq.speed;
      break;
    case PH_FAIR_REQ:
      breq = FAIR_REQ;
      req_speed = phyArbReq.speed;
      break;
  }
}

// continuously running routine to process request signals received on Beta ports and link
void process_requests(){
  boolean idle_arb_state_timeout;       // TRUE if node has an ungranted request from the link or 
                                        // local PHY, see description of Arb state timeout in text
  BetaRequestCode best_req;
  byte apm, ipm;
  int bip, bap; // best isoch and async ports
  portSymbol portCurrent;
  boolean in_packet[NPORT];
  boolean ignore_port[NPORT];           // TRUE if temporarily ignoring a packet from a port due to
                                        // a collision (packet received while already receiving a packet)
  int i,j;
  if (power_reset) {
    arb_reset = FALSE;
    for (i = 0; i < NPORT; i++) {
      advance_OK[i] = TRUE;
      in_packet[i] = FALSE;
      ignore_port[i] = FALSE;
      collision[i] = FALSE;
      fifo_rd_ptr[i] = 0;
    }
    while (power_reset)
      ;
  }
  
  // update own_req with request from link or internal PHY request
  // set own aynch req

  immediate_request = immediate_link_request || immediate_phy_request || immediate_restore_request;
  
  if ((senior_border) &&                // the timer only runs on the senior border
    (max_beta_timer > MAX_BETA_TIME) && DS_stuck)
      own_req.async = BORDER;
  else if (cycle_start_request)
    own_req.async = CYCLE_START_REQ;
  else if (current_request || (restore_request && !immediate_restore_request) || loop_test_request || 
            resolve_collision_request || isbr) 
    // connection management requests for restoring and loop free build,
    // collision request and short bus reset requests look like current requests
    own_req.async = CURRENT;
  else if (next_odd_request)            // previously checked for async phase
    own_req.async = NEXT_ODD;
  else if (next_even_request)
    own_req.async = NEXT_EVEN;
  else if (odd_async_phase)
    own_req.async = NONE_ODD;
  else
    own_req.async = NONE_EVEN;
  
  
  // Set own isoch req remembering to upgrade any in-phase isoch request to 
  // ISOCH_CURRENT during the isochronous interval.  Also, only ISOCH_CURRENT and
  // ISOCH_NONE are allowed in junior beta clouds.
  if (iso_cycle && ((isoch_odd_request && odd_isoch_phase) ||  // in-phase odd request?
                    (isoch_even_request && !odd_isoch_phase))) // in-phase even request?
    own_req.isoch = ISOCH_CURRENT;
  else if (B_node_root && isoch_odd_request)
    own_req.isoch = ISOCH_ODD;          // never present in junior beta cloud
  else if (B_node_root && isoch_even_request)
    own_req.isoch = ISOCH_EVEN;         // never present in junior beta cloud
  else
    own_req.isoch = ISOCH_NONE;

  idle_arb_state_timeout = (!((own_req.async == NONE_ODD || own_req.async == NONE_EVEN) &&  
      own_req.isoch == ISOCH_NONE));    // link or local PHY is requesting, so ensure that 
      // Arbitration state timeout occurs when in Idle for MAX_ARB_STATE_TIME after this is set TRUE
  
  // process the port fifos         
  // gather incoming requests from ports, remember/forget past requests
  // set flags, portRarb and portRspeed
  for (i = 0; i < NPORT; i++) {
    if ( (!(active[i] || restore[i]) || // always dequeue the fifo unless in active or restore
          !in_packet[i] || advance_OK[i] )) {
      if (packet_ending[i]) {
        in_packet[i] = FALSE;           // last symbol terminated a packet
        ignore_port[i] = FALSE;         // no longer need to ignore
        packet_ending[i] = FALSE;       // and with the new arb state, now past the end of the packet
      }
      // OK to process next item, if there's anything to process!
      if (fifo_rd_ptr[i] != fifo_wr_ptr[i]) {  
        fifo_rd_ptr[i] = ++fifo_rd_ptr[i] % FIFO_DEPTH;
        portCurrent = portR[i][fifo_rd_ptr[i]];
        advance_OK[i] = FALSE;
        if (portCurrent.tag == DS_RAW_ARB) {
          // Raw arb states received from a Legacy port require filtering to resolve many of the
          // overloaded states.  This segment of code illustrates use of the current PHY state
          // to perform the filtering and is not intended to preclude alternate methods
          portCurrent.tag = ARB_STATE;
          switch (portCurrent.rx_dsarb) {
            case RX_IDLE :
              portCurrent.arb = IDLE;
              break;
            case RX_PARENT_NOTIFY | RX_REQUEST_CANCEL :
              portCurrent.arb = (PHY_state < A0 ? PARENT_NOTIFY : REQUEST_CANCEL);
              break;
            case RX_IDENT_DONE :
              portCurrent.arb = IDENT_DONE;
              break;
            case RX_SELF_ID_GRANT | RX_REQUEST :
              portCurrent.arb = (PHY_state < A0 ? SELF_ID_GRANT : LEGACY_REQUEST);
              break;
            case RX_ROOT_CONTENTION | RX_GRANT | RX_SUSPEND :
              portCurrent.arb = (PHY_state < A0 ? ROOT_CONTENTION : (PHY_state == A0 ? SUSPEND : GRANT));
              break;
            case RX_PARENT_HANDSHAKE | RX_DATA_END :
              portCurrent.arb = (PHY_state < S0 ? PARENT_HANDSHAKE : DATA_END);
              break;
            case RX_CHILD_HANDSHAKE | RX_DISABLE_NOTIFY :
              portCurrent.arb = (PHY_state < A0 ? CHILD_HANDSHAKE : DISABLE_NOTIFY);
              break;
            case RX_DATA_PREFIX :
              portCurrent.arb = (PHY_state < S0 ? portCurrent.arb : DATA_PREFIX);
              break;
            case RX_BUS_RESET :
              portCurrent.arb = BUS_RESET;
              break;
          }
        }

        switch (portCurrent.tag) {
          case ARB_REQUEST:
            receive_req[i] = portCurrent.req;
            portRarb[i] = IDLE;           // ARB_STATE is IDLE when ARB REQUESTS are received
            if (in_packet[i]) {           // sudden end of packet
              next_arb[i] = TRUE;
              packet_ending[i] = TRUE;
            }
            break;
            
          case ARB_STATE:
            if (portCurrent.arb == SPEED) {
              if (!Beta_mode[i] && !(PHY_state == S4 || // filter out speeds received at 
                PHY_state == S2 || PHY_state == S3 || // inappropriate times on DS ports
                PHY_state == A0 || PHY_state == A1 || PHY_state == A2 || PHY_state == RX ))
                break;                    // from dealing with current FIFO item
              portRspeed[i] = portCurrent.speed;
              if (!Beta_mode[i] && 
                (PHY_state == S2 || PHY_state == S3 || PHY_state == S4)) {
                break;                    // DS mode speed exchanges - don't treat as start of packet
              }
              current_pkt[i] = portCurrent.pkt;
            }

            portRarb[i] = portCurrent.arb; // set the current arb state

            switch (portCurrent.arb) {    // deal with all the cases where the fifo is read
                                          // one item at a time for the arb state machine
                                          // and advance the fifo automatically for all the others
              case DATA_PREFIX:
              case DATA_NULL:
              case SPEED:
              case CYCLE_START_ODD:
              case CYCLE_START_EVEN:
                receive_req[i].async = A_NONE; // forget the latest received arb state 
                receive_req[i].isoch = I_NONE;
                if (ignore_port[i]) {     // dealing with a collision
                  collision[i] = TRUE;
                  break;
                }
                if (active[i] && ((PHY_state == RX && (i != receive_port)) || 
                    PHY_state == TX)) {   // allow DP on newly connected ports
                  collision[i] = TRUE;    // data collision, need to keep the rest of the bus
                              // busy until this packet completes
                  ignore_port[i] = TRUE;  // flag to ignore the rest of the packet
                  break;
                }
                if (!in_packet[i]) in_packet[i] = TRUE; // throttle the FIFO
                else {  // already in a packet
                  next_arb[i] = TRUE;  // signal that this is the latest
                  if (portCurrent.arb == DATA_NULL)     // DATA_NULL can end a packet
                    packet_ending[i] = TRUE;
                }
                break;
              case GRANT:                 // may terminate a packet or occur in isolation
              case GRANT_ISOCH:           // may terminate a packet or occur in isolation
                if (ignore_port[i]) {     // dealing with a collision
                  collision[i] = TRUE;
                  break;
                }
                if (in_packet[i]) {
                  next_arb[i] = TRUE;
                  packet_ending[i] = TRUE;
                }
                break;
              case DATA_END:
                if (ignore_port[i]) {     // dealing with a collision
                  collision[i] = TRUE;
                  packet_ending[i] = TRUE;
                  break;
                }
                next_arb[i] = TRUE;
                packet_ending[i] = TRUE;
                break;
              case IDLE:                  // may also cause sudden packet termination
              case BUS_RESET:
              case ARB_CONTEXT:
                receive_req[i].async = A_NONE; // forget the latest received arb state 
                receive_req[i].isoch = I_NONE;
                if (in_packet[i]) {
                  next_arb[i] = TRUE;
                  packet_ending[i] = TRUE;
                  portRspeed[i] = S100;   // and erase memory of speed signal
                }
                break;
              default:
                ignore_port[i] = FALSE;
                break;
            }
            break;                        // finished dealing with ARB states
          case DATA:
            if (ignore_port[i]) {         // dealing with a collision
              collision[i] = TRUE;
              break;
            }
            current_data[i] = portCurrent.data;
            portRarb[i] = DATA_BYTE;
            next_arb[i] = TRUE;
            portRspeed[i] = S100;         // and erase memory of speed signal
            break;
        }                                 // end of dealing with the latest in the fifo
      }
    }
    //combine best async and isoch request from other ports and own request
    if (active[i]) {
      best_req.async = own_req.async;        // prefer link first
      best_req.isoch = own_req.isoch;
      
      apm = (odd_async_phase ? 0xf0 : 0x0f); // select async priority mask
      ipm = (odd_isoch_phase ? 0xf0 : 0x0f); // select isoch priority mask
      bip = bap = NPORT;
      for (j = 0; j < NPORT; j++) {          // then prefer junior ports
        if (j != i && (j != senior_port)) {   // careful to avoid requests on a restoring port
          best_req.async = active[j] && ((receive_req[j].async & apm) > 
              (best_req.async & apm)) ?
            receive_req[bap = j].async : best_req.async;
          best_req.isoch = active[j] && ((receive_req[j].isoch & ipm) > 
              (best_req.isoch & ipm)) ?
            receive_req[bip = j].isoch : best_req.isoch;
        }
      }
      if (!proxy_root && (i != senior_port)) { // finally prefer senior port on other ports
        best_req.async = active[senior_port] && ((receive_req[senior_port].async & apm) > 
            (best_req.async & apm)) ?
          receive_req[bap = senior_port].async : best_req.async;
        best_req.isoch = active[senior_port] && ((receive_req[senior_port].isoch & ipm) > 
            (best_req.isoch & ipm)) ?
          receive_req[bip = senior_port].isoch : best_req.isoch;
      }
      
      arbreqT[i] = best_req;            //different for each port
      if (senior_border && (i == senior_port)) { // need to convert to Legacy and pass any 
                                        // request up to DS parent
        isoch_pending = ((iso_cycle && ((best_req.isoch & ipm) >= (ISOCH_IN_PHASE & ipm))) ||
                         (!iso_cycle && ((best_req.isoch & ipm) == (ISOCH_CURRENT & ipm))));
        async_pending = (!iso_cycle && ((best_req.async & apm) >= (CURRENT & apm)));
        pending_port = iso_cycle ? bip: bap;
                                        // pending_port is meaningless unless either
                                        // isoch_pending or async_pending is TRUE
      } 
    }
  }
  if (!senior_border || B_node_root)  // if no longer a senior border in a junior cloud
                                      // clear pending flags used by arb_OK
    isoch_pending = async_pending = FALSE;
}

boolean gap_token(int port) {            // check for a gap token on the specified port
  switch (portRarb[port]) {
    case ASYNC_START:
      if (sent_async_start_token) return FALSE; // may still be in the top of the FIFO
      send_async_start_token = TRUE;
      return TRUE;
    case ARB_RESET_ODD:
      if (sent_arb_reset) return FALSE;
      odd_async_phase = TRUE;
      arb_reset = TRUE;
      return TRUE;
    case ARB_RESET_EVEN:
      if (sent_arb_reset) return FALSE;
      odd_async_phase = FALSE;
      arb_reset = TRUE;
      return TRUE;
    default:
      return FALSE;
  }
}