/* BlueDataDistributor - data distribution module from ethernet to 32 serial ports
 * version 0.1.1 date 2006-10-07
 * Copyright (C) 2006 Stefan Schuermans <stefan@blinkenarea.org>
 * a BlinkenArea project - http://www.blinkenarea.org/
 */

#include <string.h>
#include <avr/io.h>

#include "serial.h"
#include "status.h"

// ring buffer for serial data to output on the serial ports
//  - every element contains SERIAL_OUTPUT_CNT bytes
#define SERIAL_BUFFER_CNT 32
unsigned char SerialBuffers[SERIAL_BUFFER_CNT][SERIAL_OUTPUT_CNT];
// pointers for above ring buffer
volatile unsigned char SerialBufferStart = 0;
volatile unsigned char SerialBufferEnd = 0;
// bit mask of next bit to output (0x00 for start bit, 0xFF for stop bit)
volatile unsigned char SerialBitMask = 0x00;

// initialize
void SerialInit( void ) // (extern)
{
	//set up IO pins of serial outputs as output
	PORTB |= 0xFD; // PB1 is used for programming
	DDRB |= 0xFD;
	PORTD = 0xFF;
	DDRD = 0xFF;
	PORTE |= 0xEC; // PE0-1 are used for programming, PE4 is interrupt input
	DDRE |= 0xEC;
	PORTF = 0xFF;
	DDRF = 0xFF;
	PORTG |= 0x17; // PG3 is status LED, PG5-7 do not exist
	DDRG |= 0x17;
}

// output the specified bit of a byte on every serial port
//  - data must point to SERIAL_OUTPUT_CNT=32 bytes
//  - bit_mask must be 0x01, 0x02, ... 0x80 for bits 0, 1, ..., 7
//  - bit_mask must be 0x00 for the start bit and 0xFF for the stop bit
// extern void SerialOutputBit( unsigned char * data, unsigned char bit_mask );
// this function is written in assembler, see serial_asm.S

// bit time of serial port elapsed
void SerialOutputBitTime( void )
{
  unsigned char start;

  // no data to output
  if( SerialBufferStart == SerialBufferEnd )
    return;

  // output current bit
  SerialOutputBit( SerialBuffers[SerialBufferStart], SerialBitMask );

  // next bit
  switch( SerialBitMask ) {
    case 0x00: // start bit -> first bit
      SerialBitMask = 0x01;
      break;
    case 0x80: // last bit -> stop bit
      SerialBitMask = 0xFF;
      break;
    case 0xFF: // stop bit -> next byte
      SerialBitMask = 0x00;
      start = SerialBufferStart + 1;
      if( start >= SERIAL_BUFFER_CNT )
        start = 0;
      SerialBufferStart = start;
      break;
    default: // next bit
      SerialBitMask <<= 1;
      break;
  }
}

// schedule data to be output on serial ports
//  - data_len should be a multiple of SERIAL_OUTPUT_CNT
void SerialOutput( unsigned char * data, unsigned int data_len )
{
  unsigned char cnt, end, i, new_end;

  // get number of bytes to output on each serial port
  cnt = data_len / SERIAL_OUTPUT_CNT;
  if( cnt > 0 )
    StatusEventSerial( );

  // put entrie data set into buffers - or nothing of data set
  end = SerialBufferEnd;
  for( i = 0; i < cnt; i++ ) {
    // check if all buffers are full
    new_end = end + 1;
    if( new_end >= SERIAL_BUFFER_CNT )
      new_end = 0;
    if( new_end == SerialBufferStart )
      return; // all buffers are full -> drop all bytes from this data set (do not use only the first bytes of the data set, there's nothing we can do about too much data coming in on ethernet)

    // copy data into next buffer
    memcpy( SerialBuffers[end], data, SERIAL_OUTPUT_CNT );

    // mark buffer temporary as filled
    end = new_end;

    // next byte (for each serial port)
    data += SERIAL_OUTPUT_CNT;
  }

  // mark buffers as filled
  //  - do this after it is certain that entire data set could be put into buffers
  SerialBufferEnd = end;
}

