/***********************************************************************
 *  avrp - Atmel AVR programming software to use with Atmel's
 *         serial-port programmers.
 *  Copyright (C) 1997-1998 Jon Anders Haugum
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; see the file COPYING.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *
 *
 *  Author of avrp can be reached at:
 *     email: jonah@colargol.tihlde.hist.no
 *     www: http://www.colargol.tihlde.hist.no/~jonah/el/avrp.html
 *     Postal address: Jon Anders Haugum
 *                     Øvre Møllenbergsgt 52
 *                     7014 Trondheim
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <devices/serial.h>
#include <devices/timer.h>

#include <proto/exec.h>

#include "avrp.h"


struct ustimer
	{
	int ODok;
	struct timerequest *TimerIO;
	struct MsgPort *TimerMsgPort;
	};

struct Ser
	{
	char *Name;
	int ser_open;
	struct IOExtSer *IO;
	struct MsgPort *IO_port;
	struct ustimer *ust;
	};


void usDelay(struct ustimer *ust, int us);
struct ustimer *usDelayInit(void);
void usDelayFree(struct ustimer *ust);


void *OpenSer(char *SerPort)
	{
	int i, err;
	struct Ser *Ser;
	char buff[64];

	strncpy(buff, SerPort, 63);
	for(i = strlen(buff) - 1; (buff[i] != ':') && (i > 0); i--);
	if(buff[i] == ':')
		{
		buff[i] = '\0';
		Ser = (struct Ser *)malloc(sizeof(struct Ser));
		if(Ser)
			{
			Ser->Name = SerPort;
			Ser->ser_open = False;
			Ser->IO = NULL;
			Ser->ust = NULL;
			Ser->IO_port = CreateMsgPort();
			if(Ser->IO_port)
				{
				Ser->IO = (struct IOExtSer *)CreateExtIO(Ser->IO_port, sizeof(struct IOExtSer));
				if(Ser->IO)
					{
					err = OpenDevice(buff, atoi(&buff[i + 1]), (struct IORequest *)Ser->IO, 0);
					if(!err)
						{
						Ser->ser_open = True;
						Ser->IO->IOSer.io_Command = SDCMD_SETPARAMS;
						Ser->IO->io_RBufLen = 64;
						Ser->IO->io_Baud = 19200;
						Ser->IO->io_ReadLen = 8;
						Ser->IO->io_WriteLen = 8;
						Ser->IO->io_StopBits = 1;
						Ser->IO->io_ExtFlags = 0;
						Ser->IO->io_SerFlags = SERF_XDISABLED;
						DoIO((struct IORequest *)Ser->IO);
						Ser->ust = usDelayInit();
						if(Ser->ust)
							return(Ser);
						}
					else
						{
						if(err == SerErr_DevBusy)
							printf("Error: Serial device is busy\n");
						else
							printf("Error: Failed to open serial device (error: %d)\n", err);
						}
					}
				else
					printf("Error: Unable to allocate memory for IOExtSer\n");
				}
			else
				printf("Error: Failed to create message port for serial device\n");
			CloseSer(Ser);
			}
		else
			printf("Error: Unable to allocate memory\n");
		}
	else
		printf("Error: Found no ':' in serial port argument\n");
	return(0);
	}




void CloseSer(void *Ser)
	{
	if(((struct Ser *)Ser)->ust) usDelayFree(((struct Ser *)Ser)->ust);
	if(((struct Ser *)Ser)->ser_open) CloseDevice((struct IORequest *)((struct Ser *)Ser)->IO);
	if(((struct Ser *)Ser)->IO) DeleteExtIO((struct IORequest *)((struct Ser *)Ser)->IO);
	if(((struct Ser *)Ser)->IO_port) DeleteMsgPort(((struct Ser *)Ser)->IO_port);
	free(Ser);
	}


void SerWriteByte(void *Ser, unsigned char Byte)
	{
	((struct Ser *)Ser)->IO->IOSer.io_Command = CMD_WRITE;
	((struct Ser *)Ser)->IO->IOSer.io_Flags = 0;
	((struct Ser *)Ser)->IO->IOSer.io_Length = 1;
	((struct Ser *)Ser)->IO->IOSer.io_Data = &Byte;
	DoIO((struct IORequest *)((struct Ser *)Ser)->IO);
	}


int SerReadData(void *Ser, unsigned char *Buff, int Count)
	{
	int i;

	((struct Ser *)Ser)->IO->IOSer.io_Command = SDCMD_QUERY;
	DoIO((struct IORequest *)((struct Ser *)Ser)->IO);
	if(((struct Ser *)Ser)->IO->IOSer.io_Actual != Count)
		{
		for(i = 0; (((struct Ser *)Ser)->IO->IOSer.io_Actual != Count) && (i < 500); i++)
			{
			usDelay(((struct Ser *)Ser)->ust, 100);
			DoIO((struct IORequest *)((struct Ser *)Ser)->IO);
			}
		}

	if(((struct Ser *)Ser)->IO->IOSer.io_Actual != 0)
		{
		((struct Ser *)Ser)->IO->IOSer.io_Command = CMD_READ;
		((struct Ser *)Ser)->IO->IOSer.io_Flags = 0;
		((struct Ser *)Ser)->IO->IOSer.io_Length = ((struct Ser *)Ser)->IO->IOSer.io_Actual;
		((struct Ser *)Ser)->IO->IOSer.io_Data = Buff;
		DoIO((struct IORequest *)((struct Ser *)Ser)->IO);
		if(((struct Ser *)Ser)->IO->IOSer.io_Error)
			printf("IOError: %d\n", (int)((struct Ser *)Ser)->IO->IOSer.io_Error);
		}
	return((int)((struct Ser *)Ser)->IO->IOSer.io_Actual);
	}


void usDelay(struct ustimer *ust, int us)
	{
	ust->TimerIO->tr_node.io_Command = TR_ADDREQUEST;
	ust->TimerIO->tr_time.tv_micro = us;
	ust->TimerIO->tr_time.tv_secs = 0;
	DoIO((struct IORequest *)ust->TimerIO);
	}


struct ustimer *usDelayInit(void)
	{
	struct ustimer *ust;

	ust = calloc(1, sizeof(struct ustimer));
	if(ust)
		{
		ust->ODok = FALSE;
		ust->TimerMsgPort = CreateMsgPort();
		if(ust->TimerMsgPort)
			{
			ust->TimerIO = (struct timerequest *)CreateExtIO(ust->TimerMsgPort, sizeof(struct timerequest));
			if(ust->TimerIO)
				{
				if(!OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)ust->TimerIO, 0))
					{
					ust->ODok = TRUE;
					}
				else
					printf("Error: Unable to open timer.device\n");
				}
			else
				printf("Error: Unable to create ExtIO for timer\n");
			}
		else
			printf("Error: Unable to create messageport for timer\n");
		if(!ust->ODok)
			{
			usDelayFree(ust);
			ust = NULL;
			}
		}
	else
		printf("Error: Unable to allocate memory for timer\n");
	return(ust);
	}


void usDelayFree(struct ustimer *ust)
	{
	if(ust->ODok) CloseDevice((struct IORequest *)ust->TimerIO);
	if(ust->TimerIO) DeleteExtIO((struct IORequest *)ust->TimerIO);
	if(ust->TimerMsgPort) DeleteMsgPort(ust->TimerMsgPort);
	free(ust);
	}
