1 /* This file contains the driver for the mixer on
2 * a SoundBlaster 16 soundcard.
4 * The driver supports the following operations (using message format m2):
6 * m_type DEVICE IO_ENDPT COUNT POSITION ADRRESS
7 * ----------------------------------------------------------------
8 * | DEV_OPEN | device | proc nr | | | |
9 * |------------+---------+---------+---------+---------+---------|
10 * | DEV_CLOSE | device | proc nr | | | |
11 * |------------+---------+---------+---------+---------+---------|
12 * | DEV_IOCTL | device | proc nr |func code| | buf_ptr |
13 * ----------------------------------------------------------------
15 * The file contains one entry point:
17 * sb16mixer_task: main entry when system is brought up
19 * August 24 2005 Ported driver to user space (Peter Boonstoppel)
20 * May 20 1995 Author: Michel R. Prevenier
27 _PROTOTYPE(void main
, (void));
28 FORWARD
_PROTOTYPE( int mixer_init
, (void));
29 FORWARD
_PROTOTYPE( int mixer_open
, (message
*m_ptr
));
30 FORWARD
_PROTOTYPE( int mixer_close
, (message
*m_ptr
));
31 FORWARD
_PROTOTYPE( int mixer_ioctl
, (message
*m_ptr
));
32 FORWARD
_PROTOTYPE( int mixer_get
, (int reg
));
33 FORWARD
_PROTOTYPE( int get_set_volume
, (message
*m_ptr
, int flag
));
34 FORWARD
_PROTOTYPE( int get_set_input
, (message
*m_ptr
, int flag
, int channel
));
35 FORWARD
_PROTOTYPE( int get_set_output
, (message
*m_ptr
, int flag
));
38 PRIVATE
int mixer_avail
= 0; /* Mixer exists? */
44 /*===========================================================================*
46 *===========================================================================*/
49 int err
, caller
, proc_nr
;
51 /* Here is the main loop of the mixer task. It waits for a message, carries
52 * it out, and sends a reply.
57 caller
= mess
.m_source
;
58 proc_nr
= mess
.IO_ENDPT
;
61 case HARDWARE
: /* Leftover interrupt. */
63 case FS_PROC_NR
: /* The only legitimate caller. */
66 dprint("sb16: got message from %d\n", caller
);
70 /* Now carry out the work. */
72 case DEV_OPEN
: err
= mixer_open(&mess
); break;
73 case DEV_CLOSE
: err
= mixer_close(&mess
); break;
75 case DEV_IOCTL
: err
= mixer_ioctl(&mess
); break;
77 default: err
= EINVAL
; break;
80 /* Finally, prepare and send the reply message. */
81 mess
.m_type
= TASK_REPLY
;
82 mess
.REP_ENDPT
= proc_nr
;
84 dprint("%d %d", err
, OK
);
86 mess
.REP_STATUS
= err
; /* error code */
87 send(caller
, &mess
); /* send reply to caller */
92 /*=========================================================================*
94 *=========================================================================*/
95 PRIVATE
int mixer_open(m_ptr
)
98 dprint("mixer_open\n");
100 /* try to detect the mixer type */
101 if (!mixer_avail
&& mixer_init() != OK
) return EIO
;
107 /*=========================================================================*
109 *=========================================================================*/
110 PRIVATE
int mixer_close(m_ptr
)
113 dprint("mixer_close\n");
119 /*=========================================================================*
121 *=========================================================================*/
122 PRIVATE
int mixer_ioctl(m_ptr
)
127 dprint("mixer: got ioctl %d\n", m_ptr
->REQUEST
);
130 switch(m_ptr
->REQUEST
) {
131 case MIXIOGETVOLUME
: status
= get_set_volume(m_ptr
, 0); break;
132 case MIXIOSETVOLUME
: status
= get_set_volume(m_ptr
, 1); break;
133 case MIXIOGETINPUTLEFT
: status
= get_set_input(m_ptr
, 0, 0); break;
134 case MIXIOGETINPUTRIGHT
: status
= get_set_input(m_ptr
, 0, 1); break;
135 case MIXIOGETOUTPUT
: status
= get_set_output(m_ptr
, 0); break;
136 case MIXIOSETINPUTLEFT
: status
= get_set_input(m_ptr
, 1, 0); break;
137 case MIXIOSETINPUTRIGHT
: status
= get_set_input(m_ptr
, 1, 1); break;
138 case MIXIOSETOUTPUT
: status
= get_set_output(m_ptr
, 1); break;
139 default: status
= ENOTTY
;
146 /*=========================================================================*
148 *=========================================================================*/
149 PRIVATE
int mixer_init()
151 /* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the
152 * value written can be read back the mixer is there
155 mixer_set(MIXER_DAC_LEVEL
, 0x10); /* write something to it */
156 if(mixer_get(MIXER_DAC_LEVEL
) != 0x10) {
157 dprint("sb16: Mixer not detected\n");
161 /* Enable Automatic Gain Control */
162 mixer_set(MIXER_AGC
, 0x01);
164 dprint("Mixer detected\n");
171 /*=========================================================================*
173 *=========================================================================*/
174 PRIVATE
int mixer_get(reg
)
179 sb16_outb(MIXER_REG
, reg
);
180 for(i
= 0; i
< 100; i
++);
181 return sb16_inb(MIXER_DATA
) & 0xff;
185 /*=========================================================================*
187 *=========================================================================*/
188 PRIVATE
int get_set_volume(m_ptr
, flag
)
190 int flag
; /* 0 = get, 1 = set */
192 phys_bytes user_phys
;
193 struct volume_level level
;
194 int cmd_left
, cmd_right
, shift
, max_level
;
196 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)&level
, (phys_bytes
)sizeof(level
));
201 switch(level
.device
) {
203 cmd_left
= MIXER_MASTER_LEFT
;
204 cmd_right
= MIXER_MASTER_RIGHT
;
207 cmd_left
= MIXER_DAC_LEFT
;
208 cmd_right
= MIXER_DAC_RIGHT
;
211 cmd_left
= MIXER_FM_LEFT
;
212 cmd_right
= MIXER_FM_RIGHT
;
215 cmd_left
= MIXER_CD_LEFT
;
216 cmd_right
= MIXER_CD_RIGHT
;
219 cmd_left
= MIXER_LINE_LEFT
;
220 cmd_right
= MIXER_LINE_RIGHT
;
223 cmd_left
= cmd_right
= MIXER_MIC_LEVEL
;
226 cmd_left
= cmd_right
= MIXER_PC_LEVEL
;
231 cmd_left
= MIXER_TREBLE_LEFT
;
232 cmd_right
= MIXER_TREBLE_RIGHT
;
237 cmd_left
= MIXER_BASS_LEFT
;
238 cmd_right
= MIXER_BASS_RIGHT
;
246 if(flag
) { /* Set volume level */
247 if(level
.right
< 0) level
.right
= 0;
248 else if(level
.right
> max_level
) level
.right
= max_level
;
249 if(level
.left
< 0) level
.left
= 0;
250 else if(level
.left
> max_level
) level
.left
= max_level
;
252 mixer_set(cmd_right
, (level
.right
<< shift
));
253 mixer_set(cmd_left
, (level
.left
<< shift
));
254 } else { /* Get volume level */
255 level
.left
= mixer_get(cmd_left
);
256 level
.right
= mixer_get(cmd_right
);
258 level
.left
>>= shift
;
259 level
.right
>>= shift
;
261 /* Copy back to user */
262 sys_datacopy(SELF
, (vir_bytes
)&level
, m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, (phys_bytes
)sizeof(level
));
269 /*=========================================================================*
271 *=========================================================================*/
272 PRIVATE
int get_set_input(m_ptr
, flag
, channel
)
274 int flag
; /* 0 = get, 1 = set */
275 int channel
; /* 0 = left, 1 = right */
277 phys_bytes user_phys
;
278 struct inout_ctrl input
;
279 int input_cmd
, input_mask
, mask
, del_mask
, shift
;
281 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)&input
, (phys_bytes
)sizeof(input
));
283 input_cmd
= (channel
== 0 ? MIXER_IN_LEFT
: MIXER_IN_RIGHT
);
285 mask
= mixer_get(input_cmd
);
287 switch (input
.device
) {
308 if (flag
) { /* Set input */
309 input_mask
= ((input
.left
== ON
? 1 : 0) << 1) | (input
.right
== ON
? 1 : 0);
311 if (shift
> 0) input_mask
<<= shift
;
312 else input_mask
>>= 1;
317 mixer_set(input_cmd
, mask
);
318 } else { /* Get input */
320 input
.left
= ((mask
>> (shift
+1)) & 1 == 1 ? ON
: OFF
);
321 input
.right
= ((mask
>> shift
) & 1 == 1 ? ON
: OFF
);
323 input
.left
= ((mask
& 1) == 1 ? ON
: OFF
);
326 /* Copy back to user */
327 sys_datacopy(SELF
, (vir_bytes
)&input
, m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, (phys_bytes
)sizeof(input
));
334 /*=========================================================================*
336 *=========================================================================*/
337 PRIVATE
int get_set_output(m_ptr
, flag
)
339 int flag
; /* 0 = get, 1 = set */
341 phys_bytes user_phys
;
342 struct inout_ctrl output
;
343 int output_mask
, mask
, del_mask
, shift
;
345 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)&output
, (phys_bytes
)sizeof(output
));
347 mask
= mixer_get(MIXER_OUTPUT_CTRL
);
349 switch (output
.device
) {
366 if (flag
) { /* Set input */
367 output_mask
= ((output
.left
== ON
? 1 : 0) << 1) | (output
.right
== ON
? 1 : 0);
369 if (shift
> 0) output_mask
<<= shift
;
370 else output_mask
>>= 1;
375 mixer_set(MIXER_OUTPUT_CTRL
, mask
);
376 } else { /* Get input */
378 output
.left
= ((mask
>> (shift
+1)) & 1 == 1 ? ON
: OFF
);
379 output
.right
= ((mask
>> shift
) & 1 == 1 ? ON
: OFF
);
381 output
.left
= ((mask
& 1) == 1 ? ON
: OFF
);
384 /* Copy back to user */
385 sys_datacopy(SELF
, (vir_bytes
)&output
, m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, (phys_bytes
)sizeof(output
));