#include "can200_api.h"

#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#include <devctl.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#include "can_codes.h"

/**
 * \file can200_api.c
 * \brief Файл содержит реализацию функций для работы с платами CAN200.
 */

/**
 * \fn int CAN200_HardReset(int fd)
 * \brief Аппаратный сброс платы
 *
 * Функция выполняет аппаратный сброс плат CAN-200PC, CAN-200MP, CAN-200PC104
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_HardReset(int fd)
{
	/* Контроль входных параметров */
	assert(fd > 0);

	if (fd < 0)
	{
		return EBADF;
	}
	return devctl(fd, __DION(_DCMD_MISC, CAN_HARDRESET), NULL, 0, NULL);
}

/**
 * \fn int CAN200_SetDriverMode(int fd)
 * \brief Настройка выходных преобразователей платы
 *
 * Функция выполняет переключение платы в рабочий режим
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return один из кодов ошибки возвращаемых функцией devctl
 * \warning Данную функцию необходимо вызвать до начала приема/выдачи данных
 */
//-----------------------------------------------------------------------------
int CAN200_SetDriverMode(int fd)
{
	/* Контроль входных параметров */
	assert(fd > 0);

	if (fd < 0)
	{
		return EBADF;
	}
	return devctl(fd, __DION(_DCMD_MISC, CAN_SETDRIVERMODE), NULL, 0, NULL);
}

