View of xos/drivers/timer.c


XOS | Parent Directory | View | Download

/* Pilote pour le timer Intel 8253/8254 Programmable Interval Timer */
/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   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 3 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, see <http://www.gnu.org/licenses/>. */
 
#include <alarm_struct.h>
#include <proc.h>
#include <printk.h>
#include <asm.h>
#include <config.h>
#include <stddef.h>
 
#define DIVISOR (1193180 / TIMER_FREQUENCY)
 
static volatile unsigned long ticks;
static struct alarm_struct *first_alarm;
 
/* Alarmes */
 
static void insert_alarm(struct alarm_struct *alarm)
{
  struct alarm_struct *alm1, *alm2;
 
  alm1 = NULL;
  alm2 = first_alarm;
  while (alm2 && alm2->time <= alarm->time)
    alm1 = alm2, alm2 = alm2->next;
  alarm->previous = alm1;
  alarm->next = alm2;
  if (alm1)
    alm1->next = alarm;
  else
    first_alarm = alarm;
  if (alm2)
    alm2->previous = alarm;
  alarm->active = 1;
}
 
static void remove_alarm(struct alarm_struct *alarm)
{
  if (alarm->previous)
    alarm->previous->next = alarm->next;
  else
    first_alarm = alarm->next;
  if (alarm->next)
    alarm->next->previous = alarm->previous;
  alarm->active = 0;
}
 
/* Interface */
 
void timer_init()
{
  ticks = 0;
  first_alarm = NULL;
  outbp(0x34, 0x43);            /* Compteur 0, LSB/MSB, mode 2, binaire */
  outb(DIVISOR & 0xff, 0x40);   /* LSB */
  outb(DIVISOR >> 8, 0x40);     /* MSB */
}
 
void timer_print_info()
{
  printk("Timer: Intel 8254 Programmable Interval Timer\n");
  printk("Timer frequency set to %dHz.\n", TIMER_FREQUENCY);
}
 
void do_timer_interrupt(const struct interrupted_instruction_struct *interrupted_instruction)
{
  struct alarm_struct *alm;
 
  /* le temps passe */
  ticks++;
 
  /* alarmes */
  while (first_alarm && first_alarm->time <= ticks) {
    alm = first_alarm;
    remove_alarm(alm);
    alm->handler(alm);
  }
 
  /* ordonnancement des processus */
  preempt(interrupted_instruction); /* doit etre la derniere instruction du gestionnaire d'interruption !!! */
}
 
void alarm_init(struct alarm_struct *alarm, void (*handler)(struct alarm_struct *))
{
  alarm->handler = handler;
  alarm->active = 0;
  /* Inutile d'itinialiser les autres membres. */
}
 
void alarm_set(struct alarm_struct *alarm, unsigned long milliseconds)
{
  int intr;
 
  disable_intr(intr);
  if (alarm->active)
    remove_alarm(alarm);
  alarm->time = ticks + milliseconds * TIMER_FREQUENCY / 1000;
  insert_alarm(alarm);
  restore_intr(intr);
}
 
unsigned long alarm_unset(struct alarm_struct *alarm)
{
  int intr;
  unsigned long left;
 
  disable_intr(intr);
  if (alarm->active) {
    left = (alarm->time - ticks) * 1000 / TIMER_FREQUENCY;
    remove_alarm(alarm);
  }
  else
    left = 0;
  restore_intr(intr);
  return left;
}