Add PC speaker driver and connect it to '\a' in the VGA console.
[pintos-anon] / src / devices / pit.c
1 #include "devices/pit.h"
2 #include <debug.h>
3 #include <stdint.h>
4 #include "threads/interrupt.h"
5 #include "threads/io.h"
6
7 /* Interface to 8254 Programmable Interrupt Timer (PIT).
8    Refer to [8254] for details. */
9
10 /* 8254 registers. */
11 #define PIT_PORT_CONTROL          0x43                /* Control port. */
12 #define PIT_PORT_COUNTER(CHANNEL) (0x40 + (CHANNEL))  /* Counter port. */
13
14 /* PIT cycles per second. */
15 #define PIT_HZ 1193180
16
17 /* Configure the given CHANNEL in the PIT.  In a PC, the PIT's
18    three output channels are hooked up like this:
19
20      - Channel 0 is connected to interrupt line 0, so that it can
21        be used as a periodic timer interrupt, as implemented in
22        Pintos in devices/timer.c.
23
24      - Channel 1 is used for dynamic RAM refresh (in older PCs).
25        No good can come of messing with this.
26
27      - Channel 2 is connected to the PC speaker, so that it can
28        be used to play a tone, as implemented in Pintos in
29        devices/speaker.c.
30
31    MODE specifies the form of output:
32
33      - Mode 2 is a periodic pulse: the channel's output is 1 for
34        most of the period, but drops to 0 briefly toward the end
35        of the period.  This is useful for hooking up to an
36        interrupt controller to generate a periodic interrupt.
37
38      - Mode 3 is a square wave: for the first half of the period
39        it is 1, for the second half it is 0.  This is useful for
40        generating a tone on a speaker.
41
42      - Other modes are less useful.
43
44    FREQUENCY is the number of periods per second, in Hz. */
45 void
46 pit_configure_channel (int channel, int mode, int frequency)
47 {
48   uint16_t count;
49   enum intr_level old_level;
50
51   ASSERT (channel == 0 || channel == 2);
52   ASSERT (mode == 2 || mode == 3);
53
54   /* Convert FREQUENCY to a PIT counter value.  The PIT has a
55      clock that runs at PIT_HZ cycles per second.  We must
56      translate FREQUENCY into a number of these cycles. */
57   if (frequency < 19)
58     {
59       /* Frequency is too low: the quotient would overflow the
60          16-bit counter.  Force it to 0, which the PIT treats as
61          65536, the highest possible count.  This yields a 18.2
62          Hz timer, approximately. */
63       count = 0;
64     }
65   else if (frequency > PIT_HZ)
66     {
67       /* Frequency is too high: the quotient would underflow to
68          0, which the PIT would interpret as 65536.  A count of 1
69          is illegal in mode 2, so we force it to 2, which yields
70          a 596.590 kHz timer, approximately.  (This timer rate is
71          probably too fast to be useful anyhow.) */
72       count = 2;
73     }
74   else
75     count = (PIT_HZ + frequency / 2) / frequency;
76
77   /* Configure the PIT mode and load its counters. */
78   old_level = intr_disable ();
79   outb (PIT_PORT_CONTROL, (channel << 6) | 0x30 | (mode << 1));
80   outb (PIT_PORT_COUNTER (channel), count);
81   outb (PIT_PORT_COUNTER (channel), count >> 8);
82   intr_set_level (old_level);
83 }