/******************************************************************************
 * include/xen/notifier.h
 *
 * Routines to manage notifier chains for passing status changes to any
 * interested routines.
 *
 * Original code from Linux kernel 2.6.27 (Alan Cox <Alan.Cox@linux.org>)
 */
 
#ifndef __XEN_NOTIFIER_H__
#define __XEN_NOTIFIER_H__

#include <xen/types.h>
#include <xen/errno.h>
#include <xen/kernel.h>
#include <xen/list.h>

/*
 * Xen includes only one type of notifier chains inherited from Linux:
 *     Raw notifier chains: There are no restrictions on callbacks,
 *        registration, or unregistration.  All locking and protection
 *        must be provided by the caller.
 */

struct notifier_block {
    int (*notifier_call)(struct notifier_block *, unsigned long, void *);
    struct list_head chain;
    int priority;
};

struct notifier_head {
    struct notifier_block head;
};

#define NOTIFIER_INIT(name) { .head.chain = LIST_HEAD_INIT(name.head.chain) }

#define NOTIFIER_HEAD(name) \
    struct notifier_head name = NOTIFIER_INIT(name)

void notifier_chain_register(
    struct notifier_head *nh, struct notifier_block *nb);
void notifier_chain_unregister(
    struct notifier_head *nh, struct notifier_block *nb);

int notifier_call_chain(
    struct notifier_head *nh, unsigned long val, void *v,
    struct notifier_block **pcursor);

/* Notifier flag values: OR into @val passed to notifier_call_chain(). */
#define NOTIFY_FORWARD 0x0000 /* Call chain highest-priority-first */
#define NOTIFY_REVERSE 0x8000 /* Call chain lowest-priority-first */

/* Handler completion values */
#define NOTIFY_DONE      0x0000
#define NOTIFY_STOP_MASK 0x8000
#define NOTIFY_STOP      (NOTIFY_STOP_MASK|NOTIFY_DONE)
#define NOTIFY_BAD       (NOTIFY_STOP_MASK|EINVAL)

/* Encapsulate (negative) errno value. */
static inline int notifier_from_errno(int err)
{
    return NOTIFY_STOP_MASK | -err;
}

/* Restore (negative) errno value from notify return value. */
static inline int notifier_to_errno(int ret)
{
    return -(ret & ~NOTIFY_STOP_MASK);
}

#endif /* __XEN_NOTIFIER_H__ */
