]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/src/can_quertl.c
45129b41b480418d395e0dfb754a8893ddae69bc
[lincan.git] / lincan / src / can_quertl.c
1 /* can_quertl.c - CAN message queues functions for the RT-Linux
2  * Linux CAN-bus device driver.
3  * New CAN queues by Pavel Pisa - OCERA team member
4  * email:pisa@cmp.felk.cvut.cz
5  * This software is released under the GPL-License.
6  * Version lincan-0.2  9 Jul 2003
7  */
8
9 #ifdef CAN_WITH_RTL
10
11 #include "../include/can.h"
12 #include "../include/can_sysdep.h"
13 #include "../include/can_queue.h"
14
15 #include <rtl_malloc.h>
16
17 /* 
18  * Modifies Tx message processing 
19  *  0 .. local message processing disabled
20  *  1 .. local messages disabled by default but can be enabled by canque_set_filt
21  *  2 .. local messages enabled by default, can be disabled by canque_set_filt
22  */
23 extern int processlocal;
24
25
26 #define CANQUE_PENDOPS_LIMIT 15
27 #define CANQUE_PENDOPS_MASK  ((1<<CANQUE_PENDOPS_LIMIT)-1)
28
29 struct list_head canque_pending_edges_list;
30 can_spinlock_t canque_pending_edges_lock;
31
32 static int canqueue_rtl_irq = 0;
33
34 void
35 canqueue_rtl2lin_handler(int irq, void *ignore, struct pt_regs *ignoreregs)
36 {
37         can_spin_irqflags_t flags;
38         struct canque_edge_t *qedge;
39         unsigned pending_inops;
40         unsigned pending_outops;
41         int i;
42         
43         can_spin_lock_irqsave (&canque_pending_edges_lock, flags);
44
45         while(!list_empty(&canque_pending_edges_list)){
46                 qedge=list_entry(canque_pending_edges_list.next,struct canque_edge_t,pending_peers);
47                 list_del(&qedge->pending_peers);
48                 canque_fifo_clear_fl(&qedge->fifo, NOTIFYPEND);
49                 pending_inops=qedge->pending_inops;
50                 qedge->pending_inops=0;
51                 pending_outops=qedge->pending_outops;
52                 qedge->pending_outops=0;
53                 can_spin_unlock_irqrestore (&canque_pending_edges_lock, flags);
54
55                 if(pending_outops & ~CANQUE_PENDOPS_MASK){
56                         pending_outops &= CANQUE_PENDOPS_MASK;
57                         canque_notify_outends(qedge,CANQUEUE_NOTIFY_ERROR | qedge->fifo.error_code);
58                 }
59                 for(i=0;pending_outops;i++,pending_outops>>=1){
60                         if(pending_outops&1)
61                                 canque_notify_outends(qedge,i);
62                 }
63                 if(pending_inops & ~CANQUE_PENDOPS_MASK){
64                         pending_inops &= CANQUE_PENDOPS_MASK;
65                         canque_notify_inends(qedge,CANQUEUE_NOTIFY_ERROR | qedge->fifo.error_code);
66                 }
67                 for(i=0;pending_inops;i++,pending_inops>>=1){
68                         if(pending_inops&1)
69                                 canque_notify_inends(qedge,i);
70                 }
71                 
72                 canque_edge_decref(qedge);
73                 can_spin_lock_irqsave (&canque_pending_edges_lock, flags);
74         }
75
76         can_spin_unlock_irqrestore (&canque_pending_edges_lock, flags);
77
78         return;
79 }
80
81
82 /**
83  * canqueue_rtl2lin_check_and_pend - postpones edge notification if called from RT-Linux 
84  * @qends: notification target ends
85  * @qedge: edge delivering notification
86  * @what:  notification type
87  *
88  * Return Value: if called from Linux context, returns 0 and lefts notification processing
89  *              on caller responsibility. If called from RT-Linux contexts, schedules postponed
90  *              event delivery and returns 1
91  */
92 int canqueue_rtl2lin_check_and_pend(struct canque_ends_t *qends,
93                          struct canque_edge_t *qedge, int what)
94 {
95         can_spin_irqflags_t flags;
96
97         if(rtl_rt_system_is_idle()) return 0;
98
99         can_spin_lock_irqsave (&canque_pending_edges_lock, flags);
100         
101         if(what>CANQUE_PENDOPS_LIMIT) what=CANQUE_PENDOPS_LIMIT;
102
103         if(qends == qedge->inends) {
104                 set_bit(what,&qedge->pending_inops);
105         } else if(qends == qedge->outends) {
106                 set_bit(what,&qedge->pending_outops);
107         }
108
109         if(!canque_fifo_test_and_set_fl(&qedge->fifo, NOTIFYPEND)){
110                 canque_edge_incref(qedge);
111                 list_add_tail(&qedge->pending_peers,&canque_pending_edges_list);
112                 rtl_global_pend_irq (canqueue_rtl_irq);
113         }
114
115         can_spin_unlock_irqrestore (&canque_pending_edges_lock, flags);
116         
117         return 1;
118
119 }
120
121
122 /**
123  * canque_fifo_init_rtl - initialize one CAN FIFO
124  * @fifo: pointer to the FIFO structure
125  * @slotsnr: number of requested slots
126  *
127  * Return Value: The negative value indicates, that there is no memory
128  *      to allocate space for the requested number of the slots.
129  */
130 int canque_fifo_init_rtl(struct canque_fifo_t *fifo, int slotsnr)
131 {
132         int size;
133         if(!slotsnr) slotsnr=MAX_BUF_LENGTH;
134         size=sizeof(struct canque_slot_t)*slotsnr;
135         fifo->entry=rt_malloc(size);
136         if(!fifo->entry) return -1;
137         fifo->slotsnr=slotsnr;
138         return canque_fifo_init_slots(fifo);
139 }
140
141 /**
142  * canque_fifo_done_rtl - frees slots allocated for CAN FIFO
143  * @fifo: pointer to the FIFO structure
144  */
145 int canque_fifo_done_rtl(struct canque_fifo_t *fifo)
146 {
147         if(fifo->entry)
148                 rt_free(fifo->entry);
149         fifo->entry=NULL;
150         return 1;
151 }
152
153 void canque_dispose_edge_rtl(struct canque_edge_t *qedge)
154 {
155         canque_fifo_done_rtl(&qedge->fifo);
156         rt_free(qedge);
157 }
158
159 /**
160  * canque_new_edge_rtl - allocate new edge structure in the RT-Linux context
161  * @slotsnr: required number of slots in the newly allocated edge structure
162  *
163  * Return Value: Returns pointer to allocated slot structure or %NULL if
164  *      there is not enough memory to process operation.
165  */
166 struct canque_edge_t *canque_new_edge_rtl(int slotsnr)
167 {
168         struct canque_edge_t *qedge;
169         qedge = (struct canque_edge_t *)rt_malloc(sizeof(struct canque_edge_t));
170         if(qedge == NULL) return NULL;
171
172         memset(qedge,0,sizeof(struct canque_edge_t));
173         can_spin_lock_init(&qedge->fifo.fifo_lock);
174         canque_fifo_set_fl(&qedge->fifo,RTL_MEM);
175         if(canque_fifo_init_rtl(&qedge->fifo, slotsnr)<0){
176                 rt_free(qedge);
177                 return NULL;
178         }
179         atomic_set(&qedge->edge_used,1);
180         qedge->filtid = 0;
181         qedge->filtmask = canque_filtid2internal(0l, (processlocal<2)? MSG_LOCAL:0);
182         qedge->edge_prio = 0;
183     #if defined(CAN_DEBUG) && 0
184         /* not exactly clean, but enough for debugging */
185         atomic_inc(&edge_num_cnt);
186         qedge->edge_num=atomic_read(&edge_num_cnt);
187     #endif /* CAN_DEBUG */
188         return qedge;
189 }
190
191 void canque_ends_free_rtl(struct canque_ends_t *qends)
192 {
193         rt_free(qends);
194 }
195
196
197 /**
198  * canqueue_notify_rtl - notification callback handler for Linux userspace clients
199  * @qends: pointer to the callback side ends structure
200  * @qedge: edge which invoked notification 
201  * @what: notification type
202  */
203 void canqueue_notify_rtl(struct canque_ends_t *qends, struct canque_edge_t *qedge, int what)
204 {
205         rtl_irqstate_t flags;
206         
207         switch(what){
208                 case CANQUEUE_NOTIFY_EMPTY:
209                         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
210                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_emptyq);
211                         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
212                         if(canque_fifo_test_and_clear_fl(&qedge->fifo, FREEONEMPTY))
213                                 canque_edge_decref(qedge);
214                         break;
215                 case CANQUEUE_NOTIFY_SPACE:
216                         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
217                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_writeq);
218                         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
219                         break;
220                 case CANQUEUE_NOTIFY_PROC:
221                         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
222                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_readq);
223                         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
224                         break;
225                 case CANQUEUE_NOTIFY_NOUSR:
226                         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
227                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_readq);
228                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_writeq);
229                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_emptyq);
230                         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
231                         break;
232                 case CANQUEUE_NOTIFY_DEAD_WANTED:
233                 case CANQUEUE_NOTIFY_DEAD:
234                         if(canque_fifo_test_and_clear_fl(&qedge->fifo, READY))
235                                 canque_edge_decref(qedge);
236                         break;
237                 case CANQUEUE_NOTIFY_ATTACH:
238                         break;
239         }
240 }
241
242
243 /**
244  * canqueue_ends_init_rtl - RT-Linux clients specific ends initialization
245  * @qends: pointer to the callback side ends structure
246  */
247 int canqueue_ends_init_rtl(struct canque_ends_t *qends)
248 {
249         canqueue_ends_init_gen(qends);
250         qends->context=NULL;
251         rtl_spin_lock_init(&(qends->endinfo.rtlinfo.rtl_lock));
252         rtl_wait_init(&(qends->endinfo.rtlinfo.rtl_readq));
253         rtl_wait_init(&(qends->endinfo.rtlinfo.rtl_writeq));
254         rtl_wait_init(&(qends->endinfo.rtlinfo.rtl_emptyq));
255         
256         qends->notify=canqueue_notify_rtl;
257         return 0;
258 }
259
260 /**
261  * canqueue_ends_dispose_rtl - finalizing of the ends structure for Linux kernel clients
262  * @qends: pointer to ends structure
263  * @sync: flag indicating, that user wants to wait for processing of all remaining
264  *      messages
265  *
266  * Return Value: Function should be designed such way to not fail.
267  */
268 int canqueue_ends_dispose_rtl(struct canque_ends_t *qends, int sync)
269 {
270         rtl_irqstate_t flags;
271         int delayed;
272
273         canqueue_block_inlist(qends);
274         canqueue_block_outlist(qends);
275
276         /*Wait for sending of all pending messages in the output FIFOs*/
277         /*if(sync)
278                 canqueue_ends_sync_all_rtl(qends);*/
279         
280         /* Finish or kill all outgoing edges listed in inends */
281         delayed=canqueue_ends_kill_inlist(qends, 1);
282         /* Kill all incoming edges listed in outends */
283         delayed|=canqueue_ends_kill_outlist(qends);
284
285         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
286         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_readq);
287         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_writeq);
288         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_emptyq);
289         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
290
291         if(delayed || !(qends->ends_flags&CAN_ENDSF_MEM_RTL)){
292                 canqueue_ends_dispose_postpone(qends);
293
294                 return 1;
295         }
296
297         canque_ends_free_rtl(qends);
298         return 0;
299 }
300
301
302
303 void canqueue_rtl_initialize(void)
304 {
305         INIT_LIST_HEAD(&canque_pending_edges_list);
306         can_spin_lock_init(&canque_pending_edges_lock);
307
308         canqueue_rtl_irq = rtl_get_soft_irq (canqueue_rtl2lin_handler, "rtl_canqueue_irq");
309 }
310
311
312 void canqueue_rtl_done(void)
313 {
314         rtl_free_soft_irq (canqueue_rtl_irq);
315
316 }
317
318
319 #endif /*CAN_WITH_RTL*/