X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/blobdiff_plain/4ddb8468e0688f83f24fc041cfb62512ee45194a..4cf24de229090b1ab6279570a564d224e13dd706:/lincan/src/read.c diff --git a/lincan/src/read.c b/lincan/src/read.c new file mode 100644 index 0000000..4e02801 --- /dev/null +++ b/lincan/src/read.c @@ -0,0 +1,185 @@ +/* read.c + * Linux CAN-bus device driver. + * Written by Arnaud Westenberg email:arnaud@wanadoo.nl + * This software is released under the GPL-License. + * Version 0.7 6 Aug 2001 + */ + +#include +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include + +#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 canfifo_t *fifo, + struct msgobj_t *obj, char *buffer, size_t length) +{ + int can_timeout, ret; + int bytes_avail = 0, bytes_to_copy = 0; + + cli(); + if (fifo->rx_readp == fifo->rx_writep) { // Buffer is empty + if (file->f_flags & O_NONBLOCK) { + sti(); + return -EAGAIN; + } + obj->ret = 0; + can_timeout = interruptible_sleep_on_timeout(&fifo->readq, + CANTIMEOUT); + sti(); + if (signal_pending(current)) { + DEBUGMSG("Rx interrupted\n"); + return -EINTR; + } + if (!can_timeout) { + DEBUGMSG("Rx timeout\n"); + return -EIO; + } + if (obj->ret < 0) + return obj->ret; + } + /* Calculate available bytes in the buffer */ + cli(); + bytes_avail = ((int)fifo->rx_readp < (int)fifo->rx_writep) ? + ((int)fifo->rx_writep - (int)fifo->rx_readp) : + ((int)fifo->rx_writep - (int)fifo->rx_readp + + (int)fifo->rx_size); + sti(); + + bytes_to_copy = (length < bytes_avail) ? length : bytes_avail; + ret = bytes_to_copy; + + /* Copy the data to user space */ + while (bytes_to_copy > 0) { + copy_to_user(buffer, fifo->rx_readp, sizeof(struct canmsg_t)); + buffer += sizeof(struct canmsg_t); + bytes_to_copy -= sizeof(struct canmsg_t); + fifo->rx_readp++; + if (fifo->rx_readp >= fifo->buf_rx_entry + MAX_BUF_LENGTH) + fifo->rx_readp = fifo->buf_rx_entry; + } + + return ret; +} + +/* This is the 'RTR' read handler for remote transmission request messages */ +inline ssize_t can_rtr_read(struct chip_t *chip, struct msgobj_t *obj, + char *buffer) +{ + unsigned long flags; + struct rtr_id *rtr_current, *new_rtr_entry; + struct canmsg_t read_msg; + + DEBUGMSG("Remote transmission request\n"); + 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) { + 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; + } +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) + init_waitqueue(&new_rtr_entry->rtr_wq); +#else + init_waitqueue_head(&new_rtr_entry->rtr_wq); +#endif + new_rtr_entry->id = read_msg.id; + new_rtr_entry->rtr_message = &read_msg; + new_rtr_entry->next=NULL; + + 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); + + spin_lock_irqsave(&hardware_p->rtr_lock, flags); + copy_to_user(buffer, &read_msg, sizeof(struct canmsg_t)); + 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; + } + spin_unlock_irqrestore(&hardware_p->rtr_lock, flags); + kfree(new_rtr_entry); + + return obj->ret; +} + +ssize_t can_read(struct file *file, char *buffer, size_t length, loff_t *offset) +{ + struct msgobj_t *obj; + struct chip_t *chip; + struct canfifo_t *fifo; + struct canmsg_t read_msg; + int ret=0; + + 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; + } + if (length > 8 * sizeof(struct canmsg_t)) { + DEBUGMSG("Reading more than 8 CAN messages, this is not supported.\n"); + DEBUGMSG("Defaulting to 8 messages.\n"); + length = 8 * sizeof(struct canmsg_t); + } + /* Initialize hardware pointers */ + if ( (obj = objects_p[MINOR_NR]) == NULL) { + CANMSG("Could not assign buffer structure\n"); + return -1; + } + if ( (chip = obj->hostchip) == NULL) { + CANMSG("Device is not correctly configured,\n"); + CANMSG("please reload the driver.\n"); + return -1; + } + if ( (fifo = obj->fifo) == NULL) { + CANMSG("Could not assign buffer memory.\n"); + return -1; + } + + copy_from_user(&read_msg, buffer, sizeof(struct canmsg_t)); + if (read_msg.flags & MSG_RTR) + ret = can_rtr_read(chip, obj, buffer); + else + ret = can_std_read(file, fifo, obj, buffer, length); + + return ret; +} + +