// bport_rx.c
#define unsubscripted

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

// variables global to two or more routines in the receive state machine

int char_check;
int character_in;
int descram;                            // rightmost 8 bits of descrambler state
int descram_old;                        // represents present state of descrambler
#define DESCRAM_TRAIN_CYCLES 22
disparityType disparity_indicator;      // disparity indicated by a received DATA_PREFIX packet delimiter
int invalid_count;
int last_character_in;
portSymbol last_localR;                 // record of last localR
portSymbol localR;                      // record of decoded received symbol
int pad_ratio;
boolean pkt;                            // when true, port is receiving a packet
boolean pkt_prefix;                     // when true, port is receiving packet prefix
int polarity;                           // when 1, the polarity of received character stream is  
                                        // deliberately inverted by port.
int port_error_reg;                     // record of number of coding errors detected
boolean rx_comma;                       // true when the most recently received character was a K28.5
speedCode rx_pkt_speed;                 // speed of received packet
pktType rx_pkt_type;                    // type of packet format received
disparityType rx_rd;                    // disparity of received character stream, takes values 
                                        // negative_rd and positive_rd (encoded as 0 and 1 respectively)
int rx_req;                             // request type value
boolean rx_req_code;                    // true when the most recently received character was a Dx.0 or Dx.4
int rx_scram_req;                       // scrambled request type value
int rx_speed_diff;
boolean rx_sync_error;                  // TRUE if failed to complete synchronization
boolean scrambler_sync;
boolean scrambler_sync_error;           // TRUE if unexpected request type is received 
                                        // during scrambler synchronization
boolean speed_known;                    // true after Sa or Sb has been received or first byte of 
                                        // packet withouta speed signal (S100 Legacy packet only)
#define SYNC_CHECK 17       
boolean train_descrambler;              // when true receiver should train scrambler 
                                        // using samples from received signals
int valid_count;

// first, useful functions and routines

// shared between bport_rx and bport_tx
disparityType update_rd(int character, disparityType rd) {
  int i, no_of_ones=0;
  
  for (i=0; i<6; i++) 
    if (((character <<i) & 0x200) != 0) no_of_ones = no_of_ones + 1;
  if (no_of_ones>3 || (character & 0x3F0)==0x070) 
    rd = positive_rd;                   // rd is positive if 6 msb's are 000111
  else if (no_of_ones<3 || (character & 0x3F0)==0x380) 
    rd = negative_rd;                   // rd is negative if 6 msb's are 111000
  no_of_ones=0;
  for (i=6; i<10; i++) 
    if (((character <<i) & 0x200) != 0) no_of_ones = no_of_ones + 1;
  if (no_of_ones>2 || (character & 0xF)==0x3) rd = positive_rd; // rd is positive if 4 lsb's are 0011
  else if (no_of_ones<2 || (character & 0xF)==0xC) rd = negative_rd;// rd is negative if 4 lsb's are 1100
  return (rd);
}

// shared between Beta and DS receive ports
void push(portSymbol symbol_to_push) {
  // Regardless of whether the FIFO is full, always write the latest symbol to FIFO
  // (overwriting the last written element, if necessary) so that a final packet ending
  // state is guaranteed to be pushed into the FIFO.  Take care, however, to prevent the 
  // write pointer from advancing to become equal to the read pointer which would 
  // signify an empty FIFO.
  int new_pointer = (fifo_wr_ptr + 1) % FIFO_DEPTH;
  if (new_pointer != fifo_rd_ptr) fifo_wr_ptr = new_pointer;
  portR[fifo_wr_ptr]=symbol_to_push;
}

// Called whenever a data byte was expected, but not received.  To prevent
// the fifo unloading side from experiencing an underrun due to 8b/10b bit
// errors, some data is pushed into the fifo for every byte at the packet
// rate.  When an expected data byte isn't found, this routine synthesizes
// a valid data byte by way of example.  This implementation isn't intended
// to preclude the selection of other synthesized symbols including other
// data values or other characters. 
void push_data_zero() {
  portSymbol data_symbol;
  data_symbol.tag=DATA;
  data_symbol.data=0x00;
  push(data_symbol);
}

