/*----- PROTECTED REGION ID(USB2.cpp) ENABLED START -----*/
//=============================================================================
//
// file :        USB2.cpp
//
// description : C++ source for the USB2 class and its commands.
//               The class is derived from Device. It represents the
//               CORBA servant object which will be accessed from the
//               network. All commands which can be executed on the
//               USB2 are implemented in this file.
//
// project :     
//
// This file is part of Tango device class.
// 
// Tango 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 3 of the License, or
// (at your option) any later version.
// 
// Tango 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 Tango.  If not, see <http://www.gnu.org/licenses/>.
// 
// $Author: alessio $
//
// $Revision: 1.5 $
// $Date: 2018-10-24 08:12:41 $
//
// $HeadURL:  $
//
//=============================================================================
//                This file is generated by POGO
//        (Program Obviously used to Generate tango Object)
//=============================================================================


#include <USB2.h>
#include <USB2Class.h>

#include <csignal>

/*----- PROTECTED REGION END -----*/	//	USB2.cpp

/**
 *  USB2 class description:
 *    
 */

//================================================================
//  The following table gives the correspondence
//  between command and method names.
//
//  Command name  |  Method name
//================================================================
//  Write         |  write
//  Read          |  read
//  ReadUntil     |  read_until
//================================================================

//================================================================
//  Attributes managed are:
//================================================================
//  InputLength    |  Tango::DevLong	Scalar
//  OutputLength   |  Tango::DevLong	Scalar
//  Reconnections  |  Tango::DevLong	Scalar
//================================================================

