/* * Copyright (C) 2014 Philippe Gerum . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #include "../alchemy/alarm.h" #ifdef DOXYGEN_CPP /** * @ingroup trank * @{ * * @fn int COMPAT__rt_task_create(RT_TASK *task, const char *name, int stksize, int prio, int mode) * @brief Create a real-time task (compatibility service). * * This service creates a task with access to the full set of Xenomai * real-time services. * * This service creates a task with access to the full set of Xenomai * real-time services. If @a prio is non-zero, the new task belongs to * Xenomai's real-time FIFO scheduling class, aka SCHED_FIFO. If @a * prio is zero, the task belongs to the regular SCHED_OTHER class. * * Creating tasks with zero priority is useful for running non * real-time processes which may invoke blocking real-time services, * such as pending on a semaphore, reading from a message queue or a * buffer, and so on. * * Once created, the task is left dormant until it is actually started * by rt_task_start(). * * @param task The address of a task descriptor which can be later * used to identify uniquely the created object, upon success of this * call. * * @param name An ASCII string standing for the symbolic name of the * task. When non-NULL and non-empty, a copy of this string is * used for indexing the created task into the object registry. * * @param stksize The size of the stack (in bytes) for the new * task. If zero is passed, a system-dependent default size will be * substituted. * * @param prio The base priority of the new task. This value must be * in the [0 .. 99] range, where 0 is the lowest effective priority. * * @param mode The task creation mode. The following flags can be * OR'ed into this bitmask: * * - T_FPU allows the task to use the FPU whenever available on the * platform. This flag may be omitted, as it is automatically set when * a FPU is present on the platform, cleared otherwise. * * - T_SUSP causes the task to start in suspended mode. In such a * case, the thread will have to be explicitly resumed using the * rt_task_resume() service for its execution to actually begin. * * - T_CPU(cpuid) makes the new task affine to CPU # @b cpuid. CPU * identifiers range from 0 to 7 (inclusive). * * - T_JOINABLE allows another task to wait on the termination of the * new task. rt_task_join() shall be called for this task to clean up * any resources after its termination. * * Passing T_FPU|T_CPU(1) in the @a mode parameter thus creates a task * with FPU support enabled and which will be affine to CPU #1. * * - When running over the Cobalt core, T_WARNSW causes the SIGDEBUG * signal to be sent to the current task whenever it switches to the * secondary mode. This feature is useful to detect unwanted * migrations to the Linux domain. This flag has no effect over the * Mercury core. * * @return Zero is returned upon success. Otherwise: * * - -EINVAL is returned if either @a prio, @a mode or @a stksize are * invalid. * * - -ENOMEM is returned if the system fails to get memory from the * main heap in order to create the task. * * - -EEXIST is returned if the @a name is conflicting with an already * registered task. * * @apitags{thread-unrestricted, switch-secondary} * * @sideeffect * * - calling rt_task_create() causes SCHED_FIFO tasks to switch to * secondary mode. * * - members of Xenomai's SCHED_FIFO class running in the primary * domain have utmost priority over all Linux activities in the * system, including Linux interrupt handlers. * * @note Tasks can be referred to from multiple processes which all * belong to the same Xenomai session. * * @deprecated This is a compatibility service from the Transition * Kit. */ int COMPAT__rt_task_create(RT_TASK *task, const char *name, int stksize, int prio, int mode); /** * @fn int COMPAT__rt_task_set_periodic(RT_TASK *task, RTIME idate, RTIME period) * @brief Make a real-time task periodic (compatibility service). * * Make a task periodic by programing its first release point and its * period in the processor time line. @a task should then call * rt_task_wait_period() to sleep until the next periodic release * point in the processor timeline is reached. * * @param task The task descriptor. If @a task is NULL, the current * task is made periodic. @a task must belong the current process. * * @param idate The initial (absolute) date of the first release * point, expressed in clock ticks (see note). If @a idate is equal * to TM_NOW, the current system date is used. Otherwise, if @a task * is NULL or equal to @a rt_task_self(), the caller is delayed until * @a idate has elapsed. * * @param period The period of the task, expressed in clock ticks (see * note). Passing TM_INFINITE stops the task's periodic timer if * enabled, then returns successfully. * * @return Zero is returned upon success. Otherwise: * * - -EINVAL is returned if @a task is NULL but the caller is not a * Xenomai task, or if @a task is non-NULL but not a valid task * descriptor. * * - -ETIMEDOUT is returned if @a idate is different from TM_INFINITE * and represents a date in the past. * * @apitags{thread-unrestricted, switch-primary} * * @note The caller must be an Alchemy task if @a task is NULL. * * @note Unlike the original Xenomai 2.x call, this emulation delays * the caller until @a idate has elapsed only if @a task is NULL or * equal to rt_task_self(). * * @sideeffect Over Cobalt, -EINVAL is returned if @a period is * different from TM_INFINITE but shorter than the user scheduling * latency value for the target system, as displayed by * /proc/xenomai/latency. * * @note The @a idate and @a period values are interpreted as a * multiple of the Alchemy clock resolution (see * --alchemy-clock-resolution option, defaults to 1 nanosecond). * * @deprecated This is a compatibility service from the Transition * Kit. */ int COMPAT__rt_task_set_periodic(RT_TASK *task, RTIME idate, RTIME period); /** * @fn int COMPAT__rt_alarm_create(RT_ALARM *alarm, const char *name) * @brief Create an alarm object (compatibility service). * * This routine creates an object triggering an alarm routine at a * specified time in the future. Alarms can be periodic or oneshot, * depending on the reload interval value passed to rt_alarm_start(). * A task can wait for timeouts using the rt_alarm_wait() service. * * @param alarm The address of an alarm descriptor which can be later * used to identify uniquely the created object, upon success of this * call. * * @param name An ASCII string standing for the symbolic name of the * alarm. When non-NULL and non-empty, a copy of this string is used * for indexing the created alarm into the object registry. * * @return Zero is returned upon success. Otherwise: * * - -ENOMEM is returned if the system fails to get memory from the * local pool in order to create the alarm. * * - -EEXIST is returned if the @a name is conflicting with an already * registered alarm. * * - -EPERM is returned if this service was called from an * asynchronous context. * * @apitags{thread-unrestricted, switch-secondary} * * @note Alarms are process-private objects and thus cannot be shared * by multiple processes, even if they belong to the same Xenomai * session. * * @deprecated This is a compatibility service from the Transition * Kit. */ int COMPAT__rt_alarm_create(RT_ALARM *alarm, const char *name); /** * @fn int rt_alarm_wait(RT_ALARM *alarm) * @brief Wait for the next alarm shot (compatibility service). * * This service allows the current task to suspend execution until the * specified alarm triggers. The priority of the current task is * raised above all other tasks - except those also undergoing an * alarm wait. * * @return Zero is returned upon success, after the alarm timed * out. Otherwise: * * - -EINVAL is returned if @a alarm is not a valid alarm descriptor. * * - -EPERM is returned if this service was called from an invalid * context. * * - -EINTR is returned if rt_task_unblock() was called for the * current task before the request is satisfied. * * - -EIDRM is returned if @a alarm is deleted while the caller was * sleeping on it. In such a case, @a alarm is no more valid upon * return of this service. * * @apitags{xthread-only, switch-primary} * * @deprecated This is a compatibility service from the Transition * Kit. * */ int rt_alarm_wait(RT_ALARM *alarm); /** * @fn int COMPAT__rt_event_create(RT_EVENT *event, const char *name, unsigned long ivalue, int mode) * @brief Create an event flag group. * * This call is the legacy form of the rt_event_create() service, * using a long event mask. The new form uses a regular integer to * hold the event mask instead. * * @param event The address of an event descriptor which can be later * used to identify uniquely the created object, upon success of this * call. * * @param name An ASCII string standing for the symbolic name of the * event. When non-NULL and non-empty, a copy of this string is used * for indexing the created event into the object registry. * * @param ivalue The initial value of the group's event mask. * * @param mode The event group creation mode. The following flags can * be OR'ed into this bitmask: * * - EV_FIFO makes tasks pend in FIFO order on the event flag group. * * - EV_PRIO makes tasks pend in priority order on the event flag group. * * @return Zero is returned upon success. Otherwise: * * - -EINVAL is returned if @a mode is invalid. * * - -ENOMEM is returned if the system fails to get memory from the * main heap in order to create the event flag group. * * - -EEXIST is returned if the @a name is conflicting with an already * registered event flag group. * * - -EPERM is returned if this service was called from an * asynchronous context. * * @apitags{thread-unrestricted, switch-secondary} * * @note Event flag groups can be shared by multiple processes which * belong to the same Xenomai session. * * @deprecated This is a compatibility service from the Transition * Kit. */ int COMPAT__rt_event_create(RT_EVENT *event, const char *name, unsigned long ivalue, int mode); /** * @fn int COMPAT__rt_event_signal(RT_EVENT *event, unsigned long mask) * @brief Signal an event. * * This call is the legacy form of the rt_event_signal() service, * using a long event mask. The new form uses a regular integer to * hold the event mask instead. * * @param event The event descriptor. * * @param mask The set of events to be posted. * * @return Zero is returned upon success. Otherwise: * * - -EINVAL is returned if @a event is not an event flag group * descriptor. * * @apitags{unrestricted, switch-primary} * * @deprecated This is a compatibility service from the Transition * Kit. */ int COMPAT__rt_event_signal(RT_EVENT *event, unsigned long mask); /** * @fn int COMPAT__rt_event_clear(RT_EVENT *event,unsigned long mask,unsigned long *mask_r) * @brief Clear event flags. * * This call is the legacy form of the rt_event_clear() service, * using a long event mask. The new form uses a regular integer to * hold the event mask instead. * @param event The event descriptor. * * @param mask The set of event flags to be cleared. * * @param mask_r If non-NULL, @a mask_r is the address of a memory * location which will receive the previous value of the event flag * group before the flags are cleared. * * @return Zero is returned upon success. Otherwise: * * - -EINVAL is returned if @a event is not a valid event flag group * descriptor. * * @apitags{unrestricted, switch-primary} * * @deprecated This is a compatibility service from the Transition * Kit. */ int COMPAT__rt_event_clear(RT_EVENT *event, unsigned long mask, unsigned long *mask_r); /** * @fn int COMPAT__rt_pipe_create(RT_PIPE *pipe, const char *name, int minor, size_t poolsize) * @brief Create a message pipe. * * This call is the legacy form of the rt_pipe_create() service, which * returns a zero status upon success. The new form returns the @a * minor number assigned to the connection instead, which is useful * when P_MINOR_AUTO is specified in the call (see the discussion * about the @a minor parameter). * * This service opens a bi-directional communication channel for * exchanging messages between Xenomai threads and regular Linux * threads. Pipes natively preserve message boundaries, but can also * be used in byte-oriented streaming mode from Xenomai to Linux. * * rt_pipe_create() always returns immediately, even if no thread has * opened the associated special device file yet. On the contrary, the * non real-time side could block upon attempt to open the special * device file until rt_pipe_create() is issued on the same pipe from * a Xenomai thread, unless O_NONBLOCK was given to the open(2) system * call. * * @param pipe The address of a pipe descriptor which can be later used * to identify uniquely the created object, upon success of this call. * * @param name An ASCII string standing for the symbolic name of the * pipe. When non-NULL and non-empty, a copy of this string is used * for indexing the created pipe into the object registry. * * Named pipes are supported through the use of the registry. Passing * a valid @a name parameter when creating a message pipe causes a * symbolic link to be created from * /proc/xenomai/registry/rtipc/xddp/@a name to the associated special * device (i.e. /dev/rtp*), so that the specific @a minor information * does not need to be known from those processes for opening the * proper device file. In such a case, both sides of the pipe only * need to agree upon a symbolic name to refer to the same data path, * which is especially useful whenever the @a minor number is picked * up dynamically using an adaptive algorithm, such as passing * P_MINOR_AUTO as @a minor value. * * @param minor The minor number of the device associated with the * pipe. Passing P_MINOR_AUTO causes the minor number to be * auto-allocated. In such a case, a symbolic link will be * automatically created from * /proc/xenomai/registry/rtipc/xddp/@a name to the allocated pipe * device entry. Valid minor numbers range from 0 to * CONFIG_XENO_OPT_PIPE_NRDEV-1. * * @param poolsize Specifies the size of a dedicated buffer pool for the * pipe. Passing 0 means that all message allocations for this pipe are * performed on the Cobalt core heap. * * @return This compatibility call returns zero upon * success. Otherwise: * * - -ENOMEM is returned if the system fails to get memory from the * main heap in order to create the pipe. * * - -ENODEV is returned if @a minor is different from P_MINOR_AUTO * and is not a valid minor number. * * - -EEXIST is returned if the @a name is conflicting with an already * registered pipe. * * - -EBUSY is returned if @a minor is already open. * * - -EPERM is returned if this service was called from an * asynchronous context. * * @apitags{thread-unrestricted, switch-secondary} */ int COMPAT__rt_pipe_create(RT_PIPE *pipe, const char *name, int minor, size_t poolsize); #else /* !DOXYGEN_CPP */ int rt_task_create(RT_TASK *task, const char *name, int stksize, int prio, int mode) { int ret, susp, cpus, cpu; cpu_set_t cpuset; susp = mode & T_SUSP; cpus = mode & T_CPUMASK; ret = __CURRENT(rt_task_create(task, name, stksize, prio, mode & ~(T_SUSP|T_CPUMASK|T_LOCK))); if (ret) return ret; if (cpus) { CPU_ZERO(&cpuset); for (cpu = 0, cpus >>= 24; cpus && cpu < 8; cpu++, cpus >>= 1) { if (cpus & 1) CPU_SET(cpu, &cpuset); } ret = rt_task_set_affinity(task, &cpuset); if (ret) { rt_task_delete(task); return ret; } } return susp ? rt_task_suspend(task) : 0; } int rt_task_spawn(RT_TASK *task, const char *name, int stksize, int prio, int mode, void (*entry)(void *arg), void *arg) { int ret; ret = rt_task_create(task, name, stksize, prio, mode); if (ret) return ret; return rt_task_start(task, entry, arg); } int rt_task_set_periodic(RT_TASK *task, RTIME idate, RTIME period) { int ret; ret = __CURRENT(rt_task_set_periodic(task, idate, period)); if (ret) return ret; if (idate != TM_NOW) { if (task == NULL || task == rt_task_self()) ret = rt_task_wait_period(NULL); else trank_warning("task won't wait for start time"); } return ret; } struct trank_alarm_wait { pthread_mutex_t lock; pthread_cond_t event; int alarm_pulses; }; static void trank_alarm_handler(void *arg) { struct trank_alarm_wait *aw = arg; __RT(pthread_mutex_lock(&aw->lock)); aw->alarm_pulses++; __RT(pthread_cond_broadcast(&aw->event)); __RT(pthread_mutex_unlock(&aw->lock)); } int rt_alarm_create(RT_ALARM *alarm, const char *name) { struct trank_alarm_wait *aw; pthread_mutexattr_t mattr; pthread_condattr_t cattr; int ret; aw = xnmalloc(sizeof(*aw)); if (aw == NULL) return -ENOMEM; aw->alarm_pulses = 0; pthread_mutexattr_init(&mattr); pthread_mutexattr_settype(&mattr, mutex_type_attribute); pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT); pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_PRIVATE); ret = __bt(-__RT(pthread_mutex_init(&aw->lock, &mattr))); pthread_mutexattr_destroy(&mattr); if (ret) goto fail_lock; pthread_condattr_init(&cattr); pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_PRIVATE); ret = __bt(-pthread_cond_init(&aw->event, &cattr)); pthread_condattr_destroy(&cattr); if (ret) goto fail_cond; ret = __CURRENT(rt_alarm_create(alarm, name, trank_alarm_handler, aw)); if (ret) goto fail_alarm; return 0; fail_alarm: __RT(pthread_cond_destroy(&aw->event)); fail_cond: __RT(pthread_mutex_destroy(&aw->lock)); fail_lock: xnfree(aw); return ret; } static struct alchemy_alarm *find_alarm(RT_ALARM *alarm) { struct alchemy_alarm *acb; if (bad_pointer(alarm)) return NULL; acb = (struct alchemy_alarm *)alarm->handle; if (bad_pointer(acb) || acb->magic != alarm_magic) return NULL; return acb; } int rt_alarm_wait(RT_ALARM *alarm) { struct threadobj *current = threadobj_current(); struct sched_param_ex param_ex; struct trank_alarm_wait *aw; struct alchemy_alarm *acb; int ret, prio, pulses; acb = find_alarm(alarm); if (acb == NULL) return -EINVAL; threadobj_lock(current); prio = threadobj_get_priority(current); if (prio != threadobj_irq_prio) { param_ex.sched_priority = threadobj_irq_prio; /* Working on self, so -EIDRM can't happen. */ threadobj_set_schedparam(current, SCHED_FIFO, ¶m_ex); } threadobj_unlock(current); aw = acb->arg; /* * Emulate the original behavior: wait for the next pulse (no * event buffering, broadcast to all waiters), while * preventing spurious wakeups. */ __RT(pthread_mutex_lock(&aw->lock)); pulses = aw->alarm_pulses; for (;;) { ret = -__RT(pthread_cond_wait(&aw->event, &aw->lock)); if (ret || aw->alarm_pulses != pulses) break; } __RT(pthread_mutex_unlock(&aw->lock)); return __bt(ret); } int rt_alarm_delete(RT_ALARM *alarm) { struct trank_alarm_wait *aw; struct alchemy_alarm *acb; int ret; acb = find_alarm(alarm); if (acb == NULL) return -EINVAL; aw = acb->arg; ret = __CURRENT(rt_alarm_delete(alarm)); if (ret) return ret; __RT(pthread_cond_destroy(&aw->event)); __RT(pthread_mutex_destroy(&aw->lock)); xnfree(aw); return 0; } int rt_event_create(RT_EVENT *event, const char *name, unsigned long ivalue, int mode) { return __CURRENT(rt_event_create(event, name, ivalue, mode)); } int rt_event_signal(RT_EVENT *event, unsigned long mask) { return __CURRENT(rt_event_signal(event, mask)); } int rt_event_clear(RT_EVENT *event, unsigned long mask, unsigned long *mask_r) { unsigned int _mask; int ret; ret = __CURRENT(rt_event_clear(event, mask, &_mask)); if (ret) return ret; *mask_r = _mask; return 0; } int rt_pipe_create(RT_PIPE *pipe, const char *name, int minor, size_t poolsize) { int ret; ret = __CURRENT(rt_pipe_create(pipe, name, minor, poolsize)); return ret < 0 ? ret : 0; } #endif /* !DOXYGEN_CPP */ /** @} */