boolean port_error_monitor() {

  if(localR.tag==INVALID) {
    invalid_count=(invalid_count==4? 4:invalid_count++);
    valid_count=0;
  } else {
    if(invalid_count>0) {   
      valid_count=valid_count++;  
      if(valid_count>1) {     
        invalid_count=invalid_count-1;  // If second consecutive valid then decrement invalid counter 
        valid_count=0;  
      }   
    }
  }
  return (invalid_count==4);   
    // Sync lost on reaching threshold of four, causing receiver to enter rx sync lost state
}

void rx_control_map(int rx_ctrl) {

  switch(rx_ctrl) {
    case 0b0000: localR.tag=ARB_STATE; localR.arb=ASYNC_START; break;
    case 0b0001: localR.tag=ARB_STATE; localR.arb=CYCLE_START_EVEN; break;
    case 0b0010: localR.tag=ARB_STATE; localR.arb=CYCLE_START_ODD; break;
    case 0b0011: localR.tag=ARB_STATE; localR.arb=ATTACH_REQUEST; break;
    case 0b0100: localR.tag=ARB_STATE; localR.arb=SPEEDa; break;
    case 0b0101: localR.tag=ARB_STATE; localR.arb=DATA_END; break;
    case 0b0110: localR.tag=ARB_STATE; localR.arb=DATA_NULL; break;
    case 0b0111: localR.tag=ARB_STATE; localR.arb=SPEEDb; break;
    case 0b1000: localR.tag=ARB_STATE; localR.arb=GRANT; break;
    case 0b1001: localR.tag=ARB_STATE; localR.arb=DATA_PREFIX; disparity_indicator=positive_rd; break;
    case 0b1010: localR.tag=ARB_STATE; localR.arb=DATA_PREFIX; disparity_indicator=negative_rd;break;
    case 0b1011: localR.tag=ARB_STATE; localR.arb=GRANT_ISOCH; break;
    case 0b1100: localR.tag=ARB_STATE; localR.arb=SPEEDc; break;
    case 0b1101: localR.tag=ARB_STATE; localR.arb=ARB_RESET_EVEN; break;
    case 0b1110: localR.tag=ARB_STATE; localR.arb=ARB_RESET_ODD; break;
    case 0b1111: localR.tag=ARB_STATE; localR.arb=BUS_RESET; break;
  }
}

void request_type_map(int request) {
  int async_part;                       // numerical representation of the asynchronous request type
  int isoch_part;                       // numerical representation of the isochronous request type
  BetaRequestCode rxrequest;
  
  async_part = (request & 0xE0) >> 5;
  isoch_part = (request & 0b01) | ((request & 0b11000) >> 2);
  if(isoch_part==0) {                   // configuration request type  
    switch(async_part) {
      case 0b000: localR.tag=CONFIG_REQUEST; localR.arb=TRAINING; break;
      case 0b001: localR.tag=CONFIG_REQUEST; localR.arb=DISABLE_NOTIFY; break;
      case 0b010: localR.tag=CONFIG_REQUEST; localR.arb=CHILD_NOTIFY | IDENT_DONE ; break;
      case 0b011: localR.tag=CONFIG_REQUEST; localR.arb=OPERATION; break;
      case 0b100: localR.tag=CONFIG_REQUEST; localR.arb=STANDBY; break;
      case 0b101: localR.tag=CONFIG_REQUEST; localR.arb=SUSPEND; break;
      case 0b110: localR.tag=CONFIG_REQUEST; localR.arb=PARENT_NOTIFY; break;
      case 0b111: localR.tag=CONFIG_REQUEST; localR.arb=LEGACY_REQUEST; break;
    }
  } else {
    switch(async_part) {
      case 0b000: localR.tag=INVALID; break;
      case 0b001: localR.tag=ARB_REQUEST; rxrequest.async=CURRENT; break;
      case 0b010: localR.tag=ARB_REQUEST; rxrequest.async=NEXT_EVEN; break;
      case 0b011: localR.tag=ARB_REQUEST; rxrequest.async=CYCLE_START_REQ; break;
      case 0b100: localR.tag=ARB_REQUEST; rxrequest.async=NONE_ODD; break;
      case 0b101: localR.tag=ARB_REQUEST; rxrequest.async=NEXT_ODD; break;
      case 0b110: localR.tag=ARB_REQUEST; rxrequest.async=NONE_EVEN; break;
      case 0b111: localR.tag=ARB_REQUEST; rxrequest.async=BORDER; break;
    }
    if (localR.tag != INVALID) {
      switch(isoch_part) {
        case 0b000: localR.tag=INVALID; break;
        case 0b001: localR.tag=ARB_REQUEST; rxrequest.isoch=ISOCH_CURRENT; break;
        case 0b010: localR.tag=ARB_REQUEST; rxrequest.isoch=ISOCH_NONE; break;
        case 0b011: localR.tag=INVALID; break;
        case 0b100: localR.tag=ARB_REQUEST; rxrequest.isoch=ISOCH_EVEN; break;
        case 0b101: localR.tag=INVALID; break;
        case 0b110: localR.tag=ARB_REQUEST; rxrequest.isoch=ISOCH_ODD; break;
        case 0b111: localR.tag=INVALID; break;
      }
    }
    localR.req=rxrequest;
  }
}

