#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/sched.h>

#include "sja1000.h"
#include "can.h"
#include "can_irq.h"
#include "can_ioctl.h"
#include "can_util.h"
#include "compat_access_ok.h"

//-----------------------------------------------------------------------------
static int verification_data(CAN_data *data, unsigned int count, unsigned char *max_type);
/* Проверка выдаваемых данных */

//-----------------------------------------------------------------------------
ssize_t can_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
	/* Реализация функции write */
	int dev, channel, port;
	size_t len = 0;
	int res;
	struct can_tx_fifo_t *tx_fifo, *tx_fifo_tmp;
	int blocking;
	unsigned long flags;
	struct private_data_t *private_data;
	CAN_data *data;
	unsigned char max_type;

    private_data = (struct private_data_t *)(filp->private_data);
	dev = private_data->num_dev;
	channel = private_data->num_channel;

	blocking = !(filp->f_flags & O_NONBLOCK);
	port = can[dev].wPorts[channel];

	if (!access_ok(buf, count))
	{
		return -EINVAL;
	}
	
	if (0 != (count % sizeof(CAN_data)))
	{
		/* Длина выдаваемых данных не кратна размеру CAN_data */
		return -EINVAL;
	}

	if (0 == count)
	{
		/* Пустой вызов */
		return 0;
	}

	/* Выделяем память */
	data = (CAN_data *)kmalloc(count, GFP_KERNEL);
	if (NULL == data)
	{
		return -EFAULT;
	}

	/* Копируем данные */
	res = copy_from_user(data, buf, count);
	if (0 != res)
	{
		kfree(data);
		return -EFAULT;
	}
	
	/* Проверка данных */
	if (0 != verification_data(data, count / sizeof(CAN_data), &max_type))
	{
		kfree(data);
		return -EFAULT;
	}

	/* Выделяем память */
	tx_fifo = (struct can_tx_fifo_t *)kmalloc(sizeof(struct can_tx_fifo_t), GFP_KERNEL);
	if (NULL == tx_fifo)
	{
		kfree(data);
		return -EFAULT;
	}

	tx_fifo->data = data;
	tx_fifo->offset = 0;
	tx_fifo->len = count;
	tx_fifo->tx_counter = TX_COUNTER;
	tx_fifo->blocking = blocking;
	tx_fifo->complete = 0;
	init_waitqueue_head(&tx_fifo->tx_wait);
	tx_fifo->private_data = private_data;
	tx_fifo->next = NULL;

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

	/* Проверка на режим BUS-OFF */
	if (BUS_OFF == can[dev].bus_mode[channel])
	{
		/* Освобождаем спин и разрешаем локальные прерывания */
		spin_unlock_irqrestore(&can[dev].tx_fifo_lock[channel], flags);

		/* Освобождаем выделенную память */
		kfree(tx_fifo->data);
		kfree(tx_fifo);

		return -EIO;
	}
	
	if (ResetMode == test_reset_request(can[dev].wPorts[channel]))
	{
		/* Освобождаем спин и разрешаем локальные прерывания */
		spin_unlock_irqrestore(&can[dev].tx_fifo_lock[channel], flags);

		/* Освобождаем выделенную память */
		kfree(tx_fifo->data);
		kfree(tx_fifo);

		return -EIO;
	}
	
	if ((BasicCAN == get_work_mode(can[dev].wPorts[channel])) && (PeliCAN == max_type))
	{
		/* Освобождаем спин и разрешаем локальные прерывания */
		spin_unlock_irqrestore(&can[dev].tx_fifo_lock[channel], flags);

		/* Освобождаем выделенную память */
		kfree(tx_fifo->data);
		kfree(tx_fifo);

		return -EINVAL;
	}
	
	private_data->tx_fifo = tx_fifo;
	private_data->status = CAN_TRANSMIT_WAIT;
	
	/* Добавляем данные в список выдачи */
	if (NULL == can[dev].tx_fifo[channel])
	{
		can[dev].tx_fifo[channel] = tx_fifo;
		if (1 != can[dev].stop_trans[channel])
		{
			/* Запускаем выдачу */
			transmit_message(&can[dev], channel);
		}
	}
	else
	{
		tx_fifo_tmp = can[dev].tx_fifo[channel];
		while (NULL != tx_fifo_tmp->next)
		{
			tx_fifo_tmp = tx_fifo_tmp->next;
		}
		tx_fifo_tmp->next = tx_fifo;
	}

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

		if (0 != wait_event_interruptible(tx_fifo->tx_wait, tx_fifo->complete))
		{
			mutex_lock(&can[dev].ioctl_mutex[channel]);

			/* Останавливаем выдачу */
			stop_trans(&can[dev], channel);

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

			/* Удаляем буфер из очереди */
			if (NULL != can[dev].tx_fifo[channel])
			{
				if (can[dev].tx_fifo[channel] == tx_fifo)
				{
					/* Текущий выдаваемый буфер */
					can[dev].tx_fifo[channel] = can[dev].tx_fifo[channel]->next;
				}
				else
				{
					tx_fifo_tmp = can[dev].tx_fifo[channel];
					while (NULL != tx_fifo_tmp->next)
					{
						if (tx_fifo_tmp->next == tx_fifo)
						{
							tx_fifo_tmp->next = tx_fifo_tmp->next->next;
							break;
						}
						tx_fifo_tmp = tx_fifo_tmp->next;
					}
				}
			}
			private_data->tx_fifo = NULL;

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

			start_trans(&can[dev], channel);

			mutex_unlock(&can[dev].ioctl_mutex[channel]);

			if (0 != tx_fifo->offset)
			{
				len = tx_fifo->offset;
			}
			else
			{
				len = -EINVAL;
			}

			/* Освобождаем выделенную память */
			kfree(tx_fifo->data);
			kfree(tx_fifo);

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

		private_data->tx_fifo = NULL;

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

		len = tx_fifo->offset;

		/* Освобождаем выделенную память */
		kfree(tx_fifo->data);
		kfree(tx_fifo);
	}
	else
	{
		/* Освобождаем спин и разрешаем локальные прерывания */
		spin_unlock_irqrestore(&can[dev].tx_fifo_lock[channel], flags);

		len = count;
	}

    return len;
}

//-----------------------------------------------------------------------------
static int verification_data(CAN_data *data, unsigned int count, unsigned char *max_type)
{
	/* Проверка выдаваемых данных */
	int i;

	*max_type = BasicCAN;
	
	for (i = 0; i < count; i++)
	{
		/* Бит RTR 0 или 1 */
		if ((0 != data[i].rtr) && (1 != data[i].rtr))
		{
			return -1;
		}
		
		/* DLC от 0 до 8 */
		if (data[i].dlc > 8)
		{
			return -1;
		}
		
		/* Соответствие типа кадра и значения идентификатора */
		if (BasicCAN == data[i].type)
		{
			if (data[i].id > 0x7ff)
			{
				return -1;
			}
		}
		else if (PeliCAN == data[i].type)
		{
			*max_type = PeliCAN;
			if (data[i].id > 0x1fffffff)
			{
				return -1;
			}
		}
		else
		{
			return -1;
		}
	}
	
	return 0;
}

