/*****************************************************************************

 @(#) $Id: ss7_timers.h,v 0.7.2.1 2000/09/07 12:49:16 brian Exp $

 -----------------------------------------------------------------------------

 Copyright (C) 1997, 1998, 1999, 2000  Brian Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program 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 General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 Last Modified $Date: 2000/09/07 12:49:16 $ by $Author: brian $

 -----------------------------------------------------------------------------

 $Log: ss7_timers.h,v $
 Revision 0.7.2.1  2000/09/07 12:49:16  brian
 Got ss7link interface support for drivers compiling.

 Revision 0.7  2000/09/07 11:17:16  brian
 Initial import of OpenSS7 stack.

 Revision 1.1.1.1  2000/09/05 11:00:20  brian
 Initial import of new OpenSS7 stack for Linux.

 *****************************************************************************/

#ifndef __LINUX_SS7_TIMERS_H__
#define __LINUX_SS7_TIMERS_H__

/*
 *  ------------------------------------------------------------------------
 *
 *  Timers
 *
 *      The SS7 timers are implemented using kernel timers.  If you look at
 *      <net/ss7link.h) for the timeout values, you will see that none of
 *      the timeouts need be that terribly accurate.  For linux kernel
 *      timers, the accurracy is a jiffy (1/100th of a second).  This
 *      accuracy is fine.  Most SS7 protocol testers do not implement timer
 *      accuracy greater than 1/10th of a second.  Luckily, there is no
 *      limit on the number of kernel timers that can be allocated at a
 *      point in time.
 *
 *      For timer operation under SS7, it is important that timers which
 *      are already started be able to be started again without
 *      consequence, and further that expired timers be able to the stopped
 *      without consequence.  The following functions ensure that.  When we
 *      start a timer, we make sure that it is stopped previously.  When we
 *      stop a timer, the kernel takes care of whether it has already
 *      expired or not.
 *
 *      Perhaps, however, we should use mutual exclusion when entering the
 *      state machines which have timers set.  This is because, a race
 *      condition may occur between the timeout (which executes in its own
 *      thread) and an ISR action (which executes in its own thread).  The
 *      ISR should be blocked from the state machine until the timer thread
 *      exists?  Or, maybe visa versa (but harder to implement).  The
 *      timeout routine could be shelled and the shell could check to see
 *      whether the timer had been cancelled immediately after it had
 *      fired.
 *
 *      This is important.  I didn't think of this before.  I should check
 *      the timeout functions for critical sections.
 *
 *      Most of the timers are associated with the receive side (with the
 *      exception of T6 and T7 busy timers for transmit).  Blocking the Rx
 *      from the stqte machine would be OK for these timers because the Rx
 *      runs the state machine as a bottom half.  Blocking the Tx from the
 *      state machine could cause problems, unless the Tx is written to
 *      change the state machine invocation to a bottom half, which would
 *      probably be wise.
 *
 *  ------------------------------------------------------------------------
 */

/*
 *  Bottom-Half dispatcher...
 */

static int taskq_overlaps = 0;
static __inline
void
ss7_sm_dispatch(task_queue taskq, void *routine)
{
    if (!taskq->next) {
        taskq->routine = routine;
        queue_task(taskq,&tq_immediate);
        mark_bh(IMMEDIATE_BH);
        return;
    }
    taskq_overlaps++; /* aaarghhhhh! requests are faster than state machine! */
    if (net_ratelimit())
        printk(KERN_ERR "SS7/LINK: %d task queue overlaps: CPU too slow...\n",
                taskq_overlaps);
    return;
}

static void
ss7_timer_timeout(struct ss7if_timer *t)
{
    (void)&ss7_timer_timeout;
    start_bh_atomic();
    /*
     *  FIXME: Still unsatisfactory.  No way to cancel timers.
     */
    queue_task(&t->taskq,&tq_immediate);
    mark_bh(IMMEDIATE_BH);
    end_bh_atomic();
}

static __inline void
ss7if_timer_stop(struct ss7_iface *l, int n)
{
    struct ss7if_timer *t = &l->timers[n];
    struct timer_list *k = &t->kernel_timer;
    unsigned long flags;

    save_flags(flags); cli();
    {
        if (k->next||k->prev) del_timer(k);
    }
    restore_flags(flags);
}

static __inline void
ss7if_timer_start(struct ss7_iface *l, int n, void (*cb)(struct ss7_iface *l))
{
    struct ss7if_timer *t = &l->timers[n];
    struct timer_list *k = &t->kernel_timer;

    unsigned long flags;
    save_flags(flags); cli();
    {
        if (k->next||k->prev) del_timer(k);
        t->taskq.routine = (void *)cb;
        k->expires = jiffies + t->duration;
        add_timer(k);
    }
    restore_flags(flags);
}

static __inline void
ss7if_timer_set_duration(struct ss7_iface *l, int n, int d)
{
    struct ss7if_timer *t = &l->timers[n];

    t->duration = d;
}

static __inline void
ss7if_timer_start_duration(struct ss7_iface *l, int n, int d,
        void (*cb)(struct ss7_iface *l))
{
    ss7if_timer_set_duration(l,n,d);
    ss7if_timer_start(l,n,cb);
}

#endif  __LINUX_SS7_TIMERS_H__