int rx_control_decode(int character_in) {
  int i;
  
  for (i=0; i<16; i++) {
    if (character_in==control_table[i]) return(i); // check for a Cz
  }
  return(-1);
}

int rx_reqtype_decode(int character_in, disparityType rd) {
  int i;
  
  for (i=0; i<256; i++) {
    if (character_in==data_table[i][rd]) return((i & 0b110) == 0 ? i: -1); // check Dx.0's and Dx.4's
  }
  return(-1);
}

int rx_data_decode(int character_in, disparityType rd) {
  int i;
  
  for (i=0; i<256; i++) {
    if (character_in==data_table[i][rd]) return(i); // check all Dx.y's
  }
  return(-1);
}

int rx_comma_decode(int character_in, disparityType rd) {

  if (character_in==comma_table[rd]) return(0x38); // check for a K28.5, and, if so,
                                        // decode as if D28.0 (NB bits reversed)
  else return(-1);
}

void update_descrambler() {
  int i;
  int descram_new;  // next state 
  
  if (train_descrambler  && (rx_scram_req >= 0))
     descram_old = (descram_old & 0x7FE) | (rx_scram_req & 0x001);
  // replacing rx_scram_req bit 0 (from the right) into descram_old when training descrambler
  descram_new = descram_old;
  
  for (i=0; i<8; i++) {
    descram_new = descram_new << 1;
      descram_new = descram_new | (((descram_old & 0x400) >> 10) ^
                                  ((descram_old & 0x100) >> 8));
      descram_old = descram_new;
  }
  
  descram = descram_old & 0x0FF;        // used for XORing with input byte
}

boolean rx_character() {
  
  int i;
  PMD_data_ind_service PMD_data;
  dataBit received_bit;
  int rx_scram_data;
  int rx_scram_ctrl;
  int rx_control;
  
  last_localR=localR;
  last_character_in=character_in;
  rx_comma=FALSE;
  rx_req_code=FALSE;
  character_in=0;
  for (i=0;i<10;i++) {
    PMD_data = waitPMD_DATA_indication();
    received_bit = PMD_data.data;
    character_in = (character_in << 1) | (received_bit ^ polarity);
  }
  
  rx_scram_ctrl = rx_scram_req = rx_scram_data = -1;  // Reset remaining result from last character
  if((rx_scram_ctrl=rx_control_decode(character_in))>=0) {
    rx_control=(rx_scram_ctrl^(((descram&0x80)>>4) | ((descram&0x20)>>3) | 
      ((descram&0x8)>>2) | ((descram&0x2)>>1)));
    rx_control_map(rx_control);
  } else if(((rx_scram_req=rx_reqtype_decode(character_in,rx_rd)) >=0) && !pkt && !pkt_prefix){
    rx_req_code=TRUE;
    rx_req=(rx_scram_req ^ descram) & 0xF9;
    request_type_map(rx_req);
  } else if(((rx_scram_data=rx_data_decode(character_in,rx_rd))>=0) && (pkt || pkt_prefix) ) {
    localR.tag=DATA;
    localR.data=rx_scram_data ^ descram;
  } else if(((rx_scram_req=rx_comma_decode(character_in, rx_rd))>=0) && !pkt && !pkt_prefix) {
    rx_comma=TRUE;
    rx_req_code=TRUE;
    rx_req=(rx_scram_req ^ descram) & 0xF9;
    request_type_map(rx_req);
  } else 
    localR.tag=INVALID;
  
  update_descrambler();
  if (rx_scram_ctrl < 0) 
    rx_rd=update_rd(character_in, rx_rd);
    // control characters are all neutral disparity.
    // update_rd() should never be used for control characters.
  if (localR.tag == ARB_STATE && localR.arb==DATA_PREFIX)
    rx_rd=disparity_indicator;
    // rd is always reset when a data prefix symbol is received.
  return(port_error_monitor());         // check that synchronization is OK
}

