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
, (const message
*m_ptr
));
30 FORWARD
_PROTOTYPE( int mixer_close
, (const message
*m_ptr
));
31 FORWARD
_PROTOTYPE( int mixer_ioctl
, (const message
*m_ptr
));
32 FORWARD
_PROTOTYPE( int mixer_get
, (int reg
));
33 FORWARD
_PROTOTYPE( int get_set_volume
, (const message
*m_ptr
, int flag
));
34 FORWARD
_PROTOTYPE( int get_set_input
, (const message
*m_ptr
, int flag
, int channel
));
35 FORWARD
_PROTOTYPE( int get_set_output
, (const message
*m_ptr
, int flag
));
38 PRIVATE
int mixer_avail
= 0; /* Mixer exists? */
43 /* SEF functions and variables. */
44 FORWARD
_PROTOTYPE( void sef_local_startup
, (void) );
45 FORWARD
_PROTOTYPE( int sef_cb_init_fresh
, (int type
, sef_init_info_t
*info
) );
47 /*===========================================================================*
49 *===========================================================================*/
53 int err
, caller
, proc_nr
;
55 /* SEF local startup. */
58 /* Here is the main loop of the mixer task. It waits for a message, carries
59 * it out, and sends a reply.
62 driver_receive(ANY
, &mess
, &ipc_status
);
64 caller
= mess
.m_source
;
65 proc_nr
= mess
.IO_ENDPT
;
68 case HARDWARE
: /* Leftover interrupt. */
70 case FS_PROC_NR
: /* The only legitimate caller. */
73 dprint("sb16: got message from %d\n", caller
);
77 /* Now carry out the work. */
79 case DEV_OPEN
: err
= mixer_open(&mess
); break;
80 case DEV_CLOSE
: err
= mixer_close(&mess
); break;
82 case DEV_IOCTL
: err
= mixer_ioctl(&mess
); break;
84 default: err
= EINVAL
; break;
87 /* Finally, prepare and send the reply message. */
88 mess
.m_type
= TASK_REPLY
;
89 mess
.REP_ENDPT
= proc_nr
;
91 dprint("%d %d", err
, OK
);
93 mess
.REP_STATUS
= err
; /* error code */
94 send(caller
, &mess
); /* send reply to caller */
98 /*===========================================================================*
100 *===========================================================================*/
101 PRIVATE
void sef_local_startup()
103 /* Register init callbacks. */
104 sef_setcb_init_fresh(sef_cb_init_fresh
);
105 sef_setcb_init_lu(sef_cb_init_fresh
);
106 sef_setcb_init_restart(sef_cb_init_fresh
);
108 /* Register live update callbacks. */
109 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready
);
110 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard
);
112 /* Let SEF perform startup. */
116 /*===========================================================================*
117 * sef_cb_init_fresh *
118 *===========================================================================*/
119 PRIVATE
int sef_cb_init_fresh(int type
, sef_init_info_t
*info
)
121 /* Initialize the sb16 mixer driver. */
122 /* Announce we are up! */
128 /*=========================================================================*
130 *=========================================================================*/
131 PRIVATE
int mixer_open(const message
*m_ptr
)
133 dprint("mixer_open\n");
135 /* try to detect the mixer type */
136 if (!mixer_avail
&& mixer_init() != OK
) return EIO
;
142 /*=========================================================================*
144 *=========================================================================*/
145 PRIVATE
int mixer_close(const message
*m_ptr
)
147 dprint("mixer_close\n");
153 /*=========================================================================*
155 *=========================================================================*/
156 PRIVATE
int mixer_ioctl(const message
*m_ptr
)
160 dprint("mixer: got ioctl %d\n", m_ptr
->REQUEST
);
163 switch(m_ptr
->REQUEST
) {
164 case MIXIOGETVOLUME
: status
= get_set_volume(m_ptr
, 0); break;
165 case MIXIOSETVOLUME
: status
= get_set_volume(m_ptr
, 1); break;
166 case MIXIOGETINPUTLEFT
: status
= get_set_input(m_ptr
, 0, 0); break;
167 case MIXIOGETINPUTRIGHT
: status
= get_set_input(m_ptr
, 0, 1); break;
168 case MIXIOGETOUTPUT
: status
= get_set_output(m_ptr
, 0); break;
169 case MIXIOSETINPUTLEFT
: status
= get_set_input(m_ptr
, 1, 0); break;
170 case MIXIOSETINPUTRIGHT
: status
= get_set_input(m_ptr
, 1, 1); break;
171 case MIXIOSETOUTPUT
: status
= get_set_output(m_ptr
, 1); break;
172 default: status
= ENOTTY
;
179 /*=========================================================================*
181 *=========================================================================*/
182 PRIVATE
int mixer_init()
184 /* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the
185 * value written can be read back the mixer is there
188 mixer_set(MIXER_DAC_LEVEL
, 0x10); /* write something to it */
189 if(mixer_get(MIXER_DAC_LEVEL
) != 0x10) {
190 dprint("sb16: Mixer not detected\n");
194 /* Enable Automatic Gain Control */
195 mixer_set(MIXER_AGC
, 0x01);
197 dprint("Mixer detected\n");
204 /*=========================================================================*
206 *=========================================================================*/
207 PRIVATE
int mixer_get(int reg
)
211 sb16_outb(MIXER_REG
, reg
);
212 for(i
= 0; i
< 100; i
++);
213 return sb16_inb(MIXER_DATA
) & 0xff;
217 /*=========================================================================*
219 *=========================================================================*/
220 PRIVATE
int get_set_volume(const message
*m_ptr
, int flag
)
221 /* flag 0 = get, 1 = set */
223 struct volume_level level
;
224 int cmd_left
, cmd_right
, shift
, max_level
;
226 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)&level
, (phys_bytes
)sizeof(level
));
231 switch(level
.device
) {
233 cmd_left
= MIXER_MASTER_LEFT
;
234 cmd_right
= MIXER_MASTER_RIGHT
;
237 cmd_left
= MIXER_DAC_LEFT
;
238 cmd_right
= MIXER_DAC_RIGHT
;
241 cmd_left
= MIXER_FM_LEFT
;
242 cmd_right
= MIXER_FM_RIGHT
;
245 cmd_left
= MIXER_CD_LEFT
;
246 cmd_right
= MIXER_CD_RIGHT
;
249 cmd_left
= MIXER_LINE_LEFT
;
250 cmd_right
= MIXER_LINE_RIGHT
;
253 cmd_left
= cmd_right
= MIXER_MIC_LEVEL
;
256 cmd_left
= cmd_right
= MIXER_PC_LEVEL
;
261 cmd_left
= MIXER_TREBLE_LEFT
;
262 cmd_right
= MIXER_TREBLE_RIGHT
;
267 cmd_left
= MIXER_BASS_LEFT
;
268 cmd_right
= MIXER_BASS_RIGHT
;
276 if(flag
) { /* Set volume level */
277 if(level
.right
< 0) level
.right
= 0;
278 else if(level
.right
> max_level
) level
.right
= max_level
;
279 if(level
.left
< 0) level
.left
= 0;
280 else if(level
.left
> max_level
) level
.left
= max_level
;
282 mixer_set(cmd_right
, (level
.right
<< shift
));
283 mixer_set(cmd_left
, (level
.left
<< shift
));
284 } else { /* Get volume level */
285 level
.left
= mixer_get(cmd_left
);
286 level
.right
= mixer_get(cmd_right
);
288 level
.left
>>= shift
;
289 level
.right
>>= shift
;
291 /* Copy back to user */
292 sys_datacopy(SELF
, (vir_bytes
)&level
, m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, (phys_bytes
)sizeof(level
));
299 /*=========================================================================*
301 *=========================================================================*/
302 PRIVATE
int get_set_input(const message
*m_ptr
, int flag
, int channel
)
304 * flag 0 = get, 1 = set
305 * channel 0 = left, 1 = right
308 struct inout_ctrl input
;
309 int input_cmd
, input_mask
, mask
, del_mask
, shift
;
311 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)&input
, (phys_bytes
)sizeof(input
));
313 input_cmd
= (channel
== 0 ? MIXER_IN_LEFT
: MIXER_IN_RIGHT
);
315 mask
= mixer_get(input_cmd
);
317 switch (input
.device
) {
338 if (flag
) { /* Set input */
339 input_mask
= ((input
.left
== ON
? 1 : 0) << 1) | (input
.right
== ON
? 1 : 0);
341 if (shift
> 0) input_mask
<<= shift
;
342 else input_mask
>>= 1;
347 mixer_set(input_cmd
, mask
);
348 } else { /* Get input */
350 input
.left
= ((((mask
>> (shift
+1)) & 1) == 1) ? ON
: OFF
);
351 input
.right
= ((((mask
>> shift
) & 1) == 1) ? ON
: OFF
);
353 input
.left
= (((mask
& 1) == 1) ? ON
: OFF
);
356 /* Copy back to user */
357 sys_datacopy(SELF
, (vir_bytes
)&input
, m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, (phys_bytes
)sizeof(input
));
364 /*=========================================================================*
366 *=========================================================================*/
367 PRIVATE
int get_set_output(const message
*m_ptr
, int flag
)
368 /* flag 0 = get, 1 = set */
370 struct inout_ctrl output
;
371 int output_mask
, mask
, del_mask
, shift
;
373 sys_datacopy(m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, SELF
, (vir_bytes
)&output
, (phys_bytes
)sizeof(output
));
375 mask
= mixer_get(MIXER_OUTPUT_CTRL
);
377 switch (output
.device
) {
394 if (flag
) { /* Set input */
395 output_mask
= ((output
.left
== ON
? 1 : 0) << 1) | (output
.right
== ON
? 1 : 0);
397 if (shift
> 0) output_mask
<<= shift
;
398 else output_mask
>>= 1;
403 mixer_set(MIXER_OUTPUT_CTRL
, mask
);
404 } else { /* Get input */
406 output
.left
= ((((mask
>> (shift
+1)) & 1) == 1) ? ON
: OFF
);
407 output
.right
= ((((mask
>> shift
) & 1) == 1) ? ON
: OFF
);
409 output
.left
= (((mask
& 1) == 1) ? ON
: OFF
);
412 /* Copy back to user */
413 sys_datacopy(SELF
, (vir_bytes
)&output
, m_ptr
->IO_ENDPT
, (vir_bytes
)m_ptr
->ADDRESS
, (phys_bytes
)sizeof(output
));