// dsport_tx.c

#define unsubscripted

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

dataBit tx_data, tx_strobe;             // Memory of last signal sent on DS ports
speedCode data_speed;

TX_arbstate encode(ArbState arb_state) {
  switch (arb_state) {
    case DATA_END:        return(TX_DATA_END);
    case GRANT:           return(TX_GRANT);
    case DATA_PREFIX:     return(TX_DATA_PREFIX);
    case BUS_RESET:       return(TX_BUS_RESET);
    case CHILD_NOTIFY:    return(TX_CHILD_NOTIFY);
//    case IDENT_DONE:    return(TX_IDENT_DONE);  // identical to CHILD_NOTIFY
    case PARENT_NOTIFY:   return(TX_PARENT_NOTIFY);
    case DISABLE_NOTIFY:  return(TX_DISABLE_NOTIFY);
    case SUSPEND:         return(TX_SUSPEND);
    case LEGACY_REQUEST:  return(TX_REQUEST);
    case IDLE:            return(TX_IDLE);
  }
} 

void ds_tx_bit(dataBit bit_to_send) {   // Transmit a bit on all transmitting DS ports
  int i;
  portData pd;
  //wait for next port bit time
  for (i=0; i < (1 << (DS_PHY_SPEED-data_speed));i++) wait_event(PH_DS_BIT_CLOCK);
  if (bit_to_send == tx_data)           // If no change in data
    tx_strobe = tx_strobe == 0 ? 1 : 0; // Invert strobe
  tx_data = bit_to_send;
  pd.TpA = (tx_strobe == 1)? H:L;
  pd.TpB = (tx_data == 1)? H:L;
  PMD_DSPORT_DATA_request(pd);
}

void ds_tx_byte(byte data_byte) {       // Transmit a byte
  int i;
  for (i = 0; i < 8; i++ )
    ds_tx_bit((data_byte >> (7-i)) & 0b1);
}

void tx_dribble_bits(TX_arbstate ending_status, boolean in_packet) {
  int i;
  if (in_packet) {  // Local port has sent data bits and is required to send real dribble bits
    switch (data_speed) {                 // Bit width of PHY/link interface may require pad bits
      case S400:                          // Pad with six extra (dribble) bits, 8 total
        ds_tx_bit(1);
        ds_tx_bit(1);
        ds_tx_bit(1);
        ds_tx_bit(1);
        ds_tx_bit(1);
        ds_tx_bit(1);
        break;
      case S200:                          // Pad with two extra (dribble) bits, 4 total
        ds_tx_bit(1);
        ds_tx_bit(1);
        break;
      default:
        break;                            // No need for extra (dribble) bits
    }
    ds_tx_bit(ending_status == TX_DATA_PREFIX ? 1 : 0); 
  
    for (i=0; i < (1 << (DS_PHY_SPEED-data_speed));i++) wait_event(PH_DS_BIT_CLOCK);
    PMD_DSPORT_ARB_request(ending_status);           // ...and the last dribble bit
  } else { // Local port hasn't sent any data bits, so continue DATA_PREFIX
           // for the dribble bit duration (equal to 2 S100 bit times)
    for (i=0; i < (2 << (DS_PHY_SPEED-S100));i++) wait_event(PH_DS_BIT_CLOCK);
    PMD_DSPORT_ARB_request(ending_status);
  }
}

void dsport_transmit_actions() {        // continuously running
  int i;
  boolean in_packet;                    // tracks whether local port has sent actual data bits
  while (power_reset || !dsport_on) {
    in_packet = FALSE;
    data_speed = S100;
  }   
  if(portT.tag==DATA) {
    in_packet = TRUE;
    ds_tx_byte(portT.data);
  } else {                              // tag == ARB_STATE
    switch(portT.arb) {
      case DATA_PREFIX:
      case DATA_NULL:
        if (non_null_packet)                  // DP or DN at the end of a packet
          tx_dribble_bits(TX_DATA_PREFIX, in_packet);
        in_packet = FALSE;
        // data_speed is carried over from previous packet if not
        // already set by a speed signal on this packet
        PMD_DSPORT_TXSPEED_request(S100);            // turn off speed signaling if going
        tx_data = 1;                    // Initialize the memory of last signal sent on DS ports
        tx_strobe = 0;
        PMD_DSPORT_ARB_request(TX_DATA_PREFIX);
        break;    
      case SPEED:
        if (non_null_packet)                  // concatenated packet
          tx_dribble_bits(TX_DATA_PREFIX, in_packet);
        in_packet = FALSE;
        data_speed = portT.speed;       // start tx of speed and memorize it
        PMD_DSPORT_ARB_request(TX_DATA_PREFIX);
        PMD_DSPORT_TXSPEED_request(data_speed);
        break;
      case SPEED_RAW:                   // control speed signal drivers directly (for self_ID)
        PMD_DSPORT_TXSPEED_request(portT.speed);
        break;
      case DATA_END:
        if (non_null_packet)  // add dribble bits unless a NULL packet
          tx_dribble_bits(TX_DATA_END, in_packet);
        in_packet = FALSE;
        data_speed = S100;              // ready for next packet
        PMD_DSPORT_ARB_request(TX_DATA_END);
        break;
      case IDLE:
      case ARB_CONTEXT:
        in_packet = FALSE;
        data_speed = S100;
        PMD_DSPORT_ARB_request(TX_IDLE);
        break;
      default:
        PMD_DSPORT_ARB_request(encode(portT.arb));
        break;
    }
  }
}