namespace USB2_ns
{
/*----- PROTECTED REGION ID(USB2::namespace_starting) ENABLED START -----*/

//	static initializations

/*----- PROTECTED REGION END -----*/	//	USB2::namespace_starting

//--------------------------------------------------------
/**
 *	Method     : USB2::USB2()
 *	Description: Constructors for a Tango device
 *                implementing the classUSB2
 */
//--------------------------------------------------------
USB2::USB2(Tango::DeviceClass *cl, std::string &s)
 : TANGO_BASE_CLASS(cl, s.c_str())
{
	/*----- PROTECTED REGION ID(USB2::constructor_1) ENABLED START -----*/
	init_device();
	
	/*----- PROTECTED REGION END -----*/	//	USB2::constructor_1
}
//--------------------------------------------------------
USB2::USB2(Tango::DeviceClass *cl, const char *s)
 : TANGO_BASE_CLASS(cl, s)
{
	/*----- PROTECTED REGION ID(USB2::constructor_2) ENABLED START -----*/
	init_device();
	
	/*----- PROTECTED REGION END -----*/	//	USB2::constructor_2
}
//--------------------------------------------------------
USB2::USB2(Tango::DeviceClass *cl, const char *s, const char *d)
 : TANGO_BASE_CLASS(cl, s, d)
{
	/*----- PROTECTED REGION ID(USB2::constructor_3) ENABLED START -----*/
	init_device();
	
	/*----- PROTECTED REGION END -----*/	//	USB2::constructor_3
}
//--------------------------------------------------------
USB2::~USB2()
{
	delete_device();
}

//--------------------------------------------------------
/**
 *	Method     : USB2::delete_device()
 *	Description: will be called at device destruction or at init command
 */
//--------------------------------------------------------
void USB2::delete_device()
{
	DEBUG_STREAM << "USB2::delete_device() " << device_name << std::endl;
	/*----- PROTECTED REGION ID(USB2::delete_device) ENABLED START -----*/
	
	//	Delete device allocated objects
	close();

	/*----- PROTECTED REGION END -----*/	//	USB2::delete_device
	delete[] attr_InputLength_read;
	delete[] attr_OutputLength_read;
	delete[] attr_Reconnections_read;
}

//--------------------------------------------------------
/**
 *	Method     : USB2::init_device()
 *	Description: will be called at device initialization.
 */
//--------------------------------------------------------
void USB2::init_device()
{
	DEBUG_STREAM << "USB2::init_device() create device " << device_name << std::endl;
	/*----- PROTECTED REGION ID(USB2::init_device_before) ENABLED START -----*/
	
	//	Initialization before get_device_property() call
	reconnections = 0;
	ctx = NULL;
	dev_handle = NULL;
	init_error.clear();
	conn_state = 0;
	/*----- PROTECTED REGION END -----*/	//	USB2::init_device_before


	//	Get the device properties from database
	get_device_property();

	attr_InputLength_read = new Tango::DevLong[1];
	attr_OutputLength_read = new Tango::DevLong[1];
	attr_Reconnections_read = new Tango::DevLong[1];
	//	No longer if mandatory property not set.
	if (mandatoryNotDefined)
		return;

	/*----- PROTECTED REGION ID(USB2::init_device) ENABLED START -----*/

	// Disabling SIGPIPE signal
	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		init_error = "Fail to ignore SIGPIPE signal";

	// Initialize device
	if (init_error.empty()) {
		open();
		check_state();
	}
	/*----- PROTECTED REGION END -----*/	//	USB2::init_device
}

//--------------------------------------------------------
/**
 *	Method     : USB2::get_device_property()
 *	Description: Read database to initialize property data members.
 */
//--------------------------------------------------------
void USB2::get_device_property()
{
	/*----- PROTECTED REGION ID(USB2::get_device_property_before) ENABLED START -----*/
	
	//	Initialize property data members
	
	/*----- PROTECTED REGION END -----*/	//	USB2::get_device_property_before

	mandatoryNotDefined = false;

	//	Read device properties from database.
	Tango::DbData	dev_prop;
	dev_prop.push_back(Tango::DbDatum("Timeout"));
	dev_prop.push_back(Tango::DbDatum("VendorID"));
	dev_prop.push_back(Tango::DbDatum("ProductID"));
	dev_prop.push_back(Tango::DbDatum("Interface"));
	dev_prop.push_back(Tango::DbDatum("EndpointUP"));
	dev_prop.push_back(Tango::DbDatum("EndpointDOWN"));
	dev_prop.push_back(Tango::DbDatum("MaxPacketSize"));

	//	is there at least one property to be read ?
	if (dev_prop.size()>0)
	{
		//	Call database and extract values
		if (Tango::Util::instance()->_UseDb==true)
			get_db_device()->get_property(dev_prop);

		//	get instance on USB2Class to get class property
		Tango::DbDatum	def_prop, cl_prop;
		USB2Class	*ds_class =
			(static_cast<USB2Class *>(get_device_class()));
		int	i = -1;

		//	Try to initialize Timeout from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  timeout;
		else {
			//	Try to initialize Timeout from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  timeout;
		}
		//	And try to extract Timeout value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  timeout;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

		//	Try to initialize VendorID from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  vendorID;
		else {
			//	Try to initialize VendorID from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  vendorID;
		}
		//	And try to extract VendorID value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  vendorID;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

		//	Try to initialize ProductID from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  productID;
		else {
			//	Try to initialize ProductID from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  productID;
		}
		//	And try to extract ProductID value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  productID;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

		//	Try to initialize Interface from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  interface;
		else {
			//	Try to initialize Interface from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  interface;
		}
		//	And try to extract Interface value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  interface;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

		//	Try to initialize EndpointUP from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  endpointUP;
		else {
			//	Try to initialize EndpointUP from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  endpointUP;
		}
		//	And try to extract EndpointUP value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  endpointUP;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

		//	Try to initialize EndpointDOWN from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  endpointDOWN;
		else {
			//	Try to initialize EndpointDOWN from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  endpointDOWN;
		}
		//	And try to extract EndpointDOWN value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  endpointDOWN;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

		//	Try to initialize MaxPacketSize from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  maxPacketSize;
		else {
			//	Try to initialize MaxPacketSize from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  maxPacketSize;
		}
		//	And try to extract MaxPacketSize value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  maxPacketSize;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

	}

	/*----- PROTECTED REGION ID(USB2::get_device_property_after) ENABLED START -----*/
	
	//	Check device property data members init
	vid = stoi(vendorID, NULL, 16);
	pid = stoi(productID, NULL, 16);

	ep_up = stoi(endpointUP, NULL, 16);
	ep_down = stoi(endpointDOWN, NULL, 16);

	if (timeout <= 0)
		timeout = 1000;

	DEBUG_STREAM << "Connecting to " << productID << " of the " << vendorID 
		<< " using " << interface << " with MaxPacketSize " << maxPacketSize
		<< " on "<< endpointUP << " " << endpointDOWN 
		<<  " with a timeout of " << timeout << " ms "<< endl;

	timeout_timeval.tv_sec = timeout / 1000;
	timeout_timeval.tv_usec = timeout % 1000 * 1000;

	/*----- PROTECTED REGION END -----*/	//	USB2::get_device_property_after
}
//--------------------------------------------------------
/**
 *	Method     : USB2::check_mandatory_property()
 *	Description: For mandatory properties check if defined in database.
 */
//--------------------------------------------------------
void USB2::check_mandatory_property(Tango::DbDatum &class_prop, Tango::DbDatum &dev_prop)
{
	//	Check if all properties are empty
	if (class_prop.is_empty() && dev_prop.is_empty())
	{
		TangoSys_OMemStream	tms;
		tms << std::endl <<"Property \'" << dev_prop.name;
		if (Tango::Util::instance()->_UseDb==true)
			tms << "\' is mandatory but not defined in database";
		else
			tms << "\' is mandatory but cannot be defined without database";
		append_status(tms.str());
		mandatoryNotDefined = true;
		/*----- PROTECTED REGION ID(USB2::check_mandatory_property) ENABLED START -----*/
		cerr << tms.str() << " for " << device_name << endl;
		
		/*----- PROTECTED REGION END -----*/	//	USB2::check_mandatory_property
	}
}


//--------------------------------------------------------
/**
 *	Method     : USB2::always_executed_hook()
 *	Description: method always executed before any command is executed
 */
//--------------------------------------------------------
void USB2::always_executed_hook()
{
	DEBUG_STREAM << "USB2::always_executed_hook()  " << device_name << std::endl;
	if (mandatoryNotDefined)
	{
		Tango::Except::throw_exception(
					(const char *)"PROPERTY_NOT_SET",
					get_status().c_str(),
					(const char *)"USB2::always_executed_hook()");
	}
	/*----- PROTECTED REGION ID(USB2::always_executed_hook) ENABLED START -----*/
	
	//	code always executed before all requests
	tout = timeout_timeval;

	if (! init_error.empty()) {
		set_state(Tango::FAULT);
		set_status(init_error);
		DEBUG_STREAM << init_error << endl;
		return;
	}

	check_state();

	/*----- PROTECTED REGION END -----*/	//	USB2::always_executed_hook
}

//--------------------------------------------------------
/**
 *	Method     : USB2::read_attr_hardware()
 *	Description: Hardware acquisition for attributes
 */
//--------------------------------------------------------
void USB2::read_attr_hardware(TANGO_UNUSED(std::vector<long> &attr_list))
{
	DEBUG_STREAM << "USB2::read_attr_hardware(std::vector<long> &attr_list) entering... " << std::endl;
	/*----- PROTECTED REGION ID(USB2::read_attr_hardware) ENABLED START -----*/
	
	//	Add your own code
	
	/*----- PROTECTED REGION END -----*/	//	USB2::read_attr_hardware
}

//--------------------------------------------------------
/**
 *	Read attribute InputLength related method
 *
 *
 *	Data type:	Tango::DevLong
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void USB2::read_InputLength(Tango::Attribute &attr)
{
	DEBUG_STREAM << "USB2::read_InputLength(Tango::Attribute &attr) entering... " << std::endl;
	/*----- PROTECTED REGION ID(USB2::read_InputLength) ENABLED START -----*/
	//	Set the attribute value
	Tango::AttrQuality qual;
	long len = input_queue_length();
	if (len >= 0) {
		qual = Tango::ATTR_VALID;
		attr_InputLength_read[0] = len + data.size(); 
	} else {
		qual = Tango::ATTR_INVALID;
		attr_InputLength_read[0] = data.size();
	}
	attr.set_value_date_quality(attr_InputLength_read, time(NULL), qual);
	/*----- PROTECTED REGION END -----*/	//	USB2::read_InputLength
}
//--------------------------------------------------------
/**
 *	Read attribute OutputLength related method
 *
 *
 *	Data type:	Tango::DevLong
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void USB2::read_OutputLength(Tango::Attribute &attr)
{
	DEBUG_STREAM << "USB2::read_OutputLength(Tango::Attribute &attr) entering... " << std::endl;
	/*----- PROTECTED REGION ID(USB2::read_OutputLength) ENABLED START -----*/
	//	Set the attribute value
	Tango::AttrQuality qual;
	long len = output_queue_length();
	if (len >= 0) {
		qual = Tango::ATTR_VALID;
		attr_OutputLength_read[0] = len;
	}	else {
		qual = Tango::ATTR_INVALID;
		attr_OutputLength_read[0] = 0;
	}
	attr.set_value_date_quality(attr_OutputLength_read, time(NULL), qual);
	/*----- PROTECTED REGION END -----*/	//	USB2::read_OutputLength
}
//--------------------------------------------------------
/**
 *	Read attribute Reconnections related method
 *
 *
 *	Data type:	Tango::DevLong
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void USB2::read_Reconnections(Tango::Attribute &attr)
{
	DEBUG_STREAM << "USB2::read_Reconnections(Tango::Attribute &attr) entering... " << std::endl;
	/*----- PROTECTED REGION ID(USB2::read_Reconnections) ENABLED START -----*/
	//	Set the attribute value
	attr_Reconnections_read[0] = reconnections;
	attr.set_value(attr_Reconnections_read);
	
	/*----- PROTECTED REGION END -----*/	//	USB2::read_Reconnections
}

