/*
 * Copyright (c) 2001 Tommy Bohlin <tommy@gatespace.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
/* tekram.c
 */

#include <irda.h>

/**********************************************************************
 * Constants
 **********************************************************************/

static const char id_tekram[]="tekram";

#define TEKRAM_PW     0x10

#define TEKRAM_115200 (TEKRAM_PW|0x00)
#define TEKRAM_57600  (TEKRAM_PW|0x01)
#define TEKRAM_38400  (TEKRAM_PW|0x02)
#define TEKRAM_19200  (TEKRAM_PW|0x03)
#define TEKRAM_9600   (TEKRAM_PW|0x04)
#define TEKRAM_2400   (TEKRAM_PW|0x08)

#define TEKRAM_TV     (TEKRAM_PW|0x05)

/**********************************************************************
 * Data structures
 **********************************************************************/

typedef struct Tekram {
  SerialDevice sd;
  SerialPort* sp;
  int state;
  int speed;
} Tekram;

/**********************************************************************
 * Tekram control
 **********************************************************************/

static int encodeSpeed(int speed) {
  switch(speed) {
  case 115200: return TEKRAM_115200;
  case 57600:  return TEKRAM_57600;
  case 38400:  return TEKRAM_38400;
  case 19200:  return TEKRAM_19200;
  case 2400:   return TEKRAM_2400;
  default:     return TEKRAM_9600;
  }
}

static void speedTimer(void* sd)
{
  Tekram* tek=(Tekram*)sd;
  SerialPort* sp=tek->sp;
  
  switch(tek->state) {
  case 1:
    sp->setLine(sp,LINE_RTS);
    tek->state=2;
    evtSetTimer(1,speedTimer,tek);
    break;
  case 2:
    sp->setLine(sp,LINE_DTR|LINE_RTS);
    tek->state=3;
    evtSetTimer(1,speedTimer,tek);  /* At least 50us */
    break;
  case 3:
    sp->setLine(sp,LINE_DTR);
    tek->state=4;
    evtSetTimer(1,speedTimer,tek);  /* At least 7us */
    break;
  case 4:
    sp->putChar(sp,encodeSpeed(tek->speed));
    tek->state=5;
    evtSetTimer(100,speedTimer,tek);
    break;
  case 5:
    sp->setLine(sp,LINE_DTR|LINE_RTS);
    if(tek->speed!=9600) sp->setSpeed(sp,tek->speed);
    tek->state=0;
    /* Wait at least 50us before using */
    break;
  }
}

/**********************************************************************
 * Methods
 **********************************************************************/

static int tekSetSpeed(SerialDevice* sd, int speed)
{
  Tekram* tek=(Tekram*)sd;
  SerialPort* sp=tek->sp;

  tek->state=1;
  tek->speed=speed;
  sp->setSpeed(sp,9600);
  sp->setLine(sp,0);
  evtSetTimer(50,speedTimer,tek);

  /* Conservative estimate */
  return 200;
}

static int tekGetSpeedMask(SerialDevice* sd)
{
  Tekram* tek=(Tekram*)sd;

  return tek->sp->getSpeedMask(tek->sp)&
    (SPEED_2400|SPEED_9600|SPEED_19200|SPEED_38400|SPEED_57600|SPEED_115200);
}

static int tekGetMinTurnaroundMask(SerialDevice* sd)
{
  return MIN_TA_10ms;
}

static int tekGetChar(SerialDevice* sd)
{
  Tekram* tek=(Tekram*)sd;

  return tek->sp->getChar(tek->sp);
}

static void tekPutChar(SerialDevice* sd, int c)
{
  Tekram* tek=(Tekram*)sd;

  if(tek->state==0) tek->sp->putChar(tek->sp,c);
}

static void tekClose(SerialDevice* sd)
{
  Tekram* tek=(Tekram*)sd;
  SerialPort* sp=tek->sp;

  evtCancelTimer(speedTimer,tek);
  sp->setLine(sp,0);
  sp->handle=0;
  sp->status=0;
  freeMem(tek);
}

static void tekStatus(SerialPort* sp, int event)
{
  Tekram* tek=(Tekram*)sp->handle;

  if(tek->sd.status) tek->sd.status(&tek->sd,event);
}

/**********************************************************************
 * External interface
 **********************************************************************/

SerialDevice* createTekram210Device(SerialPort* sp)
{
  Tekram* tek=allocMem(id_tekram,sizeof(Tekram));

  tek->sd.close=tekClose;
  tek->sd.setSpeed=tekSetSpeed;
  tek->sd.getSpeedMask=tekGetSpeedMask;
  tek->sd.getMinTurnaroundMask=tekGetMinTurnaroundMask;
  tek->sd.getChar=tekGetChar;
  tek->sd.putChar=tekPutChar;
  tek->sd.handle=0;
  tek->sd.status=0;
  tek->sp=sp;
  tek->state=0;
  tek->speed=0;

  sp->handle=tek;
  sp->status=tekStatus;

  return &tek->sd;
}
