Structured comments updated.
[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 unsigned long canqueue_rtl2lin_pend;
32
33 int canqueue_rtl_irq = 0;
34
35 void
36 canqueue_rtl2lin_handler(int irq, void *ignore, struct pt_regs *ignoreregs)
37 {
38         can_spin_irqflags_t flags;
39         struct canque_edge_t *qedge;
40         unsigned pending_inops;
41         unsigned pending_outops;
42         int i;
43         
44         can_spin_lock_irqsave (&canque_pending_edges_lock, flags);
45
46         while(!list_empty(&canque_pending_edges_list)){
47                 qedge=list_entry(canque_pending_edges_list.next,struct canque_edge_t,pending_peers);
48                 list_del(&qedge->pending_peers);
49                 canque_fifo_clear_fl(&qedge->fifo, NOTIFYPEND);
50                 pending_inops=qedge->pending_inops;
51                 qedge->pending_inops=0;
52                 pending_outops=qedge->pending_outops;
53                 qedge->pending_outops=0;
54                 can_spin_unlock_irqrestore (&canque_pending_edges_lock, flags);
55
56                 if(pending_outops & ~CANQUE_PENDOPS_MASK){
57                         pending_outops &= CANQUE_PENDOPS_MASK;
58                         canque_notify_outends(qedge,CANQUEUE_NOTIFY_ERROR | qedge->fifo.error_code);
59                 }
60                 for(i=0;pending_outops;i++,pending_outops>>=1){
61                         if(pending_outops&1)
62                                 canque_notify_outends(qedge,i);
63                 }
64                 if(pending_inops & ~CANQUE_PENDOPS_MASK){
65                         pending_inops &= CANQUE_PENDOPS_MASK;
66                         canque_notify_inends(qedge,CANQUEUE_NOTIFY_ERROR | qedge->fifo.error_code);
67                 }
68                 for(i=0;pending_inops;i++,pending_inops>>=1){
69                         if(pending_inops&1)
70                                 canque_notify_inends(qedge,i);
71                 }
72                 
73                 canque_edge_decref(qedge);
74                 can_spin_lock_irqsave (&canque_pending_edges_lock, flags);
75         }
76
77         can_spin_unlock_irqrestore (&canque_pending_edges_lock, flags);
78
79         if(test_and_clear_bit(CAN_RTL2LIN_PEND_DEAD_b,&canqueue_rtl2lin_pend))
80                 tasklet_schedule(&canque_dead_tl);
81
82         return;
83 }
84
85
86 /**
87  * canqueue_rtl2lin_check_and_pend - postpones edge notification if called from RT-Linux 
88  * @qends: notification target ends
89  * @qedge: edge delivering notification
90  * @what:  notification type
91  *
92  * Return Value: if called from Linux context, returns 0 and lefts notification processing
93  *              on caller responsibility. If called from RT-Linux contexts, schedules postponed
94  *              event delivery and returns 1
95  */
96 int canqueue_rtl2lin_check_and_pend(struct canque_ends_t *qends,
97                          struct canque_edge_t *qedge, int what)
98 {
99         can_spin_irqflags_t flags;
100
101         if(rtl_rt_system_is_idle()) return 0;
102
103         can_spin_lock_irqsave (&canque_pending_edges_lock, flags);
104         
105         if(what>CANQUE_PENDOPS_LIMIT) what=CANQUE_PENDOPS_LIMIT;
106
107         if(qends == qedge->inends) {
108                 set_bit(what,&qedge->pending_inops);
109         } else if(qends == qedge->outends) {
110                 set_bit(what,&qedge->pending_outops);
111         }
112
113         if(!canque_fifo_test_and_set_fl(&qedge->fifo, NOTIFYPEND)){
114                 canque_edge_incref(qedge);
115                 list_add_tail(&qedge->pending_peers,&canque_pending_edges_list);
116                 rtl_global_pend_irq (canqueue_rtl_irq);
117         }
118
119         can_spin_unlock_irqrestore (&canque_pending_edges_lock, flags);
120         
121         return 1;
122
123 }
124
125
126 /**
127  * canque_get_inslot4id_wait_rtl - find or wait for best outgoing edge and slot for given ID
128  * @qends: ends structure belonging to calling communication object
129  * @qedgep: place to store pointer to found edge
130  * @slotp: place to store pointer to  allocated slot
131  * @cmd: command type for slot
132  * @id: communication ID of message to send into edge
133  * @prio: optional priority of message
134  *
135  * Same as canque_get_inslot4id(), except, that it waits for free slot
136  * in case, that queue is full. Function is specific for Linux userspace clients.
137  * Return Value: If there is no usable edge negative value is returned.
138  */
139 int canque_get_inslot4id_wait_rtl(struct canque_ends_t *qends,
140         struct canque_edge_t **qedgep, struct canque_slot_t **slotp,
141         int cmd, unsigned long id, int prio)
142 {
143         rtl_irqstate_t flags;
144         int ret;
145         unsigned old_age;
146         rtl_sigset_t sigset;
147         
148         old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_writeq_age);
149         while((ret=canque_get_inslot4id(qends,qedgep,slotp,cmd,id,prio))==-1){
150                 rtl_sigemptyset(&sigset);
151                 rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
152                 if(old_age == atomic_read(&qends->endinfo.rtlinfo.rtl_writeq_age))
153                         sigset=rtl_wait_sleep(&qends->endinfo.rtlinfo.rtl_writeq, &qends->endinfo.rtlinfo.rtl_lock);
154                 rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
155                 if(RTL_SIGINTR(&sigset))
156                         return -1;
157                 old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_writeq_age);
158         }
159         
160         return ret;
161 }
162
163
164 /**
165  * canque_get_outslot_wait_rtl - receive or wait for ready slot for given ends
166  * @qends: ends structure belonging to calling communication object
167  * @qedgep: place to store pointer to found edge
168  * @slotp: place to store pointer to received slot
169  *
170  * The same as canque_test_outslot(), except it waits in the case, that there is
171  * no ready slot for given ends. Function is specific for Linux userspace clients.
172  * Return Value: Negative value informs, that there is no ready output
173  *      slot for given ends. Positive value is equal to the command
174  *      slot has been allocated by the input side.
175  */
176 int canque_get_outslot_wait_rtl(struct canque_ends_t *qends,
177         struct canque_edge_t **qedgep, struct canque_slot_t **slotp)
178 {
179         rtl_irqstate_t flags;
180         int ret;
181         unsigned old_age;
182         rtl_sigset_t sigset;
183         
184         old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_readq_age);
185         while((ret=canque_test_outslot(qends,qedgep,slotp))==-1){
186                 rtl_sigemptyset(&sigset);
187                 rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
188                 if(old_age == atomic_read(&qends->endinfo.rtlinfo.rtl_readq_age))
189                         sigset=rtl_wait_sleep(&qends->endinfo.rtlinfo.rtl_readq, &qends->endinfo.rtlinfo.rtl_lock);
190                 rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
191                 if(RTL_SIGINTR(&sigset))
192                         return -1;
193                 old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_readq_age);
194         }
195         return ret;
196 }
197
198
199 /**
200  * canque_sync_wait_rtl - wait for all slots processing
201  * @qends: ends structure belonging to calling communication object
202  * @qedge: pointer to edge
203  *
204  * Functions waits for ends transition into empty state.
205  * Return Value: Positive value indicates, that edge empty state has been reached.
206  *      Negative or zero value informs about interrupted wait or other problem.
207  */
208 int canque_sync_wait_rtl(struct canque_ends_t *qends, struct canque_edge_t *qedge)
209 {
210         rtl_irqstate_t flags;
211         int ret;
212         unsigned old_age;
213         rtl_sigset_t sigset;
214         
215         old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_emptyq_age);
216         while(!(ret=canque_fifo_test_fl(&qedge->fifo,EMPTY)?1:0)){
217                 rtl_sigemptyset(&sigset);
218                 rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
219                 if(old_age == atomic_read(&qends->endinfo.rtlinfo.rtl_emptyq_age))
220                         sigset=rtl_wait_sleep(&qends->endinfo.rtlinfo.rtl_emptyq, &qends->endinfo.rtlinfo.rtl_lock);
221                 rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
222                 if(RTL_SIGINTR(&sigset))
223                         return -1;
224                 old_age=atomic_read(&qends->endinfo.rtlinfo.rtl_emptyq_age);
225         }
226         
227         return ret;
228 }
229
230
231 /**
232  * canque_fifo_init_rtl - initialize one CAN FIFO
233  * @fifo: pointer to the FIFO structure
234  * @slotsnr: number of requested slots
235  *
236  * Return Value: The negative value indicates, that there is no memory
237  *      to allocate space for the requested number of the slots.
238  */
239 int canque_fifo_init_rtl(struct canque_fifo_t *fifo, int slotsnr)
240 {
241         int size;
242         if(!slotsnr) slotsnr=MAX_BUF_LENGTH;
243         size=sizeof(struct canque_slot_t)*slotsnr;
244         fifo->entry=rt_malloc(size);
245         if(!fifo->entry) return -1;
246         fifo->slotsnr=slotsnr;
247         return canque_fifo_init_slots(fifo);
248 }
249
250
251 /**
252  * canque_fifo_done_rtl - frees slots allocated for CAN FIFO
253  * @fifo: pointer to the FIFO structure
254  */
255 int canque_fifo_done_rtl(struct canque_fifo_t *fifo)
256 {
257         if(fifo->entry)
258                 rt_free(fifo->entry);
259         fifo->entry=NULL;
260         return 1;
261 }
262
263 void canque_dispose_edge_rtl(struct canque_edge_t *qedge)
264 {
265         canque_fifo_done_rtl(&qedge->fifo);
266         rt_free(qedge);
267 }
268
269 /**
270  * canque_new_edge_rtl - allocate new edge structure in the RT-Linux context
271  * @slotsnr: required number of slots in the newly allocated edge structure
272  *
273  * Return Value: Returns pointer to allocated slot structure or %NULL if
274  *      there is not enough memory to process operation.
275  */
276 struct canque_edge_t *canque_new_edge_rtl(int slotsnr)
277 {
278         struct canque_edge_t *qedge;
279         qedge = (struct canque_edge_t *)rt_malloc(sizeof(struct canque_edge_t));
280         if(qedge == NULL) return NULL;
281
282         memset(qedge,0,sizeof(struct canque_edge_t));
283         can_spin_lock_init(&qedge->fifo.fifo_lock);
284         canque_fifo_set_fl(&qedge->fifo,RTL_MEM);
285         if(canque_fifo_init_rtl(&qedge->fifo, slotsnr)<0){
286                 rt_free(qedge);
287                 return NULL;
288         }
289         atomic_set(&qedge->edge_used,1);
290         qedge->filtid = 0;
291         qedge->filtmask = canque_filtid2internal(0l, (processlocal<2)? MSG_LOCAL:0);
292         qedge->edge_prio = 0;
293     #if defined(CAN_DEBUG) && 0
294         /* not exactly clean, but enough for debugging */
295         atomic_inc(&edge_num_cnt);
296         qedge->edge_num=atomic_read(&edge_num_cnt);
297     #endif /* CAN_DEBUG */
298         return qedge;
299 }
300
301 void canque_ends_free_rtl(struct canque_ends_t *qends)
302 {
303         rt_free(qends);
304 }
305
306
307 /**
308  * canqueue_notify_rtl - notification callback handler for Linux userspace clients
309  * @qends: pointer to the callback side ends structure
310  * @qedge: edge which invoked notification 
311  * @what: notification type
312  */
313 void canqueue_notify_rtl(struct canque_ends_t *qends, struct canque_edge_t *qedge, int what)
314 {
315         rtl_irqstate_t flags;
316         
317         switch(what){
318                 case CANQUEUE_NOTIFY_EMPTY:
319                         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
320                         atomic_inc(&qends->endinfo.rtlinfo.rtl_emptyq_age);
321                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_emptyq);
322                         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
323                         if(canque_fifo_test_and_clear_fl(&qedge->fifo, FREEONEMPTY))
324                                 canque_edge_decref(qedge);
325                         break;
326                 case CANQUEUE_NOTIFY_SPACE:
327                         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
328                         atomic_inc(&qends->endinfo.rtlinfo.rtl_writeq_age);
329                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_writeq);
330                         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
331                         break;
332                 case CANQUEUE_NOTIFY_PROC:
333                         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
334                         atomic_inc(&qends->endinfo.rtlinfo.rtl_readq_age);
335                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_readq);
336                         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
337                         break;
338                 case CANQUEUE_NOTIFY_NOUSR:
339                         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
340
341                         atomic_inc(&qends->endinfo.rtlinfo.rtl_readq_age);
342                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_readq);
343
344                         atomic_inc(&qends->endinfo.rtlinfo.rtl_writeq_age);
345                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_writeq);
346
347                         atomic_inc(&qends->endinfo.rtlinfo.rtl_emptyq_age);
348                         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_emptyq);
349
350                         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
351                         break;
352                 case CANQUEUE_NOTIFY_DEAD_WANTED:
353                 case CANQUEUE_NOTIFY_DEAD:
354                         if(canque_fifo_test_and_clear_fl(&qedge->fifo, READY))
355                                 canque_edge_decref(qedge);
356                         break;
357                 case CANQUEUE_NOTIFY_ATTACH:
358                         break;
359         }
360 }
361
362
363 /**
364  * canqueue_ends_init_rtl - RT-Linux clients specific ends initialization
365  * @qends: pointer to the callback side ends structure
366  */
367 int canqueue_ends_init_rtl(struct canque_ends_t *qends)
368 {
369         canqueue_ends_init_gen(qends);
370         qends->context=NULL;
371         rtl_spin_lock_init(&(qends->endinfo.rtlinfo.rtl_lock));
372         rtl_wait_init(&(qends->endinfo.rtlinfo.rtl_readq));
373         rtl_wait_init(&(qends->endinfo.rtlinfo.rtl_writeq));
374         rtl_wait_init(&(qends->endinfo.rtlinfo.rtl_emptyq));
375         
376         qends->notify=canqueue_notify_rtl;
377         qends->endinfo.rtlinfo.pend_flags=0;
378         return 0;
379 }
380
381 /**
382  * canqueue_ends_dispose_rtl - finalizing of the ends structure for Linux kernel clients
383  * @qends: pointer to ends structure
384  * @sync: flag indicating, that user wants to wait for processing of all remaining
385  *      messages
386  *
387  * Return Value: Function should be designed such way to not fail.
388  */
389 int canqueue_ends_dispose_rtl(struct canque_ends_t *qends, int sync)
390 {
391         rtl_irqstate_t flags;
392         int delayed;
393
394         canqueue_block_inlist(qends);
395         canqueue_block_outlist(qends);
396
397         /*Wait for sending of all pending messages in the output FIFOs*/
398         /*if(sync)
399                 canqueue_ends_sync_all_rtl(qends);*/
400         
401         /* Finish or kill all outgoing edges listed in inends */
402         delayed=canqueue_ends_kill_inlist(qends, 1);
403         /* Kill all incoming edges listed in outends */
404         delayed|=canqueue_ends_kill_outlist(qends);
405
406         rtl_spin_lock_irqsave(&qends->endinfo.rtlinfo.rtl_lock, flags);
407         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_readq);
408         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_writeq);
409         rtl_wait_wakeup(&qends->endinfo.rtlinfo.rtl_emptyq);
410         rtl_spin_unlock_irqrestore(&qends->endinfo.rtlinfo.rtl_lock, flags);
411
412         if(delayed || !(qends->ends_flags&CAN_ENDSF_MEM_RTL)){
413                 canqueue_ends_dispose_postpone(qends);
414
415                 return 1;
416         }
417
418         canque_ends_free_rtl(qends);
419         return 0;
420 }
421
422
423
424 /**
425  * canqueue_rtl_initialize - initialization of global RT-Linux specific features
426  */
427 void canqueue_rtl_initialize(void)
428 {
429         INIT_LIST_HEAD(&canque_pending_edges_list);
430         can_spin_lock_init(&canque_pending_edges_lock);
431
432         canqueue_rtl_irq = rtl_get_soft_irq (canqueue_rtl2lin_handler, "rtl_canqueue_irq");
433 }
434
435
436 /**
437  * canqueue_rtl_done - finalization of glopal RT-Linux specific features
438  */
439 void canqueue_rtl_done(void)
440 {
441         rtl_free_soft_irq (canqueue_rtl_irq);
442
443 }
444
445
446 #endif /*CAN_WITH_RTL*/