//--------------------------------------------------------
/**
 *	Method     : USB2::add_dynamic_attributes()
 *	Description: Create the dynamic attributes if any
 *                for specified device.
 */
//--------------------------------------------------------
void USB2::add_dynamic_attributes()
{
	/*----- PROTECTED REGION ID(USB2::add_dynamic_attributes) ENABLED START -----*/
	
	//	Add your own code to create and add dynamic attributes if any
	
	/*----- PROTECTED REGION END -----*/	//	USB2::add_dynamic_attributes
}

//--------------------------------------------------------
/**
 *	Command Write related method
 *
 *
 *	@param argin
 */
//--------------------------------------------------------
void USB2::write(const Tango::DevVarCharArray *argin)
{
	DEBUG_STREAM << "USB2::Write()  - " << device_name << std::endl;
	/*----- PROTECTED REGION ID(USB2::write) ENABLED START -----*/
	vector<unsigned char> argin_data;
	argin_data << *argin;
	size_t bytes_total = 0, bytes_to_write = argin_data.size();

	if (! init_error.empty()) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", init_error.c_str(), __PRETTY_FUNCTION__);
	}

	if (max(output_queue_length(), 0) != 0 || ! dev_handle)
		goto error;
	
	while (bytes_total < bytes_to_write) {
		int bytes_written;
		conn_state = libusb_bulk_transfer(dev_handle, ep_down, 
				argin_data.data() + bytes_total,
				bytes_to_write - bytes_total, &bytes_written, timeout);
		if (conn_state == LIBUSB_ERROR_TIMEOUT)
			goto timeout;
		if ( bytes_written > 0) {
			bytes_total += bytes_written;
		} else if (bytes_written == 0) {
			goto error;
		} else { /* bytes_written < 0 */
			goto error;
		}
	}

	timeval twait;
	timerclear(&twait);
	twait.tv_usec = 1000;
	while (max(output_queue_length(), 0) != 0) {
		if (! sleep(twait))
			goto timeout;
		timeradd(&twait, &twait, &twait);
	}

	return;