boolean character_sync() {

  rx_character();
  if (rx_req_code)  
    char_check=(char_check==3? 3:char_check++);
  else 
    char_check=0;
  return (rx_comma && (char_check==3));
}


boolean bspeed_filter() {
  if (pkt) return(FALSE);
  
  if(localR.tag==ARB_STATE && localR.arb==SPEEDc) {
    rx_speed_diff=rx_speed_diff++;    // for every SPEEDc increase the speed ratio...
  } else if(localR.tag==ARB_STATE && localR.arb==SPEEDa) {
    rx_pkt_speed=port_speed-rx_speed_diff; // actual speed is a function of the port speed
    pad_ratio = (1 << rx_speed_diff);
    rx_speed_diff = 0;                  // ready for reading the next speed code
    rx_pkt_type=LEGACY;                 // SPEEDa indicates a Legacy packet.
    speed_known=TRUE;
    return(TRUE);
  } else if(localR.tag==ARB_STATE && localR.arb==SPEEDb) {
    rx_pkt_speed=port_speed-rx_speed_diff; // actual speed is a function of the port speed
    pad_ratio = (1 << rx_speed_diff);
    rx_speed_diff = 0;                  // ready for reading the next speed code
    rx_pkt_type=BETA;                   // SPEEDb indicates a Beta packet.
    speed_known=TRUE;
    return(TRUE);
  }
  return(FALSE);
}

void train_character_sync() {           // informative only: method is beyond scope of this standard.

  int i, j;
  PMD_data_ind_service PMD_data;
  dataBit received_bit;
  int rx_bits;                          // holds serial bit history for comma detection
                                        // Only 16 bits are needed since the comma is 7 bits in length
                  
  rx_bits=(last_character_in << 6) |  (character_in >> 4);
  j = 0;
  for (i=0; i<10; i++) {
    if((rx_bits>>9 == 0b0011111) || (rx_bits>>9 == 0b1100000)) {
      j = i;
      break;
    }
    rx_bits = (rx_bits << 1) & 0xFFFF;
  }
  
  // if a comma was found and it was not properly aligned, j will be non-
  // zero and will represent the distance from the comma to the next
  // symbol boundary.
  
  for (i=1; i<=j; i++) {
    PMD_data = waitPMD_DATA_indication();
    received_bit = PMD_data.data;
    character_in = (character_in << 1) | (received_bit ^ polarity);
  }
}

// and now the main actions

void rx_off_actions() {
  while (power_reset) {
    fifo_wr_ptr = 0;
  }
  sync_lost_signal=TRUE;                // Ensures transmit state machine will stay in PTX1 until the 
                                        // receive state machine has achieved character sync
  sync_error_signal=TRUE;               // Ensures transmit state machine will stay in PTX2 after character
                                        // sync until the receive state machine has detected remote 
                                        // synchronization
  valid_count = invalid_count = 0;      // count of invalid symbols - used in a monitor with hysteresis
                                        // invalid_count will increase to 4 during synchronization, and
                                        // then decrease to 0 during PRX3: Local Sync
  polarity = 0;                         // reset polarity to "not inverting" 
  character_in = last_character_in = 0;
  descram = descram_old = 0;
  localR.tag = last_localR.tag = INVALID;
  pkt = pkt_prefix = FALSE;
  train_descrambler = FALSE;
}

