]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/src/can_devrtl.c
Merge: Worker thread wake test protected by rtl_no_interrupts.
[lincan.git] / lincan / src / can_devrtl.c
1 /* can_devrtl.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.3  17 Jun 2004
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 #include "../include/main.h"
15 #include "../include/setup.h"
16
17 #include <rtl_malloc.h>
18
19 #ifdef CAN_ENABLE_VME_SUPPORT
20 #include "ca91c042.h"
21 /* Modified version of ca91c042 driver can be found in
22  * components/comm/contrib directory. */
23 #endif
24
25 can_spinlock_t can_irq_manipulation_lock;
26
27 unsigned int can_rtl_isr( unsigned int irq_num, struct pt_regs *r )
28 {
29         struct canchip_t *chip;
30         struct candevice_t *candev;
31         int board_nr;
32         int chip_nr;
33         int irq2linux=0;
34         int ret;
35         pthread_t thread=NULL;
36
37         DEBUGMSG("can_rtl_isr invoked for irq %d\n",irq_num);
38         
39         /* I hate next loop, but RT-Linux does not provide context to ISR */
40         for (board_nr=hardware_p->nr_boards; board_nr--; ) {
41                 if((candev=hardware_p->candevice[board_nr])==NULL)
42                         continue;
43                 for(chip_nr=candev->nr_all_chips; chip_nr--; ) {
44                         if((chip=candev->chip[chip_nr])==NULL)
45                                 continue;
46                         if(chip->chip_irq!=irq_num) continue;
47
48                         if(chip->chipspecops->irq_accept)
49                                 ret=chip->chipspecops->irq_accept(chip->chip_irq,chip);
50
51                         set_bit(MSGOBJ_IRQ_REQUEST_b,&chip->pend_flags);
52                         set_bit(MSGOBJ_WORKER_WAKE_b,&chip->pend_flags);
53                         if(chip->flags & CHIP_IRQ_PCI)
54                                 irq2linux=1;
55 #ifdef CAN_ENABLE_VME_SUPPORT
56                         if (chip->flags & CHIP_IRQ_VME)
57                                 tundra_rtl_ack_irq_vector(irq_num);
58 #endif
59                         if(!chip->worker_thread) continue;
60                         thread=chip->worker_thread;
61                         pthread_kill(thread,RTL_SIGNAL_WAKEUP);
62                 }
63         }
64
65         /* The following lines are commented out because of it is not
66          * possible to share level activated (PCI) IRQs between Linux
67          * and RT-Linux. */
68 /*      if(irq2linux) */
69 /*              rtl_global_pend_irq(irq_num); */
70
71         /*if(thread) rtl_reschedule_thread(thread);*/
72
73         rtl_schedule();
74
75         return 0;
76 }
77
78
79
80 /*
81 RTL_MARK_READY(pthread_self())
82 RTL_MARK_SUSPENDED(pthread_self());
83 return rtl_schedule();
84 can_enable_irq
85 can_disable_irq 
86 rtl_critical( state )
87 rtl_end_critical( state )
88 rtl_request_global_irq( irq, isr ); 
89 rtl_free_global_irq( irq )
90 */
91
92 void * can_chip_worker_thread(void *arg)
93 {
94         struct canchip_t *chip = (struct canchip_t *) arg;
95         struct msgobj_t *obj;
96         int ret, i;
97         int loop_cnt;
98         rtl_irqstate_t flags;
99         
100         if(!chip) return 0;
101         
102         
103         if (!(chip->flags & CHIP_CONFIGURED)){
104                 if (chip->chipspecops->chip_config(chip))
105                         CANMSG("Error configuring chip.\n");
106                 else
107                         chip->flags |= CHIP_CONFIGURED; 
108
109                 if((chip->msgobj[0])!=NULL)
110                         if (chip->chipspecops->pre_read_config(chip,chip->msgobj[0])<0)
111                                 CANMSG("Error initializing chip for receiving\n");
112                                 
113         } /* End of chip configuration */
114         set_bit(MSGOBJ_IRQ_REQUEST_b,&chip->pend_flags);
115         
116
117         while (1) {
118                 DEBUGMSG("Worker thread for chip %d active\n",chip->chip_idx);
119                 if(test_and_clear_bit(MSGOBJ_IRQ_REQUEST_b,&chip->pend_flags)){
120                         DEBUGMSG("IRQ_REQUEST processing ...\n");
121                         loop_cnt = 100;
122                         if(chip->chipspecops->irq_handler) do{
123                                 ret=chip->chipspecops->irq_handler(chip->chip_irq,chip);
124                         }while(ret && --loop_cnt);
125                         continue;
126                 }
127                 if(test_and_clear_bit(MSGOBJ_TX_REQUEST_b,&chip->pend_flags)){
128                         DEBUGMSG("TX_REQUEST processing ...\n");
129                         for(i=0;i<chip->max_objects;i++){
130                                 if((obj=chip->msgobj[i])==NULL)
131                                         continue;
132                                 if(can_msgobj_test_fl(obj,TX_REQUEST)) {
133                                         DEBUGMSG("Calling wakeup_tx\n");
134                                         chip->chipspecops->wakeup_tx(chip, obj);
135                                 }
136                                 if(can_msgobj_test_fl(obj,FILTCH_REQUEST)) {
137                                         DEBUGMSG("Calling filtch_rq\n");
138                                         if(chip->chipspecops->filtch_rq)
139                                                 chip->chipspecops->filtch_rq(chip, obj);
140                                 }
141                         }
142                         continue;
143                 }
144
145                 /*re-enable chip IRQ, I am not sure, if this is required,
146                   but it seems to not work without that */
147                 if(chip->chip_irq>=0) {
148                         if ((chip->flags & CHIP_IRQ_VME) == 0) can_enable_irq(chip->chip_irq);
149                     #ifdef CAN_ENABLE_VME_SUPPORT
150                       #if 0
151                         else tundra_rtl_enable_pci_irq();
152                       #endif
153                         /* FIXME: Bad practice. Doesn't work with more
154                          * than one card.
155                          *
156                          * irq_accept added to the LinCAN driver now,
157                          * and above workaround should not be required.
158                          * Enable rtl_hard_enable_irq() at line 
159                          * ca91c042.c:1045
160                          */
161                     #endif /*CAN_ENABLE_VME_SUPPORT*/
162
163                 }
164
165                 rtl_no_interrupts (flags);
166                 RTL_MARK_SUSPENDED(pthread_self());
167                 if(test_and_clear_bit(MSGOBJ_WORKER_WAKE_b,&chip->pend_flags)){
168                         RTL_MARK_READY(pthread_self());
169                         rtl_restore_interrupts (flags);
170                         continue;
171                 }
172                 rtl_restore_interrupts (flags);
173                 rtl_schedule();
174
175         }
176         return 0;
177 }
178
179
180 int can_chip_setup_irq(struct canchip_t *chip)
181 {
182         int ret;
183         struct sched_param sched_param;
184         pthread_attr_t attrib;
185         pthread_attr_t *attrib_p=NULL;
186         
187         if(chip==NULL)
188                 return -1;
189         
190         if(can_rtl_priority>=0){
191                 pthread_attr_init(&attrib);
192                 sched_param.sched_priority = can_rtl_priority;
193                 pthread_attr_setschedparam(&attrib, &sched_param);
194                 /* pthread_attr_setschedpolicy(&attrib, SCHED_FIFO); */
195                 attrib_p=&attrib;
196         }
197         
198         if(chip->chipspecops->irq_handler && !(chip->flags & CHIP_IRQ_CUSTOM)){
199                 int (*my_request_irq)(unsigned int vector, unsigned int (*rtl_handler)(unsigned int irq, struct pt_regs *regs));
200 #ifdef CAN_ENABLE_VME_SUPPORT
201                 if ((chip->flags & CHIP_IRQ_VME) != 0)
202                         my_request_irq = rtl_request_vmeirq;
203                 else
204 #endif
205                         my_request_irq = rtl_request_irq;
206
207                 if (my_request_irq(chip->chip_irq,can_rtl_isr))
208                         return -1;
209                 else {
210                         DEBUGMSG("Registered interrupt %d\n",chip->chip_irq);
211                         chip->flags |= CHIP_IRQ_SETUP;
212                 }
213         }
214         ret=pthread_create(&chip->worker_thread, attrib_p, can_chip_worker_thread, chip);
215         if(ret<0) chip->worker_thread=NULL;
216         
217         return ret;
218 }
219
220
221 void can_chip_free_irq(struct canchip_t *chip)
222 {
223         if(chip->worker_thread)
224                 pthread_delete_np(chip->worker_thread);
225         if((chip->flags & CHIP_IRQ_SETUP) && (chip->chip_irq>=0)
226             && !(chip->flags & CHIP_IRQ_CUSTOM)) {
227                 int (*my_free_irq)(unsigned int vector);
228 #ifdef CAN_ENABLE_VME_SUPPORT
229                 if ((chip->flags & CHIP_IRQ_VME) != 0)
230                         my_free_irq = rtl_free_vmeirq;
231                 else
232 #endif
233                         my_free_irq = rtl_free_irq;
234                 my_free_irq(chip->chip_irq);
235                 chip->flags &= ~CHIP_IRQ_SETUP;
236         }
237 }
238
239
240 #endif /*CAN_WITH_RTL*/