X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/blobdiff_plain/b34a548a5dfd2f0b068fd835a4cd51680d851068..f77a6eb55fdb81f0aebffa7010d5038e95dfacab:/lincan/src/can_quekern.c diff --git a/lincan/src/can_quekern.c b/lincan/src/can_quekern.c index 39c0330..a102ac2 100644 --- a/lincan/src/can_quekern.c +++ b/lincan/src/can_quekern.c @@ -1,10 +1,35 @@ -/* can_quekern.c - CAN message queues functions for the Linux kernel - * Linux CAN-bus device driver. - * 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 - */ +/**************************************************************************/ +/* File: can_quekern.c - CAN message queues functions for the Linux kernel*/ +/* */ +/* LinCAN - (Not only) Linux CAN bus driver */ +/* Copyright (C) 2002-2009 DCE FEE CTU Prague */ +/* Copyright (C) 2002-2009 Pavel Pisa */ +/* Funded by OCERA and FRESCOR IST projects */ +/* */ +/* LinCAN is free software; you can redistribute it and/or modify it */ +/* under terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2, or (at your option) any */ +/* later version. LinCAN is distributed in the hope that it will be */ +/* useful, but WITHOUT ANY WARRANTY; without even the implied warranty */ +/* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. You should have received a */ +/* copy of the GNU General Public License along with LinCAN; see file */ +/* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, */ +/* Cambridge, MA 02139, USA. */ +/* */ +/* To allow use of LinCAN in the compact embedded systems firmware */ +/* and RT-executives (RTEMS for example), main authors agree with next */ +/* special exception: */ +/* */ +/* Including LinCAN header files in a file, instantiating LinCAN generics */ +/* or templates, or linking other files with LinCAN objects to produce */ +/* an application image/executable, does not by itself cause the */ +/* resulting application image/executable to be covered by */ +/* the GNU General Public License. */ +/* This exception does not however invalidate any other reasons */ +/* why the executable file might be covered by the GNU Public License. */ +/* Publication of enhanced or derived LinCAN files is required although. */ +/**************************************************************************/ #include "../include/can.h" #include "../include/can_sysdep.h" @@ -15,19 +40,19 @@ extern atomic_t edge_num_cnt; #ifdef CAN_DEBUG - #define DEBUGQUE(fmt,args...) can_printk(KERN_ERR "can_queue (debug): " fmt,\ + #define DEBUGQUE(fmt,args...) can_printk(KERN_ERR "can_quekern (debug): " fmt,\ ##args) #else #define DEBUGQUE(fmt,args...) #endif -#define ERRMSGQUE(fmt,args...) can_printk(KERN_ERR "can_queue: " fmt,\ +#define ERRMSGQUE(fmt,args...) can_printk(KERN_ERR "can_quekern: " fmt,\ ##args) -/* - * Modifies Tx message processing +/* + * Modifies Tx message processing * 0 .. local message processing disabled * 1 .. local messages disabled by default but can be enabled by canque_set_filt * 2 .. local messages enabled by default, can be disabled by canque_set_filt @@ -80,12 +105,14 @@ void canque_dead_func(unsigned long data) canque_fifo_done_kern(&qedge->fifo); kfree(qedge); } - + 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,13 +133,45 @@ 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*/ -void canque_edge_do_dead(struct canque_edge_t *edge, int dead_fl) + tasklet_schedule(&canque_dead_tl); +} + + +void canque_edge_do_dead(struct canque_edge_t *edge) { can_spin_irqflags_t flags; - - if(dead_fl) return; - + + canque_notify_bothends(edge,CANQUEUE_NOTIFY_NOUSR); + #ifdef CAN_WITH_RTL + /* The problem of the above call is, that in RT-Linux to Linux notify + case is edge scheduled for delayed notify delivery, this needs + to be reflected there */ + if(atomic_read(&edge->edge_used)>0){ + can_spin_lock_irqsave(&edge->inends->ends_lock, flags); + can_spin_lock(&edge->outends->ends_lock); + if(atomic_read(&edge->edge_used)>0){ + /* left edge to live for a while, banshee comes again in a while */ + canque_fifo_clear_fl(&edge->fifo,DEAD); + can_spin_unlock(&edge->outends->ends_lock); + can_spin_unlock_irqrestore(&edge->inends->ends_lock, flags); + can_printk(KERN_ERR "can_quertl (debug): canque_edge_do_dead postponed\n"); + return; + } + can_spin_unlock(&edge->outends->ends_lock); + can_spin_unlock_irqrestore(&edge->inends->ends_lock, flags); + } + #endif /*CAN_WITH_RTL*/ + if(canqueue_disconnect_edge(edge)<0){ ERRMSGQUE("canque_edge_do_dead: canqueue_disconnect_edge failed !!!\n"); return; @@ -121,7 +180,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(); } @@ -138,8 +197,15 @@ void canque_edge_do_dead(struct canque_edge_t *edge, int dead_fl) /** * canqueue_notify_kern - notification callback handler for Linux userspace clients * @qends: pointer to the callback side ends structure - * @qedge: edge which invoked notification + * @qedge: edge which invoked notification * @what: notification type + * + * The notification event is handled directly by call of this function except case, + * when called from RT-Linux context in mixed mode Linux/RT-Linux compilation. + * It is not possible to directly call Linux kernel synchronization primitives + * in such case. The notification request is postponed and signaled by @pending_inops flags + * by call canqueue_rtl2lin_check_and_pend() function. + * The edge reference count is increased until until all pending notifications are processed. */ void canqueue_notify_kern(struct canque_ends_t *qends, struct canque_edge_t *qedge, int what) { @@ -151,7 +217,7 @@ void canqueue_notify_kern(struct canque_ends_t *qends, struct canque_edge_t *qed DEBUGQUE("canqueue_notify_kern postponed\n"); return; } - + switch(what){ case CANQUEUE_NOTIFY_EMPTY: wake_up(&qends->endinfo.fileinfo.emptyq); @@ -162,14 +228,14 @@ void canqueue_notify_kern(struct canque_ends_t *qends, struct canque_edge_t *qed wake_up(&qends->endinfo.fileinfo.writeq); #ifdef CAN_ENABLE_KERN_FASYNC /* Asynchronous I/O processing */ - kill_fasync(&qends->endinfo.fileinfo.fasync, SIGIO, POLL_OUT); + kill_fasync(&qends->endinfo.fileinfo.fasync, SIGIO, POLL_OUT); #endif /*CAN_ENABLE_KERN_FASYNC*/ break; case CANQUEUE_NOTIFY_PROC: wake_up(&qends->endinfo.fileinfo.readq); #ifdef CAN_ENABLE_KERN_FASYNC /* Asynchronous I/O processing */ - kill_fasync(&qends->endinfo.fileinfo.fasync, SIGIO, POLL_IN); + kill_fasync(&qends->endinfo.fileinfo.fasync, SIGIO, POLL_IN); #endif /*CAN_ENABLE_KERN_FASYNC*/ break; case CANQUEUE_NOTIFY_NOUSR: @@ -201,7 +267,7 @@ int canqueue_ends_init_kern(struct canque_ends_t *qends) #ifdef CAN_ENABLE_KERN_FASYNC qends->endinfo.fileinfo.fasync=NULL; #endif /*CAN_ENABLE_KERN_FASYNC*/ - + qends->notify=canqueue_notify_kern; DEBUGQUE("canqueue_ends_init_kern\n"); return 0; @@ -227,7 +293,7 @@ int canque_get_inslot4id_wait_kern(struct canque_ends_t *qends, { int ret=-1; DEBUGQUE("canque_get_inslot4id_wait_kern for cmd %d, id %ld, prio %d\n",cmd,id,prio); - wait_event_interruptible((qends->endinfo.fileinfo.writeq), + wait_event_interruptible((qends->endinfo.fileinfo.writeq), (ret=canque_get_inslot4id(qends,qedgep,slotp,cmd,id,prio))!=-1); return ret; } @@ -249,7 +315,7 @@ int canque_get_outslot_wait_kern(struct canque_ends_t *qends, { int ret=-1; DEBUGQUE("canque_get_outslot_wait_kern\n"); - wait_event_interruptible((qends->endinfo.fileinfo.readq), + wait_event_interruptible((qends->endinfo.fileinfo.readq), (ret=canque_test_outslot(qends,qedgep,slotp))!=-1); return ret; } @@ -267,7 +333,7 @@ int canque_sync_wait_kern(struct canque_ends_t *qends, struct canque_edge_t *qed { int ret=-1; DEBUGQUE("canque_sync_wait_kern\n"); - wait_event_interruptible((qends->endinfo.fileinfo.emptyq), + wait_event_interruptible((qends->endinfo.fileinfo.emptyq), (ret=canque_fifo_test_fl(&qedge->fifo,EMPTY)?1:0)); return ret; } @@ -340,7 +406,7 @@ struct canque_edge_t *canque_new_edge_kern(int slotsnr) #ifdef USE_SYNC_DISCONNECT_EDGE_KERN -/** +/*not included in doc * canqueue_disconnect_edge_kern - disconnect edge from communicating entities with wait * @qends: ends structure belonging to calling communication object * @qedge: pointer to edge @@ -356,12 +422,12 @@ int canqueue_disconnect_edge_kern(struct canque_ends_t *qends, struct canque_edg DEBUGQUE("canqueue_disconnect_edge_kern %d called\n",qedge->edge_num); if(!canque_fifo_test_and_set_fl(&qedge->fifo,DEAD)){ canque_notify_bothends(qedge, CANQUEUE_NOTIFY_DEAD); - + if(atomic_read(&qedge->edge_used)>0) atomic_dec(&qedge->edge_used); DEBUGQUE("canqueue_disconnect_edge_kern %d waiting\n",qedge->edge_num); - wait_event((qends->endinfo.fileinfo.emptyq), + wait_event((qends->endinfo.fileinfo.emptyq), (canqueue_disconnect_edge(qedge)>=0)); /*set_current_state(TASK_UNINTERRUPTIBLE);*/ @@ -410,7 +476,7 @@ int canqueue_disconnect_list_kern(struct canque_ends_t *qends, struct list_head int canqueue_ends_sync_all_kern(struct canque_ends_t *qends) { struct canque_edge_t *qedge; - + canque_for_each_inedge(qends, qedge){ DEBUGQUE("canque_sync_wait_kern called for edge %d\n",qedge->edge_num); canque_sync_wait_kern(qends, qedge); @@ -427,7 +493,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(); } @@ -450,7 +516,7 @@ int canqueue_ends_dispose_kern(struct canque_ends_t *qends, int sync) /*Wait for sending of all pending messages in the output FIFOs*/ if(sync) canqueue_ends_sync_all_kern(qends); - + /* Finish or kill all outgoing edges listed in inends */ delayed=canqueue_ends_kill_inlist(qends, 1); /* Kill all incoming edges listed in outends */