xref: /reactos/hal/halx86/generic/beep.c (revision b36d9bd9)
1 /*
2  * PROJECT:         ReactOS HAL
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            hal/halx86/generic/beep.c
5  * PURPOSE:         Speaker support (beeping)
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <hal.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* FUNCTIONS *****************************************************************/
16 
17 /*
18  * @implemented
19  */
20 BOOLEAN
21 NTAPI
22 HalMakeBeep(IN ULONG Frequency)
23 {
24     SYSTEM_CONTROL_PORT_B_REGISTER SystemControl;
25     TIMER_CONTROL_PORT_REGISTER TimerControl;
26     ULONG Divider;
27     BOOLEAN Result = FALSE;
28 
29     //
30     // Acquire CMOS Lock
31     //
32     HalpAcquireCmosSpinLock();
33 
34     //
35     // Turn the timer off by disconnecting its output pin and speaker gate
36     //
37     SystemControl.Bits = __inbyte(SYSTEM_CONTROL_PORT_B);
38     SystemControl.SpeakerDataEnable = FALSE;
39     SystemControl.Timer2GateToSpeaker = FALSE;
40     __outbyte(SYSTEM_CONTROL_PORT_B, SystemControl.Bits);
41 
42     //
43     // Check if we have a frequency
44     //
45     if (Frequency)
46     {
47         //
48         // Set the divider
49         //
50         Divider = PIT_FREQUENCY / Frequency;
51 
52         //
53         // Check if it's too large
54         //
55         if (Divider <= 0x10000)
56         {
57             //
58             // Program the PIT for binary mode
59             //
60             TimerControl.BcdMode = FALSE;
61 
62             //
63             // Program the PIT to generate a square wave (Mode 3) on channel 2.
64             // Channel 0 is used for the IRQ0 clock interval timer, and channel
65             // 1 is used for DRAM refresh.
66             //
67             // Mode 2 gives much better accuracy, but generates an output signal
68             // that drops to low for each input signal cycle at 0.8381 useconds.
69             // This is too fast for the PC speaker to process and would result
70             // in no sound being emitted.
71             //
72             // Mode 3 will generate a high pulse that is a bit longer and will
73             // allow the PC speaker to notice. Additionally, take note that on
74             // channel 2, when input goes low the counter will stop and output
75             // will go to high.
76             //
77             TimerControl.OperatingMode = PitOperatingMode3;
78             TimerControl.Channel = PitChannel2;
79 
80             //
81             // Set the access mode that we'll use to program the reload value.
82             //
83             TimerControl.AccessMode = PitAccessModeLowHigh;
84 
85             //
86             // Now write the programming bits
87             //
88             __outbyte(TIMER_CONTROL_PORT, TimerControl.Bits);
89 
90             //
91             // Next we write the reload value for channel 2
92             //
93             __outbyte(TIMER_CHANNEL2_DATA_PORT, Divider & 0xFF);
94             __outbyte(TIMER_CHANNEL2_DATA_PORT, (Divider >> 8) & 0xFF);
95 
96             //
97             // Reconnect the speaker to the timer and re-enable the output pin
98             //
99             SystemControl.Bits = __inbyte(SYSTEM_CONTROL_PORT_B);
100             SystemControl.SpeakerDataEnable = TRUE;
101             SystemControl.Timer2GateToSpeaker = TRUE;
102             __outbyte(SYSTEM_CONTROL_PORT_B, SystemControl.Bits);
103             Result = TRUE;
104         }
105     }
106 
107     //
108     // Release CMOS lock
109     //
110     HalpReleaseCmosSpinLock();
111 
112     //
113     // Return success
114     //
115     return Result;
116 }
117