/*
 * Copyright (C) 2014 Philippe Gerum <rpm@xenomai.org>.
 *
 * 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.
 */
#ifndef _BOILERPLATE_LIBC_H
#define _BOILERPLATE_LIBC_H

#include <limits.h>

#ifdef __IN_XENO__
/*
 * Quirks for dealing with outdated libc* issues.  This header will be
 * parsed by the Xenomai implementation only, applications based on it
 * have to provide their own set of wrappers as they should decide by
 * themselves what to do when a feature is missing.
 */
#include <xeno_config.h>
#include <errno.h>
#include <boilerplate/compiler.h>

#if !HAVE_DECL_PTHREAD_PRIO_NONE
enum {
	PTHREAD_PRIO_NONE,
	PTHREAD_PRIO_INHERIT,
	PTHREAD_PRIO_PROTECT
};
#endif /* !HAVE_DECL_PTHREAD_PRIO_NONE */

#ifndef HAVE_FORK
static inline int fork(void)
{
	errno = ENOSYS;
	return -1;
}
#endif

#ifndef HAVE_PTHREAD_ATFORK
#ifndef HAVE_FORK
static inline
int pthread_atfork(void (*prepare)(void), void (*parent)(void),
		   void (*child)(void))
{
	return 0;
}
#else
#error "fork() without pthread_atfork()"
#endif
#endif /* !HAVE_PTHREAD_ATFORK */

#ifndef HAVE_PTHREAD_GETATTR_NP
static inline
int pthread_getattr_np(pthread_t th, pthread_attr_t *attr)
{
	return ENOSYS;
}
#endif /* !HAVE_PTHREAD_GETATTR_NP */

#ifndef HAVE_PTHREAD_CONDATTR_SETCLOCK
static inline
int pthread_condattr_setclock(pthread_condattr_t *__restrict__ attr,
			      clockid_t clock_id)
{
	return clock_id == CLOCK_REALTIME ? 0 : ENOSYS;
}
#endif	/* !HAVE_PTHREAD_CONDATTR_SETCLOCK */

#ifndef HAVE_PTHREAD_CONDATTR_GETCLOCK
static inline
int pthread_condattr_getclock(const pthread_condattr_t *__restrict__ attr,
			      clockid_t *__restrict__ clock_id)
{
	*clock_id = CLOCK_REALTIME;

	return 0;
}
#endif	/* !HAVE_PTHREAD_CONDATTR_GETCLOCK */

#ifndef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL
static inline
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *__restrict__ attr,
				  int protocol)
{
	return protocol == PTHREAD_PRIO_NONE ? 0 : ENOSYS;
}
#endif /* !HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL */

#ifndef HAVE_PTHREAD_MUTEXATTR_GETPROTOCOL
static inline
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *
				  __restrict__ attr, int *__restrict__ protocol)
{
	*protocol = PTHREAD_PRIO_NONE;

	return 0;
}
#endif /* !HAVE_PTHREAD_MUTEXATTR_GETPROTOCOL */

#ifndef HAVE_PTHREAD_MUTEXATTR_SETPRIOCEILING
static inline
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr,
				     int prioceiling)
{
	return ENOSYS;
}
#endif /* !HAVE_PTHREAD_MUTEXATTR_SETPRIOCEILING */

#ifndef HAVE_PTHREAD_MUTEXATTR_GETPRIOCEILING
static inline
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *
				      __restrict attr,
				     int *__restrict prioceiling)
{
	return ENOSYS;
}
#endif /* !HAVE_PTHREAD_MUTEXATTR_GETPRIOCEILING */

#ifndef HAVE_PTHREAD_MUTEX_SETPRIOCEILING
static inline
int pthread_mutex_setprioceiling(pthread_mutex_t *__restrict attr,
				 int prioceiling,
				 int *__restrict old_ceiling)
{
	return ENOSYS;
}
#endif /* !HAVE_PTHREAD_MUTEXATTR_SETPRIOCEILING */

#ifndef HAVE_PTHREAD_MUTEX_GETPRIOCEILING
static inline
int pthread_mutex_getprioceiling(pthread_mutex_t *__restrict attr,
				 int *__restrict prioceiling)
{
	return ENOSYS;
}
#endif /* !HAVE_PTHREAD_MUTEXATTR_GETPRIOCEILING */

#ifndef HAVE_PTHREAD_ATTR_SETAFFINITY_NP
#include <sched.h>
static inline
int pthread_attr_setaffinity_np(pthread_attr_t *attr,
				size_t cpusetsize, const cpu_set_t *cpuset)
{
	if (CPU_ISSET(0, cpuset) && CPU_COUNT(cpuset) == 1)
		return 0;
	return ENOSYS;
}
#endif /* !HAVE_PTHREAD_ATTR_SETAFFINITY_NP */

