Retired DEV_{READ,WRITE,GATHER,SCATTER,IOCTL} (safe versions *_S are to
[minix3.git] / drivers / sb16 / sb16_mixer.c
blob6e4a9f1415d181e51e9b6e3dacb9e6034e341aa7
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
24 #include "sb16.h"
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? */
41 #define dprint (void)
44 /*===========================================================================*
45 * main
46 *===========================================================================*/
47 PUBLIC void main() {
48 message mess;
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.
54 while (TRUE) {
55 receive(ANY, &mess);
57 caller = mess.m_source;
58 proc_nr = mess.IO_ENDPT;
60 switch (caller) {
61 case HARDWARE: /* Leftover interrupt. */
62 continue;
63 case FS_PROC_NR: /* The only legitimate caller. */
64 break;
65 default:
66 dprint("sb16: got message from %d\n", caller);
67 continue;
70 /* Now carry out the work. */
71 switch(mess.m_type) {
72 case DEV_OPEN: err = mixer_open(&mess); break;
73 case DEV_CLOSE: err = mixer_close(&mess); break;
74 #ifdef DEV_IOCTL
75 case DEV_IOCTL: err = mixer_ioctl(&mess); break;
76 #endif
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 /*=========================================================================*
93 * mixer_open
94 *=========================================================================*/
95 PRIVATE int mixer_open(m_ptr)
96 message *m_ptr;
98 dprint("mixer_open\n");
100 /* try to detect the mixer type */
101 if (!mixer_avail && mixer_init() != OK) return EIO;
103 return OK;
107 /*=========================================================================*
108 * mixer_close
109 *=========================================================================*/
110 PRIVATE int mixer_close(m_ptr)
111 message *m_ptr;
113 dprint("mixer_close\n");
115 return OK;
119 /*=========================================================================*
120 * mixer_ioctl
121 *=========================================================================*/
122 PRIVATE int mixer_ioctl(m_ptr)
123 message *m_ptr;
125 int status;
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;
142 return status;
146 /*=========================================================================*
147 * mixer_init
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");
158 return EIO;
161 /* Enable Automatic Gain Control */
162 mixer_set(MIXER_AGC, 0x01);
164 dprint("Mixer detected\n");
166 mixer_avail = 1;
167 return OK;
171 /*=========================================================================*
172 * mixer_get
173 *=========================================================================*/
174 PRIVATE int mixer_get(reg)
175 int reg;
177 int i;
179 sb16_outb(MIXER_REG, reg);
180 for(i = 0; i < 100; i++);
181 return sb16_inb(MIXER_DATA) & 0xff;
185 /*=========================================================================*
186 * get_set_volume *
187 *=========================================================================*/
188 PRIVATE int get_set_volume(m_ptr, flag)
189 message *m_ptr;
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));
198 shift = 3;
199 max_level = 0x1F;
201 switch(level.device) {
202 case Master:
203 cmd_left = MIXER_MASTER_LEFT;
204 cmd_right = MIXER_MASTER_RIGHT;
205 break;
206 case Dac:
207 cmd_left = MIXER_DAC_LEFT;
208 cmd_right = MIXER_DAC_RIGHT;
209 break;
210 case Fm:
211 cmd_left = MIXER_FM_LEFT;
212 cmd_right = MIXER_FM_RIGHT;
213 break;
214 case Cd:
215 cmd_left = MIXER_CD_LEFT;
216 cmd_right = MIXER_CD_RIGHT;
217 break;
218 case Line:
219 cmd_left = MIXER_LINE_LEFT;
220 cmd_right = MIXER_LINE_RIGHT;
221 break;
222 case Mic:
223 cmd_left = cmd_right = MIXER_MIC_LEVEL;
224 break;
225 case Speaker:
226 cmd_left = cmd_right = MIXER_PC_LEVEL;
227 shift = 6;
228 max_level = 0x03;
229 break;
230 case Treble:
231 cmd_left = MIXER_TREBLE_LEFT;
232 cmd_right = MIXER_TREBLE_RIGHT;
233 shift = 4;
234 max_level = 0x0F;
235 break;
236 case Bass:
237 cmd_left = MIXER_BASS_LEFT;
238 cmd_right = MIXER_BASS_RIGHT;
239 shift = 4;
240 max_level = 0x0F;
241 break;
242 default:
243 return EINVAL;
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));
265 return OK;
269 /*=========================================================================*
270 * get_set_input *
271 *=========================================================================*/
272 PRIVATE int get_set_input(m_ptr, flag, channel)
273 message *m_ptr;
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) {
288 case Fm:
289 shift = 5;
290 del_mask = 0x1F;
291 break;
292 case Cd:
293 shift = 1;
294 del_mask = 0x79;
295 break;
296 case Line:
297 shift = 3;
298 del_mask = 0x67;
299 break;
300 case Mic:
301 shift = 0;
302 del_mask = 0x7E;
303 break;
304 default:
305 return EINVAL;
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;
314 mask &= del_mask;
315 mask |= input_mask;
317 mixer_set(input_cmd, mask);
318 } else { /* Get input */
319 if (shift > 0) {
320 input.left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
321 input.right = ((mask >> shift) & 1 == 1 ? ON : OFF);
322 } else {
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));
330 return OK;
334 /*=========================================================================*
335 * get_set_output *
336 *=========================================================================*/
337 PRIVATE int get_set_output(m_ptr, flag)
338 message *m_ptr;
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) {
350 case Cd:
351 shift = 1;
352 del_mask = 0x79;
353 break;
354 case Line:
355 shift = 3;
356 del_mask = 0x67;
357 break;
358 case Mic:
359 shift = 0;
360 del_mask = 0x7E;
361 break;
362 default:
363 return EINVAL;
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;
372 mask &= del_mask;
373 mask |= output_mask;
375 mixer_set(MIXER_OUTPUT_CTRL, mask);
376 } else { /* Get input */
377 if (shift > 0) {
378 output.left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
379 output.right = ((mask >> shift) & 1 == 1 ? ON : OFF);
380 } else {
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));
388 return OK;