/**
 * \fn int CAN200_GetConfig(int fd, struct CAN_info_t *info)
 * \brief Получение описания платы
 *
 * Функция возвращает описание платы (тип, серийный номер и т.д.)
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param info (OUT) Указатель на структуру CAN_info_t, где будет сохранена информация о плате
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_GetConfig(int fd, struct CAN_info_t *info)
{
	int res;
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != info);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == info)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_GETCONFIG, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		memcpy(info, &devctl_req.info, sizeof(struct CAN_info_t));
	}
	return res;
}

/**
 * \fn int CAN200_GetNumChannel(int fd, int *num_channel)
 * \brief Получение количества каналов в плате
 *
 * Функция возвращает количество каналов в плате
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param num_channel (OUT) Указатель на переменную, где будет сохранено количество каналов в плате
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_GetNumChannel(int fd, int *num_channel)
{
	int res;
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != num_channel);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == num_channel)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_GETNUMCHANNEL, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*num_channel = devctl_req.num_channel;
	}
	return res;
}

/**
 * \fn int CAN200_GetStatus(int fd, unsigned char *status)
 * \brief Получение статуса работы платы CAN200
 *
 * Функция возвращает статус платы. Расшифровка статуса приозводится в соответствии с объединением CAN_status_t
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param status (OUT) Указатель переменную, где будет сохранен статус
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_GetStatus(int fd, unsigned char *status)
{
	int res;
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != status);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == status)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_GETSTATUS, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*status = devctl_req.status;
	}
	return res;
}

/**
 * \fn int CAN200_GetOverCounter(int fd, int *counter)
 * \brief Получение количества отброшенных кадров
 *
 * Фунцкия возвращает количество кадров отброшенных из-за нехватки места в буфере приема драйвера
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param counter (OUT) Указатель переменную, где будет сохранено кол-во отброшенных кадров
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_GetOverCounter(int fd, int *counter)
{
	int res;
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != counter);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == counter)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_GETOVERCOUNTER, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*counter = devctl_req.over_counter;
	}
	return res;
}

/**
 * \fn int CAN200_GetWorkMode(int fd, unsigned char *mode)
 * \brief Получение текущего режима работы платы
 *
 * Функция возвращает текущий режи работы платы. Возможные варианты режима работы: #BasicCAN и #PeliCAN
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param mode (OUT) Указатель переменную, где будет сохранен текущий режим работы
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_GetWorkMode(int fd, unsigned char *mode)
{
	int res;
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != mode);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == mode)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_GETWORKMODE, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*mode = devctl_req.mode;
	}
	return res;
}

/**
 * \fn int CAN200_SetWorkMode(int fd, unsigned char mode)
 * \brief Установка режима работы платы
 *
 * Функция устанавливает режим работы платы. Возможные варианты режима работы: #BasicCAN и #PeliCAN
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param mode (IN) Задаваемый режим работы платы
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_SetWorkMode(int fd, unsigned char mode)
{
	/* Установка режима работы платы */
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);
	assert((BasicCAN == mode) || (PeliCAN == mode));

	if (fd < 0)
	{
		return EBADF;
	}
	if ((BasicCAN != mode) && (PeliCAN != mode))
	{
		return EINVAL;
	}

	devctl_req.mode = mode;

	return devctl(fd, __DIOT(_DCMD_MISC, CAN_SETWORKMODE, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
}

/**
 * \fn int CAN200_GetCANSpeed(int fd, unsigned int *speed)
 * \brief Получение текущей настройки скорости обмена по шине CAN
 *
 * Функция возвращает текущую скорость обмена по шине CAN. Возможные варианты скорости:
 * #CAN_SPEED_1000, #CAN_SPEED_800, #CAN_SPEED_500, #CAN_SPEED_250, #CAN_SPEED_125,
 * #CAN_SPEED_20, #CAN_SPEED_10, #CAN_SPEED_USER_DEFINED
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param speed (OUT) Указатель переменную, где будет сохранена текущая скорость
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_GetCANSpeed(int fd, unsigned int *speed)
{
	int res;
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != speed);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == speed)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_GETCANSPEED, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*speed = devctl_req.speed;
	}
	return res;
}

/**
 * \fn int CAN200_SetCANSpeed(int fd, unsigned int speed)
 * \brief Получение текущей настройки скорости обмена по шине CAN
 *
 * Функция установки скорости обмена по шине CAN. Возможные варианты скорости:
 * #CAN_SPEED_1000, #CAN_SPEED_800, #CAN_SPEED_500, #CAN_SPEED_250, #CAN_SPEED_125,
 * #CAN_SPEED_20, #CAN_SPEED_10, #CAN_SPEED_USER_DEFINED
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param speed (IN) Задаваемая скорость
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_SetCANSpeed(int fd, unsigned int speed)
{
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);
	assert( (CAN_SPEED_1000 == speed) || (CAN_SPEED_800 == speed) ||
			(CAN_SPEED_500  == speed) || (CAN_SPEED_250 == speed) ||
			(CAN_SPEED_125  == speed) || (CAN_SPEED_50  == speed) ||
			(CAN_SPEED_20   == speed) || (CAN_SPEED_10  == speed) ||
			(IS_CAN_SPEED_USER_DEFINED(speed)));

	if (fd < 0)
	{
		return EBADF;
	}
	if ((CAN_SPEED_1000 != speed) && (CAN_SPEED_800 != speed) &&
		(CAN_SPEED_500  != speed) && (CAN_SPEED_250 != speed) &&
		(CAN_SPEED_125  != speed) && (CAN_SPEED_50  != speed) &&
		(CAN_SPEED_20   != speed) && (CAN_SPEED_10  != speed) &&
		(!IS_CAN_SPEED_USER_DEFINED(speed)))
	{
		return EINVAL;
	}

	devctl_req.speed = speed;

	return devctl(fd, __DIOT(_DCMD_MISC, CAN_SETCANSPEED, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
}

/** \addtogroup BasicCAN
 * \{
 * \fn int CAN200_B_GetFilter(int fd, unsigned char *mask, unsigned char *filter)
 * \brief Получение фильтра входных пакетов (режим #BasicCAN)
 *
 * Функция возвращает текущее значение фильтра входных пакетов
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param mask (OUT) Указатель переменную, где будет сохранено значение маски фильтра
 * \param filter (OUT) Указатель переменную, где будет сохранена значение кода фильтра
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_B_GetFilter(int fd, unsigned char *mask, unsigned char *filter)
{
	int res;
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != mask);
	assert(NULL != filter);

	if (fd < 0)
	{
		return EBADF;
	}
	if ((NULL == mask) || (NULL == filter))
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_B_GETFILTER, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*mask = devctl_req.b_filter.mask;
		*filter = devctl_req.b_filter.filter;
	}

	return res;
}
/** \} */

/** \addtogroup BasicCAN
 * \{
 * \fn int CAN200_B_SetFilter(int fd, unsigned char mask, unsigned char filter)
 * \brief Установка фильтра входных пакетов (режим #BasicCAN)
 *
 * Функция устанавливает значение фильтра входных пакетов
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param mask (OUT) Значение маски фильтра
 * \param filter (OUT) Значение кода фильтра
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_B_SetFilter(int fd, unsigned char mask, unsigned char filter)
{
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);

	if (fd < 0)
	{
		return EBADF;
	}

	devctl_req.b_filter.mask = mask;
	devctl_req.b_filter.filter = filter;

	return devctl(fd, __DIOT(_DCMD_MISC, CAN_B_SETFILTER, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
}
/** \} */

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_GetFilter(int fd, unsigned char *mode, unsigned char *amr0, unsigned char *amr1,
	unsigned char *amr2, unsigned char *amr3, unsigned char *acr0, unsigned char *acr1,
	unsigned char *acr2, unsigned char *acr3)
 * \brief Получение фильтра входных пакетов (режим #PeliCAN)
 *
 * Функция возвращает текущее значение фильтра входных пакетов
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param mode (OUT) Режим работы фильтра
 * \param amr0, amr1, amr2, amr3 (OUT) Указатель переменную, где будет сохранено значение маски фильтра
 * \param acr0, acr1, acr2, acr3 (OUT) Указатель переменную, где будет сохранена значение кода фильтра
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_GetFilter(int fd, unsigned char *mode, unsigned char *amr0, unsigned char *amr1,
	unsigned char *amr2, unsigned char *amr3, unsigned char *acr0, unsigned char *acr1,
	unsigned char *acr2, unsigned char *acr3)
{
	/* Получение фильтра режима PeliCAN */
	int res;
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != mode);
	assert(NULL != amr0);
	assert(NULL != amr1);
	assert(NULL != amr2);
	assert(NULL != amr3);
	assert(NULL != acr0);
	assert(NULL != acr1);
	assert(NULL != acr2);
	assert(NULL != acr3);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == mode)
	{
		return EINVAL;
	}
	if ((NULL == amr0) || (NULL == amr1) || (NULL == amr2) || (NULL == amr3))
	{
		return EINVAL;
	}
	if ((NULL == acr0) || (NULL == acr1) || (NULL == acr2) || (NULL == acr3))
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_P_GETFILTER, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*amr0 = devctl_req.p_filter.mask[0];
		*amr1 = devctl_req.p_filter.mask[1];
		*amr2 = devctl_req.p_filter.mask[2];
		*amr3 = devctl_req.p_filter.mask[3];
		*acr0 = devctl_req.p_filter.filter[0];
		*acr1 = devctl_req.p_filter.filter[1];
		*acr2 = devctl_req.p_filter.filter[2];
		*acr3 = devctl_req.p_filter.filter[3];
		*mode = devctl_req.p_filter.mode;
	}
	return res;
}
/** \} */

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_SetFilter(int fd, unsigned char mode, unsigned char amr0, unsigned char amr1,
	unsigned char amr2, unsigned char amr3, unsigned char acr0, unsigned char acr1,
	unsigned char acr2, unsigned char acr3)
 * \brief Установка фильтра входных пакетов (режим #PeliCAN)
 *
 * Функция устанавливает значение фильтра входных пакетов
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param mode (IN) Режим работы фильтра
 * \param amr0, amr1, amr2, amr3 (IN) Значение маски фильтра
 * \param acr0, acr1, acr2, acr3 (IN) Значение кода фильтра
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_SetFilter(int fd, unsigned char mode, unsigned char amr0, unsigned char amr1,
	unsigned char amr2, unsigned char amr3, unsigned char acr0, unsigned char acr1,
	unsigned char acr2, unsigned char acr3)
{
	/* Установка фильтра режима PeliCAN */
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);
	assert((0 == mode) || (1 == mode));

	if (fd < 0)
	{
		return EBADF;
	}
	if ((0 != mode) && (1 != mode))
	{
		return EINVAL;
	}

	devctl_req.p_filter.mask[0] = amr0;
	devctl_req.p_filter.mask[1] = amr1;
	devctl_req.p_filter.mask[2] = amr2;
	devctl_req.p_filter.mask[3] = amr3;
	devctl_req.p_filter.filter[0] = acr0;
	devctl_req.p_filter.filter[1] = acr1;
	devctl_req.p_filter.filter[2] = acr2;
	devctl_req.p_filter.filter[3] = acr3;
	devctl_req.p_filter.mode = mode;

	return devctl(fd, __DIOT(_DCMD_MISC, CAN_P_SETFILTER, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
}
/** \} */

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_GetRxErrorCounter(int fd, unsigned char *counter)
 * \brief Получение счетчика ошибок приема (режим #PeliCAN)
 *
 * Фунцкия возвращает текущее значение счетчика ошибок приема
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param counter (OUT) Указатель переменную, где будет сохранено значение счетчика
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_GetRxErrorCounter(int fd, unsigned char *counter)
{
	int res;
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != counter);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == counter)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_P_GETRXERRORCOUNTER, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*counter = devctl_req.counter;
	}
	return res;
}
/** \} */

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_SetRxErrorCounter(int fd, unsigned char counter)
 * \brief Установка счетчика ошибок приема (режим #PeliCAN)
 *
 * Фунцкия устанавливает значение счетчика ошибок приема
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param counter (IN) Значение счетчика ошибок приема
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_SetRxErrorCounter(int fd, unsigned char counter)
{
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);

	if (fd < 0)
	{
		return EBADF;
	}

	devctl_req.counter = counter;
	
	return devctl(fd, __DIOT(_DCMD_MISC, CAN_P_SETRXERRORCOUNTER, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
}
/** \} */

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_GetTxErrorCounter(int fd, unsigned char *counter)
 * \brief Получение счетчика ошибок выдачи (режим #PeliCAN)
 *
 * Фунцкия возвращает текущее значение счетчика ошибок выдачи
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param counter (OUT) Указатель переменную, где будет сохранено значение счетчика
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_GetTxErrorCounter(int fd, unsigned char *counter)
{
	int res;
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != counter);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == counter)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_P_GETTXERRORCOUNTER, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*counter = devctl_req.counter;
	}
	return res;
}
/** \} */

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_SetTxErrorCounter(int fd, unsigned char counter)
 * \brief Установка счетчика ошибок выдачи (режим #PeliCAN)
 *
 * Фунцкия устанавливает значение счетчика ошибок выдачи
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param counter (IN) Значение счетчика ошибок выдачи
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_SetTxErrorCounter(int fd, unsigned char counter)
{
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);

	if (fd < 0)
	{
		return EBADF;
	}

	devctl_req.counter = counter;
	
	return devctl(fd, __DIOT(_DCMD_MISC, CAN_P_SETTXERRORCOUNTER, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
}
/** \} */

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_GetErrorWarningLimit(int fd, unsigned char *counter)
 * \brief Получение верхнего предела счетчиков ошибок (режим #PeliCAN)
 *
 * Фунцкия возвращает текущее значение верхнего предела счетчиков ошибок
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param counter (OUT) Указатель переменную, где будет сохранено значение счетчика
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_GetErrorWarningLimit(int fd, unsigned char *counter)
{
	int res;
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != counter);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == counter)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_P_GETERRORWARNINGLIMIT, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*counter = devctl_req.counter;
	}
	return res;
}
/** \} */

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_SetErrorWarningLimit(int fd, unsigned char counter)
 * \brief Установка верхнего предела счетчиков ошибок (режим #PeliCAN)
 *
 * Фунцкия устанавливает значение верхнего предела счетчиков ошибок
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param counter (IN) Значение счетчика ошибок выдачи
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_SetErrorWarningLimit(int fd, unsigned char counter)
{
	/* Установка верхнего предела счетчиков ошибок режима PeliCAN */
	union devctl_req_t devctl_req;
	
	/* Контроль входных параметров */
	assert(fd > 0);

	if (fd < 0)
	{
		return EBADF;
	}

	devctl_req.counter = counter;
	
	return devctl(fd, __DIOT(_DCMD_MISC, CAN_P_SETERRORWARNINGLIMIT, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
}
/** \} */

/**
 * \fn int CAN200_SetBusOn(int fd)
 * \brief Перевод платы в режим bus on
 *
 * Функция позволяет переключить плату из режима bus off в режим bus on
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_SetBusOn(int fd)
{
	/* Контроль входных параметров */
	assert(fd > 0);

	if (fd < 0)
	{
		return EBADF;
	}
	/* Переключение канала в рабочий режим, после Bus Off */
	return devctl(fd, __DION(_DCMD_MISC, CAN_SETBUSON), NULL, 0, NULL);
}

/**
 * \fn int CAN200_GetTransmitStatus(int fd, int *status, int *len)
 * \brief Получение статуса выдачи пакетов
 *
 * Функция возвращает текущее состояние выдачи (завершена/не завершена/ошибка) и кол-во
 * оставщихся данных для выдачи. Возможные значение статуса выдачи: #CAN_TRANSMIT_WAIT,
 * #CAN_TRANSMIT, #CAN_TRANSMIT_COMPLETE, #CAN_TRANSMIT_ERROR
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param status (IN) Указатель переменную, где будет сохранен статус выдачи
 * \param len (IN) Указатель переменную, где будет сохранена длина оставшихся данных
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_GetTransmitStatus(int fd, int *status, int *len)
{
	union devctl_req_t devctl_req;
	int res;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != status);
	assert(NULL != len);

	if (fd < 0)
	{
		return EBADF;
	}
	if ((NULL == status) || (NULL == len))
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_GETTRANSMITSTATUS, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*status = devctl_req.sts.status;
		*len = devctl_req.sts.len;
	}
	return res;
}

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_GetErrorCode(int fd, unsigned char *code)
 * \brief Получение кода ошибки (режим #PeliCAN)
 *
 * Фунцкия возврашает код произошедшей ошибки.
 * Расшифровка кода ошибки производится в соответствии с объединением #CAN_error_t
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param code (OUT) Значение кода ошибки
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_GetErrorCode(int fd, unsigned char *code)
{
	/* Получение кода ошибки */
	int res;
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != code);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == code)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_P_GETERRORCODE, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*code = devctl_req.code;
	}
	return res;
}
/** \} */

