/* * Analogy for Linux, RTDM helpers * * Copyright (C) 1997-2000 David A. Schleef * Copyright (C) 2008 Alexis Berlemont * * Xenomai 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; either version 2 of the License, or * (at your option) any later version. * * Xenomai 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 Xenomai; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include /* --- Time section --- */ static nanosecs_abs_t a4l_clkofs; void a4l_init_time(void) { nanosecs_abs_t t1, t2; t1 = rtdm_clock_read(); t2 = ktime_to_ns(ktime_get_real()); a4l_clkofs = t2 - t1; } nanosecs_abs_t a4l_get_time(void) { return a4l_clkofs + rtdm_clock_read(); } /* --- IRQ section --- */ static int a4l_handle_irq(rtdm_irq_t *irq_handle) { struct a4l_irq_descriptor *dsc = rtdm_irq_get_arg(irq_handle, struct a4l_irq_descriptor); if (dsc->handler((unsigned int)irq_handle->irq, dsc->cookie) == 0) return RTDM_IRQ_HANDLED; else return RTDM_IRQ_NONE; } int __a4l_request_irq(struct a4l_irq_descriptor *dsc, unsigned int irq, a4l_irq_hdlr_t handler, unsigned long flags, void *cookie) { /* Fills the IRQ descriptor */ dsc->handler = handler; dsc->cookie = cookie; dsc->irq = irq; /* Registers the RT IRQ handler */ return rtdm_irq_request(&dsc->rtdm_desc, (int)irq, a4l_handle_irq, flags, "Analogy device", dsc); } int __a4l_free_irq(struct a4l_irq_descriptor * dsc) { return rtdm_irq_free(&dsc->rtdm_desc); } /* --- Synchronization section --- */ static void a4l_nrt_sync_handler(rtdm_nrtsig_t *nrt_sig, void *arg) { struct a4l_sync *snc = (struct a4l_sync *) arg; wake_up_interruptible(&snc->wq); } int a4l_init_sync(struct a4l_sync *snc) { int ret = 0; /* Initializes the flags field */ snc->status = 0; /* If the process is NRT, we need a wait queue structure */ init_waitqueue_head(&snc->wq); /* Initializes the RTDM event */ rtdm_event_init(&snc->rtdm_evt, 0); /* Initializes the gateway to NRT context */ rtdm_nrtsig_init(&snc->nrt_sig, a4l_nrt_sync_handler, snc); return ret; } void a4l_cleanup_sync(struct a4l_sync *snc) { rtdm_nrtsig_destroy(&snc->nrt_sig); rtdm_event_destroy(&snc->rtdm_evt); } int a4l_wait_sync(struct a4l_sync *snc, int rt) { int ret = 0; if (test_bit(__EVT_PDING, &snc->status)) goto out_wait; if (rt != 0) { /* If the calling process is in primary mode, we can use RTDM API ... */ set_bit(__RT_WAITER, &snc->status); ret = rtdm_event_wait(&snc->rtdm_evt); } else { /* ... else if the process is NRT, the Linux wait queue system is used */ set_bit(__NRT_WAITER, &snc->status); ret = wait_event_interruptible(snc->wq, test_bit(__EVT_PDING, &snc->status)); } out_wait: clear_bit(__EVT_PDING, &snc->status); return ret; } int a4l_timedwait_sync(struct a4l_sync * snc, int rt, unsigned long long ns_timeout) { int ret = 0; unsigned long timeout; if (test_bit(__EVT_PDING, &snc->status)) goto out_wait; if (rt != 0) { /* If the calling process is in primary mode, we can use RTDM API ... */ set_bit(__RT_WAITER, &snc->status); ret = rtdm_event_timedwait(&snc->rtdm_evt, ns_timeout, NULL); } else { /* ... else if the process is NRT, the Linux wait queue system is used */ timeout = do_div(ns_timeout, 1000); /* We consider the Linux kernel cannot tick at a frequency higher than 1 MHz If the timeout value is lower than 1us, we round up to 1us */ timeout = (timeout == 0) ? 1 : usecs_to_jiffies(timeout); set_bit(__NRT_WAITER, &snc->status); ret = wait_event_interruptible_timeout(snc->wq, test_bit(__EVT_PDING, &snc->status), timeout); } out_wait: clear_bit(__EVT_PDING, &snc->status); return ret; } void a4l_flush_sync(struct a4l_sync * snc) { /* Clear the status bitfield */ snc->status = 0; /* Flush the RTDM event */ rtdm_event_clear(&snc->rtdm_evt); } void a4l_signal_sync(struct a4l_sync * snc) { int hit = 0; set_bit(__EVT_PDING, &snc->status); /* a4l_signal_sync() is bound not to be called upon the right user process context; so, the status flags stores its mode. Thus the proper event signaling function is called */ if (test_and_clear_bit(__RT_WAITER, &snc->status)) { rtdm_event_signal(&snc->rtdm_evt); hit++; } if (test_and_clear_bit(__NRT_WAITER, &snc->status)) { rtdm_nrtsig_pend(&snc->nrt_sig); hit++; } if (hit == 0) { /* At first signaling, we may not know the proper way to send the event */ rtdm_event_signal(&snc->rtdm_evt); rtdm_nrtsig_pend(&snc->nrt_sig); } }