📄 rtai.c
字号:
/*Copyright (C) 2002,2003 Axis Communications ABAuthors: Martin P Andersson (martin.andersson@linux.nu) Jens-Henrik Lindskov (mumrick@linux.nu)This program is free software; you can redistribute it and/or modifyit under the terms of version 2 of the GNU General Public License aspublished by the Free Software Foundation.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA--------------------------------------------------------------------------Acknowledgements- Paolo Mantegazza (mantegazza@aero.polimi.it) creator of RTAI --------------------------------------------------------------------------*/#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/slab.h>#include <asm/system.h>#include <rtai_version.h>#ifdef CONFIG_PROC_FS#include <linux/stat.h>#include <linux/proc_fs.h>#include <rtai_proc_fs.h>#endif#define NR_SYSRQS 32#define TIMER_IRQ 2 /* Prescaler=4 gives 6.25 MHz. This means a value of 62500 needs to be loaded in the cascaded counter to give an interrupt every 10 ms. 62500dec=F424hex. F4hex=244dec, 24hex=36dec. */#define RTAI_CASCADED_TIMER0_DIV 36#define RTAI_CASCADED_TIMER1_DIV 244#define RTAI_CASCADED_TIMER_PRESCALE 4#include <asm/rtai.h>/* --------------------------------------------------------------------------*/static struct global_rt_status { int pending_irqs; int pending_srqs; unsigned int used_by_linux;} volatile global;static struct cpu_own_status { unsigned long intr_flag; unsigned long linux_intr_flag;} volatile cpu;static void* global_irq_handlers[NR_IRQS];static struct sysrq_t { unsigned int label; void (*rtai_handler)(void); long long (*user_handler)(unsigned long whatever);} sysrq[NR_SYSRQS];static unsigned long irq_action_flags[NR_IRQS];static int chained_to_linux[NR_IRQS];static int rtai_mounted = 0;/* The datastructures saved when taking control from Linux */static struct rt_hal linux_rthal; /* The original rthal-struct.*/#ifdef CONFIG_ETRAX_DISABLE_CASCADED_TIMERS_IN_RTAIstatic unsigned long saved_r_timer_ctrl_shadow; /* Saved timer shadow.*/#endifstatic void *saved_timer_action_handler; /* Saved timer-action handler*//* --------------------------------------------------------------------------*//* Scheduler related data */unsigned int rtai_delay; /* Current timer divide factor on timer0 */unsigned long long rtai_tsc; /* timer0 ticks since we started counting */unsigned int rtai_lastcount; /* Last read value on *R_TIMER0_DATA */struct calibration_data tuned = {CPU_FREQ, FREQ_APIC, 0, 0, 0, {0}};struct rt_times rt_times;/* --------------------------------------------------------------------------*//* Some prototypes */long long dispatch_srq(int srq, unsigned long whatever);#ifdef CONFIG_PROC_FSstatic int rtai_proc_register(void);static void rtai_proc_unregister(void);#endif/* Just some dummies to get rid of compiler warnings */static long long do_nothing(int what, unsigned long ever) { return 0;}static void do_nothing_uint(unsigned int whatever) { }#ifdef CONFIG_ETRAX_WATCHDOGextern void reset_watchdog(void);#endif/* --------------------------------------------------------------------------*/static inline void mask_irq(unsigned int irq_nr){ *R_VECT_MASK_CLR = 1 << irq_nr;}static inline void unmask_irq(unsigned int irq_nr){ *R_VECT_MASK_SET = 1 << irq_nr;}/* --------------------------------------------------------------------------*/unsigned int rt_startup_irq(unsigned int irq){ startup_irq(irq); return 0;}void rt_shutdown_irq(unsigned int irq){ shutdown_irq(irq);}void rt_enable_irq(unsigned int irq){ unmask_irq(irq);}void rt_disable_irq(unsigned int irq){ mask_irq(irq);}/* * The ACK is not implemented on cris because no generic way to * do this exists. It is up to the RT-handler to ACK properly. */void rt_mask_and_ack_irq(unsigned int irq){ mask_irq(irq);}void rt_mask_irq(unsigned int irq){ mask_irq(irq);}void rt_unmask_irq(unsigned int irq){ unmask_irq(irq);}/* * The ACK is not implemented on cris because no generic way to * do this exists. It is up to the RT-handler to ACK properly. */void rt_ack_irq(unsigned int irq){}/* --------------------------------------------------------------------------*//* Only makes sense on smp */#define send_ipi_shorthand(shorthand, irq)#define send_ipi_logical(dest, irq)/* --------------------------------------------------------------------------*//* * Installs a RT-handler on the requested irq. */int rt_request_global_irq(unsigned int irq, void (*handler)(void)){ unsigned long flags; if (irq >= NR_IRQS || !handler) { return -EINVAL; } flags = hard_lock_all(); if (global_irq_handlers[irq]) { hard_unlock_all(flags); return -EBUSY; } global_irq_handlers[irq] = handler; hard_unlock_all(flags); return 0;}/* * Uninstalls the previously installed RT-handler on the requested irq. */int rt_free_global_irq(unsigned int irq){ unsigned long flags = hard_lock_all(); if (irq >= NR_IRQS || !global_irq_handlers[irq]) { hard_unlock_all(flags); return -EINVAL; } global_irq_handlers[irq] = 0; hard_unlock_all(flags); return 0;}/* * Installs a new linux interrupt handler. Forces linux to share the * interrupt if necessary. */int rt_request_linux_irq(unsigned int irq, void (*linux_handler)(int irq, void *dev_id, struct pt_regs *regs), char *linux_handler_id, void *dev_id){ unsigned long flags; if (irq >= NR_IRQS || !linux_handler) { return -EINVAL; } flags = hard_lock_all(); /* Force linux to share the irq */ /* Save the flags to restore them in rt_free_linux_irq */ if (!chained_to_linux[irq]++) { if (irq_action[irq]) { irq_action_flags[irq] = irq_action[irq]->flags; irq_action[irq]->flags |= SA_SHIRQ; } } hard_unlock_all(flags); /* Call the standard linux function in irq.c */ request_irq(irq, linux_handler, SA_SHIRQ, linux_handler_id, dev_id); return 0;}/* * Copied from irq.c with some small changes. Called from rt_free_linux_irq * instead of free_irq in irq.c when there is a RT-handler installed on the * same interrupt. Does not set the interrupt to the badhandler. */static void linux_free_irq(unsigned int irq, void *dev_id){ struct irqaction *action, **p; unsigned long flags; for (p = irq_action + irq; (action = *p) != NULL; p = &action->next) { if (action->dev_id != dev_id) continue; /* Found it - now free it */ save_flags(flags); cli(); *p = action->next; restore_flags(flags); kfree(action); return; }}/* * Uninstalls the previously installed linux handler on irq. */int rt_free_linux_irq(unsigned int irq, void *dev_id){ unsigned long flags; flags = hard_lock_all(); if (irq >= NR_IRQS || !chained_to_linux[irq]) { hard_unlock_all(flags); return -EINVAL; } if (global_irq_handlers[irq]){ hard_unlock_all(flags); /* Call our special function above. */ linux_free_irq(irq, dev_id); } else{ hard_unlock_all(flags); /* Call the standard linux function in irq.c */ free_irq(irq, dev_id); } flags = hard_lock_all(); if (!(--chained_to_linux[irq]) && irq_action[irq]) { irq_action[irq]->flags = irq_action_flags[irq]; } hard_unlock_all(flags); return 0;}/* * Pend an interrupt to linux. * Called by a RT-handler if it wants to share the interrupt with linux. */void rt_pend_linux_irq(unsigned int irq){ set_bit_non_atomic(irq, (int*) &global.pending_irqs);}/* * The call mechanism for the user_handler is not yet implemented. */int rt_request_srq(unsigned int label, void (*rtai_handler)(void), long long (*user_handler)(unsigned long whatever)){ unsigned long flags; int srq; if (!rtai_handler) { return -EINVAL; } flags = hard_lock_all(); /* srq 0 and 1 are reserved */ for (srq = 2; srq < NR_SYSRQS; srq++) { if (!(sysrq[srq].rtai_handler)) { sysrq[srq].rtai_handler = rtai_handler; sysrq[srq].label = label; if (user_handler) { sysrq[srq].user_handler = user_handler; rthal.do_SRQ = dispatch_srq; } hard_unlock_all(flags); return srq; } } hard_unlock_all(flags); return -EBUSY;}int rt_free_srq(unsigned int srq){ unsigned long flags; flags = hard_lock_all(); if (srq < 2 || srq >= NR_SYSRQS || !sysrq[srq].rtai_handler) { hard_unlock_all(flags); return -EINVAL; } sysrq[srq].rtai_handler = 0; sysrq[srq].user_handler = 0; sysrq[srq].label = 0; for (srq = 2; srq < NR_SYSRQS; srq++) { if (sysrq[srq].user_handler) { hard_unlock_all(flags); return 0; } } rthal.do_SRQ = do_nothing; hard_unlock_all(flags); return 0;}void rt_pend_linux_srq(unsigned int srq){ set_bit_non_atomic(srq, (int*) &global.pending_srqs);}/* --------------------------------------------------------------------------*/static void linux_cli(void){ /* Soft-disable interrupts */ cpu.intr_flag = 0;}static void linux_sti(void){ static volatile int sti_lock = 0; static struct pt_regs dummy_regs; int irq; int irq_mask; /* Hard-disable interrupts */ hard_cli(); /* When one process is in sti, dispatching irqs, */ /* another one should not be able to enter. */ if (sti_lock) { cpu.intr_flag = 1; /* Need to enable interrupts since they were enabled before calling this function. */ hard_sti(); return; } /* Lock */ sti_lock = 1; /* Soft-disable interrupts */ cpu.intr_flag = 0; irq = 0; irq_mask = 1; /* Process all pending IRQs and SRQs */ while (global.pending_irqs || global.pending_srqs) { /* "irq" active? */ if (irq_mask & global.pending_irqs) { /* Clear the irq */ clear_bit_non_atomic(irq, (int*) &global.pending_irqs); /* Hard-enable interrupts (during handler) */ hard_sti(); /* Call standard Linux do_IRQ(). */ linux_rthal.do_IRQ(irq, &dummy_regs); /* Hard-disable interrupts */ hard_cli(); /* The interrupt had to remain masked until now * because there is no generic ACK on cris. * Interrupts with RT-handlers are unmasked * immediately in dispatch_irq. */ unmask_irq(irq); } /* If a SRQ has been pended and there is a SRQ-handler */ if ((irq_mask & global.pending_srqs) && sysrq[irq].rtai_handler) { /* Clear the srq */ clear_bit_non_atomic(irq, (int*) &global.pending_srqs); /* Hard-enable interrupts (during handler) */ hard_sti(); /* Call installed SRQ-handler */ sysrq[irq].rtai_handler(); /* Hard-disable interrupts */ hard_cli(); } /* The interrupts should be enabled here to reduce latency */ hard_sti(); irq_mask = irq_mask << 1; ++irq; if (irq >= NR_IRQS) { irq = 0; irq_mask = 1; } hard_cli(); } /* We are through, so release the lock */ sti_lock = 0; /* Soft-enable interrupts */ cpu.intr_flag = 1; /* Hard-enable interrupts */ hard_sti();}static unsigned long linux_save_flags(void){ return cpu.intr_flag;}static void linux_restore_flags(unsigned long flags){ /* check if interrupts were enabled when flag was saved */ if (flags) { linux_sti(); } else { cpu.intr_flag = 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -