xref: /openbsd/sys/dev/isa/sbdsp.c (revision 610f49f8)
1 /*	$OpenBSD: sbdsp.c,v 1.20 2002/02/12 22:31:01 fgsch Exp $	*/
2 
3 /*
4  * Copyright (c) 1991-1993 Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the Computer Systems
18  *	Engineering Group at Lawrence Berkeley Laboratory.
19  * 4. Neither the name of the University nor of the Laboratory may be used
20  *    to endorse or promote products derived from this software without
21  *    specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36 
37 /*
38  * SoundBlaster Pro code provided by John Kohl, based on lots of
39  * information he gleaned from Steve Haehnichen <steve@vigra.com>'s
40  * SBlast driver for 386BSD and DOS driver code from Daniel Sachs
41  * <sachs@meibm15.cen.uiuc.edu>.
42  * Lots of rewrites by Lennart Augustsson <augustss@cs.chalmers.se>
43  * with information from SB "Hardware Programming Guide" and the
44  * Linux drivers.
45  */
46 
47 #include "midi.h"
48 
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/errno.h>
52 #include <sys/ioctl.h>
53 #include <sys/syslog.h>
54 #include <sys/device.h>
55 #include <sys/proc.h>
56 #include <sys/buf.h>
57 #include <uvm/uvm_extern.h>
58 
59 #include <machine/cpu.h>
60 #include <machine/intr.h>
61 #include <machine/bus.h>
62 
63 #include <sys/audioio.h>
64 #include <dev/audio_if.h>
65 #include <dev/midi_if.h>
66 #include <dev/mulaw.h>
67 #include <dev/auconv.h>
68 
69 #include <dev/isa/isavar.h>
70 #include <dev/isa/isadmavar.h>
71 
72 #include <dev/isa/sbreg.h>
73 #include <dev/isa/sbdspvar.h>
74 
75 
76 #ifdef AUDIO_DEBUG
77 #define DPRINTF(x)	if (sbdspdebug) printf x
78 #define DPRINTFN(n,x)	if (sbdspdebug >= (n)) printf x
79 int	sbdspdebug = 0;
80 #else
81 #define DPRINTF(x)
82 #define DPRINTFN(n,x)
83 #endif
84 
85 #ifndef SBDSP_NPOLL
86 #define SBDSP_NPOLL 3000
87 #endif
88 
89 struct {
90 	int wdsp;
91 	int rdsp;
92 	int wmidi;
93 } sberr;
94 
95 /*
96  * Time constant routines follow.  See SBK, section 12.
97  * Although they don't come out and say it (in the docs),
98  * the card clearly uses a 1MHz countdown timer, as the
99  * low-speed formula (p. 12-4) is:
100  *	tc = 256 - 10^6 / sr
101  * In high-speed mode, the constant is the upper byte of a 16-bit counter,
102  * and a 256MHz clock is used:
103  *	tc = 65536 - 256 * 10^ 6 / sr
104  * Since we can only use the upper byte of the HS TC, the two formulae
105  * are equivalent.  (Why didn't they say so?)  E.g.,
106  *	(65536 - 256 * 10 ^ 6 / x) >> 8 = 256 - 10^6 / x
107  *
108  * The crossover point (from low- to high-speed modes) is different
109  * for the SBPRO and SB20.  The table on p. 12-5 gives the following data:
110  *
111  *				SBPRO			SB20
112  *				-----			--------
113  * input ls min			4	KHz		4	KHz
114  * input ls max			23	KHz		13	KHz
115  * input hs max			44.1	KHz		15	KHz
116  * output ls min		4	KHz		4	KHz
117  * output ls max		23	KHz		23	KHz
118  * output hs max		44.1	KHz		44.1	KHz
119  */
120 /* XXX Should we round the tc?
121 #define SB_RATE_TO_TC(x) (((65536 - 256 * 1000000 / (x)) + 128) >> 8)
122 */
123 #define SB_RATE_TO_TC(x) (256 - 1000000 / (x))
124 #define SB_TC_TO_RATE(tc) (1000000 / (256 - (tc)))
125 
126 struct sbmode {
127 	short	model;
128 	u_char	channels;
129 	u_char	precision;
130 	u_short	lowrate, highrate;
131 	u_char	cmd;
132 	u_char	cmdchan;
133 };
134 static struct sbmode sbpmodes[] = {
135  { SB_1,    1,  8,  4000, 22727, SB_DSP_WDMA      },
136  { SB_20,   1,  8,  4000, 22727, SB_DSP_WDMA_LOOP },
137  { SB_2x,   1,  8,  4000, 22727, SB_DSP_WDMA_LOOP },
138  { SB_2x,   1,  8, 22727, 45454, SB_DSP_HS_OUTPUT },
139  { SB_PRO,  1,  8,  4000, 22727, SB_DSP_WDMA_LOOP },
140  { SB_PRO,  1,  8, 22727, 45454, SB_DSP_HS_OUTPUT },
141  { SB_PRO,  2,  8, 11025, 22727, SB_DSP_HS_OUTPUT },
142  /* Yes, we write the record mode to set 16-bit playback mode. weird, huh? */
143  { SB_JAZZ, 1,  8,  4000, 22727, SB_DSP_WDMA_LOOP, SB_DSP_RECORD_MONO },
144  { SB_JAZZ, 1,  8, 22727, 45454, SB_DSP_HS_OUTPUT, SB_DSP_RECORD_MONO },
145  { SB_JAZZ, 2,  8, 11025, 22727, SB_DSP_HS_OUTPUT, SB_DSP_RECORD_STEREO },
146  { SB_JAZZ, 1, 16,  4000, 22727, SB_DSP_WDMA_LOOP, JAZZ16_RECORD_MONO },
147  { SB_JAZZ, 1, 16, 22727, 45454, SB_DSP_HS_OUTPUT, JAZZ16_RECORD_MONO },
148  { SB_JAZZ, 2, 16, 11025, 22727, SB_DSP_HS_OUTPUT, JAZZ16_RECORD_STEREO },
149  { SB_16,   1,  8,  5000, 45000, SB_DSP16_WDMA_8  },
150  { SB_16,   2,  8,  5000, 45000, SB_DSP16_WDMA_8  },
151 #define PLAY16 15 /* must be the index of the next entry in the table */
152  { SB_16,   1, 16,  5000, 45000, SB_DSP16_WDMA_16 },
153  { SB_16,   2, 16,  5000, 45000, SB_DSP16_WDMA_16 },
154  { -1 }
155 };
156 static struct sbmode sbrmodes[] = {
157  { SB_1,    1,  8,  4000, 12987, SB_DSP_RDMA      },
158  { SB_20,   1,  8,  4000, 12987, SB_DSP_RDMA_LOOP },
159  { SB_2x,   1,  8,  4000, 12987, SB_DSP_RDMA_LOOP },
160  { SB_2x,   1,  8, 12987, 14925, SB_DSP_HS_INPUT  },
161  { SB_PRO,  1,  8,  4000, 22727, SB_DSP_RDMA_LOOP, SB_DSP_RECORD_MONO },
162  { SB_PRO,  1,  8, 22727, 45454, SB_DSP_HS_INPUT,  SB_DSP_RECORD_MONO },
163  { SB_PRO,  2,  8, 11025, 22727, SB_DSP_HS_INPUT,  SB_DSP_RECORD_STEREO },
164  { SB_JAZZ, 1,  8,  4000, 22727, SB_DSP_RDMA_LOOP, SB_DSP_RECORD_MONO },
165  { SB_JAZZ, 1,  8, 22727, 45454, SB_DSP_HS_INPUT,  SB_DSP_RECORD_MONO },
166  { SB_JAZZ, 2,  8, 11025, 22727, SB_DSP_HS_INPUT,  SB_DSP_RECORD_STEREO },
167  { SB_JAZZ, 1, 16,  4000, 22727, SB_DSP_RDMA_LOOP, JAZZ16_RECORD_MONO },
168  { SB_JAZZ, 1, 16, 22727, 45454, SB_DSP_HS_INPUT,  JAZZ16_RECORD_MONO },
169  { SB_JAZZ, 2, 16, 11025, 22727, SB_DSP_HS_INPUT,  JAZZ16_RECORD_STEREO },
170  { SB_16,   1,  8,  5000, 45000, SB_DSP16_RDMA_8  },
171  { SB_16,   2,  8,  5000, 45000, SB_DSP16_RDMA_8  },
172  { SB_16,   1, 16,  5000, 45000, SB_DSP16_RDMA_16 },
173  { SB_16,   2, 16,  5000, 45000, SB_DSP16_RDMA_16 },
174  { -1 }
175 };
176 
177 void	sbversion __P((struct sbdsp_softc *));
178 void	sbdsp_jazz16_probe __P((struct sbdsp_softc *));
179 void	sbdsp_set_mixer_gain __P((struct sbdsp_softc *sc, int port));
180 void	sbdsp_to __P((void *));
181 void	sbdsp_pause __P((struct sbdsp_softc *));
182 int	sbdsp_set_timeconst __P((struct sbdsp_softc *, int));
183 int	sbdsp16_set_rate __P((struct sbdsp_softc *, int, int));
184 int	sbdsp_set_in_ports __P((struct sbdsp_softc *, int));
185 void	sbdsp_set_ifilter __P((void *, int));
186 int	sbdsp_get_ifilter __P((void *));
187 
188 int	sbdsp_block_output __P((void *));
189 int	sbdsp_block_input __P((void *));
190 static	int sbdsp_adjust __P((int, int));
191 
192 int	sbdsp_midi_intr __P((void *));
193 
194 #ifdef AUDIO_DEBUG
195 void	sb_printsc __P((struct sbdsp_softc *));
196 
197 void
198 sb_printsc(sc)
199 	struct sbdsp_softc *sc;
200 {
201 	int i;
202 
203 	printf("open %d dmachan %d/%d %d/%d iobase 0x%x irq %d\n",
204 	    (int)sc->sc_open, sc->sc_i.run, sc->sc_o.run,
205 	    sc->sc_drq8, sc->sc_drq16,
206 	    sc->sc_iobase, sc->sc_irq);
207 	printf("irate %d itc %x orate %d otc %x\n",
208 	    sc->sc_i.rate, sc->sc_i.tc,
209 	    sc->sc_o.rate, sc->sc_o.tc);
210 	printf("spkron %u nintr %lu\n",
211 	    sc->spkr_state, sc->sc_interrupts);
212 	printf("intr8 %p arg8 %p\n",
213 	    sc->sc_intr8, sc->sc_arg16);
214 	printf("intr16 %p arg16 %p\n",
215 	    sc->sc_intr8, sc->sc_arg16);
216 	printf("gain:");
217 	for (i = 0; i < SB_NDEVS; i++)
218 		printf(" %u,%u", sc->gain[i][SB_LEFT], sc->gain[i][SB_RIGHT]);
219 	printf("\n");
220 }
221 #endif /* AUDIO_DEBUG */
222 
223 /*
224  * Probe / attach routines.
225  */
226 
227 /*
228  * Probe for the soundblaster hardware.
229  */
230 int
231 sbdsp_probe(sc)
232 	struct sbdsp_softc *sc;
233 {
234 
235 	if (sbdsp_reset(sc) < 0) {
236 		DPRINTF(("sbdsp: couldn't reset card\n"));
237 		return 0;
238 	}
239 	/* if flags set, go and probe the jazz16 stuff */
240 	if (sc->sc_dev.dv_cfdata->cf_flags & 1)
241 		sbdsp_jazz16_probe(sc);
242 	else
243 		sbversion(sc);
244 	if (sc->sc_model == SB_UNK) {
245 		/* Unknown SB model found. */
246 		DPRINTF(("sbdsp: unknown SB model found\n"));
247 		return 0;
248 	}
249 	return 1;
250 }
251 
252 /*
253  * Try add-on stuff for Jazz16.
254  */
255 void
256 sbdsp_jazz16_probe(sc)
257 	struct sbdsp_softc *sc;
258 {
259 	static u_char jazz16_irq_conf[16] = {
260 	    -1, -1, 0x02, 0x03,
261 	    -1, 0x01, -1, 0x04,
262 	    -1, 0x02, 0x05, -1,
263 	    -1, -1, -1, 0x06};
264 	static u_char jazz16_drq_conf[8] = {
265 	    -1, 0x01, -1, 0x02,
266 	    -1, 0x03, -1, 0x04};
267 
268 	bus_space_tag_t iot = sc->sc_iot;
269 	bus_space_handle_t ioh;
270 
271 	sbversion(sc);
272 
273 	DPRINTF(("jazz16 probe\n"));
274 
275 	if (bus_space_map(iot, JAZZ16_CONFIG_PORT, 1, 0, &ioh)) {
276 		DPRINTF(("bus map failed\n"));
277 		return;
278 	}
279 
280 	if (jazz16_drq_conf[sc->sc_drq8] == (u_char)-1 ||
281 	    jazz16_irq_conf[sc->sc_irq] == (u_char)-1) {
282 		DPRINTF(("drq/irq check failed\n"));
283 		goto done;		/* give up, we can't do it. */
284 	}
285 
286 	bus_space_write_1(iot, ioh, 0, JAZZ16_WAKEUP);
287 	delay(10000);			/* delay 10 ms */
288 	bus_space_write_1(iot, ioh, 0, JAZZ16_SETBASE);
289 	bus_space_write_1(iot, ioh, 0, sc->sc_iobase & 0x70);
290 
291 	if (sbdsp_reset(sc) < 0) {
292 		DPRINTF(("sbdsp_reset check failed\n"));
293 		goto done;		/* XXX? what else could we do? */
294 	}
295 
296 	if (sbdsp_wdsp(sc, JAZZ16_READ_VER)) {
297 		DPRINTF(("read16 setup failed\n"));
298 		goto done;
299 	}
300 
301 	if (sbdsp_rdsp(sc) != JAZZ16_VER_JAZZ) {
302 		DPRINTF(("read16 failed\n"));
303 		goto done;
304 	}
305 
306 	/* XXX set both 8 & 16-bit drq to same channel, it works fine. */
307 	sc->sc_drq16 = sc->sc_drq8;
308 	if (sbdsp_wdsp(sc, JAZZ16_SET_DMAINTR) ||
309 	    sbdsp_wdsp(sc, (jazz16_drq_conf[sc->sc_drq16] << 4) |
310 		jazz16_drq_conf[sc->sc_drq8]) ||
311 	    sbdsp_wdsp(sc, jazz16_irq_conf[sc->sc_irq])) {
312 		DPRINTF(("sbdsp: can't write jazz16 probe stuff\n"));
313 	} else {
314 		DPRINTF(("jazz16 detected!\n"));
315 		sc->sc_model = SB_JAZZ;
316 		sc->sc_mixer_model = SBM_CT1345; /* XXX really? */
317 	}
318 
319 done:
320 	bus_space_unmap(iot, ioh, 1);
321 }
322 
323 /*
324  * Attach hardware to driver, attach hardware driver to audio
325  * pseudo-device driver .
326  */
327 void
328 sbdsp_attach(sc)
329 	struct sbdsp_softc *sc;
330 {
331 	struct audio_params pparams, rparams;
332         int i;
333         u_int v;
334 
335 	/*
336 	 * Create our DMA maps.
337 	 */
338 	if (sc->sc_drq8 != -1) {
339 		if (isa_dmamap_create(sc->sc_isa, sc->sc_drq8,
340 		    MAX_ISADMA, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) {
341 			printf("%s: can't create map for drq %d\n",
342 			    sc->sc_dev.dv_xname, sc->sc_drq8);
343 			return;
344 		}
345 	}
346 	if (sc->sc_drq16 != -1 && sc->sc_drq16 != sc->sc_drq8) {
347 		if (isa_dmamap_create(sc->sc_isa, sc->sc_drq16,
348 		    MAX_ISADMA, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) {
349 			printf("%s: can't create map for drq %d\n",
350 			    sc->sc_dev.dv_xname, sc->sc_drq16);
351 			return;
352 		}
353 	}
354 
355 	pparams = audio_default;
356 	rparams = audio_default;
357         sbdsp_set_params(sc, AUMODE_RECORD|AUMODE_PLAY, 0, &pparams, &rparams);
358 
359 	sbdsp_set_in_ports(sc, 1 << SB_MIC_VOL);
360 
361 	if (sc->sc_mixer_model != SBM_NONE) {
362 		/* Reset the mixer.*/
363 		sbdsp_mix_write(sc, SBP_MIX_RESET, SBP_MIX_RESET);
364                 /* And set our own default values */
365 		for (i = 0; i < SB_NDEVS; i++) {
366 			switch(i) {
367 			case SB_MIC_VOL:
368 			case SB_LINE_IN_VOL:
369 				v = 0;
370 				break;
371 			case SB_BASS:
372 			case SB_TREBLE:
373 				v = SB_ADJUST_GAIN(sc, AUDIO_MAX_GAIN/2);
374 				break;
375 			case SB_CD_IN_MUTE:
376 			case SB_MIC_IN_MUTE:
377 			case SB_LINE_IN_MUTE:
378 			case SB_MIDI_IN_MUTE:
379 			case SB_CD_SWAP:
380 			case SB_MIC_SWAP:
381 			case SB_LINE_SWAP:
382 			case SB_MIDI_SWAP:
383 			case SB_CD_OUT_MUTE:
384 			case SB_MIC_OUT_MUTE:
385 			case SB_LINE_OUT_MUTE:
386 				v = 0;
387 				break;
388 			default:
389 				v = SB_ADJUST_GAIN(sc, AUDIO_MAX_GAIN / 2);
390 				break;
391 			}
392 			sc->gain[i][SB_LEFT] = sc->gain[i][SB_RIGHT] = v;
393 			sbdsp_set_mixer_gain(sc, i);
394 		}
395 		sc->in_filter = 0;	/* no filters turned on, please */
396 	}
397 
398 	printf(": dsp v%d.%02d%s\n",
399 	       SBVER_MAJOR(sc->sc_version), SBVER_MINOR(sc->sc_version),
400 	       sc->sc_model == SB_JAZZ ? ": <Jazz16>" : "");
401 
402 	timeout_set(&sc->sc_tmo, sbdsp_to, sbdsp_to);
403 	sc->sc_fullduplex = ISSB16CLASS(sc) &&
404 		sc->sc_drq8 != -1 && sc->sc_drq16 != -1 &&
405 		sc->sc_drq8 != sc->sc_drq16;
406 }
407 
408 void
409 sbdsp_mix_write(sc, mixerport, val)
410 	struct sbdsp_softc *sc;
411 	int mixerport;
412 	int val;
413 {
414 	bus_space_tag_t iot = sc->sc_iot;
415 	bus_space_handle_t ioh = sc->sc_ioh;
416 	int s;
417 
418 	s = splaudio();
419 	bus_space_write_1(iot, ioh, SBP_MIXER_ADDR, mixerport);
420 	delay(20);
421 	bus_space_write_1(iot, ioh, SBP_MIXER_DATA, val);
422 	delay(30);
423 	splx(s);
424 }
425 
426 int
427 sbdsp_mix_read(sc, mixerport)
428 	struct sbdsp_softc *sc;
429 	int mixerport;
430 {
431 	bus_space_tag_t iot = sc->sc_iot;
432 	bus_space_handle_t ioh = sc->sc_ioh;
433 	int val;
434 	int s;
435 
436 	s = splaudio();
437 	bus_space_write_1(iot, ioh, SBP_MIXER_ADDR, mixerport);
438 	delay(20);
439 	val = bus_space_read_1(iot, ioh, SBP_MIXER_DATA);
440 	delay(30);
441 	splx(s);
442 	return val;
443 }
444 
445 /*
446  * Various routines to interface to higher level audio driver
447  */
448 
449 int
450 sbdsp_query_encoding(addr, fp)
451 	void *addr;
452 	struct audio_encoding *fp;
453 {
454 	struct sbdsp_softc *sc = addr;
455 	int emul;
456 
457 	emul = ISSB16CLASS(sc) ? 0 : AUDIO_ENCODINGFLAG_EMULATED;
458 
459 	switch (fp->index) {
460 	case 0:
461 		strcpy(fp->name, AudioEulinear);
462 		fp->encoding = AUDIO_ENCODING_ULINEAR;
463 		fp->precision = 8;
464 		fp->flags = 0;
465 		return 0;
466 	case 1:
467 		strcpy(fp->name, AudioEmulaw);
468 		fp->encoding = AUDIO_ENCODING_ULAW;
469 		fp->precision = 8;
470 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
471 		return 0;
472 	case 2:
473 		strcpy(fp->name, AudioEalaw);
474 		fp->encoding = AUDIO_ENCODING_ALAW;
475 		fp->precision = 8;
476 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
477 		return 0;
478 	case 3:
479 		strcpy(fp->name, AudioEslinear);
480 		fp->encoding = AUDIO_ENCODING_SLINEAR;
481 		fp->precision = 8;
482 		fp->flags = emul;
483 		return 0;
484         }
485         if (!ISSB16CLASS(sc) && sc->sc_model != SB_JAZZ)
486 		return EINVAL;
487 
488         switch(fp->index) {
489         case 4:
490 		strcpy(fp->name, AudioEslinear_le);
491 		fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
492 		fp->precision = 16;
493 		fp->flags = 0;
494 		return 0;
495 	case 5:
496 		strcpy(fp->name, AudioEulinear_le);
497 		fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
498 		fp->precision = 16;
499 		fp->flags = emul;
500 		return 0;
501 	case 6:
502 		strcpy(fp->name, AudioEslinear_be);
503 		fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
504 		fp->precision = 16;
505 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
506 		return 0;
507 	case 7:
508 		strcpy(fp->name, AudioEulinear_be);
509 		fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
510 		fp->precision = 16;
511 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
512 		return 0;
513 	default:
514 		return EINVAL;
515 	}
516 	return 0;
517 }
518 
519 int
520 sbdsp_set_params(addr, setmode, usemode, play, rec)
521 	void *addr;
522 	int setmode, usemode;
523 	struct audio_params *play, *rec;
524 {
525 	struct sbdsp_softc *sc = addr;
526 	struct sbmode *m;
527 	u_int rate, tc, bmode;
528 	void (*swcode) __P((void *, u_char *buf, int cnt));
529 	int factor;
530 	int model;
531 	int chan;
532 	struct audio_params *p;
533 	int mode;
534 
535 	if (sc->sc_open == SB_OPEN_MIDI)
536 		return EBUSY;
537 
538 	model = sc->sc_model;
539 	if (model > SB_16)
540 		model = SB_16;	/* later models work like SB16 */
541 
542 	/*
543 	 * Prior to the SB16, we have only one clock, so make the sample
544 	 * rates match.
545 	 */
546 	if (!ISSB16CLASS(sc) &&
547 	    play->sample_rate != rec->sample_rate &&
548 	    usemode == (AUMODE_PLAY | AUMODE_RECORD)) {
549 		if (setmode == AUMODE_PLAY) {
550 			rec->sample_rate = play->sample_rate;
551 			setmode |= AUMODE_RECORD;
552 		} else if (setmode == AUMODE_RECORD) {
553 			play->sample_rate = rec->sample_rate;
554 			setmode |= AUMODE_PLAY;
555 		} else
556 			return (EINVAL);
557 	}
558 
559 	/* Set first record info, then play info */
560 	for (mode = AUMODE_RECORD; mode != -1;
561 	     mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
562 		if ((setmode & mode) == 0)
563 			continue;
564 
565 		p = mode == AUMODE_PLAY ? play : rec;
566 		/* Locate proper commands */
567 		for(m = mode == AUMODE_PLAY ? sbpmodes : sbrmodes;
568 		    m->model != -1; m++) {
569 			if (model == m->model &&
570 			    p->channels == m->channels &&
571 			    p->precision == m->precision &&
572 			    p->sample_rate >= m->lowrate &&
573 			    p->sample_rate <= m->highrate)
574 				break;
575 		}
576 		if (m->model == -1)
577 			return EINVAL;
578 		rate = p->sample_rate;
579 		swcode = 0;
580 		factor = 1;
581 		tc = 1;
582 		bmode = -1;
583 		if (model == SB_16) {
584 			switch (p->encoding) {
585 			case AUDIO_ENCODING_SLINEAR_BE:
586 				if (p->precision == 16)
587 					swcode = swap_bytes;
588 				/* fall into */
589 			case AUDIO_ENCODING_SLINEAR_LE:
590 				bmode = SB_BMODE_SIGNED;
591 				break;
592 			case AUDIO_ENCODING_ULINEAR_BE:
593 				if (p->precision == 16)
594 					swcode = swap_bytes;
595 				/* fall into */
596 			case AUDIO_ENCODING_ULINEAR_LE:
597 				bmode = SB_BMODE_UNSIGNED;
598 				break;
599 			case AUDIO_ENCODING_ULAW:
600 				if (mode == AUMODE_PLAY) {
601 					swcode = mulaw_to_ulinear16;
602 					factor = 2;
603 					m = &sbpmodes[PLAY16];
604 				} else
605 					swcode = ulinear8_to_mulaw;
606 				bmode = SB_BMODE_UNSIGNED;
607 				break;
608 			case AUDIO_ENCODING_ALAW:
609 				if (mode == AUMODE_PLAY) {
610 					swcode = alaw_to_ulinear16;
611 					factor = 2;
612 					m = &sbpmodes[PLAY16];
613 				} else
614 					swcode = ulinear8_to_alaw;
615 				bmode = SB_BMODE_UNSIGNED;
616 				break;
617 			default:
618 				return EINVAL;
619 			}
620 			if (p->channels == 2)
621 				bmode |= SB_BMODE_STEREO;
622 		} else if (m->model == SB_JAZZ && m->precision == 16) {
623 			switch (p->encoding) {
624 			case AUDIO_ENCODING_SLINEAR_LE:
625 				break;
626 			case AUDIO_ENCODING_ULINEAR_LE:
627 				swcode = change_sign16;
628 				break;
629 			case AUDIO_ENCODING_SLINEAR_BE:
630 				swcode = swap_bytes;
631 				break;
632 			case AUDIO_ENCODING_ULINEAR_BE:
633 				swcode = mode == AUMODE_PLAY ?
634 					swap_bytes_change_sign16 : change_sign16_swap_bytes;
635 				break;
636 			case AUDIO_ENCODING_ULAW:
637 				swcode = mode == AUMODE_PLAY ?
638 					mulaw_to_ulinear8 : ulinear8_to_mulaw;
639 				break;
640 			case AUDIO_ENCODING_ALAW:
641 				swcode = mode == AUMODE_PLAY ?
642 					alaw_to_ulinear8 : ulinear8_to_alaw;
643 				break;
644 			default:
645 				return EINVAL;
646 			}
647 			tc = SB_RATE_TO_TC(p->sample_rate * p->channels);
648 			p->sample_rate = SB_TC_TO_RATE(tc) / p->channels;
649 		} else {
650 			switch (p->encoding) {
651 			case AUDIO_ENCODING_SLINEAR_BE:
652 			case AUDIO_ENCODING_SLINEAR_LE:
653 				swcode = change_sign8;
654 				break;
655 			case AUDIO_ENCODING_ULINEAR_BE:
656 			case AUDIO_ENCODING_ULINEAR_LE:
657 				break;
658 			case AUDIO_ENCODING_ULAW:
659 				swcode = mode == AUMODE_PLAY ?
660 					mulaw_to_ulinear8 : ulinear8_to_mulaw;
661 				break;
662 			case AUDIO_ENCODING_ALAW:
663 				swcode = mode == AUMODE_PLAY ?
664 					alaw_to_ulinear8 : ulinear8_to_alaw;
665 				break;
666 			default:
667 				return EINVAL;
668 			}
669 			tc = SB_RATE_TO_TC(p->sample_rate * p->channels);
670 			p->sample_rate = SB_TC_TO_RATE(tc) / p->channels;
671 		}
672 
673 		chan = m->precision == 16 ? sc->sc_drq16 : sc->sc_drq8;
674 		if (mode == AUMODE_PLAY) {
675 			sc->sc_o.rate = rate;
676 			sc->sc_o.tc = tc;
677 			sc->sc_o.modep = m;
678 			sc->sc_o.bmode = bmode;
679 			sc->sc_o.dmachan = chan;
680 		} else {
681 			sc->sc_i.rate = rate;
682 			sc->sc_i.tc = tc;
683 			sc->sc_i.modep = m;
684 			sc->sc_i.bmode = bmode;
685 			sc->sc_i.dmachan = chan;
686 		}
687 
688 		p->sw_code = swcode;
689 		p->factor = factor;
690 		DPRINTF(("sbdsp_set_params: model=%d, mode=%d, rate=%ld, prec=%d, chan=%d, enc=%d -> tc=%02x, cmd=%02x, bmode=%02x, cmdchan=%02x, swcode=%p, factor=%d\n",
691 			 sc->sc_model, mode, p->sample_rate, p->precision, p->channels,
692 			 p->encoding, tc, m->cmd, bmode, m->cmdchan, swcode, factor));
693 
694 	}
695 
696 	/*
697 	 * XXX
698 	 * Should wait for chip to be idle.
699 	 */
700 	sc->sc_i.run = SB_NOTRUNNING;
701 	sc->sc_o.run = SB_NOTRUNNING;
702 
703 	if (sc->sc_fullduplex &&
704 	    usemode == (AUMODE_PLAY | AUMODE_RECORD) &&
705 	    sc->sc_i.dmachan == sc->sc_o.dmachan) {
706 		DPRINTF(("sbdsp_set_params: fd=%d, usemode=%d, idma=%d, odma=%d\n", sc->sc_fullduplex, usemode, sc->sc_i.dmachan, sc->sc_o.dmachan));
707 		if (sc->sc_o.dmachan == sc->sc_drq8) {
708 			/* Use 16 bit DMA for playing by expanding the samples. */
709 			play->sw_code = linear8_to_linear16;
710 			play->factor = 2;
711 			sc->sc_o.modep = &sbpmodes[PLAY16];
712 			sc->sc_o.dmachan = sc->sc_drq16;
713 		} else {
714 			return EINVAL;
715 		}
716 	}
717 	DPRINTF(("sbdsp_set_params ichan=%d, ochan=%d\n",
718 		 sc->sc_i.dmachan, sc->sc_o.dmachan));
719 
720 	return 0;
721 }
722 
723 void
724 sbdsp_set_ifilter(addr, which)
725 	void *addr;
726 	int which;
727 {
728 	struct sbdsp_softc *sc = addr;
729 	int mixval;
730 
731 	mixval = sbdsp_mix_read(sc, SBP_INFILTER) & ~SBP_IFILTER_MASK;
732 	switch (which) {
733 	case 0:
734 		mixval |= SBP_FILTER_OFF;
735 		break;
736 	case SB_TREBLE:
737 		mixval |= SBP_FILTER_ON | SBP_IFILTER_HIGH;
738 		break;
739 	case SB_BASS:
740 		mixval |= SBP_FILTER_ON | SBP_IFILTER_LOW;
741 		break;
742 	default:
743 		return;
744 	}
745 	sc->in_filter = mixval & SBP_IFILTER_MASK;
746 	sbdsp_mix_write(sc, SBP_INFILTER, mixval);
747 }
748 
749 int
750 sbdsp_get_ifilter(addr)
751 	void *addr;
752 {
753 	struct sbdsp_softc *sc = addr;
754 
755 	sc->in_filter =
756 		sbdsp_mix_read(sc, SBP_INFILTER) & SBP_IFILTER_MASK;
757 	switch (sc->in_filter) {
758 	case SBP_FILTER_ON|SBP_IFILTER_HIGH:
759 		return SB_TREBLE;
760 	case SBP_FILTER_ON|SBP_IFILTER_LOW:
761 		return SB_BASS;
762 	default:
763 		return 0;
764 	}
765 }
766 
767 int
768 sbdsp_set_in_ports(sc, mask)
769 	struct sbdsp_softc *sc;
770 	int mask;
771 {
772 	int bitsl, bitsr;
773 	int sbport;
774 
775 	if (sc->sc_open == SB_OPEN_MIDI)
776 		return EBUSY;
777 
778 	DPRINTF(("sbdsp_set_in_ports: model=%d, mask=%x\n",
779 		 sc->sc_mixer_model, mask));
780 
781 	switch(sc->sc_mixer_model) {
782 	case SBM_NONE:
783 		return EINVAL;
784 	case SBM_CT1335:
785 		if (mask != (1 << SB_MIC_VOL))
786 			return EINVAL;
787 		break;
788 	case SBM_CT1345:
789 		switch (mask) {
790 		case 1 << SB_MIC_VOL:
791 			sbport = SBP_FROM_MIC;
792 			break;
793 		case 1 << SB_LINE_IN_VOL:
794 			sbport = SBP_FROM_LINE;
795 			break;
796 		case 1 << SB_CD_VOL:
797 			sbport = SBP_FROM_CD;
798 			break;
799 		default:
800 			return (EINVAL);
801 		}
802 		sbdsp_mix_write(sc, SBP_RECORD_SOURCE, sbport | sc->in_filter);
803 		break;
804 	case SBM_CT1XX5:
805 	case SBM_CT1745:
806 		if (mask & ~((1<<SB_MIDI_VOL) | (1<<SB_LINE_IN_VOL) |
807 			     (1<<SB_CD_VOL) | (1<<SB_MIC_VOL)))
808 			return EINVAL;
809 		bitsr = 0;
810 		if (mask & (1<<SB_MIDI_VOL))    bitsr |= SBP_MIDI_SRC_R;
811 		if (mask & (1<<SB_LINE_IN_VOL)) bitsr |= SBP_LINE_SRC_R;
812 		if (mask & (1<<SB_CD_VOL))      bitsr |= SBP_CD_SRC_R;
813 		bitsl = SB_SRC_R_TO_L(bitsr);
814 		if (mask & (1<<SB_MIC_VOL)) {
815 			bitsl |= SBP_MIC_SRC;
816 			bitsr |= SBP_MIC_SRC;
817 		}
818 		sbdsp_mix_write(sc, SBP_RECORD_SOURCE_L, bitsl);
819 		sbdsp_mix_write(sc, SBP_RECORD_SOURCE_R, bitsr);
820 		break;
821 	}
822 	sc->in_mask = mask;
823 
824 	return 0;
825 }
826 
827 int
828 sbdsp_speaker_ctl(addr, newstate)
829 	void *addr;
830 	int newstate;
831 {
832 	struct sbdsp_softc *sc = addr;
833 
834 	if (sc->sc_open == SB_OPEN_MIDI)
835 		return EBUSY;
836 
837 	if ((newstate == SPKR_ON) &&
838 	    (sc->spkr_state == SPKR_OFF)) {
839 		sbdsp_spkron(sc);
840 		sc->spkr_state = SPKR_ON;
841 	}
842 	if ((newstate == SPKR_OFF) &&
843 	    (sc->spkr_state == SPKR_ON)) {
844 		sbdsp_spkroff(sc);
845 		sc->spkr_state = SPKR_OFF;
846 	}
847 	return 0;
848 }
849 
850 int
851 sbdsp_round_blocksize(addr, blk)
852 	void *addr;
853 	int blk;
854 {
855 	return blk & -4;	/* round to biggest sample size */
856 }
857 
858 int
859 sbdsp_open(addr, flags)
860 	void *addr;
861 	int flags;
862 {
863 	struct sbdsp_softc *sc = addr;
864 
865         DPRINTF(("sbdsp_open: sc=%p\n", sc));
866 
867 	if (sc->sc_open != SB_CLOSED)
868 		return EBUSY;
869 	if (sbdsp_reset(sc) != 0)
870 		return EIO;
871 
872 	sc->sc_open = SB_OPEN_AUDIO;
873 	sc->sc_openflags = flags;
874 	sc->sc_intrm = 0;
875 	if (ISSBPRO(sc) &&
876 	    sbdsp_wdsp(sc, SB_DSP_RECORD_MONO) < 0) {
877 		DPRINTF(("sbdsp_open: can't set mono mode\n"));
878 		/* we'll readjust when it's time for DMA. */
879 	}
880 
881 	/*
882 	 * Leave most things as they were; users must change things if
883 	 * the previous process didn't leave it they way they wanted.
884 	 * Looked at another way, it's easy to set up a configuration
885 	 * in one program and leave it for another to inherit.
886 	 */
887 	DPRINTF(("sbdsp_open: opened\n"));
888 
889 	return 0;
890 }
891 
892 void
893 sbdsp_close(addr)
894 	void *addr;
895 {
896 	struct sbdsp_softc *sc = addr;
897 
898         DPRINTF(("sbdsp_close: sc=%p\n", sc));
899 
900 	sc->sc_open = SB_CLOSED;
901 	sbdsp_spkroff(sc);
902 	sc->spkr_state = SPKR_OFF;
903 	sc->sc_intr8 = 0;
904 	sc->sc_intr16 = 0;
905 	sc->sc_intrm = 0;
906 	sbdsp_haltdma(sc);
907 
908 	DPRINTF(("sbdsp_close: closed\n"));
909 }
910 
911 /*
912  * Lower-level routines
913  */
914 
915 /*
916  * Reset the card.
917  * Return non-zero if the card isn't detected.
918  */
919 int
920 sbdsp_reset(sc)
921 	struct sbdsp_softc *sc;
922 {
923 	bus_space_tag_t iot = sc->sc_iot;
924 	bus_space_handle_t ioh = sc->sc_ioh;
925 
926 	sc->sc_intr8 = 0;
927 	sc->sc_intr16 = 0;
928 	if (sc->sc_i.run != SB_NOTRUNNING) {
929 		isa_dmaabort(sc->sc_isa, sc->sc_i.dmachan);
930 		sc->sc_i.run = SB_NOTRUNNING;
931 	}
932 	if (sc->sc_o.run != SB_NOTRUNNING) {
933 		isa_dmaabort(sc->sc_isa, sc->sc_o.dmachan);
934 		sc->sc_o.run = SB_NOTRUNNING;
935 	}
936 
937 	/*
938 	 * See SBK, section 11.3.
939 	 * We pulse a reset signal into the card.
940 	 * Gee, what a brilliant hardware design.
941 	 */
942 	bus_space_write_1(iot, ioh, SBP_DSP_RESET, 1);
943 	delay(10);
944 	bus_space_write_1(iot, ioh, SBP_DSP_RESET, 0);
945 	delay(30);
946 	if (sbdsp_rdsp(sc) != SB_MAGIC)
947 		return -1;
948 
949 	return 0;
950 }
951 
952 /*
953  * Write a byte to the dsp.
954  * We are at the mercy of the card as we use a
955  * polling loop and wait until it can take the byte.
956  */
957 int
958 sbdsp_wdsp(sc, v)
959 	struct sbdsp_softc *sc;
960 	int v;
961 {
962 	bus_space_tag_t iot = sc->sc_iot;
963 	bus_space_handle_t ioh = sc->sc_ioh;
964 	int i;
965 	u_char x;
966 
967 	for (i = SBDSP_NPOLL; --i >= 0; ) {
968 		x = bus_space_read_1(iot, ioh, SBP_DSP_WSTAT);
969 		delay(10);
970 		if ((x & SB_DSP_BUSY) == 0) {
971 			bus_space_write_1(iot, ioh, SBP_DSP_WRITE, v);
972 			delay(10);
973 			return 0;
974 		}
975 	}
976 	++sberr.wdsp;
977 	return -1;
978 }
979 
980 /*
981  * Read a byte from the DSP, using polling.
982  */
983 int
984 sbdsp_rdsp(sc)
985 	struct sbdsp_softc *sc;
986 {
987 	bus_space_tag_t iot = sc->sc_iot;
988 	bus_space_handle_t ioh = sc->sc_ioh;
989 	int i;
990 	u_char x;
991 
992 	for (i = SBDSP_NPOLL; --i >= 0; ) {
993 		x = bus_space_read_1(iot, ioh, SBP_DSP_RSTAT);
994 		delay(10);
995 		if (x & SB_DSP_READY) {
996 			x = bus_space_read_1(iot, ioh, SBP_DSP_READ);
997 			delay(10);
998 			return x;
999 		}
1000 	}
1001 	++sberr.rdsp;
1002 	return -1;
1003 }
1004 
1005 /*
1006  * Doing certain things (like toggling the speaker) make
1007  * the SB hardware go away for a while, so pause a little.
1008  */
1009 void
1010 sbdsp_to(arg)
1011 	void *arg;
1012 {
1013 	wakeup(arg);
1014 }
1015 
1016 void
1017 sbdsp_pause(sc)
1018 	struct sbdsp_softc *sc;
1019 {
1020 	extern int hz;
1021 
1022 	timeout_add(&sc->sc_tmo, hz/8);
1023 	(void)tsleep(sbdsp_to, PWAIT, "sbpause", 0);
1024 }
1025 
1026 /*
1027  * Turn on the speaker.  The SBK documention says this operation
1028  * can take up to 1/10 of a second.  Higher level layers should
1029  * probably let the task sleep for this amount of time after
1030  * calling here.  Otherwise, things might not work (because
1031  * sbdsp_wdsp() and sbdsp_rdsp() will probably timeout.)
1032  *
1033  * These engineers had their heads up their ass when
1034  * they designed this card.
1035  */
1036 void
1037 sbdsp_spkron(sc)
1038 	struct sbdsp_softc *sc;
1039 {
1040 	(void)sbdsp_wdsp(sc, SB_DSP_SPKR_ON);
1041 	sbdsp_pause(sc);
1042 }
1043 
1044 /*
1045  * Turn off the speaker; see comment above.
1046  */
1047 void
1048 sbdsp_spkroff(sc)
1049 	struct sbdsp_softc *sc;
1050 {
1051 	(void)sbdsp_wdsp(sc, SB_DSP_SPKR_OFF);
1052 	sbdsp_pause(sc);
1053 }
1054 
1055 /*
1056  * Read the version number out of the card.
1057  * Store version information in the softc.
1058  */
1059 void
1060 sbversion(sc)
1061 	struct sbdsp_softc *sc;
1062 {
1063 	int v;
1064 
1065 	sc->sc_model = SB_UNK;
1066 	sc->sc_version = 0;
1067 	if (sbdsp_wdsp(sc, SB_DSP_VERSION) < 0)
1068 		return;
1069 	v = sbdsp_rdsp(sc) << 8;
1070 	v |= sbdsp_rdsp(sc);
1071 	if (v < 0)
1072 		return;
1073 	sc->sc_version = v;
1074 	switch(SBVER_MAJOR(v)) {
1075 	case 1:
1076 		sc->sc_mixer_model = SBM_NONE;
1077 		sc->sc_model = SB_1;
1078 		break;
1079 	case 2:
1080 		/* Some SB2 have a mixer, some don't. */
1081 		sbdsp_mix_write(sc, SBP_1335_MASTER_VOL, 0x04);
1082 		sbdsp_mix_write(sc, SBP_1335_MIDI_VOL,   0x06);
1083 		/* Check if we can read back the mixer values. */
1084 		if ((sbdsp_mix_read(sc, SBP_1335_MASTER_VOL) & 0x0e) == 0x04 &&
1085 		    (sbdsp_mix_read(sc, SBP_1335_MIDI_VOL)   & 0x0e) == 0x06)
1086 			sc->sc_mixer_model = SBM_CT1335;
1087 		else
1088 			sc->sc_mixer_model = SBM_NONE;
1089 		if (SBVER_MINOR(v) == 0)
1090 			sc->sc_model = SB_20;
1091 		else
1092 			sc->sc_model = SB_2x;
1093 		break;
1094 	case 3:
1095 		sc->sc_mixer_model = SBM_CT1345;
1096 		sc->sc_model = SB_PRO;
1097 		break;
1098 	case 4:
1099 #if 0
1100 /* XXX This does not work */
1101 		/* Most SB16 have a tone controls, but some don't. */
1102 		sbdsp_mix_write(sc, SB16P_TREBLE_L, 0x80);
1103 		/* Check if we can read back the mixer value. */
1104 		if ((sbdsp_mix_read(sc, SB16P_TREBLE_L) & 0xf0) == 0x80)
1105 			sc->sc_mixer_model = SBM_CT1745;
1106 		else
1107 			sc->sc_mixer_model = SBM_CT1XX5;
1108 #else
1109 		sc->sc_mixer_model = SBM_CT1745;
1110 #endif
1111 #if 0
1112 /* XXX figure out a good way of determining the model */
1113 		/* XXX what about SB_32 */
1114 		if (SBVER_MINOR(v) == 16)
1115 			sc->sc_model = SB_64;
1116 		else
1117 #endif
1118 			sc->sc_model = SB_16;
1119 		break;
1120 	}
1121 }
1122 
1123 /*
1124  * Halt a DMA in progress.
1125  */
1126 int
1127 sbdsp_haltdma(addr)
1128 	void *addr;
1129 {
1130 	struct sbdsp_softc *sc = addr;
1131 
1132 	DPRINTF(("sbdsp_haltdma: sc=%p\n", sc));
1133 
1134 	sbdsp_reset(sc);
1135 	return 0;
1136 }
1137 
1138 int
1139 sbdsp_set_timeconst(sc, tc)
1140 	struct sbdsp_softc *sc;
1141 	int tc;
1142 {
1143 	DPRINTF(("sbdsp_set_timeconst: sc=%p tc=%d\n", sc, tc));
1144 
1145 	if (sbdsp_wdsp(sc, SB_DSP_TIMECONST) < 0 ||
1146 	    sbdsp_wdsp(sc, tc) < 0)
1147 		return EIO;
1148 
1149 	return 0;
1150 }
1151 
1152 int
1153 sbdsp16_set_rate(sc, cmd, rate)
1154 	struct sbdsp_softc *sc;
1155 	int cmd, rate;
1156 {
1157 	DPRINTF(("sbdsp16_set_rate: sc=%p cmd=0x%02x rate=%d\n", sc, cmd, rate));
1158 
1159 	if (sbdsp_wdsp(sc, cmd) < 0 ||
1160 	    sbdsp_wdsp(sc, rate >> 8) < 0 ||
1161 	    sbdsp_wdsp(sc, rate) < 0)
1162 		return EIO;
1163 	return 0;
1164 }
1165 
1166 int
1167 sbdsp_trigger_input(addr, start, end, blksize, intr, arg, param)
1168 	void *addr;
1169 	void *start, *end;
1170 	int blksize;
1171 	void (*intr) __P((void *));
1172 	void *arg;
1173 	struct audio_params *param;
1174 {
1175 	struct sbdsp_softc *sc = addr;
1176 	int stereo = param->channels == 2;
1177 	int width = param->precision * param->factor;
1178 	int filter;
1179 
1180 #ifdef DIAGNOSTIC
1181 	if (stereo && (blksize & 1)) {
1182 		DPRINTF(("stereo record odd bytes (%d)\n", blksize));
1183 		return (EIO);
1184 	}
1185 #endif
1186 
1187 	sc->sc_intrr = intr;
1188 	sc->sc_argr = arg;
1189 
1190 	if (width == 8) {
1191 #ifdef DIAGNOSTIC
1192 		if (sc->sc_i.dmachan != sc->sc_drq8) {
1193 			printf("sbdsp_trigger_input: width=%d bad chan %d\n",
1194 			    width, sc->sc_i.dmachan);
1195 			return (EIO);
1196 		}
1197 #endif
1198 		sc->sc_intr8 = sbdsp_block_input;
1199 		sc->sc_arg8 = addr;
1200 	} else {
1201 #ifdef DIAGNOSTIC
1202 		if (sc->sc_i.dmachan != sc->sc_drq16) {
1203 			printf("sbdsp_trigger_input: width=%d bad chan %d\n",
1204 			    width, sc->sc_i.dmachan);
1205 			return (EIO);
1206 		}
1207 #endif
1208 		sc->sc_intr16 = sbdsp_block_input;
1209 		sc->sc_arg16 = addr;
1210 	}
1211 
1212 	if ((sc->sc_model == SB_JAZZ) ? (sc->sc_i.dmachan > 3) : (width == 16))
1213 		blksize >>= 1;
1214 	--blksize;
1215 	sc->sc_i.blksize = blksize;
1216 
1217 	if (ISSBPRO(sc)) {
1218 		if (sbdsp_wdsp(sc, sc->sc_i.modep->cmdchan) < 0)
1219 			return (EIO);
1220 		filter = stereo ? SBP_FILTER_OFF : sc->in_filter;
1221 		sbdsp_mix_write(sc, SBP_INFILTER,
1222 		    (sbdsp_mix_read(sc, SBP_INFILTER) & ~SBP_IFILTER_MASK) |
1223 		    filter);
1224 	}
1225 
1226 	if (ISSB16CLASS(sc)) {
1227 		if (sbdsp16_set_rate(sc, SB_DSP16_INPUTRATE, sc->sc_i.rate)) {
1228 			DPRINTF(("sbdsp_trigger_input: rate=%d set failed\n",
1229 				 sc->sc_i.rate));
1230 			return (EIO);
1231 		}
1232 	} else {
1233 		if (sbdsp_set_timeconst(sc, sc->sc_i.tc)) {
1234 			DPRINTF(("sbdsp_trigger_input: tc=%d set failed\n",
1235 				 sc->sc_i.rate));
1236 			return (EIO);
1237 		}
1238 	}
1239 
1240 	DPRINTF(("sbdsp: dma start loop input start=%p end=%p chan=%d\n",
1241 	    start, end, sc->sc_i.dmachan));
1242 	isa_dmastart(sc->sc_isa, sc->sc_i.dmachan, start, end - start,
1243 	    NULL, DMAMODE_READ | DMAMODE_LOOP, BUS_DMA_NOWAIT);
1244 
1245 	return sbdsp_block_input(addr);
1246 }
1247 
1248 int
1249 sbdsp_block_input(addr)
1250 	void *addr;
1251 {
1252 	struct sbdsp_softc *sc = addr;
1253 	int cc = sc->sc_i.blksize;
1254 
1255 	DPRINTFN(2, ("sbdsp_block_input: sc=%p cc=%d\n", addr, cc));
1256 
1257 	if (sc->sc_i.run != SB_NOTRUNNING)
1258 		sc->sc_intrr(sc->sc_argr);
1259 
1260 	if (sc->sc_model == SB_1) {
1261 		/* Non-looping mode, start DMA */
1262 		if (sbdsp_wdsp(sc, sc->sc_i.modep->cmd) < 0 ||
1263 		    sbdsp_wdsp(sc, cc) < 0 ||
1264 		    sbdsp_wdsp(sc, cc >> 8) < 0) {
1265 			DPRINTF(("sbdsp_block_input: SB1 DMA start failed\n"));
1266 			return (EIO);
1267 		}
1268 		sc->sc_i.run = SB_RUNNING;
1269 	} else if (sc->sc_i.run == SB_NOTRUNNING) {
1270 		/* Initialize looping PCM */
1271 		if (ISSB16CLASS(sc)) {
1272 			DPRINTFN(3, ("sbdsp16 input command cmd=0x%02x bmode=0x%02x cc=%d\n",
1273 			    sc->sc_i.modep->cmd, sc->sc_i.bmode, cc));
1274 			if (sbdsp_wdsp(sc, sc->sc_i.modep->cmd) < 0 ||
1275 			    sbdsp_wdsp(sc, sc->sc_i.bmode) < 0 ||
1276 			    sbdsp_wdsp(sc, cc) < 0 ||
1277 			    sbdsp_wdsp(sc, cc >> 8) < 0) {
1278 				DPRINTF(("sbdsp_block_input: SB16 DMA start failed\n"));
1279 				return (EIO);
1280 			}
1281 		} else {
1282 			DPRINTF(("sbdsp_block_input: set blocksize=%d\n", cc));
1283 			if (sbdsp_wdsp(sc, SB_DSP_BLOCKSIZE) < 0 ||
1284 			    sbdsp_wdsp(sc, cc) < 0 ||
1285 			    sbdsp_wdsp(sc, cc >> 8) < 0) {
1286 				DPRINTF(("sbdsp_block_input: SB2 DMA blocksize failed\n"));
1287 				return (EIO);
1288 			}
1289 			if (sbdsp_wdsp(sc, sc->sc_i.modep->cmd) < 0) {
1290 				DPRINTF(("sbdsp_block_input: SB2 DMA start failed\n"));
1291 				return (EIO);
1292 			}
1293 		}
1294 		sc->sc_i.run = SB_LOOPING;
1295 	}
1296 
1297 	return (0);
1298 }
1299 
1300 int
1301 sbdsp_trigger_output(addr, start, end, blksize, intr, arg, param)
1302 	void *addr;
1303 	void *start, *end;
1304 	int blksize;
1305 	void (*intr) __P((void *));
1306 	void *arg;
1307 	struct audio_params *param;
1308 {
1309 	struct sbdsp_softc *sc = addr;
1310 	int stereo = param->channels == 2;
1311 	int width = param->precision * param->factor;
1312 	int cmd;
1313 
1314 #ifdef DIAGNOSTIC
1315 	if (stereo && (blksize & 1)) {
1316 		DPRINTF(("stereo playback odd bytes (%d)\n", blksize));
1317 		return (EIO);
1318 	}
1319 #endif
1320 
1321 	sc->sc_intrp = intr;
1322 	sc->sc_argp = arg;
1323 
1324 	if (width == 8) {
1325 #ifdef DIAGNOSTIC
1326 		if (sc->sc_o.dmachan != sc->sc_drq8) {
1327 			printf("sbdsp_trigger_output: width=%d bad chan %d\n",
1328 			    width, sc->sc_o.dmachan);
1329 			return (EIO);
1330 		}
1331 #endif
1332 		sc->sc_intr8 = sbdsp_block_output;
1333 		sc->sc_arg8 = addr;
1334 	} else {
1335 #ifdef DIAGNOSTIC
1336 		if (sc->sc_o.dmachan != sc->sc_drq16) {
1337 			printf("sbdsp_trigger_output: width=%d bad chan %d\n",
1338 			    width, sc->sc_o.dmachan);
1339 			return (EIO);
1340 		}
1341 #endif
1342 		sc->sc_intr16 = sbdsp_block_output;
1343 		sc->sc_arg16 = addr;
1344 	}
1345 
1346 	if ((sc->sc_model == SB_JAZZ) ? (sc->sc_o.dmachan > 3) : (width == 16))
1347 		blksize >>= 1;
1348 	--blksize;
1349 	sc->sc_o.blksize = blksize;
1350 
1351 	if (ISSBPRO(sc)) {
1352 		/* make sure we re-set stereo mixer bit when we start output. */
1353 		sbdsp_mix_write(sc, SBP_STEREO,
1354 		    (sbdsp_mix_read(sc, SBP_STEREO) & ~SBP_PLAYMODE_MASK) |
1355 		    (stereo ?  SBP_PLAYMODE_STEREO : SBP_PLAYMODE_MONO));
1356 		cmd = sc->sc_o.modep->cmdchan;
1357 		if (cmd && sbdsp_wdsp(sc, cmd) < 0)
1358 			return (EIO);
1359 	}
1360 
1361 	if (ISSB16CLASS(sc)) {
1362 		if (sbdsp16_set_rate(sc, SB_DSP16_OUTPUTRATE, sc->sc_o.rate)) {
1363 			DPRINTF(("sbdsp_trigger_output: rate=%d set failed\n",
1364 				 sc->sc_o.rate));
1365 			return (EIO);
1366 		}
1367 	} else {
1368 		if (sbdsp_set_timeconst(sc, sc->sc_o.tc)) {
1369 			DPRINTF(("sbdsp_trigger_output: tc=%d set failed\n",
1370 				 sc->sc_o.rate));
1371 			return (EIO);
1372 		}
1373 	}
1374 
1375 	DPRINTF(("sbdsp: dma start loop output start=%p end=%p chan=%d\n",
1376 	    start, end, sc->sc_o.dmachan));
1377 	isa_dmastart(sc->sc_isa, sc->sc_o.dmachan, start, end - start,
1378 	    NULL, DMAMODE_WRITE | DMAMODE_LOOP, BUS_DMA_NOWAIT);
1379 
1380 	return sbdsp_block_output(addr);
1381 }
1382 
1383 int
1384 sbdsp_block_output(addr)
1385 	void *addr;
1386 {
1387 	struct sbdsp_softc *sc = addr;
1388 	int cc = sc->sc_o.blksize;
1389 
1390 	DPRINTFN(2, ("sbdsp_block_output: sc=%p cc=%d\n", addr, cc));
1391 
1392 	if (sc->sc_o.run != SB_NOTRUNNING)
1393 		sc->sc_intrp(sc->sc_argp);
1394 
1395 	if (sc->sc_model == SB_1) {
1396 		/* Non-looping mode, initialized. Start DMA and PCM */
1397 		if (sbdsp_wdsp(sc, sc->sc_o.modep->cmd) < 0 ||
1398 		    sbdsp_wdsp(sc, cc) < 0 ||
1399 		    sbdsp_wdsp(sc, cc >> 8) < 0) {
1400 			DPRINTF(("sbdsp_block_output: SB1 DMA start failed\n"));
1401 			return (EIO);
1402 		}
1403 		sc->sc_o.run = SB_RUNNING;
1404 	} else if (sc->sc_o.run == SB_NOTRUNNING) {
1405 		/* Initialize looping PCM */
1406 		if (ISSB16CLASS(sc)) {
1407 			DPRINTF(("sbdsp_block_output: SB16 cmd=0x%02x bmode=0x%02x cc=%d\n",
1408 			    sc->sc_o.modep->cmd,sc->sc_o.bmode, cc));
1409 			if (sbdsp_wdsp(sc, sc->sc_o.modep->cmd) < 0 ||
1410 			    sbdsp_wdsp(sc, sc->sc_o.bmode) < 0 ||
1411 			    sbdsp_wdsp(sc, cc) < 0 ||
1412 			    sbdsp_wdsp(sc, cc >> 8) < 0) {
1413 				DPRINTF(("sbdsp_block_output: SB16 DMA start failed\n"));
1414 				return (EIO);
1415 			}
1416 		} else {
1417 			DPRINTF(("sbdsp_block_output: set blocksize=%d\n", cc));
1418 			if (sbdsp_wdsp(sc, SB_DSP_BLOCKSIZE) < 0 ||
1419 			    sbdsp_wdsp(sc, cc) < 0 ||
1420 			    sbdsp_wdsp(sc, cc >> 8) < 0) {
1421 				DPRINTF(("sbdsp_block_output: SB2 DMA blocksize failed\n"));
1422 				return (EIO);
1423 			}
1424 			if (sbdsp_wdsp(sc, sc->sc_o.modep->cmd) < 0) {
1425 				DPRINTF(("sbdsp_block_output: SB2 DMA start failed\n"));
1426 				return (EIO);
1427 			}
1428 		}
1429 		sc->sc_o.run = SB_LOOPING;
1430 	}
1431 
1432 	return (0);
1433 }
1434 
1435 /*
1436  * Only the DSP unit on the sound blaster generates interrupts.
1437  * There are three cases of interrupt: reception of a midi byte
1438  * (when mode is enabled), completion of dma transmission, or
1439  * completion of a dma reception.
1440  *
1441  * If there is interrupt sharing or a spurious interrupt occurs
1442  * there is no way to distinguish this on an SB2.  So if you have
1443  * an SB2 and experience problems, buy an SB16 (it's only $40).
1444  */
1445 int
1446 sbdsp_intr(arg)
1447 	void *arg;
1448 {
1449 	struct sbdsp_softc *sc = arg;
1450 	u_char irq;
1451 
1452 	DPRINTFN(2, ("sbdsp_intr: intr8=%p, intr16=%p\n",
1453 		   sc->sc_intr8, sc->sc_intr16));
1454 	if (ISSB16CLASS(sc)) {
1455 		irq = sbdsp_mix_read(sc, SBP_IRQ_STATUS);
1456 		if ((irq & (SBP_IRQ_DMA8 | SBP_IRQ_DMA16 | SBP_IRQ_MPU401)) == 0) {
1457 			DPRINTF(("sbdsp_intr: Spurious interrupt 0x%x\n", irq));
1458 			return 0;
1459 		}
1460 	} else {
1461 		/* XXXX CHECK FOR INTERRUPT */
1462 		irq = SBP_IRQ_DMA8;
1463 	}
1464 
1465 	sc->sc_interrupts++;
1466 	delay(10);		/* XXX why? */
1467 
1468 	/* clear interrupt */
1469 	if (irq & SBP_IRQ_DMA8) {
1470 		bus_space_read_1(sc->sc_iot, sc->sc_ioh, SBP_DSP_IRQACK8);
1471 		if (sc->sc_intr8)
1472 			sc->sc_intr8(sc->sc_arg8);
1473 	}
1474 	if (irq & SBP_IRQ_DMA16) {
1475 		bus_space_read_1(sc->sc_iot, sc->sc_ioh, SBP_DSP_IRQACK16);
1476 		if (sc->sc_intr16)
1477 			sc->sc_intr16(sc->sc_arg16);
1478 	}
1479 #if NMIDI > 0
1480 	if ((irq & SBP_IRQ_MPU401) && sc->sc_hasmpu) {
1481 		mpu_intr(&sc->sc_mpu_sc);
1482 	}
1483 #endif
1484 	return 1;
1485 }
1486 
1487 /* Like val & mask, but make sure the result is correctly rounded. */
1488 #define MAXVAL 256
1489 static int
1490 sbdsp_adjust(val, mask)
1491 	int val, mask;
1492 {
1493 	val += (MAXVAL - mask) >> 1;
1494 	if (val >= MAXVAL)
1495 		val = MAXVAL-1;
1496 	return val & mask;
1497 }
1498 
1499 void
1500 sbdsp_set_mixer_gain(sc, port)
1501 	struct sbdsp_softc *sc;
1502 	int port;
1503 {
1504 	int src, gain;
1505 
1506 	switch(sc->sc_mixer_model) {
1507 	case SBM_NONE:
1508 		return;
1509 	case SBM_CT1335:
1510 		gain = SB_1335_GAIN(sc->gain[port][SB_LEFT]);
1511 		switch(port) {
1512 		case SB_MASTER_VOL:
1513 			src = SBP_1335_MASTER_VOL;
1514 			break;
1515 		case SB_MIDI_VOL:
1516 			src = SBP_1335_MIDI_VOL;
1517 			break;
1518 		case SB_CD_VOL:
1519 			src = SBP_1335_CD_VOL;
1520 			break;
1521 		case SB_VOICE_VOL:
1522 			src = SBP_1335_VOICE_VOL;
1523 			gain = SB_1335_MASTER_GAIN(sc->gain[port][SB_LEFT]);
1524 			break;
1525 		default:
1526 			return;
1527 		}
1528 		sbdsp_mix_write(sc, src, gain);
1529 		break;
1530 	case SBM_CT1345:
1531 		gain = SB_STEREO_GAIN(sc->gain[port][SB_LEFT],
1532 				      sc->gain[port][SB_RIGHT]);
1533 		switch (port) {
1534 		case SB_MIC_VOL:
1535 			src = SBP_MIC_VOL;
1536 			gain = SB_MIC_GAIN(sc->gain[port][SB_LEFT]);
1537 			break;
1538 		case SB_MASTER_VOL:
1539 			src = SBP_MASTER_VOL;
1540 			break;
1541 		case SB_LINE_IN_VOL:
1542 			src = SBP_LINE_VOL;
1543 			break;
1544 		case SB_VOICE_VOL:
1545 			src = SBP_VOICE_VOL;
1546 			break;
1547 		case SB_MIDI_VOL:
1548 			src = SBP_MIDI_VOL;
1549 			break;
1550 		case SB_CD_VOL:
1551 			src = SBP_CD_VOL;
1552 			break;
1553 		default:
1554 			return;
1555 		}
1556 		sbdsp_mix_write(sc, src, gain);
1557 		break;
1558 	case SBM_CT1XX5:
1559 	case SBM_CT1745:
1560 		switch (port) {
1561 		case SB_MIC_VOL:
1562 			src = SB16P_MIC_L;
1563 			break;
1564 		case SB_MASTER_VOL:
1565 			src = SB16P_MASTER_L;
1566 			break;
1567 		case SB_LINE_IN_VOL:
1568 			src = SB16P_LINE_L;
1569 			break;
1570 		case SB_VOICE_VOL:
1571 			src = SB16P_VOICE_L;
1572 			break;
1573 		case SB_MIDI_VOL:
1574 			src = SB16P_MIDI_L;
1575 			break;
1576 		case SB_CD_VOL:
1577 			src = SB16P_CD_L;
1578 			break;
1579 		case SB_INPUT_GAIN:
1580 			src = SB16P_INPUT_GAIN_L;
1581 			break;
1582 		case SB_OUTPUT_GAIN:
1583 			src = SB16P_OUTPUT_GAIN_L;
1584 			break;
1585 		case SB_TREBLE:
1586 			src = SB16P_TREBLE_L;
1587 			break;
1588 		case SB_BASS:
1589 			src = SB16P_BASS_L;
1590 			break;
1591 		case SB_PCSPEAKER:
1592 			sbdsp_mix_write(sc, SB16P_PCSPEAKER, sc->gain[port][SB_LEFT]);
1593 			return;
1594 		default:
1595 			return;
1596 		}
1597 		sbdsp_mix_write(sc, src, sc->gain[port][SB_LEFT]);
1598 		sbdsp_mix_write(sc, SB16P_L_TO_R(src), sc->gain[port][SB_RIGHT]);
1599 		break;
1600 	}
1601 }
1602 
1603 int
1604 sbdsp_mixer_set_port(addr, cp)
1605 	void *addr;
1606 	mixer_ctrl_t *cp;
1607 {
1608 	struct sbdsp_softc *sc = addr;
1609 	int lgain, rgain;
1610 	int mask, bits;
1611 	int lmask, rmask, lbits, rbits;
1612 	int mute, swap;
1613 
1614 	if (sc->sc_open == SB_OPEN_MIDI)
1615 		return EBUSY;
1616 
1617 	DPRINTF(("sbdsp_mixer_set_port: port=%d num_channels=%d\n", cp->dev,
1618 	    cp->un.value.num_channels));
1619 
1620 	if (sc->sc_mixer_model == SBM_NONE)
1621 		return EINVAL;
1622 
1623 	switch (cp->dev) {
1624 	case SB_TREBLE:
1625 	case SB_BASS:
1626 		if (sc->sc_mixer_model == SBM_CT1345 ||
1627                     sc->sc_mixer_model == SBM_CT1XX5) {
1628 			if (cp->type != AUDIO_MIXER_ENUM)
1629 				return EINVAL;
1630 			switch (cp->dev) {
1631 			case SB_TREBLE:
1632 				sbdsp_set_ifilter(addr, cp->un.ord ? SB_TREBLE : 0);
1633 				return 0;
1634 			case SB_BASS:
1635 				sbdsp_set_ifilter(addr, cp->un.ord ? SB_BASS : 0);
1636 				return 0;
1637 			}
1638 		}
1639 	case SB_PCSPEAKER:
1640 	case SB_INPUT_GAIN:
1641 	case SB_OUTPUT_GAIN:
1642 		if (!ISSBM1745(sc))
1643 			return EINVAL;
1644 	case SB_MIC_VOL:
1645 	case SB_LINE_IN_VOL:
1646 		if (sc->sc_mixer_model == SBM_CT1335)
1647 			return EINVAL;
1648 	case SB_VOICE_VOL:
1649 	case SB_MIDI_VOL:
1650 	case SB_CD_VOL:
1651 	case SB_MASTER_VOL:
1652 		if (cp->type != AUDIO_MIXER_VALUE)
1653 			return EINVAL;
1654 
1655 		/*
1656 		 * All the mixer ports are stereo except for the microphone.
1657 		 * If we get a single-channel gain value passed in, then we
1658 		 * duplicate it to both left and right channels.
1659 		 */
1660 
1661 		switch (cp->dev) {
1662 		case SB_MIC_VOL:
1663 			if (cp->un.value.num_channels != 1)
1664 				return EINVAL;
1665 
1666 			lgain = rgain = SB_ADJUST_MIC_GAIN(sc,
1667 			  cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
1668 			break;
1669 		case SB_PCSPEAKER:
1670 			if (cp->un.value.num_channels != 1)
1671 				return EINVAL;
1672 			/* fall into */
1673 		case SB_INPUT_GAIN:
1674 		case SB_OUTPUT_GAIN:
1675 			lgain = rgain = SB_ADJUST_2_GAIN(sc,
1676 			  cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
1677 			break;
1678 		default:
1679 			switch (cp->un.value.num_channels) {
1680 			case 1:
1681 				lgain = rgain = SB_ADJUST_GAIN(sc,
1682 				  cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
1683 				break;
1684 			case 2:
1685 				if (sc->sc_mixer_model == SBM_CT1335)
1686 					return EINVAL;
1687 				lgain = SB_ADJUST_GAIN(sc,
1688 				  cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT]);
1689 				rgain = SB_ADJUST_GAIN(sc,
1690 				  cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]);
1691 				break;
1692 			default:
1693 				return EINVAL;
1694 			}
1695 			break;
1696 		}
1697 		sc->gain[cp->dev][SB_LEFT]  = lgain;
1698 		sc->gain[cp->dev][SB_RIGHT] = rgain;
1699 
1700 		sbdsp_set_mixer_gain(sc, cp->dev);
1701 		break;
1702 
1703 	case SB_RECORD_SOURCE:
1704 		if (ISSBM1745(sc)) {
1705 			if (cp->type != AUDIO_MIXER_SET)
1706 				return EINVAL;
1707 			return sbdsp_set_in_ports(sc, cp->un.mask);
1708 		} else {
1709 			if (cp->type != AUDIO_MIXER_ENUM)
1710 				return EINVAL;
1711 			sc->in_port = cp->un.ord;
1712 			return sbdsp_set_in_ports(sc, 1 << cp->un.ord);
1713 		}
1714 		break;
1715 
1716 	case SB_AGC:
1717 		if (!ISSBM1745(sc) || cp->type != AUDIO_MIXER_ENUM)
1718 			return EINVAL;
1719 		sbdsp_mix_write(sc, SB16P_AGC, cp->un.ord & 1);
1720 		break;
1721 
1722 	case SB_CD_OUT_MUTE:
1723 		mask = SB16P_SW_CD;
1724 		goto omute;
1725 	case SB_MIC_OUT_MUTE:
1726 		mask = SB16P_SW_MIC;
1727 		goto omute;
1728 	case SB_LINE_OUT_MUTE:
1729 		mask = SB16P_SW_LINE;
1730 	omute:
1731 		if (cp->type != AUDIO_MIXER_ENUM)
1732 			return EINVAL;
1733 		bits = sbdsp_mix_read(sc, SB16P_OSWITCH);
1734 		sc->gain[cp->dev][SB_LR] = cp->un.ord != 0;
1735 		if (cp->un.ord)
1736 			bits = bits & ~mask;
1737 		else
1738 			bits = bits | mask;
1739 		sbdsp_mix_write(sc, SB16P_OSWITCH, bits);
1740 		break;
1741 
1742 	case SB_MIC_IN_MUTE:
1743 	case SB_MIC_SWAP:
1744 		lmask = rmask = SB16P_SW_MIC;
1745 		goto imute;
1746 	case SB_CD_IN_MUTE:
1747 	case SB_CD_SWAP:
1748 		lmask = SB16P_SW_CD_L;
1749 		rmask = SB16P_SW_CD_R;
1750 		goto imute;
1751 	case SB_LINE_IN_MUTE:
1752 	case SB_LINE_SWAP:
1753 		lmask = SB16P_SW_LINE_L;
1754 		rmask = SB16P_SW_LINE_R;
1755 		goto imute;
1756 	case SB_MIDI_IN_MUTE:
1757 	case SB_MIDI_SWAP:
1758 		lmask = SB16P_SW_MIDI_L;
1759 		rmask = SB16P_SW_MIDI_R;
1760 	imute:
1761 		if (cp->type != AUDIO_MIXER_ENUM)
1762 			return EINVAL;
1763 		mask = lmask | rmask;
1764 		lbits = sbdsp_mix_read(sc, SB16P_ISWITCH_L) & ~mask;
1765 		rbits = sbdsp_mix_read(sc, SB16P_ISWITCH_R) & ~mask;
1766 		sc->gain[cp->dev][SB_LR] = cp->un.ord != 0;
1767 		if (SB_IS_IN_MUTE(cp->dev)) {
1768 			mute = cp->dev;
1769 			swap = mute - SB_CD_IN_MUTE + SB_CD_SWAP;
1770 		} else {
1771 			swap = cp->dev;
1772 			mute = swap + SB_CD_IN_MUTE - SB_CD_SWAP;
1773 		}
1774 		if (sc->gain[swap][SB_LR]) {
1775 			mask = lmask;
1776 			lmask = rmask;
1777 			rmask = mask;
1778 		}
1779 		if (!sc->gain[mute][SB_LR]) {
1780 			lbits = lbits | lmask;
1781 			rbits = rbits | rmask;
1782 		}
1783 		sbdsp_mix_write(sc, SB16P_ISWITCH_L, lbits);
1784 		sbdsp_mix_write(sc, SB16P_ISWITCH_L, rbits);
1785 		break;
1786 
1787 	default:
1788 		return EINVAL;
1789 	}
1790 
1791 	return 0;
1792 }
1793 
1794 int
1795 sbdsp_mixer_get_port(addr, cp)
1796 	void *addr;
1797 	mixer_ctrl_t *cp;
1798 {
1799 	struct sbdsp_softc *sc = addr;
1800 
1801 	if (sc->sc_open == SB_OPEN_MIDI)
1802 		return EBUSY;
1803 
1804 	DPRINTF(("sbdsp_mixer_get_port: port=%d\n", cp->dev));
1805 
1806 	if (sc->sc_mixer_model == SBM_NONE)
1807 		return EINVAL;
1808 
1809 	switch (cp->dev) {
1810 	case SB_TREBLE:
1811 	case SB_BASS:
1812 		if (sc->sc_mixer_model == SBM_CT1345 ||
1813                     sc->sc_mixer_model == SBM_CT1XX5) {
1814 			switch (cp->dev) {
1815 			case SB_TREBLE:
1816 				cp->un.ord = sbdsp_get_ifilter(addr) == SB_TREBLE;
1817 				return 0;
1818 			case SB_BASS:
1819 				cp->un.ord = sbdsp_get_ifilter(addr) == SB_BASS;
1820 				return 0;
1821 			}
1822 		}
1823 	case SB_PCSPEAKER:
1824 	case SB_INPUT_GAIN:
1825 	case SB_OUTPUT_GAIN:
1826 		if (!ISSBM1745(sc))
1827 			return EINVAL;
1828 	case SB_MIC_VOL:
1829 	case SB_LINE_IN_VOL:
1830 		if (sc->sc_mixer_model == SBM_CT1335)
1831 			return EINVAL;
1832 	case SB_VOICE_VOL:
1833 	case SB_MIDI_VOL:
1834 	case SB_CD_VOL:
1835 	case SB_MASTER_VOL:
1836 		switch (cp->dev) {
1837 		case SB_MIC_VOL:
1838 		case SB_PCSPEAKER:
1839 			if (cp->un.value.num_channels != 1)
1840 				return EINVAL;
1841 			/* fall into */
1842 		default:
1843 			switch (cp->un.value.num_channels) {
1844 			case 1:
1845 				cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
1846 					sc->gain[cp->dev][SB_LEFT];
1847 				break;
1848 			case 2:
1849 				cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
1850 					sc->gain[cp->dev][SB_LEFT];
1851 				cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
1852 					sc->gain[cp->dev][SB_RIGHT];
1853 				break;
1854 			default:
1855 				return EINVAL;
1856 			}
1857 			break;
1858 		}
1859 		break;
1860 
1861 	case SB_RECORD_SOURCE:
1862 		if (ISSBM1745(sc))
1863 			cp->un.mask = sc->in_mask;
1864 		else
1865 			cp->un.ord = sc->in_port;
1866 		break;
1867 
1868 	case SB_AGC:
1869 		if (!ISSBM1745(sc))
1870 			return EINVAL;
1871 		cp->un.ord = sbdsp_mix_read(sc, SB16P_AGC);
1872 		break;
1873 
1874 	case SB_CD_IN_MUTE:
1875 	case SB_MIC_IN_MUTE:
1876 	case SB_LINE_IN_MUTE:
1877 	case SB_MIDI_IN_MUTE:
1878 	case SB_CD_SWAP:
1879 	case SB_MIC_SWAP:
1880 	case SB_LINE_SWAP:
1881 	case SB_MIDI_SWAP:
1882 	case SB_CD_OUT_MUTE:
1883 	case SB_MIC_OUT_MUTE:
1884 	case SB_LINE_OUT_MUTE:
1885 		cp->un.ord = sc->gain[cp->dev][SB_LR];
1886 		break;
1887 
1888 	default:
1889 		return EINVAL;
1890 	}
1891 
1892 	return 0;
1893 }
1894 
1895 int
1896 sbdsp_mixer_query_devinfo(addr, dip)
1897 	void *addr;
1898 	mixer_devinfo_t *dip;
1899 {
1900 	struct sbdsp_softc *sc = addr;
1901 	int chan, class, is1745;
1902 
1903 	DPRINTF(("sbdsp_mixer_query_devinfo: model=%d index=%d\n",
1904 		 sc->sc_mixer_model, dip->index));
1905 
1906 	if (sc->sc_mixer_model == SBM_NONE)
1907 		return ENXIO;
1908 
1909 	chan = sc->sc_mixer_model == SBM_CT1335 ? 1 : 2;
1910 	is1745 = ISSBM1745(sc);
1911 	class = is1745 ? SB_INPUT_CLASS : SB_OUTPUT_CLASS;
1912 
1913 	switch (dip->index) {
1914 	case SB_MASTER_VOL:
1915 		dip->type = AUDIO_MIXER_VALUE;
1916 		dip->mixer_class = SB_OUTPUT_CLASS;
1917 		dip->prev = dip->next = AUDIO_MIXER_LAST;
1918 		strcpy(dip->label.name, AudioNmaster);
1919 		dip->un.v.num_channels = chan;
1920 		strcpy(dip->un.v.units.name, AudioNvolume);
1921 		return 0;
1922 	case SB_MIDI_VOL:
1923 		dip->type = AUDIO_MIXER_VALUE;
1924 		dip->mixer_class = class;
1925 		dip->prev = AUDIO_MIXER_LAST;
1926 		dip->next = is1745 ? SB_MIDI_IN_MUTE : AUDIO_MIXER_LAST;
1927 		strcpy(dip->label.name, AudioNfmsynth);
1928 		dip->un.v.num_channels = chan;
1929 		strcpy(dip->un.v.units.name, AudioNvolume);
1930 		return 0;
1931 	case SB_CD_VOL:
1932 		dip->type = AUDIO_MIXER_VALUE;
1933 		dip->mixer_class = class;
1934 		dip->prev = AUDIO_MIXER_LAST;
1935 		dip->next = is1745 ? SB_CD_IN_MUTE : AUDIO_MIXER_LAST;
1936 		strcpy(dip->label.name, AudioNcd);
1937 		dip->un.v.num_channels = chan;
1938 		strcpy(dip->un.v.units.name, AudioNvolume);
1939 		return 0;
1940 	case SB_VOICE_VOL:
1941 		dip->type = AUDIO_MIXER_VALUE;
1942 		dip->mixer_class = class;
1943 		dip->prev = AUDIO_MIXER_LAST;
1944 		dip->next = AUDIO_MIXER_LAST;
1945 		strcpy(dip->label.name, AudioNdac);
1946 		dip->un.v.num_channels = chan;
1947 		strcpy(dip->un.v.units.name, AudioNvolume);
1948 		return 0;
1949 	case SB_OUTPUT_CLASS:
1950 		dip->type = AUDIO_MIXER_CLASS;
1951 		dip->mixer_class = SB_OUTPUT_CLASS;
1952 		dip->next = dip->prev = AUDIO_MIXER_LAST;
1953 		strcpy(dip->label.name, AudioCoutputs);
1954 		return 0;
1955 	}
1956 
1957 	if (sc->sc_mixer_model == SBM_CT1335)
1958 		return ENXIO;
1959 
1960 	switch (dip->index) {
1961 	case SB_MIC_VOL:
1962 		dip->type = AUDIO_MIXER_VALUE;
1963 		dip->mixer_class = class;
1964 		dip->prev = AUDIO_MIXER_LAST;
1965 		dip->next = is1745 ? SB_MIC_IN_MUTE : AUDIO_MIXER_LAST;
1966 		strcpy(dip->label.name, AudioNmicrophone);
1967 		dip->un.v.num_channels = 1;
1968 		strcpy(dip->un.v.units.name, AudioNvolume);
1969 		return 0;
1970 
1971 	case SB_LINE_IN_VOL:
1972 		dip->type = AUDIO_MIXER_VALUE;
1973 		dip->mixer_class = class;
1974 		dip->prev = AUDIO_MIXER_LAST;
1975 		dip->next = is1745 ? SB_LINE_IN_MUTE : AUDIO_MIXER_LAST;
1976 		strcpy(dip->label.name, AudioNline);
1977 		dip->un.v.num_channels = 2;
1978 		strcpy(dip->un.v.units.name, AudioNvolume);
1979 		return 0;
1980 
1981 	case SB_RECORD_SOURCE:
1982 		dip->mixer_class = SB_RECORD_CLASS;
1983 		dip->prev = dip->next = AUDIO_MIXER_LAST;
1984 		strcpy(dip->label.name, AudioNsource);
1985 		if (ISSBM1745(sc)) {
1986 			dip->type = AUDIO_MIXER_SET;
1987 			dip->un.s.num_mem = 4;
1988 			strcpy(dip->un.s.member[0].label.name, AudioNmicrophone);
1989 			dip->un.s.member[0].mask = 1 << SB_MIC_VOL;
1990 			strcpy(dip->un.s.member[1].label.name, AudioNcd);
1991 			dip->un.s.member[1].mask = 1 << SB_CD_VOL;
1992 			strcpy(dip->un.s.member[2].label.name, AudioNline);
1993 			dip->un.s.member[2].mask = 1 << SB_LINE_IN_VOL;
1994 			strcpy(dip->un.s.member[3].label.name, AudioNfmsynth);
1995 			dip->un.s.member[3].mask = 1 << SB_MIDI_VOL;
1996 		} else {
1997 			dip->type = AUDIO_MIXER_ENUM;
1998 			dip->un.e.num_mem = 3;
1999 			strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
2000 			dip->un.e.member[0].ord = SB_MIC_VOL;
2001 			strcpy(dip->un.e.member[1].label.name, AudioNcd);
2002 			dip->un.e.member[1].ord = SB_CD_VOL;
2003 			strcpy(dip->un.e.member[2].label.name, AudioNline);
2004 			dip->un.e.member[2].ord = SB_LINE_IN_VOL;
2005 		}
2006 		return 0;
2007 
2008 	case SB_BASS:
2009 		dip->prev = dip->next = AUDIO_MIXER_LAST;
2010 		strcpy(dip->label.name, AudioNbass);
2011 		if (sc->sc_mixer_model == SBM_CT1745) {
2012 			dip->type = AUDIO_MIXER_VALUE;
2013 			dip->mixer_class = SB_EQUALIZATION_CLASS;
2014 			dip->un.v.num_channels = 2;
2015 			strcpy(dip->un.v.units.name, AudioNbass);
2016 		} else {
2017 			dip->type = AUDIO_MIXER_ENUM;
2018 			dip->mixer_class = SB_INPUT_CLASS;
2019 			dip->un.e.num_mem = 2;
2020 			strcpy(dip->un.e.member[0].label.name, AudioNoff);
2021 			dip->un.e.member[0].ord = 0;
2022 			strcpy(dip->un.e.member[1].label.name, AudioNon);
2023 			dip->un.e.member[1].ord = 1;
2024 		}
2025 		return 0;
2026 
2027 	case SB_TREBLE:
2028 		dip->prev = dip->next = AUDIO_MIXER_LAST;
2029 		strcpy(dip->label.name, AudioNtreble);
2030 		if (sc->sc_mixer_model == SBM_CT1745) {
2031 			dip->type = AUDIO_MIXER_VALUE;
2032 			dip->mixer_class = SB_EQUALIZATION_CLASS;
2033 			dip->un.v.num_channels = 2;
2034 			strcpy(dip->un.v.units.name, AudioNtreble);
2035 		} else {
2036 			dip->type = AUDIO_MIXER_ENUM;
2037 			dip->mixer_class = SB_INPUT_CLASS;
2038 			dip->un.e.num_mem = 2;
2039 			strcpy(dip->un.e.member[0].label.name, AudioNoff);
2040 			dip->un.e.member[0].ord = 0;
2041 			strcpy(dip->un.e.member[1].label.name, AudioNon);
2042 			dip->un.e.member[1].ord = 1;
2043 		}
2044 		return 0;
2045 
2046 	case SB_RECORD_CLASS:			/* record source class */
2047 		dip->type = AUDIO_MIXER_CLASS;
2048 		dip->mixer_class = SB_RECORD_CLASS;
2049 		dip->next = dip->prev = AUDIO_MIXER_LAST;
2050 		strcpy(dip->label.name, AudioCrecord);
2051 		return 0;
2052 
2053 	case SB_INPUT_CLASS:
2054 		dip->type = AUDIO_MIXER_CLASS;
2055 		dip->mixer_class = SB_INPUT_CLASS;
2056 		dip->next = dip->prev = AUDIO_MIXER_LAST;
2057 		strcpy(dip->label.name, AudioCinputs);
2058 		return 0;
2059 
2060 	}
2061 
2062 	if (sc->sc_mixer_model == SBM_CT1345)
2063 		return ENXIO;
2064 
2065 	switch(dip->index) {
2066 	case SB_PCSPEAKER:
2067 		dip->type = AUDIO_MIXER_VALUE;
2068 		dip->mixer_class = SB_INPUT_CLASS;
2069 		dip->prev = dip->next = AUDIO_MIXER_LAST;
2070 		strcpy(dip->label.name, "pc_speaker");
2071 		dip->un.v.num_channels = 1;
2072 		strcpy(dip->un.v.units.name, AudioNvolume);
2073 		return 0;
2074 
2075 	case SB_INPUT_GAIN:
2076 		dip->type = AUDIO_MIXER_VALUE;
2077 		dip->mixer_class = SB_INPUT_CLASS;
2078 		dip->prev = dip->next = AUDIO_MIXER_LAST;
2079 		strcpy(dip->label.name, AudioNinput);
2080 		dip->un.v.num_channels = 2;
2081 		strcpy(dip->un.v.units.name, AudioNvolume);
2082 		return 0;
2083 
2084 	case SB_OUTPUT_GAIN:
2085 		dip->type = AUDIO_MIXER_VALUE;
2086 		dip->mixer_class = SB_OUTPUT_CLASS;
2087 		dip->prev = dip->next = AUDIO_MIXER_LAST;
2088 		strcpy(dip->label.name, AudioNoutput);
2089 		dip->un.v.num_channels = 2;
2090 		strcpy(dip->un.v.units.name, AudioNvolume);
2091 		return 0;
2092 
2093 	case SB_AGC:
2094 		dip->type = AUDIO_MIXER_ENUM;
2095 		dip->mixer_class = SB_INPUT_CLASS;
2096 		dip->prev = dip->next = AUDIO_MIXER_LAST;
2097 		strcpy(dip->label.name, "agc");
2098 		dip->un.e.num_mem = 2;
2099 		strcpy(dip->un.e.member[0].label.name, AudioNoff);
2100 		dip->un.e.member[0].ord = 0;
2101 		strcpy(dip->un.e.member[1].label.name, AudioNon);
2102 		dip->un.e.member[1].ord = 1;
2103 		return 0;
2104 
2105 	case SB_EQUALIZATION_CLASS:
2106 		dip->type = AUDIO_MIXER_CLASS;
2107 		dip->mixer_class = SB_EQUALIZATION_CLASS;
2108 		dip->next = dip->prev = AUDIO_MIXER_LAST;
2109 		strcpy(dip->label.name, AudioCequalization);
2110 		return 0;
2111 
2112 	case SB_CD_IN_MUTE:
2113 		dip->prev = SB_CD_VOL;
2114 		dip->next = SB_CD_SWAP;
2115 		dip->mixer_class = SB_INPUT_CLASS;
2116 		goto mute;
2117 
2118 	case SB_MIC_IN_MUTE:
2119 		dip->prev = SB_MIC_VOL;
2120 		dip->next = SB_MIC_SWAP;
2121 		dip->mixer_class = SB_INPUT_CLASS;
2122 		goto mute;
2123 
2124 	case SB_LINE_IN_MUTE:
2125 		dip->prev = SB_LINE_IN_VOL;
2126 		dip->next = SB_LINE_SWAP;
2127 		dip->mixer_class = SB_INPUT_CLASS;
2128 		goto mute;
2129 
2130 	case SB_MIDI_IN_MUTE:
2131 		dip->prev = SB_MIDI_VOL;
2132 		dip->next = SB_MIDI_SWAP;
2133 		dip->mixer_class = SB_INPUT_CLASS;
2134 		goto mute;
2135 
2136 	case SB_CD_SWAP:
2137 		dip->prev = SB_CD_IN_MUTE;
2138 		dip->next = SB_CD_OUT_MUTE;
2139 		goto swap;
2140 
2141 	case SB_MIC_SWAP:
2142 		dip->prev = SB_MIC_IN_MUTE;
2143 		dip->next = SB_MIC_OUT_MUTE;
2144 		goto swap;
2145 
2146 	case SB_LINE_SWAP:
2147 		dip->prev = SB_LINE_IN_MUTE;
2148 		dip->next = SB_LINE_OUT_MUTE;
2149 		goto swap;
2150 
2151 	case SB_MIDI_SWAP:
2152 		dip->prev = SB_MIDI_IN_MUTE;
2153 		dip->next = AUDIO_MIXER_LAST;
2154 	swap:
2155 		dip->mixer_class = SB_INPUT_CLASS;
2156 		strcpy(dip->label.name, AudioNswap);
2157 		goto mute1;
2158 
2159 	case SB_CD_OUT_MUTE:
2160 		dip->prev = SB_CD_SWAP;
2161 		dip->next = AUDIO_MIXER_LAST;
2162 		dip->mixer_class = SB_OUTPUT_CLASS;
2163 		goto mute;
2164 
2165 	case SB_MIC_OUT_MUTE:
2166 		dip->prev = SB_MIC_SWAP;
2167 		dip->next = AUDIO_MIXER_LAST;
2168 		dip->mixer_class = SB_OUTPUT_CLASS;
2169 		goto mute;
2170 
2171 	case SB_LINE_OUT_MUTE:
2172 		dip->prev = SB_LINE_SWAP;
2173 		dip->next = AUDIO_MIXER_LAST;
2174 		dip->mixer_class = SB_OUTPUT_CLASS;
2175 	mute:
2176 		strcpy(dip->label.name, AudioNmute);
2177 	mute1:
2178 		dip->type = AUDIO_MIXER_ENUM;
2179 		dip->un.e.num_mem = 2;
2180 		strcpy(dip->un.e.member[0].label.name, AudioNoff);
2181 		dip->un.e.member[0].ord = 0;
2182 		strcpy(dip->un.e.member[1].label.name, AudioNon);
2183 		dip->un.e.member[1].ord = 1;
2184 		return 0;
2185 
2186 	}
2187 
2188 	return ENXIO;
2189 }
2190 
2191 void *
2192 sb_malloc(addr, direction, size, pool, flags)
2193 	void *addr;
2194 	int direction;
2195 	size_t size;
2196 	int pool;
2197 	int flags;
2198 {
2199 	struct sbdsp_softc *sc = addr;
2200 	int drq;
2201 
2202 	/* 8-bit has more restrictive alignment */
2203 	if (sc->sc_drq8 != -1)
2204 		drq = sc->sc_drq8;
2205 	else
2206 		drq = sc->sc_drq16;
2207 
2208 	return isa_malloc(sc->sc_isa, drq, size, pool, flags);
2209 }
2210 
2211 void
2212 sb_free(addr, ptr, pool)
2213 	void *addr;
2214 	void *ptr;
2215 	int pool;
2216 {
2217 	isa_free(ptr, pool);
2218 }
2219 
2220 size_t
2221 sb_round(addr, direction, size)
2222 	void *addr;
2223 	int direction;
2224 	size_t size;
2225 {
2226 	if (size > MAX_ISADMA)
2227 		size = MAX_ISADMA;
2228 	return size;
2229 }
2230 
2231 paddr_t
2232 sb_mappage(addr, mem, off, prot)
2233 	void *addr;
2234         void *mem;
2235         off_t off;
2236 	int prot;
2237 {
2238 	return isa_mappage(mem, off, prot);
2239 }
2240 
2241 int
2242 sbdsp_get_props(addr)
2243 	void *addr;
2244 {
2245 	struct sbdsp_softc *sc = addr;
2246 	return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT |
2247 	       (sc->sc_fullduplex ? AUDIO_PROP_FULLDUPLEX : 0);
2248 }
2249 
2250 #if NMIDI > 0
2251 /*
2252  * MIDI related routines.
2253  */
2254 
2255 int
2256 sbdsp_midi_open(addr, flags, iintr, ointr, arg)
2257 	void *addr;
2258 	int flags;
2259 	void (*iintr)__P((void *, int));
2260 	void (*ointr)__P((void *));
2261 	void *arg;
2262 {
2263 	struct sbdsp_softc *sc = addr;
2264 
2265         DPRINTF(("sbdsp_midi_open: sc=%p\n", sc));
2266 
2267 	if (sc->sc_open != SB_CLOSED)
2268 		return EBUSY;
2269 	if (sbdsp_reset(sc) != 0)
2270 		return EIO;
2271 
2272 	if (sc->sc_model >= SB_20)
2273 		if (sbdsp_wdsp(sc, SB_MIDI_UART_INTR)) /* enter UART mode */
2274 			return EIO;
2275 	sc->sc_open = SB_OPEN_MIDI;
2276 	sc->sc_openflags = flags;
2277 	sc->sc_intr8 = sbdsp_midi_intr;
2278 	sc->sc_arg8 = addr;
2279 	sc->sc_intrm = iintr;
2280 	sc->sc_argm = arg;
2281 	return 0;
2282 }
2283 
2284 void
2285 sbdsp_midi_close(addr)
2286 	void *addr;
2287 {
2288 	struct sbdsp_softc *sc = addr;
2289 
2290         DPRINTF(("sbdsp_midi_close: sc=%p\n", sc));
2291 
2292 	if (sc->sc_model >= SB_20)
2293 		sbdsp_reset(sc); /* exit UART mode */
2294 	sc->sc_open = SB_CLOSED;
2295 	sc->sc_intrm = 0;
2296 }
2297 
2298 int
2299 sbdsp_midi_output(addr, d)
2300 	void *addr;
2301 	int d;
2302 {
2303 	struct sbdsp_softc *sc = addr;
2304 
2305 	if (sc->sc_model < SB_20 && sbdsp_wdsp(sc, SB_MIDI_WRITE))
2306 		return EIO;
2307 	if (sbdsp_wdsp(sc, d))
2308 		return EIO;
2309 	return 0;
2310 }
2311 
2312 void
2313 sbdsp_midi_getinfo(addr, mi)
2314 	void *addr;
2315 	struct midi_info *mi;
2316 {
2317 	struct sbdsp_softc *sc = addr;
2318 
2319 	mi->name = sc->sc_model < SB_20 ? "SB MIDI cmd" : "SB MIDI UART";
2320 	mi->props = MIDI_PROP_CAN_INPUT;
2321 }
2322 
2323 int
2324 sbdsp_midi_intr(addr)
2325 	void *addr;
2326 {
2327 	struct sbdsp_softc *sc = addr;
2328 
2329 	sc->sc_intrm(sc->sc_argm, sbdsp_rdsp(sc));
2330 	return (0);
2331 }
2332 
2333 #endif
2334