/* read.c
 * Linux CAN-bus device driver.
 * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
 * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
 * email:pisa@cmp.felk.cvut.cz
 * This software is released under the GPL-License.
 * Version lincan-0.3  17 Jun 2004
 */

#include "../include/can.h"
#include "../include/can_sysdep.h"
#include "../include/main.h"
#include "../include/read.h"
#include "../include/ioctl.h"

/* This is the 'Normal' read handler for normal transmission messages */
inline ssize_t can_std_read(struct file *file, struct canque_ends_t *qends, 
			struct msgobj_t *obj, char *buffer, size_t length)
{
	int ret;
	int bytes_to_copy;
	struct canque_edge_t *qedge;
	struct canque_slot_t *slot;
	
	ret=canque_test_outslot(qends, &qedge, &slot);
	if(ret<0){
		if (file->f_flags & O_NONBLOCK) {
			return -EAGAIN;
		}
		ret=canque_get_outslot_wait_kern(qends, &qedge, &slot);
		if(ret<0){
			if (signal_pending(current)) {
				DEBUGMSG("Rx interrupted\n");
				return -EINTR;
			}
			/*if (!can_timeout) {
				DEBUGMSG("no data received\n");
				return 0;
			}*/
			return -EIO;
		}
	}
	
	copy_to_user(buffer, &slot->msg, sizeof(struct canmsg_t));
	canque_free_outslot(qends, qedge, slot);
	buffer += sizeof(struct canmsg_t);
	bytes_to_copy = length-sizeof(struct canmsg_t);
	
	while (bytes_to_copy > 0) {
		ret=canque_test_outslot(qends, &qedge, &slot);
		if(ret<0)
			break;
		copy_to_user(buffer, &slot->msg, sizeof(struct canmsg_t));
		canque_free_outslot(qends, qedge, slot);
		buffer += sizeof(struct canmsg_t);
		bytes_to_copy -= sizeof(struct canmsg_t);
	}

	return length-bytes_to_copy;
}

/* This is the 'RTR' read handler for remote transmission request messages */
inline ssize_t can_rtr_read(struct canchip_t *chip, struct msgobj_t *obj, 
								char *buffer)
{
	can_spin_irqflags_t flags;
	struct rtr_id *rtr_current, *new_rtr_entry;
	struct canmsg_t read_msg;
	
	DEBUGMSG("Remote transmission request\n");

	copy_from_user(&read_msg, buffer, sizeof(struct canmsg_t));

	can_spin_lock_irqsave(&hardware_p->rtr_lock, flags);
	if (hardware_p->rtr_queue == NULL) { //No remote messages pending
		new_rtr_entry=(struct rtr_id *)kmalloc(sizeof(struct rtr_id),GFP_ATOMIC);
		if (new_rtr_entry == NULL) {
			can_spin_unlock_irqrestore(&hardware_p->rtr_lock, 
								flags);
			return -ENOMEM;
		}
		hardware_p->rtr_queue=new_rtr_entry;
	}
	else {
		rtr_current=hardware_p->rtr_queue;
		while (rtr_current->next != NULL)
			rtr_current=rtr_current->next;
		new_rtr_entry=(struct rtr_id *)kmalloc(sizeof(struct rtr_id),GFP_ATOMIC);
		rtr_current->next=new_rtr_entry;
	}
	init_waitqueue_head(&new_rtr_entry->rtr_wq);
	new_rtr_entry->id = read_msg.id;
	new_rtr_entry->rtr_message = &read_msg;
	new_rtr_entry->next=NULL;

	can_spin_unlock_irqrestore(&hardware_p->rtr_lock, flags);

	/* Send remote transmission request */
	chip->chipspecops->remote_request(chip,obj);
	obj->ret = 0;
	interruptible_sleep_on(&new_rtr_entry->rtr_wq);

	can_spin_lock_irqsave(&hardware_p->rtr_lock, flags);
	if (hardware_p->rtr_queue == new_rtr_entry) {
		if (new_rtr_entry->next != NULL) 
			hardware_p->rtr_queue=new_rtr_entry->next;
		else
			hardware_p->rtr_queue=NULL;
	}
	else {
		rtr_current=hardware_p->rtr_queue;
		while (rtr_current->next != new_rtr_entry)
			rtr_current=rtr_current->next;
		if (new_rtr_entry->next != NULL)
			rtr_current->next=new_rtr_entry->next;
		else
			rtr_current->next=NULL;
	}
	can_spin_unlock_irqrestore(&hardware_p->rtr_lock, flags);
	kfree(new_rtr_entry);

	copy_to_user(buffer, &read_msg, sizeof(struct canmsg_t));

	return obj->ret;
}

ssize_t can_read(struct file *file, char *buffer, size_t length, loff_t *offset)
{
	struct canuser_t *canuser = (struct canuser_t*)(file->private_data);
	struct msgobj_t *obj;
	struct canchip_t *chip;
	struct canmsg_t read_msg;
	struct canque_ends_t *qends;
	int ret=0;

	if(!canuser || (canuser->magic != CAN_USER_MAGIC)){
		CANMSG("can_read: bad canuser magic\n");
		return -ENODEV;
	}

	if (length < sizeof(struct canmsg_t)) {
		DEBUGMSG("Trying to read less bytes than a CAN message, \n");
		DEBUGMSG("this will always return zero.\n");
		return 0;
	}
	/* Initialize hardware pointers */
	obj = canuser->msgobj;
	if (obj == NULL) {
		CANMSG("Could not assign buffer structure\n");
		return -1;
	}
	qends = canuser->qends;
	if ( (chip = obj->hostchip) == NULL) {
		CANMSG("Device is not correctly configured,\n");
		CANMSG("please reload the driver.\n");
		return -1;
	}

	copy_from_user(&read_msg, buffer, sizeof(struct canmsg_t));
	if (read_msg.flags & MSG_RTR)
		/* FIXME: RTR processing should go to its own IOCTLs */
		ret = can_rtr_read(chip, obj, buffer);
	else
		ret = can_std_read(file, qends, obj, buffer, length);

	return ret;
}


