From fb2dd0f20bd6264291dccb5e97609a23cae2fd1a Mon Sep 17 00:00:00 2001 From: ppisa Date: Sun, 4 Jan 2004 00:09:32 +0000 Subject: [PATCH] Added full RT-Linux POSIX interface to LinCAN driver, needs preparation of RT tests. Linux-only and RT-Linux chip thread based version passed Linux users-pace tests on PCM3680 and PiKRON ISA cards. Fundamental design flaws found in rtl_posixio.c concerning concurrent open and/or close processing. There is even bug if open fails reaction. The fiels private_data should be added to structure rtl_file. --- lincan/include/can_iortl.h | 28 ++++++++ lincan/include/can_queue.h | 25 ++++++- lincan/include/can_sysdep.h | 10 ++- lincan/include/constants.h | 6 ++ lincan/include/main.h | 22 +++++- lincan/src/Makefile.omk | 6 +- lincan/src/can_quekern.c | 21 +++++- lincan/src/can_quertl.c | 122 +++++++++++++++++++++++++++++++- lincan/src/close.c | 8 ++- lincan/src/close_rtl.c | 72 +++++++++++++++++++ lincan/src/ioctl.c | 2 +- lincan/src/ioctl_rtl.c | 108 ++++++++++++++++++++++++++++ lincan/src/main.c | 29 ++++++++ lincan/src/open.c | 7 +- lincan/src/open_rtl.c | 136 ++++++++++++++++++++++++++++++++++++ lincan/src/read.c | 2 +- lincan/src/read_rtl.c | 93 ++++++++++++++++++++++++ lincan/src/write.c | 10 +-- lincan/src/write_rtl.c | 101 ++++++++++++++++++++++++++ 19 files changed, 787 insertions(+), 21 deletions(-) create mode 100644 lincan/include/can_iortl.h create mode 100644 lincan/src/close_rtl.c create mode 100644 lincan/src/ioctl_rtl.c create mode 100644 lincan/src/open_rtl.c create mode 100644 lincan/src/read_rtl.c create mode 100644 lincan/src/write_rtl.c diff --git a/lincan/include/can_iortl.h b/lincan/include/can_iortl.h new file mode 100644 index 0000000..735bfae --- /dev/null +++ b/lincan/include/can_iortl.h @@ -0,0 +1,28 @@ +/* can_iortl.h - RT-Linux Posix file IO interface + * Linux CAN-bus device driver. + * RT-Linux support by Pavel Pisa - OCERA team member + * email:pisa@cmp.felk.cvut.cz + * This software is released under the GPL-License. + * Version lincan-0.2 9 Jul 2003 + */ + +#ifndef _CAN_IORTL_H +#define _CAN_IORTL_H + +#ifdef CAN_WITH_RTL + +int can_open_rtl_posix(struct rtl_file *fptr); + +int can_release_rtl_posix(struct rtl_file *fptr); + +ssize_t can_read_rtl_posix(struct rtl_file *fptr, char *buffer, + size_t length, loff_t *ppos); + +ssize_t can_write_rtl_posix(struct rtl_file *fptr, const char *buffer, + size_t length, loff_t *ppos); + +int can_ioctl_rtl_posix(struct rtl_file *, unsigned int, unsigned long); + +#endif /*CAN_WITH_RTL*/ + +#endif /*_CAN_IORTL_H*/ diff --git a/lincan/include/can_queue.h b/lincan/include/can_queue.h index aacfd84..b8cbb09 100644 --- a/lincan/include/can_queue.h +++ b/lincan/include/can_queue.h @@ -362,8 +362,11 @@ struct canque_ends_t { struct { rtl_spinlock_t rtl_lock; rtl_wait_t rtl_readq; + atomic_t rtl_readq_age; rtl_wait_t rtl_writeq; + atomic_t rtl_writeq_age; rtl_wait_t rtl_emptyq; + atomic_t rtl_emptyq_age; unsigned long pend_flags; } rtlinfo; #endif /*CAN_WITH_RTL*/ @@ -687,7 +690,16 @@ void canqueue_ends_dispose_postpone(struct canque_ends_t *qends); void canqueue_kern_initialize(void); #ifdef CAN_WITH_RTL -/* RT-Linux specific functions */ + +extern struct tasklet_struct canque_dead_tl; /*publication required only for RTL*/ + +/* RT-Linux specific functions and variables */ + +extern int canqueue_rtl_irq; + +extern unsigned long canqueue_rtl2lin_pend; + +#define CAN_RTL2LIN_PEND_DEAD_b 0 void canqueue_rtl_initialize(void); void canqueue_rtl_done(void); @@ -699,8 +711,19 @@ struct canque_edge_t *canque_new_edge_rtl(int slotsnr); void canque_dispose_edge_rtl(struct canque_edge_t *qedge); +int canque_get_inslot4id_wait_rtl(struct canque_ends_t *qends, + struct canque_edge_t **qedgep, struct canque_slot_t **slotp, + int cmd, unsigned long id, int prio); + +int canque_get_outslot_wait_rtl(struct canque_ends_t *qends, + struct canque_edge_t **qedgep, struct canque_slot_t **slotp); + +int canque_sync_wait_rtl(struct canque_ends_t *qends, struct canque_edge_t *qedge); + void canque_ends_free_rtl(struct canque_ends_t *qends); +int canqueue_ends_init_rtl(struct canque_ends_t *qends); + int canqueue_ends_dispose_rtl(struct canque_ends_t *qends, int sync); #else /*CAN_WITH_RTL*/ diff --git a/lincan/include/can_sysdep.h b/lincan/include/can_sysdep.h index 1de1454..56cfc4e 100644 --- a/lincan/include/can_sysdep.h +++ b/lincan/include/can_sysdep.h @@ -121,7 +121,7 @@ #else /*CAN_WITH_RTL*/ #define can_spinlock_t rtl_spinlock_t -#define can_spin_irqflags_t unsigned long +#define can_spin_irqflags_t rtl_irqstate_t #define can_spin_lock rtl_spin_lock #define can_spin_unlock rtl_spin_unlock #define can_spin_lock_irqsave rtl_spin_lock_irqsave @@ -136,6 +136,14 @@ #define can_printk rtl_printf +/* + * terrible hack to test rtl_file private_data concept, ugh !!! + * this would result in crash on architectures, where + * sizeof(int) < sizeof(void *) + */ +#define can_set_rtl_file_private_data(fptr, p) do{ fptr->f_minor=(long)(p); } while(0) +#define can_get_rtl_file_private_data(fptr) ((void*)((fptr)->f_minor)) + #endif /*CAN_WITH_RTL*/ #endif /*_CAN_SYSDEP_H*/ diff --git a/lincan/include/constants.h b/lincan/include/constants.h index ce0cbbd..df2909f 100644 --- a/lincan/include/constants.h +++ b/lincan/include/constants.h @@ -70,6 +70,12 @@ #define CANDEV_PROGRAMMABLE_IRQ (1<<0) #define CANDEV_IO_RESERVED (1<<1) +/* Next flags are specific for struct canuser_t applications connection */ +#define CANUSER_RTL_CLIENT (1<<0) +#define CANUSER_RTL_MEM (1<<1) +#define CANUSER_DIRECT (1<<2) + + enum timing_BTR1 { MAX_TSEG1 = 15, MAX_TSEG2 = 7 diff --git a/lincan/include/main.h b/lincan/include/main.h index 4d42d4d..ba01dd2 100644 --- a/lincan/include/main.h +++ b/lincan/include/main.h @@ -22,6 +22,8 @@ #define CANMSG(fmt,args...) can_printk(KERN_ERR "can.o: " fmt,##args) +extern can_spinlock_t canuser_manipulation_lock; + /** * struct canhardware_t - structure representing pointers to all CAN boards * @nr_boards: number of present boards @@ -153,9 +155,10 @@ struct chip_t { int max_objects; /* 1 for sja1000, 15 for i82527 */ + can_spinlock_t chip_lock; + #ifdef CAN_WITH_RTL pthread_t worker_thread; - rtl_spinlock_t rtl_lock; unsigned long pend_flags; #endif /*CAN_WITH_RTL*/ }; @@ -207,20 +210,33 @@ struct msgobj_t { /** * struct canuser_t - structure holding CAN user/client state + * @flags: used to distinguish Linux/RT-Linux type * @peers: for connection into list of object users * @qends: pointer to the ends structure corresponding for this user - * @file: pointer to open device file state structure * @msgobj: communication object the user is connected to * @rx_edge0: default receive queue for filter IOCTL + * @userinfo: stores user context specific information. + * The field @fileinfo.file holds pointer to open device file state structure + * for the Linux user-space client applications * @magic: magic number to check consistency when pointer is retrieved * from file private field */ struct canuser_t { + unsigned long flags; struct list_head peers; struct canque_ends_t *qends; - struct file *file; /* back ptr to file */ struct msgobj_t *msgobj; struct canque_edge_t *rx_edge0; /* simplifies IOCTL */ + union { + struct { + struct file *file; /* back ptr to file */ + } fileinfo; + #ifdef CAN_WITH_RTL + struct { + struct rtl_file *file; + } rtlinfo; + #endif /*CAN_WITH_RTL*/ + } userinfo; int magic; }; diff --git a/lincan/src/Makefile.omk b/lincan/src/Makefile.omk index bbdcba7..37efcca 100644 --- a/lincan/src/Makefile.omk +++ b/lincan/src/Makefile.omk @@ -10,10 +10,12 @@ kernel_MODULES = lincan lincan_cards_SOURCES = pip.c pccan.c smartcan.c nsi.c cc_can104.c \ pc_i03.c pcm3680.c aim104.c m437.c pcccan.c ssv.c \ bfadcan.c pikronisa.c virtual.c template.c + +lincan_rtl_SOURCES = open_rtl.c close_rtl.c read_rtl.c write_rtl.c ioctl_rtl.c lincan_SOURCES = can_queue.c can_quekern.c can_quertl.c main.c modparms.c \ devcommon.c can_devrtl.c setup.c finish.c irq.c boardlist.c \ sja1000p.c sja1000.c i82527.c \ - open.c proc.c close.c write.c read.c \ - ioctl.c select.c fasync.c proc.c $(lincan_cards_SOURCES) + open.c close.c read.c write.c ioctl.c select.c fasync.c \ + proc.c $(lincan_cards_SOURCES) $(lincan_rtl_SOURCES) diff --git a/lincan/src/can_quekern.c b/lincan/src/can_quekern.c index 39c0330..093886c 100644 --- a/lincan/src/can_quekern.c +++ b/lincan/src/can_quekern.c @@ -84,8 +84,10 @@ void canque_dead_func(unsigned long data) can_spin_lock_irqsave(&canque_dead_func_lock, flags); entry=canque_dead_ends.next; can_spin_unlock_irqrestore(&canque_dead_func_lock,flags); + /* lock can be released there, because only one instance of canque_dead_tl + can run at once and all other functions add ends only to head */ while(entry!=&canque_dead_ends){ - qends=list_entry(canque_dead_ends.next,struct canque_ends_t,dead_peers); + qends=list_entry(entry,struct canque_ends_t,dead_peers); entry=entry->next; if(!list_empty(&qends->inlist)) continue; @@ -106,6 +108,19 @@ void canque_dead_func(unsigned long data) } +static inline void canque_dead_tasklet_schedule(void) +{ + #ifdef CAN_WITH_RTL + if(!rtl_rt_system_is_idle()){ + set_bit(CAN_RTL2LIN_PEND_DEAD_b,&canqueue_rtl2lin_pend); + rtl_global_pend_irq (canqueue_rtl_irq); + return; + } + #endif /*CAN_WITH_RTL*/ + + tasklet_schedule(&canque_dead_tl); +} + void canque_edge_do_dead(struct canque_edge_t *edge, int dead_fl) { @@ -121,7 +136,7 @@ void canque_edge_do_dead(struct canque_edge_t *edge, int dead_fl) can_spin_lock_irqsave(&canque_dead_func_lock, flags); list_add(&edge->inpeers,&canque_dead_edges); can_spin_unlock_irqrestore(&canque_dead_func_lock, flags); - tasklet_schedule(&canque_dead_tl); + canque_dead_tasklet_schedule(); } @@ -427,7 +442,7 @@ void canqueue_ends_dispose_postpone(struct canque_ends_t *qends) qends->ends_flags |= CAN_ENDSF_DEAD; list_add(&qends->dead_peers,&canque_dead_ends); can_spin_unlock_irqrestore(&canque_dead_func_lock, flags); - tasklet_schedule(&canque_dead_tl); + canque_dead_tasklet_schedule(); } diff --git a/lincan/src/can_quertl.c b/lincan/src/can_quertl.c index 45129b4..0a6fa40 100644 --- a/lincan/src/can_quertl.c +++ b/lincan/src/can_quertl.c @@ -28,8 +28,9 @@ extern int processlocal; struct list_head canque_pending_edges_list; can_spinlock_t canque_pending_edges_lock; +unsigned long canqueue_rtl2lin_pend; -static int canqueue_rtl_irq = 0; +int canqueue_rtl_irq = 0; void canqueue_rtl2lin_handler(int irq, void *ignore, struct pt_regs *ignoreregs) @@ -75,6 +76,9 @@ canqueue_rtl2lin_handler(int irq, void *ignore, struct pt_regs *ignoreregs) can_spin_unlock_irqrestore (&canque_pending_edges_lock, flags); + if(test_and_clear_bit(CAN_RTL2LIN_PEND_DEAD_b,&canqueue_rtl2lin_pend)) + tasklet_schedule(&canque_dead_tl); + return; } @@ -119,6 +123,111 @@ int canqueue_rtl2lin_check_and_pend(struct canque_ends_t *qends, } +/** + * canque_get_inslot4id_wait_rtl - find or wait for best outgoing edge and slot for given ID + * @qends: ends structure belonging to calling communication object + * @qedgep: place to store pointer to found edge + * @slotp: place to store pointer to allocated slot + * @cmd: command type for slot + * @id: communication ID of message to send into edge + * @prio: optional priority of message + * + * Same as canque_get_inslot4id(), except, that it waits for free slot + * in case, that queue is full. Function is specific for Linux userspace clients. + * Return Value: If there is no usable edge negative value is returned. + */ +int canque_get_inslot4id_wait_rtl(struct canque_ends_t *qends, + struct canque_edge_t **qedgep, struct canque_slot_t **slotp, + int cmd, unsigned long id, int prio) +{ + rtl_irqstate_t flags; + int ret; + unsigned old_age; + rtl_sigset_t sigset; + + old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_writeq_age); + while((ret=canque_get_inslot4id(qends,qedgep,slotp,cmd,id,prio))==-1){ + rtl_sigemptyset(&sigset); + rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags); + if(old_age == atomic_read(&qends->endinfo.rtlinfo.rtl_writeq_age)) + sigset=rtl_wait_sleep(&qends->endinfo.rtlinfo.rtl_writeq, &qends->endinfo.rtlinfo.rtl_lock); + rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags); + if(RTL_SIGINTR(&sigset)) + return -1; + old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_writeq_age); + } + + return ret; +} + + +/** + * canque_get_outslot_wait_rtl - receive or wait for ready slot for given ends + * @qends: ends structure belonging to calling communication object + * @qedgep: place to store pointer to found edge + * @slotp: place to store pointer to received slot + * + * The same as canque_test_outslot(), except it waits in the case, that there is + * no ready slot for given ends. Function is specific for Linux userspace clients. + * Return Value: Negative value informs, that there is no ready output + * slot for given ends. Positive value is equal to the command + * slot has been allocated by the input side. + */ +int canque_get_outslot_wait_rtl(struct canque_ends_t *qends, + struct canque_edge_t **qedgep, struct canque_slot_t **slotp) +{ + rtl_irqstate_t flags; + int ret; + unsigned old_age; + rtl_sigset_t sigset; + + old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_readq_age); + while((ret=canque_test_outslot(qends,qedgep,slotp))==-1){ + rtl_sigemptyset(&sigset); + rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags); + if(old_age == atomic_read(&qends->endinfo.rtlinfo.rtl_readq_age)) + sigset=rtl_wait_sleep(&qends->endinfo.rtlinfo.rtl_readq, &qends->endinfo.rtlinfo.rtl_lock); + rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags); + if(RTL_SIGINTR(&sigset)) + return -1; + old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_readq_age); + } + return ret; +} + + +/** + * canque_sync_wait_rtl - wait for all slots processing + * @qends: ends structure belonging to calling communication object + * @qedge: pointer to edge + * + * Functions waits for ends transition into empty state. + * Return Value: Positive value indicates, that edge empty state has been reached. + * Negative or zero value informs about interrupted wait or other problem. + */ +int canque_sync_wait_rtl(struct canque_ends_t *qends, struct canque_edge_t *qedge) +{ + rtl_irqstate_t flags; + int ret; + unsigned old_age; + rtl_sigset_t sigset; + + old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_emptyq_age); + while(!(ret=canque_fifo_test_fl(&qedge->fifo,EMPTY)?1:0)){ + rtl_sigemptyset(&sigset); + rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags); + if(old_age == atomic_read(&qends->endinfo.rtlinfo.rtl_emptyq_age)) + sigset=rtl_wait_sleep(&qends->endinfo.rtlinfo.rtl_emptyq, &qends->endinfo.rtlinfo.rtl_lock); + rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags); + if(RTL_SIGINTR(&sigset)) + return -1; + old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_emptyq_age); + } + + return ret; +} + + /** * canque_fifo_init_rtl - initialize one CAN FIFO * @fifo: pointer to the FIFO structure @@ -138,6 +247,7 @@ int canque_fifo_init_rtl(struct canque_fifo_t *fifo, int slotsnr) return canque_fifo_init_slots(fifo); } + /** * canque_fifo_done_rtl - frees slots allocated for CAN FIFO * @fifo: pointer to the FIFO structure @@ -207,6 +317,7 @@ void canqueue_notify_rtl(struct canque_ends_t *qends, struct canque_edge_t *qedg switch(what){ case CANQUEUE_NOTIFY_EMPTY: rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags); + atomic_inc(&qends->endinfo.rtlinfo.rtl_emptyq_age); rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_emptyq); rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags); if(canque_fifo_test_and_clear_fl(&qedge->fifo, FREEONEMPTY)) @@ -214,19 +325,28 @@ void canqueue_notify_rtl(struct canque_ends_t *qends, struct canque_edge_t *qedg break; case CANQUEUE_NOTIFY_SPACE: rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags); + atomic_inc(&qends->endinfo.rtlinfo.rtl_writeq_age); rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_writeq); rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags); break; case CANQUEUE_NOTIFY_PROC: rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags); + atomic_inc(&qends->endinfo.rtlinfo.rtl_readq_age); rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_readq); rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags); break; case CANQUEUE_NOTIFY_NOUSR: rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags); + + atomic_inc(&qends->endinfo.rtlinfo.rtl_readq_age); rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_readq); + + atomic_inc(&qends->endinfo.rtlinfo.rtl_writeq_age); rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_writeq); + + atomic_inc(&qends->endinfo.rtlinfo.rtl_emptyq_age); rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_emptyq); + rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags); break; case CANQUEUE_NOTIFY_DEAD_WANTED: diff --git a/lincan/src/close.c b/lincan/src/close.c index 73d4764..9042ff2 100644 --- a/lincan/src/close.c +++ b/lincan/src/close.c @@ -23,6 +23,7 @@ int can_close(struct inode *inode, struct file *file) struct canuser_t *canuser = (struct canuser_t*)(file->private_data); struct canque_ends_t *qends; struct msgobj_t *obj; + can_spin_irqflags_t iflags; if(!canuser || (canuser->magic != CAN_USER_MAGIC)){ CANMSG("can_close: bad canuser magic\n"); @@ -31,23 +32,26 @@ int can_close(struct inode *inode, struct file *file) obj = canuser->msgobj; qends = canuser->qends; - + #ifdef CAN_ENABLE_KERN_FASYNC can_fasync(-1, file, 0); #endif /*CAN_ENABLE_KERN_FASYNC*/ + can_spin_lock_irqsave(&canuser_manipulation_lock, iflags); list_del(&canuser->peers); + can_spin_unlock_irqrestore(&canuser_manipulation_lock, iflags); canuser->qends = NULL; canqueue_ends_dispose_kern(qends, file->f_flags & O_SYNC); kfree(canuser); + can_spin_lock_irqsave(&canuser_manipulation_lock, iflags); if(atomic_dec_and_test(&obj->obj_used)){ can_msgobj_clear_fl(obj,OPENED); - /* FIXME: what about clearing chip HW status, stopping sending messages etc? */ }; + can_spin_unlock_irqrestore(&canuser_manipulation_lock, iflags); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50)) MOD_DEC_USE_COUNT; diff --git a/lincan/src/close_rtl.c b/lincan/src/close_rtl.c new file mode 100644 index 0000000..5f8188b --- /dev/null +++ b/lincan/src/close_rtl.c @@ -0,0 +1,72 @@ +/* close.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.2 9 Jul 2003 + */ + +#ifdef CAN_WITH_RTL + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" + +#include +#include +#include "../include/can_iortl.h" + +#define __NO_VERSION__ +#include + +static inline +int can_release_rtl_common(struct canuser_t *canuser, int file_flags) +{ + struct canque_ends_t *qends; + struct msgobj_t *obj; + can_spin_irqflags_t iflags; + + obj = canuser->msgobj; + qends = canuser->qends; + + can_spin_lock_irqsave(&canuser_manipulation_lock, iflags); + list_del(&canuser->peers); + can_spin_unlock_irqrestore(&canuser_manipulation_lock, iflags); + canuser->qends = NULL; + canqueue_ends_dispose_rtl(qends, file_flags & O_SYNC); + + can_spin_lock_irqsave(&canuser_manipulation_lock, iflags); + if(atomic_dec_and_test(&obj->obj_used)){ + can_msgobj_clear_fl(obj,OPENED); + }; + can_spin_unlock_irqrestore(&canuser_manipulation_lock, iflags); + + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50)) + MOD_DEC_USE_COUNT; + #endif + + return 0; +} + + +int can_release_rtl_posix(struct rtl_file *fptr) +{ + struct canuser_t *canuser = + (struct canuser_t *)can_get_rtl_file_private_data(fptr); + int ret; + + if(!canuser || (canuser->magic != CAN_USER_MAGIC)){ + CANMSG("can_release_rtl_posix: bad canuser magic\n"); + return -ENODEV; + } + + ret=can_release_rtl_common(canuser, fptr->f_flags); + + rt_free(canuser); + + return ret; +} + +#endif /*CAN_WITH_RTL*/ + diff --git a/lincan/src/ioctl.c b/lincan/src/ioctl.c index 38743fa..51feb5b 100644 --- a/lincan/src/ioctl.c +++ b/lincan/src/ioctl.c @@ -24,7 +24,7 @@ int can_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned struct canque_ends_t *qends; if(!canuser || (canuser->magic != CAN_USER_MAGIC)){ - CANMSG("can_close: bad canuser magic\n"); + CANMSG("can_ioctl: bad canuser magic\n"); return -ENODEV; } diff --git a/lincan/src/ioctl_rtl.c b/lincan/src/ioctl_rtl.c new file mode 100644 index 0000000..449b345 --- /dev/null +++ b/lincan/src/ioctl_rtl.c @@ -0,0 +1,108 @@ +/* ioctl.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.2 9 Jul 2003 + */ + +#ifdef CAN_WITH_RTL + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" + +#include +#include "../include/can_iortl.h" + + +int can_ioctl_rtl_posix(struct rtl_file *fptr, unsigned int cmd, unsigned long arg) +{ + struct canuser_t *canuser = + (struct canuser_t *)can_get_rtl_file_private_data(fptr); + int i=0; + unsigned short channel=0; + unsigned btr0=0, btr1=0; + struct msgobj_t *obj; + struct chip_t *chip; + struct canque_ends_t *qends; + + if(!canuser || (canuser->magic != CAN_USER_MAGIC)){ + CANMSG("can_ioctl_: bad canuser magic\n"); + return -ENODEV; + } + + obj = canuser->msgobj; + if (obj == NULL) { + CANMSG("Could not assign buffer structure\n"); + return -1; + } + + qends = canuser->qends; + chip = obj->hostchip; + if (chip == NULL) { + CANMSG("Device is not correctly configured.\n"); + CANMSG("Please reload the driver.\n"); + return -1; + } + + switch (cmd) { + case STAT: { + for (i=0x0; i<0x100; i++) + CANMSG("0x%x is 0x%x\n",i,can_read_reg(chip,i)); + break; + } + case CMD_START: { + if (chip->chipspecops->start_chip(chip)) + return -1; + break; + } + case CMD_STOP: { + if (chip->chipspecops->stop_chip(chip)) + return -1; + break; + } + case CANQUE_FLUSH: { + canque_flush(canuser->rx_edge0); + break; + } + case CONF_FILTER: { + if(canuser->rx_edge0){ + canque_set_filt(canuser->rx_edge0, arg, ~0, 0); + } + + break; + } + + case CANQUE_FILTER: { + struct canfilt_t canfilt; + copy_from_user(&canfilt, (void*)arg, sizeof(struct canfilt_t)); + if(canuser->rx_edge0){ + canque_set_filt(canuser->rx_edge0, canfilt.id, canfilt.mask, canfilt.flags); + } + break; + } + + case CONF_BAUD: { + channel = arg & 0xff; + btr0 = (arg >> 8) & 0xff; + btr1 = (arg >> 16) & 0xff; + + if (chip->chipspecops->set_btregs(chip, btr0, btr1)) { + CANMSG("Error setting bit timing registers\n"); + return -1; + } + break; + } + default: { + CANMSG("Not a valid ioctl command\n"); + return -EINVAL; + } + + } + + return 0; +} + +#endif /*CAN_WITH_RTL*/ diff --git a/lincan/src/main.c b/lincan/src/main.c index c40310d..38a0ec0 100644 --- a/lincan/src/main.c +++ b/lincan/src/main.c @@ -56,8 +56,15 @@ #include "../include/finish.h" #include "../include/fasync.h" +#ifdef CAN_WITH_RTL +#include +#include "../include/can_iortl.h" +#endif /*CAN_WITH_RTL*/ + #define EXPORT_SYMTAB +can_spinlock_t canuser_manipulation_lock; + /* Module parameters, some must be supplied at module loading time */ int major=CAN_MAJOR; MODULE_PARM(major,"1i"); @@ -130,6 +137,20 @@ struct file_operations can_fops= EXPORT_SYMBOL(can_fops); + +#ifdef CAN_WITH_RTL +struct rtl_file_operations can_fops_rtl = { + llseek: NULL, + read: can_read_rtl_posix, + write: can_write_rtl_posix, + ioctl: can_ioctl_rtl_posix, + mmap: NULL, + open: can_open_rtl_posix, + release: can_release_rtl_posix +}; +#endif /*CAN_WITH_RTL*/ + + int init_module(void) { int res=0,i=0, j; @@ -156,6 +177,11 @@ int init_module(void) #ifdef CAN_WITH_RTL canqueue_rtl_initialize(); + res=rtl_register_rtldev(major,DEVICE_NAME,&can_fops_rtl); + if (res<0) { + CANMSG("Error registering RT-Linux major number.\n"); + goto rtldev_error; + } #endif /*CAN_WITH_RTL*/ for (i=0; inr_boards; i++) { @@ -233,6 +259,8 @@ int init_module(void) canhardware_done(hardware_p); #ifdef CAN_WITH_RTL + rtl_unregister_rtldev(major,DEVICE_NAME); + rtldev_error: canqueue_rtl_done(); #endif /*CAN_WITH_RTL*/ @@ -272,6 +300,7 @@ void cleanup_module(void) canhardware_done(hardware_p); #ifdef CAN_WITH_RTL + rtl_unregister_rtldev(major,DEVICE_NAME); canqueue_rtl_done(); #endif /*CAN_WITH_RTL*/ diff --git a/lincan/src/open.c b/lincan/src/open.c index 015b521..deb027c 100644 --- a/lincan/src/open.c +++ b/lincan/src/open.c @@ -24,6 +24,7 @@ int can_open(struct inode *inode, struct file *file) struct canuser_t *canuser; struct canque_ends_t *qends; struct canque_edge_t *edge; + can_spin_irqflags_t iflags; if ( ((obj=objects_p[MINOR_NR]) == NULL) || ((chip=objects_p[MINOR_NR]->hostchip) == NULL) ) { @@ -50,7 +51,8 @@ int can_open(struct inode *inode, struct file *file) canuser = (struct canuser_t *)kmalloc(sizeof(struct canuser_t), GFP_KERNEL); if(canuser == NULL) goto no_canuser; - canuser->file = file; + canuser->flags=0; + canuser->userinfo.fileinfo.file = file; canuser->msgobj = obj; canuser->magic = CAN_USER_MAGIC; file->private_data = canuser; @@ -60,7 +62,10 @@ int can_open(struct inode *inode, struct file *file) canqueue_ends_init_kern(qends); canuser->qends = qends; + /*required to synchronize with RT-Linux context*/ + can_spin_lock_irqsave(&canuser_manipulation_lock, iflags); list_add(&canuser->peers, &obj->obj_users); + can_spin_unlock_irqrestore(&canuser_manipulation_lock, iflags); if(canqueue_connect_edge(edge=canque_new_edge_kern(MAX_BUF_LENGTH), canuser->qends, obj->qends)<0) goto no_tx_qedge; diff --git a/lincan/src/open_rtl.c b/lincan/src/open_rtl.c new file mode 100644 index 0000000..230e3eb --- /dev/null +++ b/lincan/src/open_rtl.c @@ -0,0 +1,136 @@ +/* open.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.2 9 Jul 2003 + */ + +#ifdef CAN_WITH_RTL + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" + +#include +#include +#include "../include/can_iortl.h" + +#define __NO_VERSION__ +#include + + +static inline +int can_open_rtl_common(struct canuser_t *canuser, int open_flags) +{ + struct msgobj_t *obj=canuser->msgobj; + struct chip_t *chip; + struct canque_ends_t *qends; + struct canque_edge_t *edge; + can_spin_irqflags_t iflags; + + if(!obj) return -ENODEV; + + can_msgobj_set_fl(obj,OPENED); + + chip=obj->hostchip; + if (chip) { + if (!(chip->flags & CHIP_CONFIGURED)) { + if (chip->chipspecops->chip_config(chip)) + CANMSG("Error configuring chip.\n"); + else + chip->flags |= CHIP_CONFIGURED; + + if (chip->chipspecops->pre_read_config(chip,obj)<0) + CANMSG("Error initializing chip for receiving\n"); + + } + } /* End of chip configuration */ + + + qends = (struct canque_ends_t *)rt_malloc(sizeof(struct canque_ends_t)); + if(qends == NULL) goto no_qends; + canqueue_ends_init_rtl(qends); + canuser->qends = qends; + + can_spin_lock_irqsave(&canuser_manipulation_lock, iflags); + list_add(&canuser->peers, &obj->obj_users); + can_spin_unlock_irqrestore(&canuser_manipulation_lock, iflags); + + if(canqueue_connect_edge(edge=canque_new_edge_rtl(MAX_BUF_LENGTH), + canuser->qends, obj->qends)<0) goto no_tx_qedge; + + if(canqueue_connect_edge(canuser->rx_edge0=canque_new_edge_rtl(MAX_BUF_LENGTH), + obj->qends, canuser->qends)<0) goto no_rx_qedge; + /*FIXME: more generic model should be used there*/ + canque_edge_decref(canuser->rx_edge0); + canque_edge_decref(edge); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50)) + MOD_INC_USE_COUNT; /*is this enough for RT-Linux context ?*/ +#endif + + return 0; + + no_rx_qedge: + canque_notify_bothends(edge, CANQUEUE_NOTIFY_DEAD_WANTED); + canque_edge_decref(edge); + no_tx_qedge: + list_del(&canuser->peers); + canuser->qends = NULL; + canqueue_ends_dispose_kern(qends, 1); + + no_qends: + + return -ENOMEM; +} + + +int can_open_rtl_posix(struct rtl_file *fptr) +{ + int ret; + struct msgobj_t *obj; + struct chip_t *chip; + struct canuser_t *canuser; + int minor_nr = RTL_MINOR_FROM_FILEPTR(fptr); + + if(minor_nr>=MAX_TOT_MSGOBJS) + return -ENODEV; + + if ( ((obj=objects_p[minor_nr]) == NULL) || + ((chip=objects_p[minor_nr]->hostchip) == NULL) ) { + CANMSG("There is no hardware support for the device file with minor nr.: %d\n",minor_nr); + return -ENODEV; + } + + atomic_inc(&obj->obj_used); + DEBUGMSG("Device %d opened %d times.\n", minor_nr, atomic_read(&obj->obj_used)); + + canuser = (struct canuser_t *)rt_malloc(sizeof(struct canuser_t)); + if(canuser == NULL){ + ret=-ENOMEM; + goto no_canuser; + } + canuser->flags=CANUSER_RTL_CLIENT | CANUSER_RTL_MEM; + canuser->userinfo.rtlinfo.file = fptr; + canuser->msgobj = obj; + canuser->magic = CAN_USER_MAGIC; + + /*next line would solve many problems, but RT-Linux lacks this fundamental field */ + /*fptr->private_data = canuser;*/ + /*to test code I am adding this terible hack*/ + can_set_rtl_file_private_data(fptr,canuser); + + ret=can_open_rtl_common(canuser, fptr->f_flags); + if(ret>=0) return ret; + + rt_free(canuser); + + no_canuser: + atomic_dec(&obj->obj_used); + + return ret; +} + +#endif /*CAN_WITH_RTL*/ diff --git a/lincan/src/read.c b/lincan/src/read.c index d203f0f..8824890 100644 --- a/lincan/src/read.c +++ b/lincan/src/read.c @@ -130,7 +130,7 @@ ssize_t can_read(struct file *file, char *buffer, size_t length, loff_t *offset) int ret=0; if(!canuser || (canuser->magic != CAN_USER_MAGIC)){ - CANMSG("can_close: bad canuser magic\n"); + CANMSG("can_read: bad canuser magic\n"); return -ENODEV; } diff --git a/lincan/src/read_rtl.c b/lincan/src/read_rtl.c new file mode 100644 index 0000000..807f572 --- /dev/null +++ b/lincan/src/read_rtl.c @@ -0,0 +1,93 @@ +/* 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.2 9 Jul 2003 + */ + +#ifdef CAN_WITH_RTL + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" + +#include +#include "../include/can_iortl.h" + +/* This is the 'Normal' read handler for normal transmission messages */ +static inline +ssize_t can_std_read_rtl(struct canque_ends_t *qends, int nonblock_fl, + char *buffer, size_t length) +{ + int ret; + struct canmsg_t *msg_buff = (struct canmsg_t *)buffer; + 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 (nonblock_fl) { + return -EAGAIN; + } + ret=canque_get_outslot_wait_rtl(qends, &qedge, &slot); + if(ret<0){ + if (ret==-1) { + DEBUGMSG("Rx interrupted\n"); + return -EINTR; + } + + return -EIO; + } + } + *(msg_buff++)=slot->msg; + canque_free_outslot(qends, qedge, slot); + bytes_to_copy = length-sizeof(struct canmsg_t); + + while (bytes_to_copy > 0) { + ret=canque_test_outslot(qends, &qedge, &slot); + if(ret<0) + break; + *(msg_buff++)=slot->msg; + canque_free_outslot(qends, qedge, slot); + bytes_to_copy -= sizeof(struct canmsg_t); + } + + return length-bytes_to_copy; +} + + +ssize_t can_read_rtl_posix(struct rtl_file *fptr, char *buffer, + size_t length, loff_t *ppos) +{ + struct canuser_t *canuser = + (struct canuser_t *)can_get_rtl_file_private_data(fptr); + struct canque_ends_t *qends; + int ret; + + if(!canuser || (canuser->magic != CAN_USER_MAGIC)){ + CANMSG("can_read_rtl_posix: 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; + } + + qends = canuser->qends; + + /*if (((struct canmsg_t *)buffer)->flags & MSG_RTR) + ret = can_rtr_read(chip, obj, buffer); + else*/ + ret = can_std_read_rtl(qends, fptr->f_flags & O_NONBLOCK, + buffer, length); + + return ret; +} + +#endif /*CAN_WITH_RTL*/ + diff --git a/lincan/src/write.c b/lincan/src/write.c index 1869c97..1760bc2 100644 --- a/lincan/src/write.c +++ b/lincan/src/write.c @@ -20,10 +20,10 @@ ssize_t can_write(struct file *file, const char *buffer, size_t length, loff_t * struct canque_edge_t *qedge; struct canque_slot_t *slot; int ret = 0; - int bytes_to_copy = 0; + unsigned bytes_to_copy; if(!canuser || (canuser->magic != CAN_USER_MAGIC)){ - CANMSG("can_close: bad canuser magic\n"); + CANMSG("can_write: bad canuser magic\n"); return -ENODEV; } @@ -32,7 +32,7 @@ ssize_t can_write(struct file *file, const char *buffer, size_t length, loff_t * DEBUGMSG("this will always return 0 !\n"); return 0; } - if (length > 8 * sizeof(struct canmsg_t)) { + if (length > INT_MAX) { CANMSG("Trying to write more than is supported.\n"); return -1; } @@ -85,7 +85,8 @@ ssize_t can_write(struct file *file, const char *buffer, size_t length, loff_t * /* * Try to send more messages */ - while (bytes_to_copy > 0) { + while (bytes_to_copy >= sizeof(struct canmsg_t)) { + bytes_to_copy -= sizeof(struct canmsg_t); /* Prepare first message */ copy_from_user(&msg_buff, buffer, sizeof(struct canmsg_t)); /* Automatic selection of extended format if "extended" set and ID>2047 */ @@ -96,7 +97,6 @@ ssize_t can_write(struct file *file, const char *buffer, size_t length, loff_t * slot->msg=msg_buff; canque_put_inslot(qends, qedge, slot); buffer += sizeof(struct canmsg_t); - bytes_to_copy -= sizeof(struct canmsg_t); } if(file->f_flags & O_SYNC) { diff --git a/lincan/src/write_rtl.c b/lincan/src/write_rtl.c new file mode 100644 index 0000000..a1d4647 --- /dev/null +++ b/lincan/src/write_rtl.c @@ -0,0 +1,101 @@ +/* write.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.2 9 Jul 2003 + */ + +#ifdef CAN_WITH_RTL + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" + +#include +#include "../include/can_iortl.h" + +ssize_t can_write_rtl_posix(struct rtl_file *fptr, const char *buffer, + size_t length, loff_t *ppos) +{ + struct canuser_t *canuser = + (struct canuser_t *)can_get_rtl_file_private_data(fptr); + const struct canmsg_t *msg_buff = (struct canmsg_t *)buffer; + struct canque_ends_t *qends; + struct canque_edge_t *qedge; + struct canque_slot_t *slot; + int msg_flags; + int ret; + size_t bytes_to_copy; + + if(!canuser || (canuser->magic != CAN_USER_MAGIC)){ + CANMSG("can_write_rtl_posix: bad canuser magic\n"); + return -ENODEV; + } + + if (length < sizeof(struct canmsg_t)) { + DEBUGMSG("Trying to write less bytes than a CAN message,\n"); + DEBUGMSG("this will always return 0 !\n"); + return 0; + } + if (length > INT_MAX) { + CANMSG("Trying to write more than is supported.\n"); + return -1; + } + + qends = canuser->qends; + + msg_flags=msg_buff->flags; + /* Automatic selection of extended format if "extended" set and ID>2047 */ + if (extended) if (msg_buff->id & ~0x7ffl ) msg_flags |= MSG_EXT; + + /* If the output buffer is full, return immediately in case O_NONBLOCK + * has been specified or loop until space becomes available. + */ + if ((ret=canque_get_inslot4id(qends, &qedge, &slot, + 0, msg_buff->id, 0))<0){ + DEBUGMSG("Buffer is full\n"); + if(ret < -1) + return -EIO; + + if (fptr->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret=canque_get_inslot4id_wait_rtl(qends, &qedge, &slot, + 0, msg_buff->id, 0); + if (ret == -1) + return -EINTR; + + if(ret<0) { + return -EIO; + } + } + slot->msg=*(msg_buff++); + slot->msg.flags=msg_flags; + canque_put_inslot(qends, qedge, slot); + bytes_to_copy = length-sizeof(struct canmsg_t); + + /* + * Try to send more messages + */ + while (bytes_to_copy >= sizeof(struct canmsg_t)) { + bytes_to_copy -= sizeof(struct canmsg_t); + msg_flags=msg_buff->flags; + /* Automatic selection of extended format if "extended" set and ID>2047 */ + if (extended) if (msg_buff->id & ~0x7ffl ) msg_flags |= MSG_EXT; + /* Get slot */ + if(canque_get_inslot4id(qends, &qedge, &slot, + 0, msg_buff->id, 0) < 0) break; + slot->msg=*(msg_buff++); + slot->msg.flags=msg_flags; + canque_put_inslot(qends, qedge, slot); + } + + if(fptr->f_flags & O_SYNC) { + canque_sync_wait_rtl(qends, qedge); + } + return length-bytes_to_copy; +} + +#endif /*CAN_WITH_RTL*/ -- 2.39.2