// bport_tx.c
#define unsubscripted

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

boolean disable_scrambler;              // value of disable_scrambler register, when TRUE, disables use 
                                        // of the scrambler outputs for scrambling packet data
                                        // maintained as a bit in the port's register map
portSymbol localT;                      // variable that indicates signal to be sent on port
int scram;                              // represents the rightmost 8 bits of scrambler state
int scram_old;                          // represents present state of scrambler
disparityType tx_rd;                    // disparity of transmitted character stream, takes values
                                        // negative_rd and positive_rd (encoded as 0 and 1 respectively)

// first, useful functions and routines

int control_symbol_map(ArbState txctrl, disparityType rd) {
      // Returns a numerical representation for a control token
  switch(txctrl) {
    case ASYNC_START: return(0b0000); break;
    case CYCLE_START_EVEN: return(0b0001); break;
    case CYCLE_START_ODD: return(0b0010); break;
    case ATTACH_REQUEST: return (0b0011); break; // ARB_CONTEXT overloaded here
    case SPEEDa: return(0b0100); break;
    case DATA_END: return(0b0101) ; break;
    case DATA_NULL: return(0b0110); break;
    case SPEEDb: return(0b0111); break;
    case GRANT: return(0b1000); break;
    case DATA_PREFIX: if(rd==positive_rd) return(0b1001); else return(0b1010); break;
    case GRANT_ISOCH: return(0b1011); break;
    case SPEEDc: return(0b1100); break;
    case ARB_RESET_EVEN: return(0b1101); break;
    case ARB_RESET_ODD: return(0b1110); break;
    case BUS_RESET: return(0b1111); break;
  }
}

int arb_req_symbol_map(BetaRequestCode txrequest) {
  int async_part;                       // numerical representation of the asynchronous request type
  int isoch_part;                       // numerical representation of the isochronous request type
  
  switch(txrequest.async) {
    case CURRENT: async_part=0b001; break;
    case NEXT_EVEN: async_part=0b010; break;
    case CYCLE_START_REQ: async_part=0b011; break;
    case NONE_ODD: async_part=0b100; break;
    case NEXT_ODD: async_part=0b101; break;
    case NONE_EVEN: async_part=0b110; break;
    case BORDER: async_part=0b111; break;
  }
  switch(txrequest.isoch) {
    case ISOCH_CURRENT: isoch_part=0b001; break;
    case ISOCH_EVEN: isoch_part=0b100; break;
    case ISOCH_NONE: isoch_part=0b010; break;
    case ISOCH_ODD: isoch_part=0b110; break;
  }
  return((isoch_part&0b001)|((isoch_part&0b110)<<2) | (async_part<<5));
}

int config_req_symbol_map(ArbState txrequest) {
      // Returns a numerical representation for a configuration request
  switch(txrequest) {
    case TRAINING: return(0b00000000); break;
    case DISABLE_NOTIFY: return(0b00100000); break;
    case CHILD_NOTIFY | IDENT_DONE : return(0b01000000); break;
    case OPERATION: return(0b01100000); break;
    case STANDBY: return(0b10000000); break;
    case SUSPEND: return(0b10100000); break;
    case PARENT_NOTIFY: return(0b11000000); break;
    case LEGACY_REQUEST: return(0b11100000); break;
  }
}

void update_scrambler() {
  // updates the state of the transmitter scrambler, performing 8 shift register operations
  int i;
  int scram_new;                        // represents next state
  scram_new = scram_old;
  // least significant bit is the newest bit in the scrambler
  for (i=0; i<8; i++) {
     scram_new = scram_new << 1;
     scram_new = scram_new | (((scram_old & 0x400) >> 10) ^
                             ((scram_old & 0x100) >> 8));
     scram_old = scram_new; 
  }
  
  scram = scram_old & 0x0FF;            // used for XORing with input byte

}

