#include <linux/sched.h>
#include <linux/sched.h>

#include "can.h"
#include "can_util.h"
#include "sja1000.h"

//-----------------------------------------------------------------------------
void stop_trans(struct can_t *can, int channel)
{
	/* Остановка выдачи заданного канала заданной платы */
	unsigned long flags;
	int           port;

	port = can->wPorts[channel];

	/* Захватываем спин и запрещаем локальные прерывания */
	spin_lock_irqsave(&can->tx_fifo_lock[channel], flags);

	can->stopping[channel] = 0; /* Признак остановки выдачи */
	can->stop_trans[channel] = 1; /* Команда на остановку выдачи */

	set_command(port, 0x03);

	if (NULL != can->tx_fifo[channel])
	{
		/* Освобождаем спин и разрешаем локальные прерывания */
		spin_unlock_irqrestore(&can->tx_fifo_lock[channel], flags);
		
		wait_event(can->tx_queue[channel], can->stopping[channel] != 0);
	}
	else
	{
		/* Освобождаем спин и разрешаем локальные прерывания */
		spin_unlock_irqrestore(&can->tx_fifo_lock[channel], flags);
	}
	return;
}

//-----------------------------------------------------------------------------
void start_trans(struct can_t *can, int channel)
{
	/* Запуск выдачи заданного канала заданной платы */
	unsigned long flags;

	/* Захватываем спин и запрещаем локальные прерывания */
	spin_lock_irqsave(&can->tx_fifo_lock[channel], flags);

	can->stop_trans[channel] = 0;
	can->stopping[channel] = 0;

 	/* Запускаем выдачу */
	transmit_message(can, channel);

	/* Освобождаем спин и разрешаем локальные прерывания */
	spin_unlock_irqrestore(&can->tx_fifo_lock[channel], flags);

	return;
}

//-----------------------------------------------------------------------------
void transmit_message(struct can_t *can, int channel)
{
	/* Выдача сообщения по шине CAN с проверкой соответствия режима и счетчика повторов */
	int                   res;
	CAN_data             *data;
	struct can_tx_fifo_t *tx_fifo;

	while (NULL != can->tx_fifo[channel])
	{
		tx_fifo = can->tx_fifo[channel];
		if ((CAN200_DISABLE_HARDWARE_REPETITION == can->hardware_repetition) &&
			(0 == tx_fifo->tx_counter))
		{
			/* Ошибка выдачи пакета после TX_COUNTER повторов */
			can->tx_fifo[channel] = tx_fifo->next;
			write_replay_status_and_free_mem(tx_fifo, CAN_TRANSMIT_ERROR);
			continue;
		}
		/* Устанавливаем статус выдачи */
		if (NULL != tx_fifo->private_data)
		{
			tx_fifo->private_data->status = CAN_TRANSMIT;
			tx_fifo->private_data->len = tx_fifo->offset;
		}
		/* Выдаем следующий пакет */
		data = tx_fifo->data + tx_fifo->offset / sizeof(CAN_data);
		res = write_message(can->wPorts[channel], data, can->hardware_repetition);
		if (0 == res)
		{
			break;
		}
		/* Ошибка выдачи */
		can->tx_fifo[channel] = tx_fifo->next;
		write_replay_status_and_free_mem(tx_fifo, CAN_TRANSMIT_ERROR);
	}
	return;
}

//-----------------------------------------------------------------------------
void write_replay_status_and_free_mem(struct can_tx_fifo_t *tx_fifo, int status)
{
	/* Разблокирование клиента (и освобождение памяти в случае необходимости) */

	/* Устанавливаем статус выдачи */
	if (NULL != tx_fifo->private_data)
	{
		tx_fifo->private_data->status = status;
		tx_fifo->private_data->len = tx_fifo->offset;
	}

	if (tx_fifo->blocking)
	{
		/* Клиент заблокирован */
		tx_fifo->complete = 1;
		/* Будим всех ждущих принятия пакета */
		wake_up_interruptible(&tx_fifo->tx_wait);
	}
	else
	{
		if (NULL != tx_fifo->private_data)
		{
			tx_fifo->private_data->tx_fifo = NULL;
		}
		/* Освобождаем память */
		kfree(tx_fifo->data);
		kfree(tx_fifo);
	}
}

//------------------------------------------------------------------------------
void write_replay_error_all(struct can_t *can, int channel, int status)
{
	/* Разблокирование всех клиентов в случаем ошибки (выдача) */
	struct can_tx_fifo_t *tx_fifo;

	while (NULL != can->tx_fifo[channel])
	{
		tx_fifo = can->tx_fifo[channel];
		can->tx_fifo[channel] = tx_fifo->next;
		/* Разблокируем всех клиентов */
		write_replay_status_and_free_mem(tx_fifo, status);
	}

	return;
}

//------------------------------------------------------------------------------
struct can_tx_fifo_t *remove_tx_client_from_list(struct can_t *can, int channel,
		struct can_tx_fifo_t *removed_tx_fifo)
{
	/* Удалить из списка */
	struct can_tx_fifo_t *tx_fifo;
	struct can_tx_fifo_t *tx_fifo_prev;

	tx_fifo_prev = NULL;

	tx_fifo = can->tx_fifo[channel];
	while (NULL != tx_fifo)
	{
		if (tx_fifo == removed_tx_fifo)
		{
			/* Удаляем данную запись из очереди */
			tx_fifo = tx_fifo->next;
			if (NULL == tx_fifo_prev)
			{
				/* Первый элемент в списке */
				can->tx_fifo[channel] = tx_fifo;
			}
			else
			{
				tx_fifo_prev->next = tx_fifo;
			}
			return tx_fifo;
		}
		else
		{
			tx_fifo_prev = tx_fifo;
			tx_fifo = tx_fifo->next;
		}
	}

	return NULL;
}
