#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_read.h"
#include "compat_access_ok.h"

//-----------------------------------------------------------------------------
ssize_t can_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
	/* Реализация функции read */
	int dev, channel;
	size_t len = 0;
	int size, packets;
	int res;
	struct can_rx_fifo_t *rx_fifo;
	int blocking;
	struct private_data_t *private_data;

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

	blocking = !(filp->f_flags & O_NONBLOCK);

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

	while (size > 0)
	{
		/* Есть пакет */
		packets = atomic_read(&rx_fifo->len); /* Кол-во принятых пакетов */
		if (packets > 0)
		{
			int local_min = min(packets, size);
			res = write_client(buf, local_min, &len, rx_fifo);
			if (0 != res)
			{
				return -EFAULT;
			}
			size = size - local_min;
		}
		else
		{
			/* Нет принятых данных */
			if (blocking)
			{
				/* Плата с состоянии BussOff или в сбросе */
				if ((ResetMode == test_reset_request(can[dev].wPorts[channel])) || (BUS_OFF == can[dev].bus_mode[channel]))
				{
					/* Шина оключена */
					if (0 == len)
					{
						return -EINVAL;
					}
					else
					{
						return len;
					}
				}
				if (size > CAN_FIFO_SIZE / 2)
				{
					res = wait_event_interruptible(can[dev].rx_queue[channel], ((atomic_read(&rx_fifo->len) >= CAN_FIFO_SIZE / 2) ||
																				(can[dev].bus_mode[channel] == BUS_OFF)) ||
																				(ResetMode == test_reset_request(can[dev].wPorts[channel])));
				}
				else
				{
					res = wait_event_interruptible(can[dev].rx_queue[channel], ((atomic_read(&rx_fifo->len) >= size) ||
																				(can[dev].bus_mode[channel] == BUS_OFF)) ||
																				(ResetMode == test_reset_request(can[dev].wPorts[channel])));
				}
				if (0 != res)
				{
					if (0 != len)
					{
						/* Ошибка, возврашаем скопированные ранее данные */
						return len;
					}
					else
					{
						return -EFAULT;
					}
				}
			}
			else
			{
				break;
			}
		}
	}

	return len;
}

//-----------------------------------------------------------------------------
int write_client(char *buf, int size, size_t *len, struct can_rx_fifo_t *rx_fifo)
{
	/*
	 buf - буфер для выдачи данных клиенту
	 size - кол-во выдаваемых пакетов
	 len - смешение в буфере выдачи
	 */
	int res, tmp;

	if( (rx_fifo->first + size) > CAN_FIFO_SIZE )
	{
		/* Первая часть */
		tmp = CAN_FIFO_SIZE - rx_fifo->first;
		res = copy_to_user(buf + *len, &rx_fifo->data[rx_fifo->first], sizeof(CAN_data) * tmp);
		if( res != 0 )
		{
			return -EFAULT;
		}
		atomic_sub(tmp, &rx_fifo->len);
		size = size - tmp;
		*len = *len + sizeof(CAN_data) * tmp;
		rx_fifo->first = 0;
	}
	if( size > 0 )
	{
		/* Вторая часть */
		res = copy_to_user(buf + *len, &rx_fifo->data[rx_fifo->first], sizeof(CAN_data) * size);
		if( res != 0 )
		{
			return -EFAULT;
		}
		atomic_sub(size, &rx_fifo->len);
		*len = *len + sizeof(CAN_data) * size;
		rx_fifo->first = (rx_fifo->first + size) % CAN_FIFO_SIZE;
	}
	return 0;
}