void tx_character(portSymbol tx) {
  int i, j;
  int tx_ctrl;                          // 4 bit representation of control state
  int tx_req;                           // 8 bit request symbol
  int tx_scram_ctrl;                    // scrambled control symbol
  int tx_scram_data;                    // scrambled data byte
  int tx_scram_req;                     // scrambled request type
  int character_out;                    // 10 bit character
  dataBit bit_to_send;
  
  if (tx.tag==DATA) {
    if (disable_scrambler) tx_scram_data = tx.data; // test mode for TX jitter test pattern
    else tx_scram_data=tx.data^scram;   //scramble the data byte
    character_out=data_table[tx_scram_data][tx_rd]; //lookup character
  } else if(tx.tag==CTRL) {
    tx_ctrl=control_symbol_map(tx.arb, tx_rd);
    tx_scram_ctrl=tx_ctrl^((scram&0x80)>>4 | (scram&0x20)>>3 | (scram&0x8)>>2 | (scram&0x2)>>1);
    character_out=control_table[tx_scram_ctrl];
  } else {
    if(tx.tag==ARB_REQUEST) tx_req=arb_req_symbol_map(tx.req);
    else if(tx.tag==CONFIG_REQUEST) tx_req=config_req_symbol_map(tx.arb);
    tx_scram_req=(tx_req^scram) & 0xF9;
    if(tx.tag==CONFIG_REQUEST && (tx.arb==TRAINING || tx.arb==OPERATION) && tx_scram_req==0x38) // D28.0
      character_out=comma_table[tx_rd];
    else character_out=data_table[tx_scram_req][tx_rd]; //lookup character
  }
  
  update_scrambler();       
  if (tx.tag!=CTRL) 
    tx_rd=update_rd(character_out, tx_rd);
  
  for(i=0;i<10;i++) {
    bit_to_send = 0;
    if ((character_out & 0x200) != 0) bit_to_send = 1; //send msb first
    PMD_DATA_request(bit_to_send); 
    character_out = character_out <<1;
    //wait for next port bit time
    for (j=0; j < (1 << (PHY_SPEED-port_speed));j++) wait_event(PH_BIT_CLOCK);
  } 
}

void tx_speed_signal(speedCode tx_speed, pktType pkt_type) {
  int i;
  int j;
  j=port_speed - tx_speed;
  localT.tag=CTRL;    
  for(i=0; i < 1<<(port_speed - tx_speed); i++) {
    if(i==j && pkt_type==LEGACY)
      localT.arb=SPEEDa;                // SPEEDa denotes packet is legacy format
    else if(i==j && pkt_type==BETA)
      localT.arb=SPEEDb;                // SPEEDb denotes packet is Beta format
    else
      localT.arb=SPEEDc;
    tx_character(localT); 
  }
}

void tx_off_actions() {
  while (power_reset)
    // scram_old shall be initialized to any value other than zero on power up 
    scram_old = 0x7FF;
  bport_sync_ok=FALSE;
}

void tx_sync_lost_actions() {

  tx_rd=negative_rd;                    // initialization of rd
  while(bport_on && sync_lost_signal) {
    bport_sync_ok=FALSE;
    localT.tag=CONFIG_REQUEST;
    localT.arb=TRAINING;
    tx_character(localT);
  }
}

void tx_sync_actions() {
  while(bport_on && sync_error_signal && !sync_lost_signal) {
    localT.tag=CONFIG_REQUEST;
    localT.arb=OPERATION;
    tx_character(localT);
  }
  bport_sync_ok=!sync_error_signal;
}

void bport_transmit_actions() {
  speedCode tx_speed;                   // speed of each packet.
  int tx_speed_ratio;                   // number of symbols per byte for given pkt_speed and port_speed
  int i;
  pktType pkt_type;                     // local record of current packet type
  
  while(!sync_lost_signal && bport_on) {
    if(portT.speed == DEFAULT) 
      tx_speed = port_speed;
    else 
      tx_speed = portT.speed;
    tx_speed_ratio = 1<<(port_speed - tx_speed);
    if((portT.tag==ARB_STATE) && (portT.arb == SPEED)) //port always sends a speed signal if requested 
                                        // to do so
      tx_speed_signal(tx_speed, portT.pkt);
    else if(portT.tag==DATA) {
      tx_character(portT);
      localT.tag=CTRL; 
      localT.arb=SPEEDc;
      for(i=1; i<tx_speed_ratio; i++) 
        tx_character(localT);           // send padding if required
    } else {
      localT = portT;                   // remember portT in case it goes away before stretching is done
      if (localT.tag==ARB_STATE)        // decode arbstates into control and config requests
        localT.tag = localT.arb < TRAINING ? CTRL : CONFIG_REQUEST;
      if ((localT.tag==CTRL) && (localT.arb == IDLE)) {
        localT.tag = ARB_REQUEST;
        localT.req = arbreqT;           // send the symbol defined by the background processing
      }
      tx_character(localT);
      for(i=1; i<tx_speed_ratio; i++) 
        tx_character(localT);           // stretch as required
    }
  }
}