void rx_sync_lost_actions() {

  sync_lost_signal=TRUE;                // signal transmit channel to send training request.
  rx_rd=negative_rd;                    // initialization of rd
  char_check = 0;                       // initialize char_check count
  while (!character_sync() && bport_on)
    train_character_sync();             // acquire character synchronization 
}

void scrambler_sync_actions() {
  int i;
  int sync_counter;
  
  sync_counter=0;
  scrambler_sync=FALSE;
  scrambler_sync_error=FALSE;
  
  train_descrambler=TRUE;
  i=0;
  while(bport_on && i<DESCRAM_TRAIN_CYCLES) {
    rx_character();
    i++;
  }
  train_descrambler=FALSE;
                // Following routine checks for a valid sequence of either training
                // or operation requests to occur before moving on.
  while(bport_on && !scrambler_sync_error && !scrambler_sync) {
    rx_character();
    if(localR.tag==CONFIG_REQUEST && localR.arb==TRAINING) {
      if ((last_localR.tag==CONFIG_REQUEST) && (last_localR.arb==TRAINING)) 
        // i.e. part of a consecutive stream
        sync_counter++;
      else 
        sync_counter=0;                 // i.e. isolated ocurrence
    } else if(localR.tag==CONFIG_REQUEST && localR.arb==OPERATION) {
      if ((last_localR.tag==CONFIG_REQUEST) && (last_localR.arb==OPERATION))
        // i.e. part of a consecutive stream   
        sync_counter++;
      else 
        sync_counter=0;                 // i.e. isolated ocurrence
    } else if(((rx_req == 0xF8) || (rx_req == 0x98)) && rx_req_code) { 
        // bits ABCDE will sometimes be inverted if the connection results in a polarity error
        // FG are always forced to zero, but as FGH use polarity neutral codes
        // H will never be inverted, given scrambler sync
      sync_counter=0;
      polarity = polarity == 0 ? 1: 0;
      rx_rd = (rx_rd == negative_rd) ? positive_rd : negative_rd;
    } else 
      scrambler_sync_error=TRUE;        // If any other request type is received then restart training
    
        // If there have been SYNC_CHECK consecutive occurences of TRAINING(request) received,
        // or if there have been SYNC_CHECK consecutive occurences of OPERATION(request)
        // received, then receiver is considered to be synchronized.
  
    if(sync_counter==SYNC_CHECK) {
      scrambler_sync=TRUE;              // condition for transition to rx_sync_done state.
      sync_counter=0;         
    }
  }
}

void rx_sync_actions() {
  int sync_counter;
  boolean remote_sync_lost;
  
  sync_lost_signal = FALSE;             // causes transmitter to send OPERATION request and 
                                        // indicates to receiver that lock has been achieved
  remote_sync_lost=TRUE;                // receiver does not assume peer node is synchronized 
                                        // until following checks are complete
  rx_sync_error=FALSE;          
          // Following routine checks for a sequence of valid OPERATION request
          // symbols. This indicates that the connected port is also synchronized and 
          // the check is required to occur before receiver transitions to receive state.
  sync_counter=0;
  while(bport_on && !rx_sync_error && remote_sync_lost) {
    rx_character();
    if(localR.tag==CONFIG_REQUEST && localR.arb==TRAINING) sync_counter=0;
    else if(localR.tag==CONFIG_REQUEST && localR.arb==OPERATION) {
      if ((last_localR.tag==CONFIG_REQUEST) && (last_localR.arb==OPERATION))
        sync_counter++;
      else sync_counter=0;
    } else rx_sync_error=TRUE;
    if(sync_counter==SYNC_CHECK) remote_sync_lost=FALSE;
  }
          // Once connected port is seen to be synchronized, allow some extra time for remote
          // port to receive sufficient operation requests to know local sync is done.
          // If remote port sends a control signal then it knows local sync is done.
          // At same time, local port may determine context of signals.
  if (bport_on && !rx_sync_error) {
    sync_counter=0;
    while(bport_on && sync_counter<SYNC_CHECK && 
      (localR.tag==CONFIG_REQUEST)) {
      sync_counter++;
      rx_character();   
    }
    sync_error_signal = FALSE;          
    // This causes transmitter to resume normal operation and receiver transitions to receive state.
  }
}