error:
	check_state();
	sleep(tout);
timeout:
	Tango::Except::throw_exception(
			"", "Timeout expired", __PRETTY_FUNCTION__);

	/*----- PROTECTED REGION END -----*/	//	USB2::write
}
//--------------------------------------------------------
/**
 *	Command Read related method
 *
 *
 *	@param argin
 *	@returns
 */
//--------------------------------------------------------
Tango::DevVarCharArray *USB2::read(Tango::DevLong argin)
{
	Tango::DevVarCharArray *argout;
	DEBUG_STREAM << "USB2::Read()  - " << device_name << std::endl;
	/*----- PROTECTED REGION ID(USB2::read) ENABLED START -----*/
	if (! init_error.empty()) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", init_error.c_str(), __PRETTY_FUNCTION__);
	}

	if (argin < 0) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", "Out of limit", __PRETTY_FUNCTION__);
	}

	_read(argin);

	argout = new Tango::DevVarCharArray();
	vector<unsigned char> transfer(data.begin(), data.begin() + argin);
	data.erase(data.begin(), data.begin() + argin);
	*argout << transfer;
	/*----- PROTECTED REGION END -----*/	//	USB2::read
	return argout;
}
//--------------------------------------------------------
/**
 *	Command ReadUntil related method
 *
 *
 *	@param argin
 *	@returns
 */