/** \addtogroup PeliCAN
 * \{
 * \fn int CAN200_P_GetArbitrationLostCapture(int fd, unsigned char *data)
 * \brief Получение точку потери арбитража при выдаче кадра (режим PeliCAN)
 *
 * Фунцкия возврышает код точки потери арбитража при выдаче кадра.
 * Расшифровка кода производится в соответствии с объединением #CAN_arbitration_lost_capture_t
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param data (OUT) Значение кода точки потери арбитража
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return ENOTSUP - режим работы платы не соответствует требуемому
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_P_GetArbitrationLostCapture(int fd, unsigned char *data)
{
	/* Получение точки потери данных */
	int res;
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != data);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == data)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_P_GETEARBITRATIONLOSTCAPTURE, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*data = devctl_req.counter;
	}
	return res;
}
/** \} */

/**
 * \fn int CAN200_DropTransmit(int fd, int command)
 * \brief Отмена выдачи пакетов
 *
 * Функция позволяет отменить операцию выдачи.
 * Вызов с флагом #CAN_DROP_CURRENT останавливает выдачу пакетов, удаляет из очереди все пакеты,
 * поставленные в нее (функцийе write) текущим потоком и, в случае необходимости, вновь запускает выдачу
 * Вызов с флагом #CAN_DROP_ALL останавливает выдачу пакетов и полностью очищает очередь выдачи
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param command (IN) Флаг определяющий отбрасываемые пакеты (#CAN_DROP_CURRENT или #CAN_DROP_ALL)
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_DropTransmit(int fd, int command)
{
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert((CAN_DROP_CURRENT == command) || (CAN_DROP_ALL == command));

	if (fd < 0)
	{
		return EBADF;
	}
	if ((CAN_DROP_CURRENT != command) && (CAN_DROP_ALL != command))
	{
		return EINVAL;
	}

	devctl_req.command = command;

	return devctl(fd, __DIOT(_DCMD_MISC, CAN_DROPTRANSMIT, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
}

/**
 * \fn int CAN200_GetDriverVersion(int fd, int *version)
 * \brief Отмена выдачи пакетов
 *
 * Функция возвращает версию драйвера.
 * \param fd (IN) Дискриптор платы, возврашенный функцией open
 * \param version (OUT) Версия драйвера (3 байта, младший - подподверсия, второй - подверсия, третий - версия)
 * \return EOK - успешное завершение
 * \return EBADF - неверный хэндл платы
 * \return EINVAL - ошибка входных параметров
 * \return один из кодов ошибки возвращаемых функцией devctl
 */
//-----------------------------------------------------------------------------
int CAN200_GetDriverVersion(int fd, int *version)
{
	/* Получение версии драйвера */
	int res;
	union devctl_req_t devctl_req;

	/* Контроль входных параметров */
	assert(fd > 0);
	assert(NULL != version);

	if (fd < 0)
	{
		return EBADF;
	}
	if (NULL == version)
	{
		return EINVAL;
	}

	res = devctl(fd, __DIOF(_DCMD_MISC, CAN_GETDRIVERVERSION, union devctl_req_t),
		&devctl_req, sizeof(union devctl_req_t), NULL);
	if (EOK == res)
	{
		*version = devctl_req.version;
	}
	return res;
}