void bport_receive_actions() {
  // Equivalent mostly to 1394-1995 decode_bit routine.
  int pad_count=0;                      // keeps track of padding
  boolean pushed_config_request = FALSE;
  portSymbol speed_symbol;
  pkt=speed_known=FALSE;
  rx_speed_diff=0;
  pkt_prefix=FALSE;
  last_localR.tag=INVALID;
  
  while(bport_on && !sync_error_signal) {
  // rx_character() has been called at end of rx_sync_actions()
  // If received character completes a speed signal, then a packet prefix is arriving. 
  // Speed signal is placed in fifo, and fifo update is turned on.
  
    if (bspeed_filter()) {
      speed_symbol.tag=ARB_STATE;
      speed_symbol.arb=SPEED;
      speed_symbol.speed=rx_pkt_speed;
      speed_symbol.pkt=rx_pkt_type;
      push(speed_symbol);
      pkt_prefix=TRUE;                  // in case no data prefix was already received
    } else {
      if (localR.tag == ARB_STATE && localR.arb==SPEEDc) {
        // ignore unless in position of expected data
        if(pkt && pad_count==0) push_data_zero();  // Padding in wrong place
      } else if(localR.tag==INVALID) {
        if (port_error_reg < 255) port_error_reg++; // increment error counter
        if(pkt && pad_count==0) push_data_zero(); // if a packet payload character, substitute 0x00
      }
  
  // If data is received then the packet has started, and packet prefix is done.
  // Position of data relative to padding is checked.
  // Fifo update is turned on (if not already).
  
      else if (localR.tag==DATA) {
        if (pad_count==0) {
          if (!speed_known) { // S100 Legacy packet without speed code!
            pad_ratio = (1 << port_speed); // 0 for S100, 1 for S200, 2 for S400 . . .
            speed_known = TRUE;
          }
          pkt=TRUE;
          pkt_prefix=FALSE;
          // If receiving an LTS, only buffer first occurence of each new data byte.  For all other
          // packet sequences, buffer each received byte
          if (!(untested_state && (last_localR.tag == DATA) && (last_localR.data == localR.data)))
            push(localR);
        }
      } else if(localR.tag==ARB_STATE) {    
        if(localR.arb==DATA_PREFIX) {
          if(pkt) {
            pkt_prefix=TRUE;            // concatenated packet
            pkt=speed_known=FALSE;
            rx_speed_diff=pad_count=0;
          } else if(!pkt_prefix) pkt_prefix=TRUE;
        } else {
          pkt_prefix=FALSE;
          pkt=speed_known=FALSE;
          rx_speed_diff=pad_count=0;
        }
        // Buffer first occurence of each new arbitration state....
        if (!((last_localR.tag == ARB_STATE) && (last_localR.arb == localR.arb)))
          push(localR);
      }
      if(localR.tag==ARB_REQUEST) {
        // Buffer first occurence of each new arbitration request....
        if (!((last_localR.tag == ARB_REQUEST) && 
          (last_localR.req.isoch == localR.req.isoch) &&
            (last_localR.req.async == localR.req.async)))
           push(localR);
      } 
      if(localR.tag==CONFIG_REQUEST) {
        // Filter Config Requests
        // required that two consecutive identical config requests are received
        // before one is pushed
        // but only push one for any consecutive sequence of identical requests
        if ((last_localR.tag == CONFIG_REQUEST) && 
          (last_localR.arb == localR.arb)) {
          if (!pushed_config_request) {
             localR.tag = ARB_STATE;   // convert to arb state
             push(localR);
             localR.tag = CONFIG_REQUEST; // ready for comparison with the next one
             pushed_config_request = TRUE;
          }
          // pushed config_request always TRUE at this point
        } else pushed_config_request = FALSE; // on non-matching config request
      } else pushed_config_request = FALSE;   // or on anything which is not a config request
  
      if (pkt) {
        pad_count = (pad_count++) % pad_ratio;
      } 
    }
    sync_error_signal=rx_character();
  }
  localR.tag = ARB_STATE;
  localR.arb = IDLE;
  push(localR);                         // ensure that the FIFO is not stuck
  pkt=speed_known=FALSE;
  rx_speed_diff=pad_count=0;
  pkt_prefix=FALSE;
}