//--------------------------------------------------------
Tango::DevVarCharArray *USB2::read_until(const Tango::DevVarCharArray *argin)
{
	Tango::DevVarCharArray *argout;
	DEBUG_STREAM << "USB2::ReadUntil()  - " << device_name << std::endl;
	/*----- PROTECTED REGION ID(USB2::read_until) ENABLED START -----*/
	if (! init_error.empty()) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", init_error.c_str(), __PRETTY_FUNCTION__);
	}

	if (argin->length() != 1) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", "Delimiter has to be exactly one byte", __PRETTY_FUNCTION__);
	}

	char delim = (*argin)[0];
	size_t pos = 0, dsize;
	bool found = false;

	do {
		dsize = data.size();
		for (; pos < dsize; ++pos) {
			if (memcmp(&data[pos], &delim, 1) == 0)	{
				found = true;
				break;
			}
		}
		if (found)
			break;
		_read(dsize + 1);
	} while (true);

	argout = new Tango::DevVarCharArray();
	vector<unsigned char> transfer(data.begin(), data.begin() + pos +1);
	data.erase(data.begin(), data.begin() + pos + 1);
	*argout << transfer;
	/*----- PROTECTED REGION END -----*/	//	USB2::read_until
	return argout;
}
//--------------------------------------------------------
/**
 *	Method     : USB2::add_dynamic_commands()
 *	Description: Create the dynamic commands if any
 *                for specified device.
 */
//--------------------------------------------------------
void USB2::add_dynamic_commands()
{
	/*----- PROTECTED REGION ID(USB2::add_dynamic_commands) ENABLED START -----*/
	
	//	Add your own code to create and add dynamic commands if any
	
	/*----- PROTECTED REGION END -----*/	//	USB2::add_dynamic_commands
}

/*----- PROTECTED REGION ID(USB2::namespace_ending) ENABLED START -----*/

