/*
 *
 */

#ifndef NO_INCLUDE_LOCL
#include "error.h"
#include "cp210x.h"
#endif

#include <stdio.h>
#include <string.h>

#ifdef ERROR
#undef ERROR
#endif

#define ERROR(m) ERROR_push_msg(ut->err, m)

int 
CP210X_CRD_UT10XX_open(CRD_UT10XX_t *ut)
{
	int status = 0;
	int baud = CP210X_BAUD_RATE_GEN_FREQ / 250000;

	ut->handle = usb_open(ut->hw);
	
	if (ut->handle == 0)
	{
		ERROR("usb_open failed");
		return (ERR_CRD_UT10XX_SET_UP);
	}
	
	status = usb_control_msg(ut->handle, CP210X_REQTYPE_HOST_TO_DEVICE, 
		CP210X_UART, CP210X_UART_ENABLE, 0, 0, 0, CRD_UT10XX_TIME_OUT);
		
	if (status < 0)
	{
		ERROR("could not enable UART in CP210X");
		return (ERR_CRD_UT10XX_SET_UP);
	}
	
	status = usb_control_msg(ut->handle, CP210X_REQTYPE_HOST_TO_DEVICE, 
		CP210X_BAUDRATE, baud, 0, 0, 0, CRD_UT10XX_TIME_OUT);
	
	if (status < 0)
	{
		ERROR("could not set up baudrate in CP210X");
		return (ERR_CRD_UT10XX_SET_UP);
	}
		
	status = usb_control_msg(ut->handle, CP210X_REQTYPE_HOST_TO_DEVICE, 
		CP210X_BITS, CP210X_BITS_DATA_8, 0, 0, 0, CRD_UT10XX_TIME_OUT);

	if (status < 0)
	{
		ERROR("could not set up data bits for CP210X");
		return (ERR_CRD_UT10XX_SET_UP);
	}
		
	status = usb_control_msg(ut->handle, CP210X_REQTYPE_HOST_TO_DEVICE, 
		CP210X_CONTROL, CP210X_CONTROL_DTR | CP210X_CONTROL_RTS | 
		CP210X_CONTROL_WRITE_DTR | CP210X_CONTROL_WRITE_RTS, 0, 0, 0, 
		CRD_UT10XX_TIME_OUT);
		
	if (status < 0)
	{
		ERROR("could not set up control lines for CP210X");
		return (ERR_CRD_UT10XX_SET_UP);
	}
	
	status = usb_set_configuration(ut->handle, 1);
	
	if (status < 0)
	{
		ERROR("could not set up cofiguration to 1");
		return (ERR_CRD_UT10XX_SET_UP);
	}
		
	status = usb_claim_interface(ut->handle, 0);
	
	if (status < 0)
	{
		ERROR("could not set up interface to 0");
		return (ERR_CRD_UT10XX_SET_UP);
	}
	
	return (status);
}

int 
CP210X_CRD_UT10XX_close(CRD_UT10XX_t *ut)
{
	if (ut->handle)
	{
		usb_release_interface(ut->handle, 0);
#		ifndef WIN32
		usb_reset(ut->handle);
#		endif
		usb_close(ut->handle);
		ut->handle = 0;
	}
	
	return (0);
}

int 
CP210X_CRD_UT10XX_read(CRD_UT10XX_t *ut, unsigned char *buf, int octets, int timeout_ms)
{
	unsigned char in[DF_MAX];
	int total = 0;
	int status;
	
	while ( (status = usb_bulk_read(ut->handle, CP210X_IN_BULK_EP, (char *)in, 
		DF_MAX, timeout_ms)) == 0 );
	
	total += status;
	
	if (status <= 0)
	{
		ERROR("could not read bulk transfer from CP210X");
		return (ERR_CRD_UT10XX_RECEIVE);
	}
		
	while (total < in[0] + 1)
	{
		status = usb_bulk_read(ut->handle, CP210X_IN_BULK_EP, (char *)in + total, 
			DF_MAX - total, timeout_ms);
		
		total += status;
		
		if (status <= 0)
		{
			ERROR("could not read bulk transfer from CP210X");
			return (ERR_CRD_UT10XX_RECEIVE);
		}
	}
	
	if (octets < total - 1)
	{
		ERROR("could not store all data received from CP210X in given buffer");
		return (ERR_CRD_UT10XX_TOO_SMALL_BUF);
	}
	
	memcpy(buf, in + 1, total - 1);	
	
	return (total - 1);
}

int 
CP210X_CRD_UT10XX_write(CRD_UT10XX_t *ut, const unsigned char *buf, int octets)
{
	unsigned char out[HF_MAX];
	int status;
	
	if (HF_MAX - 1 < octets)
	{
		ERROR("given buffer is to large to transmit (larger than " 
			XSTR(HF_MAX) " octets)");
		return (ERR_CRD_UT10XX_TOO_LARGE_BUF);
	}
	
	out[0] = octets;	
	memcpy(out + 1, buf, octets);
	
	status = usb_bulk_write(ut->handle, CP210X_OUT_BULK_EP, (char *)out, octets + 1, 
		CRD_UT10XX_TIME_OUT);
	
	if (status != octets + 1)
	{
		ERROR("could not write bulk transfer to CP210X");
		return (ERR_CRD_UT10XX_TRANSMIT);
	}
	
	return (octets);
}

