/* Fast userspace semaphores. 
 * Based on work by Matthew Kirkwood <matthew@hairy.beasts.org>. */
#include "usersem.h"
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
/* This must include the futex call */
#include <asm/unistd.h>

#include ARCH_ATOMICS_C

/* FIXME: Standby code in case no real unistd.h */
#ifndef __NR_futex
#ifdef __PPC
#define __NR_futex		208
#define PROT_SEM		0x08
#else
#define __NR_futex		239
#define PROT_SEM		0x08
#endif
#endif

static _syscall2(int,futex,struct futex *,futex,int, op);

int futex_region(void *area, size_t size)
{
	int pagesize = sysconf(_SC_PAGE_SIZE);

	/* Align pointer to previous start of page */
	area = (void *)((unsigned long)area / pagesize * pagesize);

	/* Round size up to page size */
	size = (size + pagesize - 1) / pagesize * pagesize;

	return mprotect(area, size, PROT_READ|PROT_WRITE|PROT_SEM);
}

void futex_init(struct futex *futx)
{
	futx->count = 1;
	__commit();
}

/* Down and up operations */
int futex_down(struct futex *futx)
{
	if (__down(&futx->count))
		return futex(futx, -1);
	return 0;
}

/* If __up increments count from 0 -> 1, noone was waiting.
   Otherwise, others might be, so enter kernel which sets to 1 and
   wakes them up. */
int futex_up(struct futex *futx)
{
	if (__up(&futx->count))
		return futex(futx, 1);
	return 0;
}