#ifndef HAVE_PTHREAD_SETAFFINITY_NP
static inline
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
			   const cpu_set_t *cpuset)
{
	if (CPU_ISSET(0, cpuset) && CPU_COUNT(cpuset) == 1)
		return 0;
	return ENOSYS;
}
#endif /* !HAVE_PTHREAD_SETAFFINITY_NP */

#ifndef HAVE_PTHREAD_SETSCHEDPRIO

static inline
int pthread_setschedprio(pthread_t thread, int prio)
{
	struct sched_param param;
	int policy, ret;

	ret = pthread_getschedparam(thread, &policy, &param);
	if (ret)
		return ret;

	param.sched_priority = prio;

	return pthread_setschedparam(thread, policy, &param);
}

#endif /* !HAVE_PTHREAD_SETSCHEDPRIO */

#if !defined(HAVE_CLOCK_NANOSLEEP) && defined(CONFIG_XENO_MERCURY)
/*
 * Best effort for a Mercury setup based on an outdated libc lacking
 * "advanced" real-time support.  Too bad if the system clock is set
 * during sleep time. This is a non-issue for Cobalt, as the libcobalt
 * implementation will always be picked instead.
 */
__weak int clock_nanosleep(clockid_t clock_id, int flags,
			   const struct timespec *request,
			   struct timespec *remain)
{
	struct timespec now, tmp;

	tmp = *request;
	if (flags) {
		clock_gettime(CLOCK_REALTIME, &now);
		tmp.tv_sec -= now.tv_sec;
		tmp.tv_nsec -= now.tv_nsec;
		if (tmp.tv_nsec < 0) {
			tmp.tv_sec--;
			tmp.tv_nsec += 1000000000;
		}
	}

	return nanosleep(&tmp, remain);
}
#endif /* !HAVE_CLOCK_NANOSLEEP && MERCURY */

#ifndef HAVE_SCHED_GETCPU
/*
 * Might be declared in uClibc headers but not actually implemented,
 * so we make the placeholder a weak symbol.
 */
__weak int sched_getcpu(void)
{
	return 0;   /* outdated uClibc: assume uniprocessor. */
}
#endif /* !HAVE_SCHED_GETCPU */

#ifndef HAVE_SHM_OPEN
__weak int shm_open(const char *name, int oflag, mode_t mode)
{
	errno = ENOSYS;
	return -1;
}
#endif	/* !HAVE_SHM_OPEN */

#ifndef HAVE_SHM_UNLINK
__weak int shm_unlink(const char *name)
{
	errno = ENOSYS;
	return -1;
}
#endif	/* !HAVE_SHM_UNLINK */

#ifndef HAVE_PTHREAD_MUTEXATTR_SETROBUST
#ifdef HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP
#define pthread_mutexattr_setrobust	pthread_mutexattr_setrobust_np
#else
static inline
int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr,
				int robustness)
{
	return ENOSYS;
}
#endif /* !HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP */
#endif /* !HAVE_PTHREAD_MUTEXATTR_SETROBUST */

#if !defined(HAVE_PTHREAD_SETNAME_NP) && defined(CONFIG_XENO_MERCURY)
static inline
int pthread_setname_np(pthread_t thread, const char *name)
{
	return ENOSYS;
}
#endif /* !HAVE_PTHREAD_SETNAME_NP && MERCURY */

#endif /* __IN_XENO__ */

#if defined(__COBALT_WRAP__) || defined(__IN_XENO__)
/*
 * clock_nanosleep() and pthread_setname_np() must be declared when the libc
 * does not declare them, both for compiling xenomai, and for compiling
 * applications wrapping these symbols to the libcobalt versions.
 */
#ifndef HAVE_CLOCK_NANOSLEEP
int clock_nanosleep(clockid_t clock_id, int flags,
		    const struct timespec *request,
		    struct timespec *remain);
#endif /* !HAVE_CLOCK_NANOSLEEP */

#ifndef HAVE_PTHREAD_SETNAME_NP
int pthread_setname_np(pthread_t thread, const char *name);
#endif /* !HAVE_PTHREAD_SETNAME_NP */
#endif /* __COBALT_WRAP__ || __IN_XENO__ */

#ifndef PTHREAD_STACK_DEFAULT
#define PTHREAD_STACK_DEFAULT			\
	({					\
		int __ret = PTHREAD_STACK_MIN;	\
		if (__ret < 65536)		\
			__ret = 65536;		\
		__ret;				\
	})
#endif /* !PTHREAD_STACK_DEFAULT */

#endif /* _BOILERPLATE_LIBC_H */
