#ifndef __CAN_H__
#define __CAN_H__

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/semaphore.h>

#include "../api/can_codes.h"

/*
	Функции для работы с платами CAN-200
*/

#define VERSION                 1
#define SUBVERSION              3
#define SUBSUBVERSION           2

#define BUS_ON					0
#define BUS_OFF					1

#define CAN_NUM_CHANNEL			4	/* Число каналов на плате */

#define CAN_FIFO_SIZE			512	/* Длина буферов приема и выдачи */

#define TX_COUNTER				128	/* Количество повторных попыток выдачи пакета */

/* Разрешение/запре аппаратного повтора выдачи пакета */
#define CAN200_DISABLE_HARDWARE_REPETITION	0
#define CAN200_ENABLE_HARDWARE_REPETITION	1
 
#define CAN200PCI				0
#define CAN200PC				1
#define CAN200PCIE				2
#define CAN200_104PCI			3

union CAN_interrupt_t
{
	struct
	{
		unsigned char ri			: 1; /* Прерывание по приему кадра */
		unsigned char ti			: 1; /* Прерывание после передачи кадра */
		unsigned char ei			: 1; /* Прерывание по ошибкам кадра */
		unsigned char doi			: 1; /* Прерывание при переполнении буфера приема */
		unsigned char wui			: 1; /* Прерывание при выходе из состояния "Сна" (только для режима PeliCAN) */
		unsigned char epi			: 1; /* Переход в "error passive status" (только для режима PeliCAN) */
		unsigned char ali			: 1; /* Прерывание при проигрыше арбитража (только для режима PeliCAN) */
		unsigned char bei			: 1; /* Прерывание при ошибке на шине (только для режима PeliCAN) */
	} bit;
	unsigned char byte;
};

struct can_rx_fifo_t
{
	int first;
	int last;
	atomic_t len;
	CAN_data data[CAN_FIFO_SIZE];
	int overrun;
	struct can_rx_fifo_t *next;
};

struct can_tx_fifo_t
{
	int                    len;			/* Длина выдаваемых данных */
	int                    offset;		/* Реально выдано данных */
	int                    tx_counter;	/* Счетчик попыток выдачи */
	CAN_data              *data;		/* Данные */
	int                    blocking;	/* Признак блокирования клиента */
	int                    complete;	/* Признак окончания выдачи */
	wait_queue_head_t      tx_wait;		/* Очереди ожидания (выдача) */
	struct private_data_t *private_data;
	struct can_tx_fifo_t  *next;
};

struct private_data_t
{
	int num_dev;
	int num_channel;
	int status; /* Статус выдачи */
	int len; /* Длина выданных данных */
	struct can_rx_fifo_t *rx_fifo;
	struct can_tx_fifo_t *tx_fifo;
};

struct can_t
{
	unsigned short        wPorts[CAN_NUM_CHANNEL];			/* Базовые порты каналов */
	unsigned short        wIrql;							/* Номер прерывания */
	unsigned short        wSizeRegion[CAN_NUM_CHANNEL];		/* Размер регионов для ввода-вывода */
	unsigned char         irq_register;						/* Признак успешной регистрации прерывания */
	unsigned char         type;								/* Тип платы */
	unsigned char         device_create[CAN_NUM_CHANNEL];	/* Признак успешного создания файла устройства */
	unsigned char         pci_enable_device;				/* Признак разрешения устройства (pci_enable_device) */
	struct pci_dev       *dev;								/* Устройство */
	struct can_rx_fifo_t *rx_fifo[CAN_NUM_CHANNEL];			/* Очереди принятых данных канала */
	wait_queue_head_t     rx_queue[CAN_NUM_CHANNEL];		/* Очереди ожидания (прием) */
	struct can_tx_fifo_t *tx_fifo[CAN_NUM_CHANNEL];			/* Структура для выдачи данных */
	wait_queue_head_t     tx_queue[CAN_NUM_CHANNEL];		/* Очереди ожидания (выдача) */
	int                   stop_trans[CAN_NUM_CHANNEL];		/* Команда на остановку выдачи */
	int                   stopping[CAN_NUM_CHANNEL];		/* Признак остановки выдачи */
	spinlock_t            irq_lock;							/* Спин-блокировка для синхронизации доступа к плате */
	spinlock_t            rx_fifo_lock[CAN_NUM_CHANNEL];	/* Спин-блокировка для синхронизации доступа к очередям приема */
	spinlock_t            tx_fifo_lock[CAN_NUM_CHANNEL];	/* Спин-блокировка для синхронизации доступа к буферу выдачи */
	struct mutex	ioctl_mutex[CAN_NUM_CHANNEL];			/* Мьютекс */
	int                   bus_mode[CAN_NUM_CHANNEL];		/* Состояние подключения к шине */
	int                   tx_counter;						/* Количество повторов выдачи пакета */
	int                   hardware_repetition;				/* == 1 при аппаратном повторе выдачи пакета */
	struct CAN_info_t     info;								/* Информация о плате */
};

extern struct can_t can[CAN_MAX_DEVICE]; /* Массив для хранения информации о платах */

/* Режим работы и режим сброса для каналов плат */
#define NormalMode	0
#define ResetMode	1

//-----------------------------------------------------------------------------
int find_can200pci(struct can_t *can200, int mac_count);
/* Поиск устройств CAN-200PCI */
//-----------------------------------------------------------------------------
int find_can200pcie(struct can_t *can200, int mac_count);
/* Поиск устройств CAN-200PCIe */
//-----------------------------------------------------------------------------
int find_can200_104pci(struct can_t *can, int max_count);
/* Поиск устройств CAN-200-104PCI */
//-----------------------------------------------------------------------------
void can_get_info(struct CAN_info_t *info, int dev, int channel);
/* Получение информации о плате */

//------------------------------------------------------------------------------
int can200_num_ports(struct can_t *can);

#endif /* __CAN_H__ */
