/* $Header: /sys/linux-0.96a/include/asm/semop.h,v 0.3 1992/06/01 01:57:34 root Exp root $
 * include/asm/semop.h
 * Copyright 1992 by H. H. Bergman.
 */

#ifndef _ASM_SEMOP_H
#define _ASM_SEMOP_H

#include <sys/types.h>
#include <linux/sched.h>

/* A semaphore is initalised to the number of elements available.
 * ==0 indicates none free, >0 indicates elements are available.
 * When an element is requested (semaP()), and none are available,
 * the requesting process sleeps until there are sufficient elements
 * available.
 * Usually there will be only one element.
 */
typedef struct {
	volatile unsigned int	avail;	/* current number of available elements */
	struct task_struct *waitqueue;	/* sleep list for waiting clients */
} semaphore;


/* The actual P and V routines (refer to E. W. Dijstra's "Cooperating 
 * Sequential Processes" for more information on semaphores) use
 * the 386 XCHG instruction to guarantee exclusive access to the
 * semaphores. Note that the 'standard' P and V routines do not have
 * a 'count' argument. 'Binary semaphores' are like standard semaphores
 * except that they are only allowed to take on the values 0 and 1.
 * These are much more efficient and will usually suffice.
 */

/* usually you will want to do this with an initializer in the
 * declaration.
 */
static void inline sema_init(semaphore * sem, unsigned int count)
{
	sem->waitqueue = NULL;
	sem->avail = count;
} /* sema_init */



/* atomically swap semaphore counter [32 bits].
 * NOTE: cli()/sti() are not needed because of the xchg instruction!
 */
static unsigned int inline sema_atomic_swap(volatile unsigned int * p, unsigned int newval)
{
	unsigned int semval = newval;

	/* Don't know for sure whether the lock prefix is required on all x86.
	 * It is supposed to make the swap an uninterruptible RMW cycle.
	 * Lock prefix is not needed on i486 processors.
	 *
	 * One operand must be in memory, the other in a register, otherwise
	 * the swap may not be atomic. [Better check with gcc -S to make sure]
	 * Hope I got all this asm stuff right...
	 */

	__asm__ volatile ("lock\n"
			"\txchgl %2, %0\n"
			: /* outputs: semval   */ "=a" (semval)
			: /* inputs: newval, p */ "0" (semval), "m" (*p)
			);
	return semval;
} /* sema_atomic_swap */



/* binary semaphore operation P(s) [precisely 1 resource]:
 * {
 *	n := 0;	(* s.avail 0: "busy", >0: "free" *)
 *	do {
 *		< n, s.avail := s.avail, n >;		(* indivisible swap *)
 *		if (n==0) sleep_on(s.queue);
 *	} while (n==0);
 * }
 *
 * binary semaphore operation V(s):
 * {
 *	n := 1;
 *	< n, s.avail := s.avail, n >;
 *	if (n!=0)
 *		error("semaphore was not busy");
 *	else
 *		wake_up(s.queue);
 * }
 */

/* Get exclusive access to the [only one] element guarded by the
 * binary semaphore. This one assumes 0<=sem->avail<=1 at all times.
 */
static void inline bsemaP(semaphore * sem)
{
	unsigned int n;

	n = 0;
	while (1) {
		/* { sem->avail == N && (N==0 || N==1) } */
		n = sema_atomic_swap(&(sem->avail), n);
		/* { n == N && sem->avail == 0 } */
		if (n!=0)
			return;
		else
			sleep_on(&(sem->waitqueue));
	}
} /* bsemaP */


/* release element */
static void inline bsemaV(semaphore * sem)
{
	unsigned int n;

	n = 1;
	/* { sem->avail == 0 } */
	n = sema_atomic_swap(&(sem->avail), n);
	/* { n == 0 && sem->avail == 1 } */
	if (n!=0)
		panic("bsemaV: trying to release free binary semaphore");
	else
		wake_up(&(sem->waitqueue));	/* "Next!" */
} /* bsemaV */


/* generic semaphores are implemented in semop.c with the routines above */
extern void semaP(semaphore * sem, unsigned int rel);
extern void semaV(semaphore * sem, unsigned int rel);


#endif /* _ASM_SEMOP_H */
