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));
|
---|
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));
|
---|
36 |
|
---|
37 |
|
---|
38 | PRIVATE int mixer_avail = 0; /* Mixer exists? */
|
---|
39 |
|
---|
40 |
|
---|
41 | #define dprint (void)
|
---|
42 |
|
---|
43 |
|
---|
44 | /*===========================================================================*
|
---|
45 | * main
|
---|
46 | *===========================================================================*/
|
---|
47 | PUBLIC void main() {
|
---|
48 | message 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 | *=========================================================================*/
|
---|
93 | PRIVATE int mixer_open(m_ptr)
|
---|
94 | message *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 | *=========================================================================*/
|
---|
108 | PRIVATE int mixer_close(m_ptr)
|
---|
109 | message *m_ptr;
|
---|
110 | {
|
---|
111 | dprint("mixer_close\n");
|
---|
112 |
|
---|
113 | return OK;
|
---|
114 | }
|
---|
115 |
|
---|
116 |
|
---|
117 | /*=========================================================================*
|
---|
118 | * mixer_ioctl
|
---|
119 | *=========================================================================*/
|
---|
120 | PRIVATE int mixer_ioctl(m_ptr)
|
---|
121 | message *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 | *=========================================================================*/
|
---|
147 | PRIVATE 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 | *=========================================================================*/
|
---|
172 | PRIVATE int mixer_get(reg)
|
---|
173 | int 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 | *=========================================================================*/
|
---|
186 | PRIVATE int get_set_volume(m_ptr, flag)
|
---|
187 | message *m_ptr;
|
---|
188 | int 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 | *=========================================================================*/
|
---|
270 | PRIVATE int get_set_input(m_ptr, flag, channel)
|
---|
271 | message *m_ptr;
|
---|
272 | int flag; /* 0 = get, 1 = set */
|
---|
273 | int 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 | *=========================================================================*/
|
---|
335 | PRIVATE int get_set_output(m_ptr, flag)
|
---|
336 | message *m_ptr;
|
---|
337 | int 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 | }
|
---|