| /****************************************************************************** | |
| * | |
| * (C)Copyright 1998,1999 SysKonnect, | |
| * a business unit of Schneider & Koch & Co. Datensysteme GmbH. | |
| * | |
| * See the file "skfddi.c" for further information. | |
| * | |
| * 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. | |
| * | |
| * The information in this file is provided "AS IS" without warranty. | |
| * | |
| ******************************************************************************/ | |
| /* | |
| SMT timer | |
| */ | |
| #include "h/types.h" | |
| #include "h/fddi.h" | |
| #include "h/smc.h" | |
| #ifndef lint | |
| static const char ID_sccs[] = "@(#)smttimer.c 2.4 97/08/04 (C) SK " ; | |
| #endif | |
| static void timer_done(struct s_smc *smc, int restart); | |
| void smt_timer_init(struct s_smc *smc) | |
| { | |
| smc->t.st_queue = NULL; | |
| smc->t.st_fast.tm_active = FALSE ; | |
| smc->t.st_fast.tm_next = NULL; | |
| hwt_init(smc) ; | |
| } | |
| void smt_timer_stop(struct s_smc *smc, struct smt_timer *timer) | |
| { | |
| struct smt_timer **prev ; | |
| struct smt_timer *tm ; | |
| /* | |
| * remove timer from queue | |
| */ | |
| timer->tm_active = FALSE ; | |
| if (smc->t.st_queue == timer && !timer->tm_next) { | |
| hwt_stop(smc) ; | |
| } | |
| for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) { | |
| if (tm == timer) { | |
| *prev = tm->tm_next ; | |
| if (tm->tm_next) { | |
| tm->tm_next->tm_delta += tm->tm_delta ; | |
| } | |
| return ; | |
| } | |
| } | |
| } | |
| void smt_timer_start(struct s_smc *smc, struct smt_timer *timer, u_long time, | |
| u_long token) | |
| { | |
| struct smt_timer **prev ; | |
| struct smt_timer *tm ; | |
| u_long delta = 0 ; | |
| time /= 16 ; /* input is uS, clock ticks are 16uS */ | |
| if (!time) | |
| time = 1 ; | |
| smt_timer_stop(smc,timer) ; | |
| timer->tm_smc = smc ; | |
| timer->tm_token = token ; | |
| timer->tm_active = TRUE ; | |
| if (!smc->t.st_queue) { | |
| smc->t.st_queue = timer ; | |
| timer->tm_next = NULL; | |
| timer->tm_delta = time ; | |
| hwt_start(smc,time) ; | |
| return ; | |
| } | |
| /* | |
| * timer correction | |
| */ | |
| timer_done(smc,0) ; | |
| /* | |
| * find position in queue | |
| */ | |
| delta = 0 ; | |
| for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) { | |
| if (delta + tm->tm_delta > time) { | |
| break ; | |
| } | |
| delta += tm->tm_delta ; | |
| } | |
| /* insert in queue */ | |
| *prev = timer ; | |
| timer->tm_next = tm ; | |
| timer->tm_delta = time - delta ; | |
| if (tm) | |
| tm->tm_delta -= timer->tm_delta ; | |
| /* | |
| * start new with first | |
| */ | |
| hwt_start(smc,smc->t.st_queue->tm_delta) ; | |
| } | |
| void smt_force_irq(struct s_smc *smc) | |
| { | |
| smt_timer_start(smc,&smc->t.st_fast,32L, EV_TOKEN(EVENT_SMT,SM_FAST)); | |
| } | |
| void smt_timer_done(struct s_smc *smc) | |
| { | |
| timer_done(smc,1) ; | |
| } | |
| static void timer_done(struct s_smc *smc, int restart) | |
| { | |
| u_long delta ; | |
| struct smt_timer *tm ; | |
| struct smt_timer *next ; | |
| struct smt_timer **last ; | |
| int done = 0 ; | |
| delta = hwt_read(smc) ; | |
| last = &smc->t.st_queue ; | |
| tm = smc->t.st_queue ; | |
| while (tm && !done) { | |
| if (delta >= tm->tm_delta) { | |
| tm->tm_active = FALSE ; | |
| delta -= tm->tm_delta ; | |
| last = &tm->tm_next ; | |
| tm = tm->tm_next ; | |
| } | |
| else { | |
| tm->tm_delta -= delta ; | |
| delta = 0 ; | |
| done = 1 ; | |
| } | |
| } | |
| *last = NULL; | |
| next = smc->t.st_queue ; | |
| smc->t.st_queue = tm ; | |
| for ( tm = next ; tm ; tm = next) { | |
| next = tm->tm_next ; | |
| timer_event(smc,tm->tm_token) ; | |
| } | |
| if (restart && smc->t.st_queue) | |
| hwt_start(smc,smc->t.st_queue->tm_delta) ; | |
| } | |