69 lines
2 KiB
C
69 lines
2 KiB
C
|
#include "devices/speaker.h"
|
||
|
#include "devices/pit.h"
|
||
|
#include "threads/io.h"
|
||
|
#include "threads/interrupt.h"
|
||
|
#include "devices/timer.h"
|
||
|
|
||
|
/* Speaker port enable I/O register. */
|
||
|
#define SPEAKER_PORT_GATE 0x61
|
||
|
|
||
|
/* Speaker port enable bits. */
|
||
|
#define SPEAKER_GATE_ENABLE 0x03
|
||
|
|
||
|
/* Sets the PC speaker to emit a tone at the given FREQUENCY, in
|
||
|
Hz. */
|
||
|
void
|
||
|
speaker_on (int frequency)
|
||
|
{
|
||
|
if (frequency >= 20 && frequency <= 20000)
|
||
|
{
|
||
|
/* Set the timer channel that's connected to the speaker to
|
||
|
output a square wave at the given FREQUENCY, then
|
||
|
connect the timer channel output to the speaker. */
|
||
|
enum intr_level old_level = intr_disable ();
|
||
|
pit_configure_channel (2, 3, frequency);
|
||
|
outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) | SPEAKER_GATE_ENABLE);
|
||
|
intr_set_level (old_level);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* FREQUENCY is outside the range of normal human hearing.
|
||
|
Just turn off the speaker. */
|
||
|
speaker_off ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Turn off the PC speaker, by disconnecting the timer channel's
|
||
|
output from the speaker. */
|
||
|
void
|
||
|
speaker_off (void)
|
||
|
{
|
||
|
enum intr_level old_level = intr_disable ();
|
||
|
outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) & ~SPEAKER_GATE_ENABLE);
|
||
|
intr_set_level (old_level);
|
||
|
}
|
||
|
|
||
|
/* Briefly beep the PC speaker. */
|
||
|
void
|
||
|
speaker_beep (void)
|
||
|
{
|
||
|
/* Only attempt to beep the speaker if interrupts are enabled,
|
||
|
because we don't want to freeze the machine during the beep.
|
||
|
We could add a hook to the timer interrupt to avoid that
|
||
|
problem, but then we'd risk failing to ever stop the beep if
|
||
|
Pintos crashes for some unrelated reason. There's nothing
|
||
|
more annoying than a machine whose beeping you can't stop
|
||
|
without a power cycle.
|
||
|
|
||
|
We can't just enable interrupts while we sleep. For one
|
||
|
thing, we get called (indirectly) from printf, which should
|
||
|
always work, even during boot before we're ready to enable
|
||
|
interrupts. */
|
||
|
if (intr_get_level () == INTR_ON)
|
||
|
{
|
||
|
speaker_on (440);
|
||
|
timer_msleep (250);
|
||
|
speaker_off ();
|
||
|
}
|
||
|
}
|