📄 hal.c
字号:
/** * @ingroup hal * @file * * ARTI -- RTAI-compatible Adeos-based Real-Time Interface. Based on * the original RTAI layer for x86. * * Original RTAI/x86 layer implementation: \n * Copyright © 2000 Paolo Mantegazza, \n * Copyright © 2000 Steve Papacharalambous, \n * Copyright © 2000 Stuart Hughes, \n * and others. * * RTAI/x86 rewrite over Adeos: \n * Copyright © 2002 Philippe Gerum. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, * USA; either version 2 of the License, or (at your option) any later * version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//** * @defgroup hal RTAI services functions. * * This module defines some functions that can be used by RTAI tasks, for * managing interrupts and communication services with Linux processes. * *@{*/#include <linux/version.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/console.h>#include <asm/system.h>#include <asm/hw_irq.h>#include <asm/irq.h>#include <asm/desc.h>#include <asm/io.h>#include <asm/mmu_context.h>#include <asm/uaccess.h>#include <asm/unistd.h>#ifdef CONFIG_X86_LOCAL_APIC#include <asm/fixmap.h>#include <asm/bitops.h>#include <asm/mpspec.h>#ifdef CONFIG_X86_IO_APIC#include <asm/io_apic.h>#endif /* CONFIG_X86_IO_APIC */#include <asm/apic.h>#endif /* CONFIG_X86_LOCAL_APIC */#define __RTAI_HAL__#include <asm/rtai_hal.h>#ifdef CONFIG_PROC_FS#include <linux/stat.h>#include <linux/proc_fs.h>#include <rtai_proc_fs.h>#endif /* CONFIG_PROC_FS */#include <stdarg.h>MODULE_LICENSE("GPL");static unsigned long rtai_cpufreq_arg = RTAI_CALIBRATED_CPU_FREQ;MODULE_PARM(rtai_cpufreq_arg,"i");#ifdef CONFIG_X86_LOCAL_APICstatic unsigned long rtai_apicfreq_arg = RTAI_CALIBRATED_APIC_FREQ;MODULE_PARM(rtai_apicfreq_arg,"i");static long long rtai_timers_sync_time;static struct apic_timer_setup_data rtai_timer_mode[RTAI_NR_CPUS];static inline void rtai_setup_periodic_apic (unsigned count, unsigned vector){ apic_read(APIC_LVTT); apic_write(APIC_LVTT,APIC_LVT_TIMER_PERIODIC|vector); apic_read(APIC_TMICT); apic_write(APIC_TMICT,count);}static inline void rtai_setup_oneshot_apic (unsigned count, unsigned vector){ apic_read(APIC_LVTT); apic_write(APIC_LVTT,vector); apic_read(APIC_TMICT); apic_write(APIC_TMICT,count);}#else /* !CONFIG_X86_LOCAL_APIC */#define rtai_setup_periodic_apic(count,vector);#define rtai_setup_oneshot_apic(count,vector);#endif /* CONFIG_X86_LOCAL_APIC */#ifdef CONFIG_SMPstatic unsigned long rtai_old_irq_affinity[NR_IRQS], rtai_set_irq_affinity[NR_IRQS];static spinlock_t rtai_iset_lock = SPIN_LOCK_UNLOCKED;#endif /* CONFIG_SMP */#ifdef CONFIG_RTAI_SCHED_ISR_LOCKstatic int rtai_isr_nesting[RTAI_NR_CPUS];static void (*rtai_isr_hook)(int nesting);#endif /* CONFIG_RTAI_SCHED_ISR_LOCK */extern struct desc_struct idt_table[];adomain_t rtai_domain;static struct { void (*handler)(unsigned irq, void *cookie); void *cookie;} rtai_realtime_irq[NR_IRQS];static struct { unsigned long flags; int count;} rtai_linux_irq[NR_IRQS];static struct { void (*k_handler)(void); long long (*u_handler)(unsigned); unsigned label;} rtai_sysreq_table[RTAI_NR_SRQS];static unsigned rtai_sysreq_virq;static unsigned long rtai_sysreq_map = 3; /* srqs #[0-1] are reserved */static unsigned long rtai_sysreq_pending;static unsigned long rtai_sysreq_running;static spinlock_t rtai_ssrq_lock = SPIN_LOCK_UNLOCKED;static volatile int rtai_sync_level;static atomic_t rtai_sync_count = ATOMIC_INIT(1);static int rtai_last_8254_counter2;static RTIME rtai_ts_8254;static struct desc_struct rtai_sysvec;static RT_TRAP_HANDLER rtai_trap_handler;static void (*rtai_exit_callback)(void);struct rt_times rt_times;struct rt_times rt_smp_times[RTAI_NR_CPUS];struct rtai_switch_data rtai_linux_context[RTAI_NR_CPUS];struct calibration_data rtai_tunables;volatile unsigned long rtai_status ;volatile unsigned long rtai_cpu_realtime;volatile unsigned long rtai_cpu_lock;volatile unsigned long rtai_cpu_lxrt;int rtai_adeos_ptdbase = -1;unsigned long rtai_critical_enter (void (*synch)(void)){ unsigned long flags = adeos_critical_enter(synch); if (atomic_dec_and_test(&rtai_sync_count)) rtai_sync_level = 0; else if (synch != NULL) printk("RTAI: warning: nested sync will fail.\n"); return flags;}void rtai_critical_exit (unsigned long flags){ atomic_inc(&rtai_sync_count); adeos_critical_exit(flags);}int rt_request_irq (unsigned irq, void (*handler)(unsigned irq, void *cookie), void *cookie){ unsigned long flags; if (handler == NULL || irq >= NR_IRQS) return -EINVAL; flags = rtai_critical_enter(NULL); if (rtai_realtime_irq[irq].handler != NULL) { rtai_critical_exit(flags); return -EBUSY; } rtai_realtime_irq[irq].handler = handler; rtai_realtime_irq[irq].cookie = cookie; rtai_critical_exit(flags); return 0;}int rt_release_irq (unsigned irq){ if (irq >= NR_IRQS) return -EINVAL; xchg(&rtai_realtime_irq[irq].handler,NULL); return 0;}void rt_set_irq_cookie (unsigned irq, void *cookie) { if (irq < NR_IRQS) rtai_realtime_irq[irq].cookie = cookie;}/* Note: Adeos already does all the magic that allows the calling the interrupt controller routines safely. *//** * start and initialize the PIC to accept interrupt request irq. * * The above function allow you to manipulate the PIC at hand, but you must * know what you are doing. Such a duty does not pertain to this manual and * you should refer to your PIC datasheet. * * Note that Linux has the same functions, but they must be used only for its * interrupts. Only the above ones can be safely used in real time handlers. * * It must also be remarked that when you install a real time interrupt handler, * RTAI already calls either rt_mask_and_ack_irq(), for level triggered * interrupts, or rt_ack_irq(), for edge triggered interrupts, before passing * control to you interrupt handler. hus generally you should just call * rt_unmask_irq() at due time, for level triggered interrupts, while nothing * should be done for edge triggered ones. Recall that in the latter case you * allow also any new interrupts on the same request as soon as you enable * interrupts at the CPU level. * * Often some of the above functions do equivalent things. Once more there is no * way of doing it right except by knowing the hardware you are manipulating. * Furthermore you must also remember that when you install a hard real time * handler the related interrupt is usually disabled, unless you are overtaking * one already owned by Linux which has been enabled by it. Recall that if * have done it right, and interrupts do not show up, it is likely you have just * to rt_enable_irq() your irq. */unsigned rt_startup_irq (unsigned irq) { return irq_desc[irq].handler->startup(irq);}/** * Shut down an IRQ source. * * No further interrupt request irq can be accepted. * * The above function allow you to manipulate the PIC at hand, but you must * know what you are doing. Such a duty does not pertain to this manual and * you should refer to your PIC datasheet. * * Note that Linux has the same functions, but they must be used only for its * interrupts. Only the above ones can be safely used in real time handlers. * * It must also be remarked that when you install a real time interrupt handler, * RTAI already calls either rt_mask_and_ack_irq(), for level triggered * interrupts, or rt_ack_irq(), for edge triggered interrupts, before passing * control to you interrupt handler. hus generally you should just call * rt_unmask_irq() at due time, for level triggered interrupts, while nothing * should be done for edge triggered ones. Recall that in the latter case you * allow also any new interrupts on the same request as soon as you enable * interrupts at the CPU level. * * Often some of the above functions do equivalent things. Once more there is no * way of doing it right except by knowing the hardware you are manipulating. * Furthermore you must also remember that when you install a hard real time * handler the related interrupt is usually disabled, unless you are overtaking * one already owned by Linux which has been enabled by it. Recall that if * have done it right, and interrupts do not show up, it is likely you have just * to rt_enable_irq() your irq. */void rt_shutdown_irq (unsigned irq) { irq_desc[irq].handler->shutdown(irq);}/** * Enable an IRQ source. * * The above function allow you to manipulate the PIC at hand, but you must * know what you are doing. Such a duty does not pertain to this manual and * you should refer to your PIC datasheet. * * Note that Linux has the same functions, but they must be used only for its * interrupts. Only the above ones can be safely used in real time handlers. * * It must also be remarked that when you install a real time interrupt handler, * RTAI already calls either rt_mask_and_ack_irq(), for level triggered * interrupts, or rt_ack_irq(), for edge triggered interrupts, before passing * control to you interrupt handler. hus generally you should just call * rt_unmask_irq() at due time, for level triggered interrupts, while nothing * should be done for edge triggered ones. Recall that in the latter case you * allow also any new interrupts on the same request as soon as you enable * interrupts at the CPU level. * * Often some of the above functions do equivalent things. Once more there is no * way of doing it right except by knowing the hardware you are manipulating. * Furthermore you must also remember that when you install a hard real time * handler the related interrupt is usually disabled, unless you are overtaking * one already owned by Linux which has been enabled by it. Recall that if * have done it right, and interrupts do not show up, it is likely you have just * to rt_enable_irq() your irq. */void rt_enable_irq (unsigned irq) { irq_desc[irq].handler->enable(irq);}/** * Disable an IRQ source. * * The above function allow you to manipulate the PIC at hand, but you must * know what you are doing. Such a duty does not pertain to this manual and * you should refer to your PIC datasheet. * * Note that Linux has the same functions, but they must be used only for its * interrupts. Only the above ones can be safely used in real time handlers. * * It must also be remarked that when you install a real time interrupt handler, * RTAI already calls either rt_mask_and_ack_irq(), for level triggered * interrupts, or rt_ack_irq(), for edge triggered interrupts, before passing * control to you interrupt handler. hus generally you should just call * rt_unmask_irq() at due time, for level triggered interrupts, while nothing * should be done for edge triggered ones. Recall that in the latter case you * allow also any new interrupts on the same request as soon as you enable * interrupts at the CPU level. * * Often some of the above functions do equivalent things. Once more there is no * way of doing it right except by knowing the hardware you are manipulating. * Furthermore you must also remember that when you install a hard real time * handler the related interrupt is usually disabled, unless you are overtaking * one already owned by Linux which has been enabled by it. Recall that if * have done it right, and interrupts do not show up, it is likely you have just * to rt_enable_irq() your irq. */void rt_disable_irq (unsigned irq) { irq_desc[irq].handler->disable(irq);}/** * Mask and acknowledge and IRQ source. * * No * other interrupts can be accepted, once also the CPU will enable * interrupts, which ones depends on the PIC at hand and on how it is * programmed. * * The above function allow you to manipulate the PIC at hand, but you must * know what you are doing. Such a duty does not pertain to this manual and * you should refer to your PIC datasheet. * * Note that Linux has the same functions, but they must be used only for its * interrupts. Only the above ones can be safely used in real time handlers. * * It must also be remarked that when you install a real time interrupt handler, * RTAI already calls either rt_mask_and_ack_irq(), for level triggered * interrupts, or rt_ack_irq(), for edge triggered interrupts, before passing * control to you interrupt handler. hus generally you should just call * rt_unmask_irq() at due time, for level triggered interrupts, while nothing * should be done for edge triggered ones. Recall that in the latter case you * allow also any new interrupts on the same request as soon as you enable * interrupts at the CPU level. * * Often some of the above functions do equivalent things. Once more there is no * way of doing it right except by knowing the hardware you are manipulating. * Furthermore you must also remember that when you install a hard real time * handler the related interrupt is usually disabled, unless you are overtaking * one already owned by Linux which has been enabled by it. Recall that if * have done it right, and interrupts do not show up, it is likely you have just * to rt_enable_irq() your irq. */void rt_mask_and_ack_irq (unsigned irq) { irq_desc[irq].handler->ack(irq);}/** * Unmask and IRQ source. * * The related request can then interrupt the CPU again, provided it has also * been acknowledged. * * The above function allow you to manipulate the PIC at hand, but you must * know what you are doing. Such a duty does not pertain to this manual and * you should refer to your PIC datasheet. * * Note that Linux has the same functions, but they must be used only for its * interrupts. Only the above ones can be safely used in real time handlers. * * It must also be remarked that when you install a real time interrupt handler, * RTAI already calls either rt_mask_and_ack_irq(), for level triggered * interrupts, or rt_ack_irq(), for edge triggered interrupts, before passing * control to you interrupt handler. hus generally you should just call * rt_unmask_irq() at due time, for level triggered interrupts, while nothing * should be done for edge triggered ones. Recall that in the latter case you * allow also any new interrupts on the same request as soon as you enable * interrupts at the CPU level. * * Often some of the above functions do equivalent things. Once more there is no * way of doing it right except by knowing the hardware you are manipulating. * Furthermore you must also remember that when you install a hard real time * handler the related interrupt is usually disabled, unless you are overtaking * one already owned by Linux which has been enabled by it. Recall that if * have done it right, and interrupts do not show up, it is likely you have just * to rt_enable_irq() your irq. */void rt_unmask_irq (unsigned irq) { irq_desc[irq].handler->end(irq);}/** * Acknowledge an IRQ source. * * The related request can then interrupt the CPU again, provided it has not * been masked. * * The above function allow you to manipulate the PIC at hand, but you must * know what you are doing. Such a duty does not pertain to this manual and * you should refer to your PIC datasheet. * * Note that Linux has the same functions, but they must be used only for its * interrupts. Only the above ones can be safely used in real time handlers. * * It must also be remarked that when you install a real time interrupt handler, * RTAI already calls either rt_mask_and_ack_irq(), for level triggered * interrupts, or rt_ack_irq(), for edge triggered interrupts, before passing * control to you interrupt handler. hus generally you should just call * rt_unmask_irq() at due time, for level triggered interrupts, while nothing * should be done for edge triggered ones. Recall that in the latter case you
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -