// transmit_actions.c

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

void transmit_actions() {               // Send a packet as link transfers it to the PHY
  int byte_count = 0, i;
  PH_data_req_service data_to_transmit;
  PHY_PKT tx_phy_pkt;
  BetaRequestCode best_req;
  ArbState grant_type;
  pktType next_format;                  // holds format and speed of packet PHY
  speedCode next_speed;                 // is preparing to transmit

  ack = end_of_packet = FALSE;
  requesting_port = NPORT;
  receive_port = NPORT;                 // Impossible port number == > PHY transmitting
  for (i = 0; i < NPORT; i++)
    collision[i] = FALSE;               // may be set true again immediately if collision 
                                        // condition persists
  resolve_collision_request = FALSE;

  if (restore_request || loop_test_request) {
    // A connection management request is pending.  Before granting, check that it is the
    // highest priority request present, considering that earlier higher priority requests
    // may have been withdrawn or canceled.  (Consequently, the current bus phase may not
    // be appropriate for connection management packets.)
    if (!(isbr_OK           || 
          send_null_packet  ||
          iso_cycle         ||
          immediate_link_request || immediate_phy_request ||
          !grant_self       || // Important to catch case of immediate_request forcing
                               // transition A0:TX, but then being withdrawn.  In that
                               // case, a connection management request should not be
                               // granted.  Note that grant_self will not be set true
                               // in A0 if an immediate_request causes the A0:TX
                               // transition
          ((link == Legacy_Link) && link_concatenation)  ||
          ((link == Legacy_Link) && breq == ISOCH_REQ)   ||
          ((link != Legacy_Link) && cycle_start_request) ||
          (!B_bus && (cur_format == LEGACY) && (cur_speed != S100)))) {
      // No higher priority request was found and the current bus phase and previous
      // packet history is appropriate for servicing the connection management request
      con_mgmnt_granted();
      grant_self = FALSE;
      end_of_packet = TRUE;
      return;
    }
  }

  grant_self = FALSE;

  // Determine which request(s) caused entry to TX and set next_format and next_speed accordingly.
  // Since some link requests can be canceled and some internal PHY requests can be
  // asynchronously withdrawn, it is possible that no requests will test true at this instant.
  // In such a case, a null packet shall be transmitted to terminate cleanly.

  if (link == Legacy_Link) {
    if (link_concatenation) {
      next_format = LEGACY;
      next_speed = req_speed;
    }
    else if (isbr_OK || send_null_packet) 
      ; // next_speed and next_format will be set later
    else if (immediate_request  ||   // OK for both immediate link and immediate phy requests
             breq == ISOCH_REQ  ||
             (!iso_cycle && breq != NO_REQ)) {
      next_format = LEGACY;  // A valid pending link request was found, set the format.
      next_speed = req_speed;
    }
    else  // Reason for entering TX:Transmit state is no longer true, possibly
          // due to canceled link requests or loop test requests
      send_null_packet = TRUE;
  }
  else {   // not a Legacy_Link
    if (isbr_OK || send_null_packet)
      ; // req_speed and format will be set later
    else if (immediate_request) {   // OK for both immediate link and immediate phy requests
      next_format = imm_format;
      next_speed = imm_speed;
    }
    else if (iso_cycle && (isoch_odd_request || isoch_even_request)) {
      next_format = i_format;
      next_speed = i_speed;
    }
    else if (!iso_cycle && cycle_start_request) {
      next_format = cyc_start_format;
      next_speed = cyc_start_speed;
    }
    else if (!iso_cycle &&
             (current_request || 
              (next_odd_request && odd_async_phase) ||
              (next_even_request && !odd_async_phase))) {
      next_format = a_format;
      next_speed = a_speed;
    }
    else  // Reason for entering TX:Transmit state is no longer true, possibly
          // due to canceled link requests or loop test requests.  Clean up with
          // a null packet
      send_null_packet = TRUE;
  }

  // Now set format and speed for short bus reset or null packet prefix
  if (!link_concatenation && (send_null_packet || isbr_OK)) 
    if (B_bus) {                        // dealing with canceled requests
      next_format = BETA;
      next_speed = PHY_SPEED;
    } else {                            // canceled request or DS_stuck
      next_format = LEGACY;
      next_speed = S100;
    }
  
  fork 
  {

    if (send_null_packet && !(link == Legacy_Link && link_concatenation)) {    // PHY originated packet
      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(next_speed, next_format); // Send data prefix & speed signal
    immediate_phy_request = FALSE;
    if (isbr_OK)                       // Avoid phantom packets...
      return;                           // note, end_of_packet not set to ensure mutual exclusion on exit 
    if (send_null_packet && !((link == Legacy_Link) && link_concatenation)) {
      // requested by boss in order to clean up DATA_PREFIX on
      // DS ports after sending a series of Beta format packets
      send_null_packet = FALSE;
      PH_DATA_indication(PH_DATA_END, 0, 0, 0);
      boss_end_packet_actions(FALSE, DATA_END);  // check for isoch to do
      end_of_packet = TRUE;
      return;
    }
 
    // It is time to pass grant to the link.  Verify that the link requests have
    // not been withdrawn.  If they have been withdrawn, clean up with a null packet.
    if (link == Legacy_Link) {
      if (link_concatenation || immediate_link_request || breq != NO_REQ) {
        PH_ARB_confirmation(PH_WON, 0, 0); // Signal grant on Ctl[0:1]
        if (breq == FAIR_REQ)        // Just used our one fair request per fairness interval?
          arb_enable = FALSE;        // Yes, clear permission (set again on next reset gap)
        breq = NO_REQ;
        immediate_link_request = FALSE;
        link_concatenation = FALSE;
      }
      else {  // link request was withdrawn at last instant, terminate packet
        boss_end_packet_actions(FALSE, DATA_END);  // check for isoch to do
        end_of_packet = TRUE;
        return;
      }
    }
    else { // not a Legacy_Link
      if (immediate_link_request) {
        PH_ARB_confirmation(PH_WON_IMMED, cur_speed, imm_link_format); 
        immediate_link_request = FALSE;
      }
      else if (isoch_odd_request || isoch_even_request) {
        PH_ARB_confirmation(PH_WON_ISOCH, cur_speed, i_link_format); 
        isoch_odd_request = isoch_even_request = FALSE;
      }
      else if (cycle_start_request) {
        PH_ARB_confirmation(PH_WON_CYCLE_START, cur_speed, cyc_start_link_format); 
        cycle_start_request = FALSE;
      }
      else if (current_request || next_odd_request || next_even_request) {
        PH_ARB_confirmation(PH_WON_ASYNC, cur_speed, a_link_format); 
        current_request = next_odd_request = next_even_request = FALSE;
      }
      else {  // link request was withdrawn at last instant, terminate packet
        boss_end_packet_actions(FALSE, DATA_END);  // check for isoch to do
        end_of_packet = TRUE;
        return;
      }
    }

    while (!end_of_packet) {
      do {                              // Wait for data or release from the link
        PH_CLOCK_indication();
        data_to_transmit = waitPH_DATA_request(); 
      } while (data_to_transmit.reqType == PH_REQ_HOLD);
        // Hold only valid before data starts
      if (data_to_transmit.reqType == PH_REQ_DATA) {
        for (i = 0; i<NPORT; i++)       // Send DATA on all ports
          tx_byte(data_to_transmit.data);
        if (byte_count < 8)             // Accumulate possible PHY packet
          tx_phy_pkt.dataBytes[byte_count] = data_to_transmit.data;
        byte_count++;
        ack = (byte_count == 1);      // For acceleration, any 8-bit packet is an ack
      } else if (data_to_transmit.reqType == PH_REQ_DATA_PREFIX) {  
        // concatenated packets from Legacy link
        end_of_packet = link_concatenation = TRUE; // End of packet indicator
        grant_self = TRUE; // Force TX:TX transition
        stop_tx_packet(DATA_NULL, NPORT);
         // MIN_PACKET_SEPARATION
        req_speed = data_to_transmit.speed;
      } else {
        if (iso_cycle)                // Implicit or explicit subaction end
          grant_type = GRANT_ISOCH;
        else if (ack || (data_to_transmit.reqType == PH_REQ_SUBACTION_END))  
                                      // ACK packet can always be
          grant_type = GRANT;         // converted to an explicit asynchronous grant
        else
          grant_type = DATA_END;      // quiet grant
        boss_end_packet_actions(FALSE, grant_type);
        end_of_packet = TRUE;         // End of packet indicator
      }
    }
  }
  { // ensure that at least one data_null gets out on all ports
    // by preventing exit from TX, unless there's more data from somewhere
    if (!B_bus)
      for (i = 0; i<(1<<(PHY_SPEED - min_operating_speed)); i++) {
        if (data_coming()) break;
        // wait long enough to send one more character
        wait_symbol_time(PHY_SPEED);
      }
  }
  join

  if ((byte_count == 8) && (tx_phy_pkt.dataQuadlet == ~tx_phy_pkt.checkQuadlet))
    // Check PHY packet for good format
    decode_phy_packet(tx_phy_pkt);      // Parse valid phy packets
}
