source: trunk/minix/drivers/sb16/sb16_mixer.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 10.0 KB
Line 
1/* This file contains the driver for the mixer on
2 * a SoundBlaster 16 soundcard.
3 *
4 * The driver supports the following operations (using message format m2):
5 *
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 * ----------------------------------------------------------------
14 *
15 * The file contains one entry point:
16 *
17 * sb16mixer_task: main entry when system is brought up
18 *
19 * August 24 2005 Ported driver to user space (Peter Boonstoppel)
20 * May 20 1995 Author: Michel R. Prevenier
21 */
22
23
24#include "sb16.h"
25
26
27_PROTOTYPE(void main, (void));
28FORWARD _PROTOTYPE( int mixer_init, (void));
29FORWARD _PROTOTYPE( int mixer_open, (message *m_ptr));
30FORWARD _PROTOTYPE( int mixer_close, (message *m_ptr));
31FORWARD _PROTOTYPE( int mixer_ioctl, (message *m_ptr));
32FORWARD _PROTOTYPE( int mixer_get, (int reg));
33FORWARD _PROTOTYPE( int get_set_volume, (message *m_ptr, int flag));
34FORWARD _PROTOTYPE( int get_set_input, (message *m_ptr, int flag, int channel));
35FORWARD _PROTOTYPE( int get_set_output, (message *m_ptr, int flag));
36
37
38PRIVATE int mixer_avail = 0; /* Mixer exists? */
39
40
41#define dprint (void)
42
43
44/*===========================================================================*
45 * main
46 *===========================================================================*/
47PUBLIC void main() {
48message mess;
49 int err, caller, proc_nr;
50
51 /* Here is the main loop of the mixer task. It waits for a message, carries
52 * it out, and sends a reply.
53 */
54 while (TRUE) {
55 receive(ANY, &mess);
56
57 caller = mess.m_source;
58 proc_nr = mess.IO_ENDPT;
59
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;
68 }
69
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 case DEV_IOCTL: err = mixer_ioctl(&mess); break;
75 default: err = EINVAL; break;
76 }
77
78 /* Finally, prepare and send the reply message. */
79 mess.m_type = TASK_REPLY;
80 mess.REP_ENDPT = proc_nr;
81
82 dprint("%d %d", err, OK);
83
84 mess.REP_STATUS = err; /* error code */
85 send(caller, &mess); /* send reply to caller */
86 }
87}
88
89
90/*=========================================================================*
91 * mixer_open
92 *=========================================================================*/
93PRIVATE int mixer_open(m_ptr)
94message *m_ptr;
95{
96 dprint("mixer_open\n");
97
98 /* try to detect the mixer type */
99 if (!mixer_avail && mixer_init() != OK) return EIO;
100
101 return OK;
102}
103
104
105/*=========================================================================*
106 * mixer_close
107 *=========================================================================*/
108PRIVATE int mixer_close(m_ptr)
109message *m_ptr;
110{
111 dprint("mixer_close\n");
112
113 return OK;
114}
115
116
117/*=========================================================================*
118 * mixer_ioctl
119 *=========================================================================*/
120PRIVATE int mixer_ioctl(m_ptr)
121message *m_ptr;
122{
123 int status;
124
125 dprint("mixer: got ioctl %d\n", m_ptr->REQUEST);
126
127
128 switch(m_ptr->REQUEST) {
129 case MIXIOGETVOLUME: status = get_set_volume(m_ptr, 0); break;
130 case MIXIOSETVOLUME: status = get_set_volume(m_ptr, 1); break;
131 case MIXIOGETINPUTLEFT: status = get_set_input(m_ptr, 0, 0); break;
132 case MIXIOGETINPUTRIGHT: status = get_set_input(m_ptr, 0, 1); break;
133 case MIXIOGETOUTPUT: status = get_set_output(m_ptr, 0); break;
134 case MIXIOSETINPUTLEFT: status = get_set_input(m_ptr, 1, 0); break;
135 case MIXIOSETINPUTRIGHT: status = get_set_input(m_ptr, 1, 1); break;
136 case MIXIOSETOUTPUT: status = get_set_output(m_ptr, 1); break;
137 default: status = ENOTTY;
138 }
139
140 return status;
141}
142
143
144/*=========================================================================*
145 * mixer_init
146 *=========================================================================*/
147PRIVATE int mixer_init()
148{
149 /* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the
150 * value written can be read back the mixer is there
151 */
152
153 mixer_set(MIXER_DAC_LEVEL, 0x10); /* write something to it */
154 if(mixer_get(MIXER_DAC_LEVEL) != 0x10) {
155 dprint("sb16: Mixer not detected\n");
156 return EIO;
157 }
158
159 /* Enable Automatic Gain Control */
160 mixer_set(MIXER_AGC, 0x01);
161
162 dprint("Mixer detected\n");
163
164 mixer_avail = 1;
165 return OK;
166}
167
168
169/*=========================================================================*
170 * mixer_get
171 *=========================================================================*/
172PRIVATE int mixer_get(reg)
173int reg;
174{
175 int i;
176
177 sb16_outb(MIXER_REG, reg);
178 for(i = 0; i < 100; i++);
179 return sb16_inb(MIXER_DATA) & 0xff;
180}
181
182
183/*=========================================================================*
184 * get_set_volume *
185 *=========================================================================*/
186PRIVATE int get_set_volume(m_ptr, flag)
187message *m_ptr;
188int flag; /* 0 = get, 1 = set */
189{
190 phys_bytes user_phys;
191 struct volume_level level;
192 int cmd_left, cmd_right, shift, max_level;
193
194 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&level, (phys_bytes)sizeof(level));
195
196 shift = 3;
197 max_level = 0x1F;
198
199 switch(level.device) {
200 case Master:
201 cmd_left = MIXER_MASTER_LEFT;
202 cmd_right = MIXER_MASTER_RIGHT;
203 break;
204 case Dac:
205 cmd_left = MIXER_DAC_LEFT;
206 cmd_right = MIXER_DAC_RIGHT;
207 break;
208 case Fm:
209 cmd_left = MIXER_FM_LEFT;
210 cmd_right = MIXER_FM_RIGHT;
211 break;
212 case Cd:
213 cmd_left = MIXER_CD_LEFT;
214 cmd_right = MIXER_CD_RIGHT;
215 break;
216 case Line:
217 cmd_left = MIXER_LINE_LEFT;
218 cmd_right = MIXER_LINE_RIGHT;
219 break;
220 case Mic:
221 cmd_left = cmd_right = MIXER_MIC_LEVEL;
222 break;
223 case Speaker:
224 cmd_left = cmd_right = MIXER_PC_LEVEL;
225 shift = 6;
226 max_level = 0x03;
227 break;
228 case Treble:
229 cmd_left = MIXER_TREBLE_LEFT;
230 cmd_right = MIXER_TREBLE_RIGHT;
231 shift = 4;
232 max_level = 0x0F;
233 break;
234 case Bass:
235 cmd_left = MIXER_BASS_LEFT;
236 cmd_right = MIXER_BASS_RIGHT;
237 shift = 4;
238 max_level = 0x0F;
239 break;
240 default:
241 return EINVAL;
242 }
243
244 if(flag) { /* Set volume level */
245 if(level.right < 0) level.right = 0;
246 else if(level.right > max_level) level.right = max_level;
247 if(level.left < 0) level.left = 0;
248 else if(level.left > max_level) level.left = max_level;
249
250 mixer_set(cmd_right, (level.right << shift));
251 mixer_set(cmd_left, (level.left << shift));
252 } else { /* Get volume level */
253 level.left = mixer_get(cmd_left);
254 level.right = mixer_get(cmd_right);
255
256 level.left >>= shift;
257 level.right >>= shift;
258
259 /* Copy back to user */
260 sys_datacopy(SELF, (vir_bytes)&level, m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(level));
261 }
262
263 return OK;
264}
265
266
267/*=========================================================================*
268 * get_set_input *
269 *=========================================================================*/
270PRIVATE int get_set_input(m_ptr, flag, channel)
271message *m_ptr;
272int flag; /* 0 = get, 1 = set */
273int channel; /* 0 = left, 1 = right */
274{
275 phys_bytes user_phys;
276 struct inout_ctrl input;
277 int input_cmd, input_mask, mask, del_mask, shift;
278
279 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&input, (phys_bytes)sizeof(input));
280
281 input_cmd = (channel == 0 ? MIXER_IN_LEFT : MIXER_IN_RIGHT);
282
283 mask = mixer_get(input_cmd);
284
285 switch (input.device) {
286 case Fm:
287 shift = 5;
288 del_mask = 0x1F;
289 break;
290 case Cd:
291 shift = 1;
292 del_mask = 0x79;
293 break;
294 case Line:
295 shift = 3;
296 del_mask = 0x67;
297 break;
298 case Mic:
299 shift = 0;
300 del_mask = 0x7E;
301 break;
302 default:
303 return EINVAL;
304 }
305
306 if (flag) { /* Set input */
307 input_mask = ((input.left == ON ? 1 : 0) << 1) | (input.right == ON ? 1 : 0);
308
309 if (shift > 0) input_mask <<= shift;
310 else input_mask >>= 1;
311
312 mask &= del_mask;
313 mask |= input_mask;
314
315 mixer_set(input_cmd, mask);
316 } else { /* Get input */
317 if (shift > 0) {
318 input.left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
319 input.right = ((mask >> shift) & 1 == 1 ? ON : OFF);
320 } else {
321 input.left = ((mask & 1) == 1 ? ON : OFF);
322 }
323
324 /* Copy back to user */
325 sys_datacopy(SELF, (vir_bytes)&input, m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(input));
326 }
327
328 return OK;
329}
330
331
332/*=========================================================================*
333 * get_set_output *
334 *=========================================================================*/
335PRIVATE int get_set_output(m_ptr, flag)
336message *m_ptr;
337int flag; /* 0 = get, 1 = set */
338{
339 phys_bytes user_phys;
340 struct inout_ctrl output;
341 int output_mask, mask, del_mask, shift;
342
343 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&output, (phys_bytes)sizeof(output));
344
345 mask = mixer_get(MIXER_OUTPUT_CTRL);
346
347 switch (output.device) {
348 case Cd:
349 shift = 1;
350 del_mask = 0x79;
351 break;
352 case Line:
353 shift = 3;
354 del_mask = 0x67;
355 break;
356 case Mic:
357 shift = 0;
358 del_mask = 0x7E;
359 break;
360 default:
361 return EINVAL;
362 }
363
364 if (flag) { /* Set input */
365 output_mask = ((output.left == ON ? 1 : 0) << 1) | (output.right == ON ? 1 : 0);
366
367 if (shift > 0) output_mask <<= shift;
368 else output_mask >>= 1;
369
370 mask &= del_mask;
371 mask |= output_mask;
372
373 mixer_set(MIXER_OUTPUT_CTRL, mask);
374 } else { /* Get input */
375 if (shift > 0) {
376 output.left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
377 output.right = ((mask >> shift) & 1 == 1 ? ON : OFF);
378 } else {
379 output.left = ((mask & 1) == 1 ? ON : OFF);
380 }
381
382 /* Copy back to user */
383 sys_datacopy(SELF, (vir_bytes)&output, m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(output));
384 }
385
386 return OK;
387}
Note: See TracBrowser for help on using the repository browser.