//	Additional Methods
bool USB2::sleep(timeval tv)
{
	if (! timerisset(&tout))
		return false;

	if (timercmp(&tout, &tv, <)) {
		::sleep(tout.tv_sec);
		usleep(tout.tv_usec);
		timerclear(&tout);
	} else { // tout >= tv
		::sleep(tv.tv_sec);
		usleep(tv.tv_usec);
		timersub(&tout, &tv, &tout);
		assert(tout.tv_sec >= 0 && tout.tv_usec >= 0);
	}
	return true;
}

void USB2::open()
{
	DEBUG_STREAM << "Opening USB device..." << endl;

	if(libusb_init(&ctx) < 0) {
		ERROR_STREAM << "Failed to initialize libusb" << endl;
		return;
	}

#ifndef NDEBUG
	libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_WARNING);
#endif

	dev_handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
	if(dev_handle == NULL) {
		libusb_exit(ctx);

		ERROR_STREAM << "Unable to open device" << endl;
		return;
	}

	int ret = libusb_claim_interface(dev_handle, interface);
	if(ret < 0) {
		libusb_release_interface(dev_handle, interface);
		libusb_close(dev_handle);
		libusb_exit(ctx);
		ERROR_STREAM << "Failed to claim interface" << endl;
		return;
	}

}

int USB2::input_queue_length()
{
	return data.size();
}

int USB2::output_queue_length()
{
	return 0;
}

void USB2::close()
{
	DEBUG_STREAM << "Closing USB device..." << endl;

	int output_len = max(output_queue_length(), 0);
	int input_len = max(input_queue_length(), 0) + data.size();

	if(input_len + output_len)
	{
		WARN_STREAM << " Bytes dropped: " << input_len << " input, "
			<< output_len << " output" << endl;
	}

	if(dev_handle)
	{
		libusb_release_interface(dev_handle, interface);
		libusb_close(dev_handle);
		libusb_exit(ctx);

		dev_handle = NULL;
	}

	data.clear();
}

void USB2::_read(size_t bytes_to_read)
{
	unsigned char buffer[10000];
	size_t bytes_total = data.size();
	int bytes_readed;

	if (! dev_handle)
		goto error;

	while (bytes_total < bytes_to_read) {
		conn_state = libusb_bulk_transfer(dev_handle, ep_up, buffer,
				maxPacketSize, &bytes_readed, timeout);
		if (conn_state == LIBUSB_ERROR_TIMEOUT)
			goto timeout;
		if (bytes_readed > 0) {
			data.insert(data.end(), &buffer[0], &buffer[bytes_readed]);
			bytes_total += bytes_readed;
		} else if (bytes_readed == 0) {
			goto error;
		} else { /* bytes_readed < 0 */
			goto error;
		}
	}
	return;
error:
	check_state();
	sleep(tout);
timeout:
	Tango::Except::throw_exception(
			"", "Timeout expired", __PRETTY_FUNCTION__);
}

void USB2::check_state()
{
	string mesg;
	switch(conn_state)
	{
		case 0: /* Success */
			set_state(Tango::ON);
			set_status("Connected");
			return;
		case LIBUSB_ERROR_TIMEOUT:
			return;
		case LIBUSB_ERROR_PIPE:
			mesg = "Pipe error";
			break;
		case LIBUSB_ERROR_OVERFLOW:
			mesg = "Overflow error";
			break;
		case LIBUSB_ERROR_NO_DEVICE:
			mesg = "No device error";
			break;
		case LIBUSB_ERROR_BUSY:
			mesg = "Busy error";
			break;
		case LIBUSB_ERROR_INVALID_PARAM:
			mesg = "Invalid parameter error";
			break;
		default:
			ERROR_STREAM << "USB error " << conn_state
				<< " not handled!" << endl;
			assert(false);
			break;
	}

	set_state(Tango::INIT);
	set_status("Reconnecting due: " + mesg);
	DEBUG_STREAM << "Reconnecting due: " << mesg << endl;

	close();
	open();
	reconnections += 1;
}

/*----- PROTECTED REGION END -----*/	//	USB2::namespace_ending
} //	namespace