/ addons / rtdm / drvlib.c
drvlib.c
   1  /**
   2   * @file
   3   * Real-Time Driver Model for RTAI, driver library
   4   *
   5   * @note Copyright (C) 2005-2007 Jan Kiszka <jan.kiszka@web.de>
   6   * @note Copyright (C) 2005 Joerg Langenberg <joerg.langenberg@gmx.net>
   7   * @note Copyright (C) 2008 Gilles Chanteperdrix <gilles.chanteperdrix@gmail.com>
   8   *
   9   * with adaptions for RTAI by Paolo Mantegazza <mantegazza@aero.polimi.it>
  10   *
  11   * RTAI is free software; you can redistribute it and/or modify it
  12   * under the terms of the GNU General Public License as published by
  13   * the Free Software Foundation; either version 2 of the License, or
  14   * (at your option) any later version.
  15   *
  16   * RTAI is distributed in the hope that it will be useful, but
  17   * WITHOUT ANY WARRANTY; without even the implied warranty of
  18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  19   * General Public License for more details.
  20   *
  21   * You should have received a copy of the GNU General Public License
  22   * along with RTAI; if not, write to the Free Software Foundation,
  23   * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  24   */
  25  
  26  /*!
  27   * @ingroup rtdm
  28   * @defgroup driverapi Driver Development API
  29   *
  30   * This is the lower interface of RTDM provided to device drivers, currently
  31   * limited to kernel-space. Real-time drivers should only use functions of
  32   * this interface in order to remain portable.
  33   */
  34  
  35  #include <linux/bitops.h>
  36  #include <asm/page.h>
  37  #include <asm/io.h>
  38  #include <asm/pgtable.h>
  39  #include <linux/delay.h>
  40  #include <linux/mman.h>
  41  #include <linux/highmem.h>
  42  
  43  #include <rtdm/rtdm_driver.h>
  44  
  45  /*!
  46   * @ingroup driverapi
  47   * @defgroup clock Clock Services
  48   * @{
  49   */
  50  
  51  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
  52  /**
  53   * @brief Get system time
  54   *
  55   * @return The system time in nanoseconds is returned
  56   *
  57   * @note The resolution of this service depends on the system timer. In
  58   * particular, if the system timer is running in periodic mode, the return
  59   * value will be limited to multiples of the timer tick period.
  60   *
  61   * @note The system timer may have to be started to obtain valid results.
  62   * Whether this happens automatically or is controlled by the
  63   * application (as with RTAI) depends on the RTDM host environment.
  64   *
  65   * Environments:
  66   *
  67   * This service can be called from:
  68   *
  69   * - Kernel module initialization/cleanup code
  70   * - Interrupt service routine
  71   * - Kernel-based task
  72   * - User-space task (RT, non-RT)
  73   *
  74   * Rescheduling: never.
  75   */
  76  nanosecs_abs_t rtdm_clock_read(void);
  77  
  78  /**
  79   * @brief Get monotonic time
  80   *
  81   * @return The monotonic time in nanoseconds is returned
  82   *
  83   * @note The resolution of this service depends on the system timer. In
  84   * particular, if the system timer is running in periodic mode, the return
  85   * value will be limited to multiples of the timer tick period.
  86   *
  87   * @note The system timer may have to be started to obtain valid results.
  88   * Whether this happens automatically or is controlled by the
  89   * application (as with RTAI) depends on the RTDM host environment.
  90   *
  91   * Environments:
  92   *
  93   * This service can be called from:
  94   *
  95   * - Kernel module initialization/cleanup code
  96   * - Interrupt service routine
  97   * - Kernel-based task
  98   * - User-space task (RT, non-RT)
  99   *
 100   * Rescheduling: never.
 101   */
 102  nanosecs_abs_t rtdm_clock_read_monotonic(void);
 103  #endif /* DOXYGEN_CPP */
 104  /** @} */
 105  
 106  /*!
 107   * @ingroup driverapi
 108   * @defgroup rtdmtask Task Services
 109   * @{
 110   */
 111  
 112  /**
 113   * @brief Intialise and start a real-time task
 114   *
 115   * After initialising a task, the task handle remains valid and can be passed
 116   * to RTDM services until either rtdm_task_destroy() or rtdm_task_join_nrt()
 117   * was invoked.
 118   *
 119   * @param[in,out] task Task handle
 120   * @param[in] name Optional task name
 121   * @param[in] task_proc Procedure to be executed by the task
 122   * @param[in] arg Custom argument passed to @c task_proc() on entry
 123   * @param[in] priority Priority of the task, see also
 124   * @ref taskprio "Task Priority Range"
 125   * @param[in] period Period in nanoseconds of a cyclic task, 0 for non-cyclic
 126   * mode
 127   *
 128   * @return 0 on success, otherwise negative error code
 129   *
 130   * Environments:
 131   *
 132   * This service can be called from:
 133   *
 134   * - Kernel module initialization/cleanup code
 135   * - Kernel-based task
 136   * - User-space task (RT, non-RT)
 137   *
 138   * Rescheduling: possible.
 139   */
 140  int rtdm_task_init_cpuid(rtdm_task_t *task, const char *name,
 141  			 rtdm_task_proc_t task_proc, void *arg,
 142  			 int priority, nanosecs_rel_t period, int cpuid)
 143  {
 144  	char *lname;
 145  	if (rt_task_init_cpuid(task, (void *)task_proc, (long)arg, PAGE_SIZE, priority, 0, 0, cpuid)) {
 146          	return -ENOMEM;
 147  	}
 148  	if (period > 0) {
 149  		rt_task_make_periodic_relative_ns(task, 0, period);
 150  	} else {
 151  		rt_task_resume(task);
 152  	}
 153  	lname = (char *)((unsigned long)name + strlen(name) - 6);
 154  	rt_register(nam2num(lname > name ? lname : name), task, IS_TASK, 0);
 155  	return 0;
 156  }
 157  
 158  
 159  
 160  
 161  
 162  
 163  
 164  
 165  
 166  
 167  
 168  EXPORT_SYMBOL(rtdm_task_init_cpuid);
 169  
 170  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
 171  /**
 172   * @brief Destroy a real-time task
 173   *
 174   * @param[in,out] task Task handle as returned by rtdm_task_init()
 175   *
 176   * @note Passing the same task handle to RTDM services after the completion of
 177   * this function is not allowed.
 178   *
 179   * Environments:
 180   *
 181   * This service can be called from:
 182   *
 183   * - Kernel module initialization/cleanup code
 184   * - Kernel-based task
 185   * - User-space task (RT, non-RT)
 186   *
 187   * Rescheduling: never.
 188   */
 189  void rtdm_task_destroy(rtdm_task_t *task);
 190  
 191  /**
 192   * @brief Adjust real-time task priority
 193   *
 194   * @param[in,out] task Task handle as returned by rtdm_task_init()
 195   * @param[in] priority New priority of the task, see also
 196   * @ref taskprio "Task Priority Range"
 197   *
 198   * Environments:
 199   *
 200   * This service can be called from:
 201   *
 202   * - Kernel module initialization/cleanup code
 203   * - Interrupt service routine
 204   * - Kernel-based task
 205   * - User-space task (RT, non-RT)
 206   *
 207   * Rescheduling: possible.
 208   */
 209  void rtdm_task_set_priority(rtdm_task_t *task, int priority);
 210  
 211  /**
 212   * @brief Adjust real-time task period
 213   *
 214   * @param[in,out] task Task handle as returned by rtdm_task_init()
 215   * @param[in] period New period in nanoseconds of a cyclic task, 0 for
 216   * non-cyclic mode
 217   *
 218   * Environments:
 219   *
 220   * This service can be called from:
 221   *
 222   * - Kernel module initialization/cleanup code
 223   * - Interrupt service routine
 224   * - Kernel-based task
 225   * - User-space task (RT, non-RT)
 226   *
 227   * Rescheduling: possible.
 228   */
 229  int rtdm_task_set_period(rtdm_task_t *task, nanosecs_rel_t period);
 230  
 231  /**
 232   * @brief Wait on next real-time task period
 233   *
 234   * @return 0 on success, otherwise:
 235   *
 236   * - -EINVAL is returned if calling task is not in periodic mode.
 237   *
 238   * - -ETIMEDOUT is returned if a timer overrun occurred, which indicates
 239   * that a previous release point has been missed by the calling task.
 240   *
 241   * Environments:
 242   *
 243   * This service can be called from:
 244   *
 245   * - Kernel-based task
 246   * - User-space task (RT)
 247   *
 248   * Rescheduling: always, unless a timer overrun occured.
 249   */
 250  int rtdm_task_wait_period(void);
 251  
 252  /**
 253   * @brief Activate a blocked real-time task
 254   *
 255   * @return Non-zero is returned if the task was actually unblocked from a
 256   * pending wait state, 0 otherwise.
 257   *
 258   * Environments:
 259   *
 260   * This service can be called from:
 261   *
 262   * - Kernel module initialization/cleanup code
 263   * - Interrupt service routine
 264   * - Kernel-based task
 265   * - User-space task (RT, non-RT)
 266   *
 267   * Rescheduling: possible.
 268   */
 269  int rtdm_task_unblock(rtdm_task_t *task);
 270  
 271  /**
 272   * @brief Get current real-time task
 273   *
 274   * @return Pointer to task handle
 275   *
 276   * Environments:
 277   *
 278   * This service can be called from:
 279   *
 280   * - Kernel-based task
 281   * - User-space task (RT, non-RT)
 282   *
 283   * Rescheduling: never.
 284   */
 285  rtdm_task_t *rtdm_task_current(void);
 286  
 287  /**
 288   * @brief Sleep a specified amount of time
 289   *
 290   * @param[in] delay Delay in nanoseconds, see @ref RTDM_TIMEOUT_xxx for
 291   * special values.
 292   *
 293   * @return 0 on success, otherwise:
 294   *
 295   * - -EINTR is returned if calling task has been unblock by a signal or
 296   * explicitly via rtdm_task_unblock().
 297   *
 298   * - -EPERM @e may be returned if an illegal invocation environment is
 299   * detected.
 300   *
 301   * Environments:
 302   *
 303   * This service can be called from:
 304   *
 305   * - Kernel-based task
 306   * - User-space task (RT)
 307   *
 308   * Rescheduling: always.
 309   */
 310  int rtdm_task_sleep(nanosecs_rel_t delay);
 311  
 312  /**
 313   * @brief Sleep until a specified absolute time
 314   *
 315   * @deprecated Use rtdm_task_sleep_abs instead!
 316   *
 317   * @param[in] wakeup_time Absolute timeout in nanoseconds
 318   *
 319   * @return 0 on success, otherwise:
 320   *
 321   * - -EINTR is returned if calling task has been unblock by a signal or
 322   * explicitly via rtdm_task_unblock().
 323   *
 324   * - -EPERM @e may be returned if an illegal invocation environment is
 325   * detected.
 326   *
 327   * Environments:
 328   *
 329   * This service can be called from:
 330   *
 331   * - Kernel-based task
 332   * - User-space task (RT)
 333   *
 334   * Rescheduling: always, unless the specified time already passed.
 335   */
 336  int rtdm_task_sleep_until(nanosecs_abs_t wakeup_time);
 337  
 338  /**
 339   * @brief Sleep until a specified absolute time
 340   *
 341   * @param[in] wakeup_time Absolute timeout in nanoseconds
 342   * @param[in] mode Selects the timer mode, see RTDM_TIMERMODE_xxx for details
 343   *
 344   * @return 0 on success, otherwise:
 345   *
 346   * - -EINTR is returned if calling task has been unblock by a signal or
 347   * explicitly via rtdm_task_unblock().
 348   *
 349   * - -EPERM @e may be returned if an illegal invocation environment is
 350   * detected.
 351   *
 352   * - -EINVAL is returned if an invalid parameter was passed.
 353   *
 354   * Environments:
 355   *
 356   * This service can be called from:
 357   *
 358   * - Kernel-based task
 359   * - User-space task (RT)
 360   *
 361   * Rescheduling: always, unless the specified time already passed.
 362   */
 363  int rtdm_task_sleep_abs(nanosecs_abs_t wakeup_time, enum rtdm_timer_mode mode);
 364  
 365  #endif /* DOXYGEN_CPP */
 366  
 367  
 368  
 369  
 370  
 371  
 372  
 373  
 374  
 375  
 376  
 377  
 378  
 379  
 380  
 381  
 382  /**
 383   * @brief Wait on a real-time task to terminate
 384   *
 385   * @param[in,out] task Task handle as returned by rtdm_task_init()
 386   * @param[in] poll_delay Delay in milliseconds between periodic tests for the
 387   * state of the real-time task. This parameter is ignored if the termination
 388   * is internally realised without polling.
 389   *
 390   * @note Passing the same task handle to RTDM services after the completion of
 391   * this function is not allowed.
 392   *
 393   * @note This service does not trigger the termination of the targeted task.
 394   * The user has to take of this, otherwise rtdm_task_join_nrt() will never
 395   * return.
 396   *
 397   * Environments:
 398   *
 399   * This service can be called from:
 400   *
 401   * - Kernel module initialization/cleanup code
 402   * - User-space task (non-RT)
 403   *
 404   * Rescheduling: possible.
 405   */
 406  void rtdm_task_join_nrt(rtdm_task_t *task, unsigned int poll_delay)
 407  {
 408  #define JOIN_TIMEOUT 1000 // in millisecs
 409  	int t;
 410  
 411  
 412  	trace_mark(xn_rtdm, task_joinnrt, "thread %p poll_delay %u",
 413  		   task, poll_delay);
 414  
 415  	for (t = 0; task->magic && t < JOIN_TIMEOUT; t += poll_delay) {
 416  		msleep(poll_delay);
 417  	}
 418  	rtdm_task_destroy(task);
 419  }
 420  
 421  
 422  
 423  
 424  
 425  
 426  
 427  
 428  EXPORT_SYMBOL(rtdm_task_join_nrt);
 429  
 430  /**
 431   * @brief Busy-wait a specified amount of time
 432   *
 433   * @param[in] delay Delay in nanoseconds. Note that a zero delay does @b not
 434   * have the meaning of @c RTDM_TIMEOUT_INFINITE here.
 435   *
 436   * @note The caller must not be migratable to different CPUs while executing
 437   * this service. Otherwise, the actual delay will be undefined.
 438   *
 439   * Environments:
 440   *
 441   * This service can be called from:
 442   *
 443   * - Kernel module initialization/cleanup code
 444   * - Interrupt service routine (should be avoided or kept short)
 445   * - Kernel-based task
 446   * - User-space task (RT, non-RT)
 447   *
 448   * Rescheduling: never (except due to external interruptions).
 449   */
 450  void rtdm_task_busy_sleep(nanosecs_rel_t delay)
 451  {
 452          xnticks_t wakeup = rtai_rdtsc() + llimd(delay, tuned.cpu_freq, 1000000000);
 453          while ((xnticks_t)(rtai_rdtsc() - wakeup) < 0)
 454                  cpu_relax();
 455  }
 456  
 457  EXPORT_SYMBOL(rtdm_task_busy_sleep);
 458  /** @} */
 459  
 460  /*!
 461   * @ingroup driverapi
 462   * @defgroup rtdmtimer Timer Services
 463   * @{
 464   */
 465  
 466  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
 467  /**
 468   * @brief Initialise a timer
 469   *
 470   * @param[in,out] timer Timer handle
 471   * @param[in] handler Handler to be called on timer expiry
 472   * @param[in] name Optional timer name
 473   *
 474   * @return 0 on success, otherwise negative error code
 475   *
 476   * Environments:
 477   *
 478   * This service can be called from:
 479   *
 480   * - Kernel module initialization/cleanup code
 481   * - Kernel-based task
 482   * - User-space task (RT, non-RT)
 483   *
 484   * Rescheduling: never.
 485   */
 486  int rtdm_timer_init(rtdm_timer_t *timer, rtdm_timer_handler_t handler,
 487  		    const char *name);
 488  #endif /* DOXYGEN_CPP */
 489  
 490  /**
 491   * @brief Destroy a timer
 492   *
 493   * @param[in,out] timer Timer handle as returned by rtdm_timer_init()
 494   *
 495   * Environments:
 496   *
 497   * This service can be called from:
 498   *
 499   * - Kernel module initialization/cleanup code
 500   * - Kernel-based task
 501   * - User-space task (RT, non-RT)
 502   *
 503   * Rescheduling: never.
 504   */
 505  void rtdm_timer_destroy(rtdm_timer_t *timer)
 506  {
 507  
 508  
 509  
 510  	xntimer_destroy(timer);
 511  
 512  }
 513  
 514  EXPORT_SYMBOL(rtdm_timer_destroy);
 515  
 516  /**
 517   * @brief Start a timer
 518   *
 519   * @param[in,out] timer Timer handle as returned by rtdm_timer_init()
 520   * @param[in] expiry Firing time of the timer, @c mode defines if relative or
 521   * absolute
 522   * @param[in] interval Relative reload value, > 0 if the timer shall work in
 523   * periodic mode with the specific interval, 0 for one-shot timers
 524   * @param[in] mode Defines the operation mode, see @ref RTDM_TIMERMODE_xxx for
 525   * possible values
 526   *
 527   * @return 0 on success, otherwise:
 528   *
 529   * - -ETIMEDOUT is returned if @c expiry describes an absolute date in the
 530   * past.
 531   *
 532   * Environments:
 533   *
 534   * This service can be called from:
 535   *
 536   * - Kernel module initialization/cleanup code
 537   * - Interrupt service routine
 538   * - Kernel-based task
 539   * - User-space task (RT, non-RT)
 540   *
 541   * Rescheduling: never.
 542   */
 543  int rtdm_timer_start(rtdm_timer_t *timer, nanosecs_abs_t expiry,
 544  		     nanosecs_rel_t interval, enum rtdm_timer_mode mode)
 545  {
 546   
 547  	int err;
 548  
 549  
 550  	err = xntimer_start(timer, xntbase_ns2ticks(rtdm_tbase, expiry),
 551  			    xntbase_ns2ticks(rtdm_tbase, interval),
 552  			    (xntmode_t)mode);
 553  
 554  
 555  	return err;
 556  }
 557  
 558  EXPORT_SYMBOL(rtdm_timer_start);
 559  
 560  /**
 561   * @brief Stop a timer
 562   *
 563   * @param[in,out] timer Timer handle as returned by rtdm_timer_init()
 564   *
 565   * Environments:
 566   *
 567   * This service can be called from:
 568   *
 569   * - Kernel module initialization/cleanup code
 570   * - Interrupt service routine
 571   * - Kernel-based task
 572   * - User-space task (RT, non-RT)
 573   *
 574   * Rescheduling: never.
 575   */
 576  void rtdm_timer_stop(rtdm_timer_t *timer)
 577  {
 578   
 579  
 580  
 581  	xntimer_stop(timer);
 582  
 583  }
 584  
 585  EXPORT_SYMBOL(rtdm_timer_stop);
 586  
 587  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
 588  /**
 589   * @brief Start a timer from inside a timer handler
 590   *
 591   * @param[in,out] timer Timer handle as returned by rtdm_timer_init()
 592   * @param[in] expiry Firing time of the timer, @c mode defines if relative or
 593   * absolute
 594   * @param[in] interval Relative reload value, > 0 if the timer shall work in
 595   * periodic mode with the specific interval, 0 for one-shot timers
 596   * @param[in] mode Defines the operation mode, see @ref RTDM_TIMERMODE_xxx for
 597   * possible values
 598   *
 599   * @return 0 on success, otherwise:
 600   *
 601   * - -ETIMEDOUT is returned if @c expiry describes an absolute date in the
 602   * past.
 603   *
 604   * Environments:
 605   *
 606   * This service can be called from:
 607   *
 608   * - Timer handler
 609   *
 610   * Rescheduling: never.
 611   */
 612  int rtdm_timer_start_in_handler(rtdm_timer_t *timer, nanosecs_abs_t expiry,
 613  				nanosecs_rel_t interval,
 614  				enum rtdm_timer_mode mode);
 615  
 616  /**
 617   * @brief Stop a timer from inside a timer handler
 618   *
 619   * @param[in,out] timer Timer handle as returned by rtdm_timer_init()
 620   *
 621   * Environments:
 622   *
 623   * This service can be called from:
 624   *
 625   * - Timer handler
 626   *
 627   * Rescheduling: never.
 628   */
 629  void rtdm_timer_stop_in_handler(rtdm_timer_t *timer);
 630  #endif /* DOXYGEN_CPP */
 631  /** @} */
 632  
 633  /* --- RTAI proper common to events, sems and mtxes --- */
 634  
 635  static inline int _sem_wait(void *sem)
 636  {
 637  	if (rt_sem_wait(sem) < RTE_LOWERR) {
 638  		return 0;
 639  	}
 640  	return _rt_whoami()->unblocked ? -EINTR : -EIDRM;
 641  }
 642  
 643  static inline int _sem_wait_timed(void *sem, nanosecs_rel_t timeout, rtdm_toseq_t *timeout_seq)
 644  {
 645  	int ret;
 646  
 647  	if (timeout < 0) {
 648  		return (ret = rt_sem_wait_if(sem)) > 0 ? 0 : ret != RTE_OBJINV ? -EWOULDBLOCK : -EIDRM;
 649  	}
 650  	if (!timeout) {
 651  		/* infinite timeout */
 652  		ret = rt_sem_wait(sem);
 653  	} else {
 654  		/* timeout sequence, i.e. abs timeout, or relative timeout */
 655  		ret = timeout_seq ? rt_sem_wait_until(sem, *timeout_seq) : rt_sem_wait_timed(sem, nano2count(timeout)); 
 656  	}
 657  	if (ret < RTE_LOWERR) {
 658  		return 0;
 659  	}
 660  	if (ret == SEM_TIMOUT) {
 661  		return -ETIMEDOUT;
 662  	}
 663  	return _rt_whoami()->unblocked ? -EINTR : -EIDRM;
 664  }
 665  
 666  /*!
 667   * @ingroup driverapi
 668   * @defgroup rtdmsync Synchronisation Services
 669   * @{
 670   */
 671  
 672  /*!
 673   * @name Timeout Sequence Management
 674   * @{
 675   */
 676  
 677  /**
 678   * @brief Initialise a timeout sequence
 679   *
 680   * This service initialises a timeout sequence handle according to the given
 681   * timeout value. Timeout sequences allow to maintain a continuous @a timeout
 682   * across multiple calls of blocking synchronisation services. A typical
 683   * application scenario is given below.
 684   *
 685   * @param[in,out] timeout_seq Timeout sequence handle
 686   * @param[in] timeout Relative timeout in nanoseconds, see
 687   * @ref RTDM_TIMEOUT_xxx for special values
 688   *
 689   * Application Scenario:
 690   * @code
 691  int device_service_routine(...)
 692  {
 693  	rtdm_toseq_t timeout_seq;
 694  	...
 695  
 696  	rtdm_toseq_init(&timeout_seq, timeout);
 697  	...
 698  	while (received < requested) {
 699  		ret = rtdm_event_timedwait(&data_available, timeout, &timeout_seq);
 700  		if (ret < 0) // including -ETIMEDOUT
 701  			break;
 702  
 703  		// receive some data
 704  		...
 705  	}
 706  	...
 707  }
 708   * @endcode
 709   * Using a timeout sequence in such a scenario avoids that the user-provided
 710   * relative @c timeout is restarted on every call to rtdm_event_timedwait(),
 711   * potentially causing an overall delay that is larger than specified by
 712   * @c timeout. Moreover, all functions supporting timeout sequences also
 713   * interpret special timeout values (infinite and non-blocking),
 714   * disburdening the driver developer from handling them separately.
 715   *
 716   * RTAI REMARK: 
 717   * This is just a confusing set of words to patch missing APIs with absolute
 718   * deadlines in xenomai. In short a timeout_seq is nothing but the absolute 
 719   * deadline and so should be used in any related RTAI API that does it so
 720   * natively already. Thus the same code in RTAI would simply be: 
 721   * @code
 722  int device_service_routine(...)
 723  {
 724   	...
 725  	while (received < requested) {
 726  		ret = rtdm_event_timedwait(&event->synch_base, rt_get_time()+timeout);
 727  		if (ret < 0) // including -ETIMEDOUT
 728  			break;
 729  		...
 730  		// receive some data
 731  		...
 732  	}
 733  	...
 734  }
 735   * @endcode
 736   * though it is not so because of an easier porting. Nonetheless the RTAI 
 737   * implementation takes care of using its simpler native way anyhow.
 738   * END OF RTAI REMARK.
 739   *
 740   * Environments:
 741   *
 742   * This service can be called from:
 743   *
 744   * - Kernel-based task
 745   * - User-space task (RT)
 746   *
 747   * Rescheduling: never.
 748   */
 749  void rtdm_toseq_init(rtdm_toseq_t *timeout_seq, nanosecs_rel_t timeout)
 750  {
 751  
 752  
 753  
 754  
 755  
 756  	*timeout_seq = rt_get_time() + nano2count(timeout);
 757  }
 758  
 759  EXPORT_SYMBOL(rtdm_toseq_init);
 760  /** @} */
 761  
 762  /*!
 763   * @name Event Services
 764   * @{
 765   */
 766  
 767  /**
 768   * @brief Initialise an event
 769   *
 770   * @param[in,out] event Event handle
 771   * @param[in] pending Non-zero if event shall be initialised as set, 0 otherwise
 772   *
 773   * Environments:
 774   *
 775   * This service can be called from:
 776   *
 777   * - Kernel module initialization/cleanup code
 778   * - Kernel-based task
 779   * - User-space task (RT, non-RT)
 780   *
 781   * Rescheduling: never.
 782   */
 783  void rtdm_event_init(rtdm_event_t *event, unsigned long pending)
 784  {
 785  	spl_t s;
 786  
 787  	trace_mark(xn_rtdm, event_init,
 788  		   "event %p pending %lu", event, pending);
 789  
 790  
 791  
 792  	if (pending)
 793  		event->pending = pending;
 794  	rt_typed_sem_init(&event->synch_base, 0, BIN_SEM | PRIO_Q);
 795  	xnlock_get_irqsave(&nklock, s);
 796  	xnselect_init(&event->select_block);
 797  
 798  	xnlock_put_irqrestore(&nklock, s);
 799  }
 800  
 801  EXPORT_SYMBOL(rtdm_event_init);
 802  
 803  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
 804  /**
 805   * @brief Destroy an event
 806   *
 807   * @param[in,out] event Event handle as returned by rtdm_event_init()
 808   *
 809   * Environments:
 810   *
 811   * This service can be called from:
 812   *
 813   * - Kernel module initialization/cleanup code
 814   * - Kernel-based task
 815   * - User-space task (RT, non-RT)
 816   *
 817   * Rescheduling: possible.
 818   */
 819  void rtdm_event_destroy(rtdm_event_t *event);
 820  
 821  /**
 822   * @brief Signal an event occurrence to currently listening waiters
 823   *
 824   * This function wakes up all current waiters of the given event, but it does
 825   * not change the event state. Subsequently callers of rtdm_event_wait() or
 826   * rtdm_event_timedwait() will therefore be blocked first.
 827   *
 828   * @param[in,out] event Event handle as returned by rtdm_event_init()
 829   *
 830   * Environments:
 831   *
 832   * This service can be called from:
 833   *
 834   * - Kernel module initialization/cleanup code
 835   * - Interrupt service routine
 836   * - Kernel-based task
 837   * - User-space task (RT, non-RT)
 838   *
 839   * Rescheduling: possible.
 840   */
 841  void rtdm_event_pulse(rtdm_event_t *event);
 842  #endif /* DOXYGEN_CPP */
 843  
 844  /**
 845   * @brief Signal an event occurrence
 846   *
 847   * This function sets the given event and wakes up all current waiters. If no
 848   * waiter is presently registered, the next call to rtdm_event_wait() or
 849   * rtdm_event_timedwait() will return immediately.
 850   *
 851   * @param[in,out] event Event handle as returned by rtdm_event_init()
 852   *
 853   * Environments:
 854   *
 855   * This service can be called from:
 856   *
 857   * - Kernel module initialization/cleanup code
 858   * - Interrupt service routine
 859   * - Kernel-based task
 860   * - User-space task (RT, non-RT)
 861   *
 862   * Rescheduling: possible.
 863   */
 864  void rtdm_event_signal(rtdm_event_t *event)
 865  {
 866  	unsigned long flags;
 867  
 868  
 869  	trace_mark(xn_rtdm, event_signal, "event %p", event);
 870  
 871  	flags = rt_global_save_flags_and_cli();
 872  	__set_bit(0, &event->pending);
 873  	rt_sem_broadcast(&event->synch_base);
 874  	rt_global_restore_flags(flags);
 875  	SELECT_SIGNAL(&event->select_block, 1);
 876  }
 877  
 878  
 879  
 880  
 881  
 882  
 883  
 884  EXPORT_SYMBOL(rtdm_event_signal);
 885  
 886  /**
 887   * @brief Wait on event occurrence
 888   *
 889   * This is the light-weight version of rtdm_event_timedwait(), implying an
 890   * infinite timeout.
 891   *
 892   * @param[in,out] event Event handle as returned by rtdm_event_init()
 893   *
 894   * @return 0 on success, otherwise:
 895   *
 896   * - -EINTR is returned if calling task has been unblock by a signal or
 897   * explicitly via rtdm_task_unblock().
 898   *
 899   * - -EIDRM is returned if @a event has been destroyed.
 900   *
 901   * - -EPERM @e may be returned if an illegal invocation environment is
 902   * detected.
 903   *
 904   * Environments:
 905   *
 906   * This service can be called from:
 907   *
 908   * - Kernel-based task
 909   * - User-space task (RT)
 910   *
 911   * Rescheduling: possible.
 912   */
 913  int rtdm_event_wait(rtdm_event_t *event)
 914  {
 915  	unsigned long flags;
 916  	int ret;
 917  
 918  	flags = rt_global_save_flags_and_cli();
 919  	if (!__test_and_clear_bit(0, &event->pending)) {
 920  		if (!(ret = _sem_wait(&event->synch_base))) {
 921  			__clear_bit(0, &event->pending);
 922  		}
 923  	} else {
 924  		ret = 0;
 925  	}
 926  	rt_global_restore_flags(flags);
 927  
 928  	return ret;
 929  }
 930  
 931  EXPORT_SYMBOL(rtdm_event_wait);
 932  
 933  /**
 934   * @brief Wait on event occurrence with timeout
 935   *
 936   * This function waits or tests for the occurence of the given event, taking
 937   * the provided timeout into account. On successful return, the event is
 938   * reset.
 939   *
 940   * @param[in,out] event Event handle as returned by rtdm_event_init()
 941   * @param[in] timeout Relative timeout in nanoseconds, see
 942   * @ref RTDM_TIMEOUT_xxx for special values
 943   * @param[in,out] timeout_seq Handle of a timeout sequence as returned by
 944   * rtdm_toseq_init() or NULL
 945   *
 946   * @return 0 on success, otherwise:
 947   *
 948   * - -ETIMEDOUT is returned if the if the request has not been satisfied
 949   * within the specified amount of time.
 950   *
 951   * - -EINTR is returned if calling task has been unblock by a signal or
 952   * explicitly via rtdm_task_unblock().
 953   *
 954   * - -EIDRM is returned if @a event has been destroyed.
 955   *
 956   * - -EPERM @e may be returned if an illegal invocation environment is
 957   * detected.
 958   *
 959   * - -EWOULDBLOCK is returned if a negative @a timeout (i.e., non-blocking
 960   * operation) has been specified.
 961   *
 962   * Environments:
 963   *
 964   * This service can be called from:
 965   *
 966   * - Kernel-based task
 967   * - User-space task (RT)
 968   *
 969   * Rescheduling: possible.
 970   */
 971  int rtdm_event_timedwait(rtdm_event_t *event, nanosecs_rel_t timeout,
 972  			 rtdm_toseq_t *timeout_seq)
 973  {
 974  	unsigned long flags;
 975  	int ret;
 976  
 977  
 978  
 979  
 980  	trace_mark(xn_rtdm, event_timedwait,
 981  		   "event %p timeout %Lu timeout_seq %p timeout_seq_value %Lu",
 982  		   event, (long long)timeout, timeout_seq, (long long)(timeout_seq ? *timeout_seq : 0));
 983  
 984  	flags = rt_global_save_flags_and_cli();
 985  	if (!__test_and_clear_bit(0, &event->pending)) {
 986  		if (!(ret = _sem_wait_timed(&event->synch_base, timeout, timeout_seq))) {
 987  			__clear_bit(0, &event->pending);
 988  			rt_global_restore_flags(flags);
 989  			SELECT_SIGNAL(&event->select_block, 0);
 990  		} else {
 991  			rt_global_restore_flags(flags);
 992  		}
 993  	} else {
 994  		rt_global_restore_flags(flags);
 995  		SELECT_SIGNAL(&event->select_block, 0);
 996  		ret = 0;
 997  	}
 998  
 999  	return ret;
1000  }
1001  
1002  
1003  
1004  
1005  
1006  
1007  
1008  
1009  
1010  
1011  
1012  
1013  
1014  
1015  
1016  
1017  
1018  
1019  
1020  
1021  
1022  
1023  
1024  
1025  
1026  
1027  
1028  
1029  
1030  
1031  
1032  EXPORT_SYMBOL(rtdm_event_timedwait);
1033  
1034  /**
1035   * @brief Clear event state
1036   *
1037   * @param[in,out] event Event handle as returned by rtdm_event_init()
1038   *
1039   * Environments:
1040   *
1041   * This service can be called from:
1042   *
1043   * - Kernel module initialization/cleanup code
1044   * - Interrupt service routine
1045   * - Kernel-based task
1046   * - User-space task (RT, non-RT)
1047   *
1048   * Rescheduling: never.
1049   */
1050  void rtdm_event_clear(rtdm_event_t *event)
1051  {
1052  
1053  
1054  	trace_mark(xn_rtdm, event_clear, "event %p", event);
1055  
1056  
1057  	event->pending = 0;
1058  	SELECT_SIGNAL(&event->select_block, 0);
1059  }
1060  
1061  
1062  
1063  
1064  EXPORT_SYMBOL(rtdm_event_clear);
1065  
1066  #ifdef CONFIG_RTAI_RTDM_SELECT
1067  /**
1068   * @brief Bind a selector to an event
1069   *
1070   * This functions binds the given selector to an event so that the former is
1071   * notified when the event state changes. Typically the select binding handler
1072   * will invoke this service.
1073   *
1074   * @param[in,out] event Event handle as returned by rtdm_event_init()
1075   * @param[in,out] selector Selector as passed to the select binding handler
1076   * @param[in] type Type of the bound event as passed to the select binding handler
1077   * @param[in] fd_index File descriptor index as passed to the select binding
1078   * handler
1079   *
1080   * @return 0 on success, otherwise:
1081   *
1082   * - -ENOMEM is returned if there is insufficient memory to establish the
1083   * dynamic binding.
1084   *
1085   * - -EINVAL is returned if @a type or @a fd_index are invalid.
1086   *
1087   * Environments:
1088   *
1089   * This service can be called from:
1090   *
1091   * - Kernel module initialization/cleanup code
1092   * - Kernel-based task
1093   * - User-space task (RT, non-RT)
1094   *
1095   * Rescheduling: never.
1096   */
1097  int rtdm_event_select_bind(rtdm_event_t *event, rtdm_selector_t *selector,
1098  			   enum rtdm_selecttype type, unsigned fd_index)
1099  {
1100  	struct xnselect_binding *binding;
1101  	int err;
1102  	spl_t s;
1103  
1104  	binding = xnmalloc(sizeof(*binding));
1105  	if (!binding)
1106  		return -ENOMEM;
1107  
1108  	xnlock_get_irqsave(&nklock, s);
1109  	err = xnselect_bind(&event->select_block,
1110  			    binding, selector, type, fd_index,
1111  			    event->pending || 
1112  			    event->synch_base.magic != RT_SEM_MAGIC);
1113  
1114  	xnlock_put_irqrestore(&nklock, s);
1115  
1116  	if (err)
1117  		xnfree(binding);
1118  
1119  	return err;
1120  }
1121  EXPORT_SYMBOL(rtdm_event_select_bind);
1122  #endif /* CONFIG_RTAI_RTDM_SELECT */
1123  /** @} */
1124  
1125  /*!
1126   * @name Semaphore Services
1127   * @{
1128   */
1129  
1130  /**
1131   * @brief Initialise a semaphore
1132   *
1133   * @param[in,out] sem Semaphore handle
1134   * @param[in] value Initial value of the semaphore
1135   *
1136   * Environments:
1137   *
1138   * This service can be called from:
1139   *
1140   * - Kernel module initialization/cleanup code
1141   * - Kernel-based task
1142   * - User-space task (RT, non-RT)
1143   *
1144   * Rescheduling: never.
1145   */
1146  void rtdm_sem_init(rtdm_sem_t *sem, unsigned long value)
1147  {
1148  	spl_t s;
1149  
1150  	trace_mark(xn_rtdm, sem_init, "sem %p value %lu", sem, value);
1151  
1152  
1153  
1154  
1155  	rt_typed_sem_init(&sem->sem, value, CNT_SEM | PRIO_Q);
1156  
1157  	xnlock_get_irqsave(&nklock, s);
1158  	xnselect_init(&sem->select_block);
1159  	xnlock_put_irqrestore(&nklock, s);
1160  }
1161  
1162  EXPORT_SYMBOL(rtdm_sem_init);
1163  
1164  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
1165  /**
1166   * @brief Destroy a semaphore
1167   *
1168   * @param[in,out] sem Semaphore handle as returned by rtdm_sem_init()
1169   *
1170   * Environments:
1171   *
1172   * This service can be called from:
1173   *
1174   * - Kernel module initialization/cleanup code
1175   * - Kernel-based task
1176   * - User-space task (RT, non-RT)
1177   *
1178   * Rescheduling: possible.
1179   */
1180  void rtdm_sem_destroy(rtdm_sem_t *sem);
1181  #endif /* DOXYGEN_CPP */
1182  
1183  /**
1184   * @brief Decrement a semaphore
1185   *
1186   * This is the light-weight version of rtdm_sem_timeddown(), implying an
1187   * infinite timeout.
1188   *
1189   * @param[in,out] sem Semaphore handle as returned by rtdm_sem_init()
1190   *
1191   * @return 0 on success, otherwise:
1192   *
1193   * - -EINTR is returned if calling task has been unblock by a signal or
1194   * explicitly via rtdm_task_unblock().
1195   *
1196   * - -EIDRM is returned if @a sem has been destroyed.
1197   *
1198   * - -EPERM @e may be returned if an illegal invocation environment is
1199   * detected.
1200   *
1201   * Environments:
1202   *
1203   * This service can be called from:
1204   *
1205   * - Kernel-based task
1206   * - User-space task (RT)
1207   *
1208   * Rescheduling: possible.
1209   */
1210  int rtdm_sem_down(rtdm_sem_t *sem)
1211  {
1212  	return _sem_wait(&sem->sem);
1213  }
1214  
1215  EXPORT_SYMBOL(rtdm_sem_down);
1216  
1217  /**
1218   * @brief Decrement a semaphore with timeout
1219   *
1220   * This function tries to decrement the given semphore's value if it is
1221   * positive on entry. If not, the caller is blocked unless non-blocking
1222   * operation was selected.
1223   *
1224   * @param[in,out] sem Semaphore handle as returned by rtdm_sem_init()
1225   * @param[in] timeout Relative timeout in nanoseconds, see
1226   * @ref RTDM_TIMEOUT_xxx for special values
1227   * @param[in,out] timeout_seq Handle of a timeout sequence as returned by
1228   * rtdm_toseq_init() or NULL
1229   *
1230   * @return 0 on success, otherwise:
1231   *
1232   * - -ETIMEDOUT is returned if the if the request has not been satisfied
1233   * within the specified amount of time.
1234   *
1235   * - -EWOULDBLOCK is returned if @a timeout is negative and the semaphore
1236   * value is currently not positive.
1237   *
1238   * - -EINTR is returned if calling task has been unblock by a signal or
1239   * explicitly via rtdm_task_unblock().
1240   *
1241   * - -EIDRM is returned if @a sem has been destroyed.
1242   *
1243   * - -EPERM @e may be returned if an illegal invocation environment is
1244   * detected.
1245   *
1246   * Environments:
1247   *
1248   * This service can be called from:
1249   *
1250   * - Kernel-based task
1251   * - User-space task (RT)
1252   *
1253   * Rescheduling: possible.
1254   */
1255  int rtdm_sem_timeddown(rtdm_sem_t *sem, nanosecs_rel_t timeout,
1256  		       rtdm_toseq_t *timeout_seq)
1257  {
1258  
1259  	int retval;
1260  
1261  
1262  
1263  
1264  	trace_mark(xn_rtdm, sem_timedwait,
1265  		   "sem %p timeout %Lu timeout_seq %p timeout_seq_value %Lu",
1266  		   sem, (long long)timeout, timeout_seq, (long long)(timeout_seq ? *timeout_seq : 0));
1267  
1268  
1269  
1270  
1271  
1272  
1273  
1274  
1275  
1276  
1277  
1278  
1279  
1280  
1281  
1282  
1283  
1284  
1285  
1286  
1287  
1288  
1289  
1290  
1291  
1292  
1293  
1294  
1295  
1296  
1297  
1298  
1299  
1300  
1301  	if ((retval = _sem_wait_timed(&sem->sem, timeout, timeout_seq)) == 1) {	
1302  		SELECT_SIGNAL(&sem->select_block, 0);
1303  	}
1304  	return retval;
1305  }
1306  
1307  EXPORT_SYMBOL(rtdm_sem_timeddown);
1308  
1309  /**
1310   * @brief Increment a semaphore
1311   *
1312   * This function increments the given semphore's value, waking up a potential
1313   * waiter which was blocked upon rtdm_sem_down().
1314   *
1315   * @param[in,out] sem Semaphore handle as returned by rtdm_sem_init()
1316   *
1317   * Environments:
1318   *
1319   * This service can be called from:
1320   *
1321   * - Kernel module initialization/cleanup code
1322   * - Interrupt service routine
1323   * - Kernel-based task
1324   * - User-space task (RT, non-RT)
1325   *
1326   * Rescheduling: possible.
1327   */
1328  void rtdm_sem_up(rtdm_sem_t *sem)
1329  {
1330  
1331  
1332  	trace_mark(xn_rtdm, sem_up, "sem %p", sem);
1333  
1334  
1335  	rt_sem_signal(&sem->sem);
1336  	if (sem->sem.count > 0) {
1337  		SELECT_SIGNAL(&sem->select_block, 1);
1338  	}
1339  }
1340  
1341  
1342  
1343  
1344  
1345  
1346  EXPORT_SYMBOL(rtdm_sem_up);
1347  
1348  #ifdef CONFIG_RTAI_RTDM_SELECT
1349  /**
1350   * @brief Bind a selector to a semaphore
1351   *
1352   * This functions binds the given selector to the semaphore so that the former
1353   * is notified when the semaphore state changes. Typically the select binding
1354   * handler will invoke this service.
1355   *
1356   * @param[in,out] sem Semaphore handle as returned by rtdm_sem_init()
1357   * @param[in,out] selector Selector as passed to the select binding handler
1358   * @param[in] type Type of the bound event as passed to the select binding handler
1359   * @param[in] fd_index File descriptor index as passed to the select binding
1360   * handler
1361   *
1362   * @return 0 on success, otherwise:
1363   *
1364   * - -ENOMEM is returned if there is insufficient memory to establish the
1365   * dynamic binding.
1366   *
1367   * - -EINVAL is returned if @a type or @a fd_index are invalid.
1368   *
1369   * Environments:
1370   *
1371   * This service can be called from:
1372   *
1373   * - Kernel module initialization/cleanup code
1374   * - Kernel-based task
1375   * - User-space task (RT, non-RT)
1376   *
1377   * Rescheduling: never.
1378   */
1379  int rtdm_sem_select_bind(rtdm_sem_t *sem, rtdm_selector_t *selector,
1380  			 enum rtdm_selecttype type, unsigned fd_index)
1381  {
1382  	struct xnselect_binding *binding;
1383  	int err;
1384  	spl_t s;
1385  
1386  	binding = xnmalloc(sizeof(*binding));
1387  	if (!binding)
1388  		return -ENOMEM;
1389  
1390  	xnlock_get_irqsave(&nklock, s);
1391  	err = xnselect_bind(&sem->select_block, binding, selector,
1392  			    type, fd_index,
1393  			    sem->sem.count > 0 ||
1394  			    sem->sem.magic != RT_SEM_MAGIC);
1395  
1396  	xnlock_put_irqrestore(&nklock, s);
1397  
1398  	if (err)
1399  		xnfree(binding);
1400  
1401  	return err;
1402  }
1403  EXPORT_SYMBOL(rtdm_sem_select_bind);
1404  #endif /* CONFIG_RTAI_RTDM_SELECT */
1405  /** @} */
1406  
1407  /*!
1408   * @name Mutex Services
1409   * @{
1410   */
1411  
1412  /**
1413   * @brief Initialise a mutex
1414   *
1415   * This function initalises a basic mutex with priority inversion protection.
1416   * "Basic", as it does not allow a mutex owner to recursively lock the same
1417   * mutex again.
1418   *
1419   * @param[in,out] mutex Mutex handle
1420   *
1421   * Environments:
1422   *
1423   * This service can be called from:
1424   *
1425   * - Kernel module initialization/cleanup code
1426   * - Kernel-based task
1427   * - User-space task (RT, non-RT)
1428   *
1429   * Rescheduling: never.
1430   */
1431  void rtdm_mutex_init(rtdm_mutex_t *mutex)
1432  {
1433  
1434  
1435  
1436  
1437  
1438  
1439  
1440  
1441  	rt_typed_sem_init(mutex, 1, RES_SEM | PRIO_Q);
1442  }
1443  
1444  EXPORT_SYMBOL(rtdm_mutex_init);
1445  
1446  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
1447  /**
1448   * @brief Destroy a mutex
1449   *
1450   * @param[in,out] mutex Mutex handle as returned by rtdm_mutex_init()
1451   *
1452   * Environments:
1453   *
1454   * This service can be called from:
1455   *
1456   * - Kernel module initialization/cleanup code
1457   * - Kernel-based task
1458   * - User-space task (RT, non-RT)
1459   *
1460   * Rescheduling: possible.
1461   */
1462  void rtdm_mutex_destroy(rtdm_mutex_t *mutex);
1463  
1464  /**
1465   * @brief Release a mutex
1466   *
1467   * This function releases the given mutex, waking up a potential waiter which
1468   * was blocked upon rtdm_mutex_lock() or rtdm_mutex_timedlock().
1469   *
1470   * @param[in,out] mutex Mutex handle as returned by rtdm_mutex_init()
1471   *
1472   * Environments:
1473   *
1474   * This service can be called from:
1475   *
1476   * - Kernel-based task
1477   * - User-space task (RT)
1478   *
1479   * Rescheduling: possible.
1480   */
1481  void rtdm_mutex_unlock(rtdm_mutex_t *mutex);
1482  #endif /* DOXYGEN_CPP */
1483  
1484  /**
1485   * @brief Request a mutex
1486   *
1487   * This is the light-weight version of rtdm_mutex_timedlock(), implying an
1488   * infinite timeout.
1489   *
1490   * @param[in,out] mutex Mutex handle as returned by rtdm_mutex_init()
1491   *
1492   * @return 0 on success, otherwise:
1493   *
1494   * - -EIDRM is returned if @a mutex has been destroyed.
1495   *
1496   * - -EPERM @e may be returned if an illegal invocation environment is
1497   * detected.
1498   *
1499   * Environments:
1500   *
1501   * This service can be called from:
1502   *
1503   * - Kernel-based task
1504   * - User-space task (RT)
1505   *
1506   * Rescheduling: possible.
1507   */
1508  int rtdm_mutex_lock(rtdm_mutex_t *mutex)
1509  {
1510  	int ret; while ((ret = _sem_wait(mutex)) == -EINTR); return ret;
1511  }
1512  
1513  EXPORT_SYMBOL(rtdm_mutex_lock);
1514  
1515  /**
1516   * @brief Request a mutex with timeout
1517   *
1518   * This function tries to acquire the given mutex. If it is not available, the
1519   * caller is blocked unless non-blocking operation was selected.
1520   *
1521   * @param[in,out] mutex Mutex handle as returned by rtdm_mutex_init()
1522   * @param[in] timeout Relative timeout in nanoseconds, see
1523   * @ref RTDM_TIMEOUT_xxx for special values
1524   * @param[in,out] timeout_seq Handle of a timeout sequence as returned by
1525   * rtdm_toseq_init() or NULL
1526   *
1527   * @return 0 on success, otherwise:
1528   *
1529   * - -ETIMEDOUT is returned if the if the request has not been satisfied
1530   * within the specified amount of time.
1531   *
1532   * - -EWOULDBLOCK is returned if @a timeout is negative and the semaphore
1533   * value is currently not positive.
1534   *
1535   * - -EIDRM is returned if @a mutex has been destroyed.
1536   *
1537   * - -EPERM @e may be returned if an illegal invocation environment is
1538   * detected.
1539   *
1540   * Environments:
1541   *
1542   * This service can be called from:
1543   *
1544   * - Kernel-based task
1545   * - User-space task (RT)
1546   *
1547   * Rescheduling: possible.
1548   */
1549  int rtdm_mutex_timedlock(rtdm_mutex_t *mutex, nanosecs_rel_t timeout,
1550  			 rtdm_toseq_t *timeout_seq)
1551  {
1552  	int retval;
1553  
1554  
1555  
1556  	trace_mark(xn_rtdm, mutex_timedlock,
1557  		   "mutex %p timeout %Lu timeout_seq %p timeout_seq_value %Lu",
1558  		   mutex, (long long)timeout, timeout_seq, (long long)(timeout_seq ? *timeout_seq : 0));
1559  
1560  	if (timeout_seq) {
1561  		while((retval = _sem_wait_timed(mutex, timeout, timeout_seq)) == -EINTR);
1562  	} else {
1563  		rtdm_toseq_t until = rt_get_time() + nano2count(timeout);
1564  		while((retval = _sem_wait_timed(mutex, timeout, &until)) == -EINTR);
1565  	}
1566  	return retval;
1567  }
1568  
1569  
1570  
1571  
1572  
1573  
1574  
1575  
1576  
1577  
1578  
1579  
1580  
1581  
1582  
1583  
1584  
1585  
1586  
1587  
1588  
1589  
1590  
1591  
1592  
1593  
1594  
1595  
1596  
1597  
1598  
1599  
1600  
1601  
1602  
1603  
1604  
1605  
1606  
1607  
1608  EXPORT_SYMBOL(rtdm_mutex_timedlock);
1609  /** @} */
1610  
1611  /** @} Synchronisation services */
1612  
1613  /*!
1614   * @ingroup driverapi
1615   * @defgroup rtdmirq Interrupt Management Services
1616   * @{
1617   */
1618  
1619  /**
1620   * @brief Register an interrupt handler
1621   *
1622   * This function registers the provided handler with an IRQ line and enables
1623   * the line.
1624   *
1625   * @param[in,out] irq_handle IRQ handle
1626   * @param[in] irq_no Line number of the addressed IRQ
1627   * @param[in] handler Interrupt handler
1628   * @param[in] flags Registration flags, see @ref RTDM_IRQTYPE_xxx for details
1629   * @param[in] device_name Device name to show up in real-time IRQ lists
1630   * @param[in] arg Pointer to be passed to the interrupt handler on invocation
1631   *
1632   * @return 0 on success, otherwise:
1633   *
1634   * - -EINVAL is returned if an invalid parameter was passed.
1635   *
1636   * - -EBUSY is returned if the specified IRQ line is already in use.
1637   *
1638   * Environments:
1639   *
1640   * This service can be called from:
1641   *
1642   * - Kernel module initialization/cleanup code
1643   * - Kernel-based task
1644   * - User-space task (RT, non-RT)
1645   *
1646   * Rescheduling: never.
1647   */
1648  int rtdm_irq_request(rtdm_irq_t *irq_handle, unsigned int irq_no,
1649  		     rtdm_irq_handler_t handler, unsigned long flags,
1650  		     const char *device_name, void *arg)
1651  {
1652          int err;
1653  
1654          xnintr_init(irq_handle, device_name, irq_no, handler, NULL, flags);
1655  
1656          err = xnintr_attach(irq_handle, arg);
1657          if (err)
1658                  return err;
1659  
1660          err = xnintr_enable(irq_handle);
1661          if (err)
1662                  xnintr_detach(irq_handle);
1663  
1664          return err;
1665  }
1666  
1667  EXPORT_SYMBOL(rtdm_irq_request);
1668  
1669  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
1670  /**
1671   * @brief Release an interrupt handler
1672   *
1673   * @param[in,out] irq_handle IRQ handle as returned by rtdm_irq_request()
1674   *
1675   * @return 0 on success, otherwise negative error code
1676   *
1677   * Environments:
1678   *
1679   * This service can be called from:
1680   *
1681   * - Kernel module initialization/cleanup code
1682   * - Kernel-based task
1683   * - User-space task (RT, non-RT)
1684   *
1685   * Rescheduling: never.
1686   */
1687  int rtdm_irq_free(rtdm_irq_t *irq_handle);
1688  
1689  /**
1690   * @brief Enable interrupt line
1691   *
1692   * @param[in,out] irq_handle IRQ handle as returned by rtdm_irq_request()
1693   *
1694   * @return 0 on success, otherwise negative error code
1695   *
1696   * Environments:
1697   *
1698   * This service can be called from:
1699   *
1700   * - Kernel module initialization/cleanup code
1701   * - Interrupt service routine
1702   * - Kernel-based task
1703   * - User-space task (RT, non-RT)
1704   *
1705   * Rescheduling: possible.
1706   */
1707  int rtdm_irq_enable(rtdm_irq_t *irq_handle);
1708  
1709  /**
1710   * @brief Disable interrupt line
1711   *
1712   * @param[in,out] irq_handle IRQ handle as returned by rtdm_irq_request()
1713   *
1714   * @return 0 on success, otherwise negative error code
1715   *
1716   * Environments:
1717   *
1718   * This service can be called from:
1719   *
1720   * - Kernel module initialization/cleanup code
1721   * - Interrupt service routine
1722   * - Kernel-based task
1723   * - User-space task (RT, non-RT)
1724   *
1725   * Rescheduling: never.
1726   */
1727  int rtdm_irq_disable(rtdm_irq_t *irq_handle);
1728  #endif /* DOXYGEN_CPP */
1729  
1730  /** @} Interrupt Management Services */
1731  
1732  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
1733  
1734  /*!
1735   * @ingroup driverapi
1736   * @defgroup nrtsignal Non-Real-Time Signalling Services
1737   *
1738   * These services provide a mechanism to request the execution of a specified
1739   * handler in non-real-time context. The triggering can safely be performed in
1740   * real-time context without suffering from unknown delays. The handler
1741   * execution will be deferred until the next time the real-time subsystem
1742   * releases the CPU to the non-real-time part.
1743   * @{
1744   */
1745  
1746  /**
1747   * @brief Register a non-real-time signal handler
1748   *
1749   * @param[in,out] nrt_sig Signal handle
1750   * @param[in] handler Non-real-time signal handler
1751   * @param[in] arg Custom argument passed to @c handler() on each invocation
1752   *
1753   * @return 0 on success, otherwise:
1754   *
1755   * - -EAGAIN is returned if no free signal slot is available.
1756   *
1757   * Environments:
1758   *
1759   * This service can be called from:
1760   *
1761   * - Kernel module initialization/cleanup code
1762   * - Kernel-based task
1763   * - User-space task (RT, non-RT)
1764   *
1765   * Rescheduling: never.
1766   */
1767  int rtdm_nrtsig_init(rtdm_nrtsig_t *nrt_sig, rtdm_nrtsig_handler_t handler,
1768  		     void *arg);
1769  
1770  /**
1771   * @brief Release a non-realtime signal handler
1772   *
1773   * @param[in,out] nrt_sig Signal handle
1774   *
1775   * Environments:
1776   *
1777   * This service can be called from:
1778   *
1779   * - Kernel module initialization/cleanup code
1780   * - Kernel-based task
1781   * - User-space task (RT, non-RT)
1782   *
1783   * Rescheduling: never.
1784   */
1785  void rtdm_nrtsig_destroy(rtdm_nrtsig_t *nrt_sig);
1786  
1787  /**
1788   * Trigger non-real-time signal
1789   *
1790   * @param[in,out] nrt_sig Signal handle
1791   *
1792   * Environments:
1793   *
1794   * This service can be called from:
1795   *
1796   * - Kernel module initialization/cleanup code
1797   * - Interrupt service routine
1798   * - Kernel-based task
1799   * - User-space task (RT, non-RT)
1800   *
1801   * Rescheduling: never in real-time context, possible in non-real-time
1802   * environments.
1803   */
1804  void rtdm_nrtsig_pend(rtdm_nrtsig_t *nrt_sig);
1805  /** @} Non-Real-Time Signalling Services */
1806  
1807  #endif /* DOXYGEN_CPP */
1808  
1809  /*!
1810   * @ingroup driverapi
1811   * @defgroup util Utility Services
1812   * @{
1813   */
1814  
1815  
1816  struct rtdm_mmap_data {
1817  	void *src_vaddr;
1818  	phys_addr_t src_paddr;
1819  	struct vm_operations_struct *vm_ops;
1820  	void *vm_private_data;
1821  };
1822  
1823  static int rtdm_mmap_buffer(struct file *filp, struct vm_area_struct *vma)
1824  {
1825  	struct rtdm_mmap_data *mmap_data = filp->private_data;
1826  	unsigned long vaddr, maddr, size;
1827  	phys_addr_t paddr;
1828  	int ret;
1829  
1830  	vma->vm_ops = mmap_data->vm_ops;
1831  	vma->vm_private_data = mmap_data->vm_private_data;
1832  
1833  	vaddr = (unsigned long)mmap_data->src_vaddr;
1834  	paddr = mmap_data->src_paddr;
1835  	if (!paddr)
1836  		/* kmalloc memory */
1837  		paddr = virt_to_phys((void *)vaddr);
1838  
1839  	maddr = vma->vm_start;
1840  	size = vma->vm_end - vma->vm_start;
1841  
1842  #ifdef CONFIG_MMU
1843  	/* Catch vmalloc memory (vaddr is 0 for I/O mapping) */
1844  	if ((vaddr >= VMALLOC_START) && (vaddr < VMALLOC_END)) {
1845  		unsigned long mapped_size = 0;
1846  
1847  		RTAI_ASSERT(RTDM, vaddr == PAGE_ALIGN(vaddr), return -EINVAL);
1848  		RTAI_ASSERT(RTDM, (size % PAGE_SIZE) == 0, return -EINVAL);
1849  
1850  		while (mapped_size < size) {
1851  			if (xnarch_remap_vm_page(vma, maddr, vaddr))
1852  				return -EAGAIN;
1853  
1854  			maddr += PAGE_SIZE;
1855  			vaddr += PAGE_SIZE;
1856  			mapped_size += PAGE_SIZE;
1857  		}
1858  		xnarch_fault_range(vma);
1859  		ret = 0;
1860  	} else
1861  #endif /* CONFIG_MMU */
1862  	if (mmap_data->src_paddr)
1863  		ret = xnarch_remap_io_page_range(filp, vma, maddr, paddr,
1864  						 size, PAGE_SHARED);
1865  	else {
1866  		ret = xnarch_remap_kmem_page_range(vma, maddr, paddr,
1867  						   size, PAGE_SHARED);
1868  		if (!ret)
1869  			xnarch_fault_range(vma);
1870  	}
1871  
1872  	return ret;
1873  }
1874  
1875  static struct file_operations rtdm_mmap_fops = {
1876  	.mmap = rtdm_mmap_buffer,
1877  };
1878  
1879  static int rtdm_do_mmap(rtdm_user_info_t *user_info,
1880  			struct rtdm_mmap_data *mmap_data,
1881  			size_t len, int prot, void **pptr)
1882  {
1883  	struct file *filp;
1884  	const struct file_operations *old_fops;
1885  	void *old_priv_data;
1886  	void *user_ptr;
1887  
1888  	RTAI_ASSERT(RTDM, xnpod_root_p(), return -EPERM;);
1889  
1890  	filp = filp_open("/dev/zero", O_RDWR, 0);
1891  	if (IS_ERR(filp))
1892  		return PTR_ERR(filp);
1893  
1894  	old_fops = filp->f_op;
1895  	filp->f_op = &rtdm_mmap_fops;
1896  
1897  	old_priv_data = filp->private_data;
1898  	filp->private_data = mmap_data;
1899  
1900  	down_write(&user_info->mm->mmap_sem);
1901  //	user_ptr = (void *)do_mmap(filp, (unsigned long)*pptr, len, prot,
1902  //				   MAP_SHARED, 0);
1903  	user_ptr = (void *)vm_mmap(filp, (unsigned long)*pptr, len, prot,
1904  				   MAP_SHARED, 0);
1905  	up_write(&user_info->mm->mmap_sem);
1906  
1907  	filp->f_op = (typeof(filp->f_op))old_fops;
1908  	filp->private_data = old_priv_data;
1909  
1910  	filp_close(filp, user_info->files);
1911  
1912  	if (IS_ERR(user_ptr))
1913  		return PTR_ERR(user_ptr);
1914  
1915  	*pptr = user_ptr;
1916  	return 0;
1917  }
1918  
1919  /**
1920   * Map a kernel memory range into the address space of the user.
1921   *
1922   * @param[in] user_info User information pointer as passed to the invoked
1923   * device operation handler
1924   * @param[in] src_addr Kernel virtual address to be mapped
1925   * @param[in] len Length of the memory range
1926   * @param[in] prot Protection flags for the user's memory range, typically
1927   * either PROT_READ or PROT_READ|PROT_WRITE
1928   * @param[in,out] pptr Address of a pointer containing the desired user
1929   * address or NULL on entry and the finally assigned address on return
1930   * @param[in] vm_ops vm_operations to be executed on the vma_area of the
1931   * user memory range or NULL
1932   * @param[in] vm_private_data Private data to be stored in the vma_area,
1933   * primarily useful for vm_operation handlers
1934   *
1935   * @return 0 on success, otherwise (most common values):
1936   *
1937   * - -EINVAL is returned if an invalid start address, size, or destination
1938   * address was passed.
1939   *
1940   * - -ENOMEM is returned if there is insufficient free memory or the limit of
1941   * memory mapping for the user process was reached.
1942   *
1943   * - -EAGAIN is returned if too much memory has been already locked by the
1944   * user process.
1945   *
1946   * - -EPERM @e may be returned if an illegal invocation environment is
1947   * detected.
1948   *
1949   * @note This service only works on memory regions allocated via kmalloc() or
1950   * vmalloc(). To map physical I/O memory to user-space use
1951   * rtdm_iomap_to_user() instead.
1952   *
1953   * @note RTDM supports two models for unmapping the user memory range again.
1954   * One is explicit unmapping via rtdm_munmap(), either performed when the
1955   * user requests it via an IOCTL etc. or when the related device is closed.
1956   * The other is automatic unmapping, triggered by the user invoking standard
1957   * munmap() or by the termination of the related process. To track release of
1958   * the mapping and therefore relinquishment of the referenced physical memory,
1959   * the caller of rtdm_mmap_to_user() can pass a vm_operations_struct on
1960   * invocation, defining a close handler for the vm_area. See Linux
1961   * documentaion (e.g. Linux Device Drivers book) on virtual memory management
1962   * for details.
1963   *
1964   * Environments:
1965   *
1966   * This service can be called from:
1967   *
1968   * - Kernel module initialization/cleanup code
1969   * - User-space task (non-RT)
1970   *
1971   * Rescheduling: possible.
1972   */
1973  int rtdm_mmap_to_user(rtdm_user_info_t *user_info,
1974  		      void *src_addr, size_t len,
1975  		      int prot, void **pptr,
1976  		      struct vm_operations_struct *vm_ops,
1977  		      void *vm_private_data)
1978  {
1979  	struct rtdm_mmap_data mmap_data =
1980  		{ src_addr, 0, vm_ops, vm_private_data };
1981  
1982  	return rtdm_do_mmap(user_info, &mmap_data, len, prot, pptr);
1983  }
1984  
1985  EXPORT_SYMBOL(rtdm_mmap_to_user);
1986  
1987  /**
1988   * Map an I/O memory range into the address space of the user.
1989   *
1990   * @param[in] user_info User information pointer as passed to the invoked
1991   * device operation handler
1992   * @param[in] src_addr physical I/O address to be mapped
1993   * @param[in] len Length of the memory range
1994   * @param[in] prot Protection flags for the user's memory range, typically
1995   * either PROT_READ or PROT_READ|PROT_WRITE
1996   * @param[in,out] pptr Address of a pointer containing the desired user
1997   * address or NULL on entry and the finally assigned address on return
1998   * @param[in] vm_ops vm_operations to be executed on the vma_area of the
1999   * user memory range or NULL
2000   * @param[in] vm_private_data Private data to be stored in the vma_area,
2001   * primarily useful for vm_operation handlers
2002   *
2003   * @return 0 on success, otherwise (most common values):
2004   *
2005   * - -EINVAL is returned if an invalid start address, size, or destination
2006   * address was passed.
2007   *
2008   * - -ENOMEM is returned if there is insufficient free memory or the limit of
2009   * memory mapping for the user process was reached.
2010   *
2011   * - -EAGAIN is returned if too much memory has been already locked by the
2012   * user process.
2013   *
2014   * - -EPERM @e may be returned if an illegal invocation environment is
2015   * detected.
2016   *
2017   * @note RTDM supports two models for unmapping the user memory range again.
2018   * One is explicit unmapping via rtdm_munmap(), either performed when the
2019   * user requests it via an IOCTL etc. or when the related device is closed.
2020   * The other is automatic unmapping, triggered by the user invoking standard
2021   * munmap() or by the termination of the related process. To track release of
2022   * the mapping and therefore relinquishment of the referenced physical memory,
2023   * the caller of rtdm_iomap_to_user() can pass a vm_operations_struct on
2024   * invocation, defining a close handler for the vm_area. See Linux
2025   * documentaion (e.g. Linux Device Drivers book) on virtual memory management
2026   * for details.
2027   *
2028   * Environments:
2029   *
2030   * This service can be called from:
2031   *
2032   * - Kernel module initialization/cleanup code
2033   * - User-space task (non-RT)
2034   *
2035   * Rescheduling: possible.
2036   */
2037  int rtdm_iomap_to_user(rtdm_user_info_t *user_info,
2038  		       phys_addr_t src_addr, size_t len,
2039  		       int prot, void **pptr,
2040  		       struct vm_operations_struct *vm_ops,
2041  		       void *vm_private_data)
2042  {
2043  	struct rtdm_mmap_data mmap_data =
2044  		{ NULL, src_addr, vm_ops, vm_private_data };
2045  
2046  	return rtdm_do_mmap(user_info, &mmap_data, len, prot, pptr);
2047  }
2048  
2049  EXPORT_SYMBOL(rtdm_iomap_to_user);
2050  
2051  /**
2052   * Unmap a user memory range.
2053   *
2054   * @param[in] user_info User information pointer as passed to
2055   * rtdm_mmap_to_user() when requesting to map the memory range
2056   * @param[in] ptr User address or the memory range
2057   * @param[in] len Length of the memory range
2058   *
2059   * @return 0 on success, otherwise:
2060   *
2061   * - -EINVAL is returned if an invalid address or size was passed.
2062   *
2063   * - -EPERM @e may be returned if an illegal invocation environment is
2064   * detected.
2065   *
2066   * Environments:
2067   *
2068   * This service can be called from:
2069   *
2070   * - Kernel module initialization/cleanup code
2071   * - User-space task (non-RT)
2072   *
2073   * Rescheduling: possible.
2074   */
2075  int rtdm_munmap(rtdm_user_info_t *user_info, void *ptr, size_t len)
2076  {
2077  	int err;
2078  
2079  	RTAI_ASSERT(RTDM, xnpod_root_p(), return -EPERM;);
2080  
2081  	down_write(&user_info->mm->mmap_sem);
2082  	err = do_munmap(user_info->mm, (unsigned long)ptr, len);
2083  	up_write(&user_info->mm->mmap_sem);
2084  
2085  	return err;
2086  }
2087  
2088  EXPORT_SYMBOL(rtdm_munmap);
2089  
2090  
2091  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
2092  
2093  /**
2094   * Real-time safe message printing on kernel console
2095   *
2096   * @param[in] format Format string (conforming standard @c printf())
2097   * @param ... Arguments referred by @a format
2098   *
2099   * @return On success, this service returns the number of characters printed.
2100   * Otherwise, a negative error code is returned.
2101   *
2102   * Environments:
2103   *
2104   * This service can be called from:
2105   *
2106   * - Kernel module initialization/cleanup code
2107   * - Interrupt service routine (consider the overhead!)
2108   * - Kernel-based task
2109   * - User-space task (RT, non-RT)
2110   *
2111   * Rescheduling: never in real-time context, possible in non-real-time
2112   * environments.
2113   */
2114  void rtdm_printk(const char *format, ...);
2115  
2116  /**
2117   * Allocate memory block in real-time context
2118   *
2119   * @param[in] size Requested size of the memory block
2120   *
2121   * @return The pointer to the allocated block is returned on success, NULL
2122   * otherwise.
2123   *
2124   * Environments:
2125   *
2126   * This service can be called from:
2127   *
2128   * - Kernel module initialization/cleanup code
2129   * - Interrupt service routine (consider the overhead!)
2130   * - Kernel-based task
2131   * - User-space task (RT, non-RT)
2132   *
2133   * Rescheduling: never.
2134   */
2135  void *rtdm_malloc(size_t size);
2136  
2137  /**
2138   * Release real-time memory block
2139   *
2140   * @param[in] ptr Pointer to memory block as returned by rtdm_malloc()
2141   *
2142   * Environments:
2143   *
2144   * This service can be called from:
2145   *
2146   * - Kernel module initialization/cleanup code
2147   * - Interrupt service routine (consider the overhead!)
2148   * - Kernel-based task
2149   * - User-space task (RT, non-RT)
2150   *
2151   * Rescheduling: never.
2152   */
2153  void rtdm_free(void *ptr);
2154  
2155  /**
2156   * Check if read access to user-space memory block is safe
2157   *
2158   * @param[in] user_info User information pointer as passed to the invoked
2159   * device operation handler
2160   * @param[in] ptr Address of the user-provided memory block
2161   * @param[in] size Size of the memory block
2162   *
2163   * @return Non-zero is return when it is safe to read from the specified
2164   * memory block, 0 otherwise.
2165   *
2166   * Environments:
2167   *
2168   * This service can be called from:
2169   *
2170   * - Kernel module initialization/cleanup code
2171   * - Kernel-based task
2172   * - User-space task (RT, non-RT)
2173   *
2174   * Rescheduling: never.
2175   */
2176  int rtdm_read_user_ok(rtdm_user_info_t *user_info, const void __user *ptr,
2177  		      size_t size);
2178  
2179  /**
2180   * Check if read/write access to user-space memory block is safe
2181   *
2182   * @param[in] user_info User information pointer as passed to the invoked
2183   * device operation handler
2184   * @param[in] ptr Address of the user-provided memory block
2185   * @param[in] size Size of the memory block
2186   *
2187   * @return Non-zero is return when it is safe to read from or write to the
2188   * specified memory block, 0 otherwise.
2189   *
2190   * Environments:
2191   *
2192   * This service can be called from:
2193   *
2194   * - Kernel module initialization/cleanup code
2195   * - Kernel-based task
2196   * - User-space task (RT, non-RT)
2197   *
2198   * Rescheduling: never.
2199   */
2200  int rtdm_rw_user_ok(rtdm_user_info_t *user_info, const void __user *ptr,
2201  		    size_t size);
2202  
2203  /**
2204   * Copy user-space memory block to specified buffer
2205   *
2206   * @param[in] user_info User information pointer as passed to the invoked
2207   * device operation handler
2208   * @param[in] dst Destination buffer address
2209   * @param[in] src Address of the user-space memory block
2210   * @param[in] size Size of the memory block
2211   *
2212   * @return 0 on success, otherwise:
2213   *
2214   * - -EFAULT is returned if an invalid memory area was accessed.
2215   *
2216   * @note Before invoking this service, verify via rtdm_read_user_ok() that the
2217   * provided user-space address can securely be accessed.
2218   *
2219   * Environments:
2220   *
2221   * This service can be called from:
2222   *
2223   * - Kernel module initialization/cleanup code
2224   * - Kernel-based task
2225   * - User-space task (RT, non-RT)
2226   *
2227   * Rescheduling: never.
2228   */
2229  int rtdm_copy_from_user(rtdm_user_info_t *user_info, void *dst,
2230  			const void __user *src, size_t size);
2231  
2232  /**
2233   * Check if read access to user-space memory block and copy it to specified
2234   * buffer
2235   *
2236   * @param[in] user_info User information pointer as passed to the invoked
2237   * device operation handler
2238   * @param[in] dst Destination buffer address
2239   * @param[in] src Address of the user-space memory block
2240   * @param[in] size Size of the memory block
2241   *
2242   * @return 0 on success, otherwise:
2243   *
2244   * - -EFAULT is returned if an invalid memory area was accessed.
2245   *
2246   * @note This service is a combination of rtdm_read_user_ok and
2247   * rtdm_copy_from_user.
2248   *
2249   * Environments:
2250   *
2251   * This service can be called from:
2252   *
2253   * - Kernel module initialization/cleanup code
2254   * - Kernel-based task
2255   * - User-space task (RT, non-RT)
2256   *
2257   * Rescheduling: never.
2258   */
2259  int rtdm_safe_copy_from_user(rtdm_user_info_t *user_info, void *dst,
2260  			     const void __user *src, size_t size);
2261  
2262  /**
2263   * Copy specified buffer to user-space memory block
2264   *
2265   * @param[in] user_info User information pointer as passed to the invoked
2266   * device operation handler
2267   * @param[in] dst Address of the user-space memory block
2268   * @param[in] src Source buffer address
2269   * @param[in] size Size of the memory block
2270   *
2271   * @return 0 on success, otherwise:
2272   *
2273   * - -EFAULT is returned if an invalid memory area was accessed.
2274   *
2275   * @note Before invoking this service, verify via rtdm_rw_user_ok() that the
2276   * provided user-space address can securely be accessed.
2277   *
2278   * Environments:
2279   *
2280   * This service can be called from:
2281   *
2282   * - Kernel module initialization/cleanup code
2283   * - Kernel-based task
2284   * - User-space task (RT, non-RT)
2285   *
2286   * Rescheduling: never.
2287   */
2288  int rtdm_copy_to_user(rtdm_user_info_t *user_info, void __user *dst,
2289  		      const void *src, size_t size);
2290  
2291  /**
2292   * Check if read/write access to user-space memory block is safe and copy
2293   * specified buffer to it
2294   *
2295   * @param[in] user_info User information pointer as passed to the invoked
2296   * device operation handler
2297   * @param[in] dst Address of the user-space memory block
2298   * @param[in] src Source buffer address
2299   * @param[in] size Size of the memory block
2300   *
2301   * @return 0 on success, otherwise:
2302   *
2303   * - -EFAULT is returned if an invalid memory area was accessed.
2304   *
2305   * @note This service is a combination of rtdm_rw_user_ok and
2306   * rtdm_copy_to_user.
2307   *
2308   * Environments:
2309   *
2310   * This service can be called from:
2311   *
2312   * - Kernel module initialization/cleanup code
2313   * - Kernel-based task
2314   * - User-space task (RT, non-RT)
2315   *
2316   * Rescheduling: never.
2317   */
2318  int rtdm_safe_copy_to_user(rtdm_user_info_t *user_info, void __user *dst,
2319  			   const void *src, size_t size);
2320  
2321  /**
2322   * Copy user-space string to specified buffer
2323   *
2324   * @param[in] user_info User information pointer as passed to the invoked
2325   * device operation handler
2326   * @param[in] dst Destination buffer address
2327   * @param[in] src Address of the user-space string
2328   * @param[in] count Maximum number of bytes to copy, including the trailing
2329   * '0'
2330   *
2331   * @return Length of the string on success (not including the trailing '0'),
2332   * otherwise:
2333   *
2334   * - -EFAULT is returned if an invalid memory area was accessed.
2335   *
2336   * @note This services already includes a check of the source address,
2337   * calling rtdm_read_user_ok() for @a src explicitly is not required.
2338   *
2339   * Environments:
2340   *
2341   * This service can be called from:
2342   *
2343   * - Kernel module initialization/cleanup code
2344   * - Kernel-based task
2345   * - User-space task (RT, non-RT)
2346   *
2347   * Rescheduling: never.
2348   */
2349  int rtdm_strncpy_from_user(rtdm_user_info_t *user_info, char *dst,
2350  			   const char __user *src, size_t count);
2351  
2352  /**
2353   * Test if running in a real-time task
2354   *
2355   * @return Non-zero is returned if the caller resides in real-time context, 0
2356   * otherwise.
2357   *
2358   * Environments:
2359   *
2360   * This service can be called from:
2361   *
2362   * - Kernel module initialization/cleanup code
2363   * - Kernel-based task
2364   * - User-space task (RT, non-RT)
2365   *
2366   * Rescheduling: never.
2367   */
2368  int rtdm_in_rt_context(void);
2369  
2370  #endif /* DOXYGEN_CPP */
2371  
2372  /** @} Utility Services */