sb16.c revision 55706
192512Sphk/* 292512Sphk * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 392512Sphk * Copyright 1997,1998 Luigi Rizzo. 492512Sphk * 592512Sphk * Derived from files in the Voxware 3.5 distribution, 692512Sphk * Copyright by Hannu Savolainen 1994, under the same copyright 792512Sphk * conditions. 892512Sphk * All rights reserved. 992512Sphk * 1092512Sphk * Redistribution and use in source and binary forms, with or without 1192512Sphk * modification, are permitted provided that the following conditions 1292512Sphk * are met: 1392512Sphk * 1. Redistributions of source code must retain the above copyright 1492512Sphk * notice, this list of conditions and the following disclaimer. 1592512Sphk * 2. Redistributions in binary form must reproduce the above copyright 1692512Sphk * notice, this list of conditions and the following disclaimer in the 1792512Sphk * documentation and/or other materials provided with the distribution. 1892512Sphk * 1992512Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2092512Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2192512Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2292512Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2392512Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2492512Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2592512Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2692512Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2792512Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2892512Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2992512Sphk * SUCH DAMAGE. 3092512Sphk * 3192512Sphk * $FreeBSD: head/sys/dev/sound/isa/sb16.c 55706 2000-01-10 03:22:28Z cg $ 3292512Sphk */ 3392512Sphk 3492512Sphk#include <dev/sound/pcm/sound.h> 3592512Sphk 3692512Sphk#include "sbc.h" 3792512Sphk 3892512Sphk#define __SB_MIXER_C__ /* XXX warning... */ 3992512Sphk#include <dev/sound/isa/sb.h> 4092512Sphk#include <dev/sound/chip.h> 4192512Sphk 4292512Sphk#define ESS_BUFFSIZE (65536 - 256) 4392512Sphk#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) 4492512Sphk 4592512Sphk/* channel interface */ 4692512Sphkstatic void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); 4792512Sphkstatic int sbchan_setdir(void *data, int dir); 4892512Sphkstatic int sbchan_setformat(void *data, u_int32_t format); 4992512Sphkstatic int sbchan_setspeed(void *data, u_int32_t speed); 5092512Sphkstatic int sbchan_setblocksize(void *data, u_int32_t blocksize); 5192512Sphkstatic int sbchan_trigger(void *data, int go); 5292512Sphkstatic int sbchan_getptr(void *data); 5392512Sphkstatic pcmchan_caps *sbchan_getcaps(void *data); 5492512Sphk 5592512Sphk/* channel interface for ESS */ 5692512Sphkstatic void *esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); 5792512Sphkstatic int esschan_setdir(void *data, int dir); 5892512Sphkstatic int esschan_setformat(void *data, u_int32_t format); 5992512Sphkstatic int esschan_setspeed(void *data, u_int32_t speed); 6092512Sphkstatic int esschan_setblocksize(void *data, u_int32_t blocksize); 6192512Sphkstatic int esschan_trigger(void *data, int go); 6292512Sphkstatic int esschan_getptr(void *data); 6392512Sphkstatic pcmchan_caps *esschan_getcaps(void *data); 6492512Sphk 6592512Sphkstatic pcmchan_caps sb_playcaps = { 6692512Sphk 4000, 22050, 6792512Sphk AFMT_U8, 6892512Sphk AFMT_U8 6992512Sphk}; 7092512Sphk 7192512Sphkstatic pcmchan_caps sb_reccaps = { 7292512Sphk 4000, 13000, 7392512Sphk AFMT_U8, 7492512Sphk AFMT_U8 7592512Sphk}; 7692512Sphk 7792512Sphkstatic pcmchan_caps sbpro_playcaps = { 7892512Sphk 4000, 45000, 7992512Sphk AFMT_STEREO | AFMT_U8, 8092512Sphk AFMT_STEREO | AFMT_U8 8192512Sphk}; 8292512Sphk 8392512Sphkstatic pcmchan_caps sbpro_reccaps = { 8492512Sphk 4000, 15000, 8592512Sphk AFMT_STEREO | AFMT_U8, 8692512Sphk AFMT_STEREO | AFMT_U8 8792512Sphk}; 8892512Sphk 8992512Sphkstatic pcmchan_caps sb16_hcaps = { 9092512Sphk 5000, 45000, 9192512Sphk AFMT_STEREO | AFMT_S16_LE, 9292512Sphk AFMT_STEREO | AFMT_S16_LE 9392512Sphk}; 9492512Sphk 9592512Sphkstatic pcmchan_caps sb16_lcaps = { 9692512Sphk 5000, 45000, 9792512Sphk AFMT_STEREO | AFMT_U8, 9892512Sphk AFMT_STEREO | AFMT_U8 9992512Sphk}; 10092512Sphk 10192512Sphkstatic pcmchan_caps sb16x_caps = { 10292512Sphk 5000, 49000, 10392512Sphk AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, 10492512Sphk AFMT_STEREO | AFMT_S16_LE 10592512Sphk}; 10692512Sphk 10792512Sphkstatic pcmchan_caps ess_playcaps = { 10892512Sphk 5000, 49000, 10992512Sphk AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, 11092512Sphk AFMT_STEREO | AFMT_S16_LE 11192512Sphk}; 11292512Sphk 11392512Sphkstatic pcmchan_caps ess_reccaps = { 11492512Sphk 5000, 49000, 11592512Sphk AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, 11692512Sphk AFMT_STEREO | AFMT_S16_LE 11792512Sphk}; 11892512Sphk 11992512Sphkstatic pcm_channel sb_chantemplate = { 12092512Sphk sbchan_init, 12192512Sphk sbchan_setdir, 12292512Sphk sbchan_setformat, 12392512Sphk sbchan_setspeed, 12492512Sphk sbchan_setblocksize, 12592512Sphk sbchan_trigger, 12692512Sphk sbchan_getptr, 12792512Sphk sbchan_getcaps, 12892512Sphk}; 12992512Sphk 13092512Sphkstatic pcm_channel ess_chantemplate = { 13192512Sphk esschan_init, 13292512Sphk esschan_setdir, 13392512Sphk esschan_setformat, 13492512Sphk esschan_setspeed, 13592512Sphk esschan_setblocksize, 13692512Sphk esschan_trigger, 13792512Sphk esschan_getptr, 13892512Sphk esschan_getcaps, 13992512Sphk}; 14092512Sphk 14192512Sphkstruct sb_info; 14292512Sphk 14392512Sphkstruct sb_chinfo { 14492512Sphk struct sb_info *parent; 14592512Sphk pcm_channel *channel; 14692512Sphk snd_dbuf *buffer; 14792512Sphk int dir; 14892512Sphk u_int32_t fmt, spd; 14992512Sphk int ess_dma_started; 15092512Sphk}; 15192512Sphk 15292512Sphkstruct sb_info { 15392512Sphk struct resource *io_base; /* I/O address for the board */ 15492512Sphk struct resource *irq; 15592512Sphk struct resource *drq1; 15692512Sphk struct resource *drq2; 15792512Sphk bus_dma_tag_t parent_dmat; 15892512Sphk 15992512Sphk int bd_id; 16092512Sphk u_long bd_flags; /* board-specific flags */ 16192512Sphk struct sb_chinfo pch, rch; 16292512Sphk}; 16392512Sphk 16492512Sphkstatic int sb_rd(struct sb_info *sb, int reg); 16592512Sphkstatic void sb_wr(struct sb_info *sb, int reg, u_int8_t val); 16692512Sphkstatic int sb_dspready(struct sb_info *sb); 16792512Sphkstatic int sb_cmd(struct sb_info *sb, u_char val); 16892512Sphkstatic int sb_cmd1(struct sb_info *sb, u_char cmd, int val); 16992512Sphkstatic int sb_cmd2(struct sb_info *sb, u_char cmd, int val); 17092512Sphkstatic u_int sb_get_byte(struct sb_info *sb); 17192512Sphkstatic void sb_setmixer(struct sb_info *sb, u_int port, u_int value); 17292512Sphkstatic int sb_getmixer(struct sb_info *sb, u_int port); 17392512Sphkstatic int sb_reset_dsp(struct sb_info *sb); 17492512Sphk 17592512Sphkstatic void sb_intr(void *arg); 17692512Sphkstatic int sb_speed(struct sb_chinfo *ch); 17792512Sphkstatic int sb_start(struct sb_chinfo *ch); 17892512Sphkstatic int sb_stop(struct sb_chinfo *ch); 17992512Sphk 18092512Sphkstatic int ess_write(struct sb_info *sb, u_char reg, int val); 18192512Sphkstatic int ess_read(struct sb_info *sb, u_char reg); 18292512Sphkstatic void ess_intr(void *arg); 18392512Sphkstatic int ess_format(struct sb_chinfo *ch, u_int32_t format); 18492512Sphkstatic int ess_speed(struct sb_chinfo *ch, int speed); 18592512Sphkstatic int ess_start(struct sb_chinfo *ch); 18692512Sphkstatic int ess_stop(struct sb_chinfo *ch); 18792512Sphkstatic int ess_abort(struct sb_chinfo *ch); 18892512Sphk 18992512Sphkstatic int sbmix_init(snd_mixer *m); 19092512Sphkstatic int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); 19192512Sphkstatic int sbmix_setrecsrc(snd_mixer *m, u_int32_t src); 19292512Sphk 19392512Sphkstatic snd_mixer sb_mixer = { 19492512Sphk "SoundBlaster mixer", 19592512Sphk sbmix_init, 19692512Sphk sbmix_set, 19792512Sphk sbmix_setrecsrc, 19892512Sphk}; 19992512Sphk 20092512Sphkstatic devclass_t pcm_devclass; 20192512Sphk 20292512Sphk/* 20392512Sphk * Common code for the midi and pcm functions 20492512Sphk * 20592512Sphk * sb_cmd write a single byte to the CMD port. 20692512Sphk * sb_cmd1 write a CMD + 1 byte arg 20792512Sphk * sb_cmd2 write a CMD + 2 byte arg 20892512Sphk * sb_get_byte returns a single byte from the DSP data port 20992512Sphk * 21092512Sphk * ess_write is actually sb_cmd1 21192512Sphk * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte 21292512Sphk */ 21392512Sphk 21492512Sphkstatic int 21592512Sphkport_rd(struct resource *port, int off) 21692512Sphk{ 21792512Sphk return bus_space_read_1(rman_get_bustag(port), 21892512Sphk rman_get_bushandle(port), 21992512Sphk off); 22092512Sphk} 22192512Sphk 22292512Sphkstatic void 22392512Sphkport_wr(struct resource *port, int off, u_int8_t data) 22492512Sphk{ 22592512Sphk return bus_space_write_1(rman_get_bustag(port), 22692512Sphk rman_get_bushandle(port), 22792512Sphk off, data); 22892512Sphk} 22992512Sphk 23092512Sphkstatic int 23192512Sphksb_rd(struct sb_info *sb, int reg) 23292512Sphk{ 23392512Sphk return port_rd(sb->io_base, reg); 23492512Sphk} 23592512Sphk 23692512Sphkstatic void 23792512Sphksb_wr(struct sb_info *sb, int reg, u_int8_t val) 23892512Sphk{ 23992512Sphk port_wr(sb->io_base, reg, val); 24092512Sphk} 24192512Sphk 24292512Sphkstatic int 24392512Sphksb_dspready(struct sb_info *sb) 24492512Sphk{ 24592512Sphk return ((sb_rd(sb, SBDSP_STATUS) & 0x80) == 0); 24692512Sphk} 24792512Sphk 24892512Sphkstatic int 24992512Sphksb_dspwr(struct sb_info *sb, u_char val) 25092512Sphk{ 25192512Sphk int i; 25292512Sphk 25392512Sphk for (i = 0; i < 1000; i++) { 25492512Sphk if (sb_dspready(sb)) { 25592512Sphk sb_wr(sb, SBDSP_CMD, val); 25692512Sphk return 1; 25792512Sphk } 25892512Sphk if (i > 10) DELAY((i > 100)? 1000 : 10); 25992512Sphk } 26092512Sphk printf("sb_dspwr(0x%02x) timed out.\n", val); 26192512Sphk return 0; 26292512Sphk} 26392512Sphk 26492512Sphkstatic int 26592512Sphksb_cmd(struct sb_info *sb, u_char val) 26692512Sphk{ 26792512Sphk#if 0 26892512Sphk printf("sb_cmd: %x\n", val); 26992512Sphk#endif 27092512Sphk return sb_dspwr(sb, val); 27192512Sphk} 27292512Sphk 27392512Sphkstatic int 27492512Sphksb_cmd1(struct sb_info *sb, u_char cmd, int val) 27592512Sphk{ 27692512Sphk#if 0 27792512Sphk printf("sb_cmd1: %x, %x\n", cmd, val); 27892512Sphk#endif 27992512Sphk if (sb_dspwr(sb, cmd)) { 28092512Sphk return sb_dspwr(sb, val & 0xff); 28192512Sphk } else return 0; 28292512Sphk} 28392512Sphk 28492512Sphkstatic int 28592512Sphksb_cmd2(struct sb_info *sb, u_char cmd, int val) 28692512Sphk{ 28792512Sphk#if 0 28892512Sphk printf("sb_cmd2: %x, %x\n", cmd, val); 28992512Sphk#endif 29092512Sphk if (sb_dspwr(sb, cmd)) { 29192512Sphk return sb_dspwr(sb, val & 0xff) && 29292512Sphk sb_dspwr(sb, (val >> 8) & 0xff); 29392512Sphk } else return 0; 29492512Sphk} 29592512Sphk 29692512Sphk/* 29792512Sphk * in the SB, there is a set of indirect "mixer" registers with 29892512Sphk * address at offset 4, data at offset 5 29992512Sphk */ 30092512Sphkstatic void 30192512Sphksb_setmixer(struct sb_info *sb, u_int port, u_int value) 30292512Sphk{ 30392512Sphk u_long flags; 30492512Sphk 30592512Sphk flags = spltty(); 30692512Sphk sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 30792512Sphk DELAY(10); 30892512Sphk sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff)); 30992512Sphk DELAY(10); 31092512Sphk splx(flags); 31192512Sphk} 31292512Sphk 31392512Sphkstatic int 31492512Sphksb_getmixer(struct sb_info *sb, u_int port) 31592512Sphk{ 31692512Sphk int val; 31792512Sphk u_long flags; 31892512Sphk 31992512Sphk flags = spltty(); 32092512Sphk sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 32192512Sphk DELAY(10); 32292512Sphk val = sb_rd(sb, SB_MIX_DATA); 32392512Sphk DELAY(10); 32492512Sphk splx(flags); 32592512Sphk 32692512Sphk return val; 32792512Sphk} 32892512Sphk 32992512Sphkstatic u_int 33092512Sphksb_get_byte(struct sb_info *sb) 33192512Sphk{ 33292512Sphk int i; 33392512Sphk 33492512Sphk for (i = 1000; i > 0; i--) { 33592512Sphk if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80) 33692512Sphk return sb_rd(sb, DSP_READ); 33792512Sphk else 33892512Sphk DELAY(20); 33992512Sphk } 34092512Sphk return 0xffff; 34192512Sphk} 34292512Sphk 34392512Sphkstatic int 34492512Sphkess_write(struct sb_info *sb, u_char reg, int val) 34592512Sphk{ 34692512Sphk return sb_cmd1(sb, reg, val); 34792512Sphk} 34892512Sphk 34992512Sphkstatic int 35092512Sphkess_read(struct sb_info *sb, u_char reg) 35192512Sphk{ 35292512Sphk return (sb_cmd(sb, 0xc0) && sb_cmd(sb, reg))? sb_get_byte(sb) : 0xffff; 35392512Sphk} 35492512Sphk 35592512Sphkstatic int 35692512Sphksb_reset_dsp(struct sb_info *sb) 35792512Sphk{ 35892512Sphk sb_wr(sb, SBDSP_RST, 3); 35992512Sphk DELAY(100); 36092512Sphk sb_wr(sb, SBDSP_RST, 0); 36192512Sphk if (sb_get_byte(sb) != 0xAA) { 36292512Sphk DEB(printf("sb_reset_dsp 0x%lx failed\n", 36392512Sphk rman_get_start(d->io_base))); 36492512Sphk return ENXIO; /* Sorry */ 36592512Sphk } 36692512Sphk if (sb->bd_flags & BD_F_ESS) 36792512Sphk sb_cmd(sb, 0xc6); 36892512Sphk return 0; 36992512Sphk} 37092512Sphk 37192512Sphkstatic void 37292512Sphksb_release_resources(struct sb_info *sb, device_t dev) 37392512Sphk{ 37492512Sphk /* should we bus_teardown_intr here? */ 37592512Sphk if (sb->irq) { 37692512Sphk bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq); 37792512Sphk sb->irq = 0; 37892512Sphk } 37992512Sphk if (sb->drq1) { 38092512Sphk bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1); 38192512Sphk sb->drq1 = 0; 38292512Sphk } 38392512Sphk if (sb->drq2) { 38492512Sphk bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2); 38592512Sphk sb->drq2 = 0; 38692512Sphk } 38792512Sphk if (sb->io_base) { 38892512Sphk bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base); 38992512Sphk sb->io_base = 0; 39092512Sphk } 39192512Sphk free(sb, M_DEVBUF); 39292512Sphk} 39392512Sphk 39492512Sphkstatic int 39592512Sphksb_alloc_resources(struct sb_info *sb, device_t dev) 39692512Sphk{ 39792512Sphk int rid; 39892512Sphk 39992512Sphk rid = 0; 40092512Sphk if (!sb->io_base) 40192512Sphk sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, 40292512Sphk &rid, 0, ~0, 1, 40392512Sphk RF_ACTIVE); 40492512Sphk rid = 0; 40592512Sphk if (!sb->irq) 40692512Sphk sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, 40792512Sphk &rid, 0, ~0, 1, 40892512Sphk RF_ACTIVE); 40992512Sphk rid = 0; 41092512Sphk if (!sb->drq1) 41192512Sphk sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, 41292512Sphk &rid, 0, ~0, 1, 41392512Sphk RF_ACTIVE); 41492512Sphk rid = 1; 41592512Sphk if (!sb->drq2 && !(sb->bd_flags & BD_F_ESS)) 41692512Sphk sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, 41792512Sphk &rid, 0, ~0, 1, 41892512Sphk RF_ACTIVE); 41992512Sphk 42092512Sphk if (sb->io_base && sb->drq1 && sb->irq) { 42192512Sphk int bs = (sb->bd_flags & BD_F_ESS)? ESS_BUFFSIZE : DSP_BUFFSIZE; 42292512Sphk 42392512Sphk isa_dma_acquire(rman_get_start(sb->drq1)); 42492512Sphk isa_dmainit(rman_get_start(sb->drq1), bs); 42592512Sphk 42692512Sphk if (sb->drq2) { 42792512Sphk isa_dma_acquire(rman_get_start(sb->drq2)); 42892512Sphk isa_dmainit(rman_get_start(sb->drq2), bs); 42992512Sphk } 43092512Sphk 43192512Sphk return 0; 43292512Sphk } else return ENXIO; 43392512Sphk} 43492512Sphk 43592512Sphkstatic void 43692512Sphksb16_swap(void *v, int dir) 437{ 438 struct sb_info *sb = v; 439 int pb = sb->pch.buffer->dl; 440 int rb = sb->rch.buffer->dl; 441 int pc = sb->pch.buffer->chan; 442 int rc = sb->rch.buffer->chan; 443 int swp = 0; 444 445 if (!pb && !rb) { 446 if (dir == PCMDIR_PLAY && pc < 4) 447 swp = 1; 448 else 449 if (dir == PCMDIR_REC && rc < 4) 450 swp = 1; 451 if (sb->bd_flags & BD_F_SB16X) 452 swp = !swp; 453 if (swp) { 454 int t; 455 456 t = sb->pch.buffer->chan; 457 sb->pch.buffer->chan = sb->rch.buffer->chan; 458 sb->rch.buffer->chan = t; 459 sb->pch.buffer->dir = B_WRITE; 460 sb->rch.buffer->dir = B_READ; 461 } 462 } 463} 464 465static int 466sb_doattach(device_t dev, struct sb_info *sb) 467{ 468 snddev_info *d = device_get_softc(dev); 469 void *ih; 470 char status[SND_STATUSLEN]; 471 int bs = (sb->bd_flags & BD_F_ESS)? ESS_BUFFSIZE : DSP_BUFFSIZE; 472 473 if (sb_alloc_resources(sb, dev)) 474 goto no; 475 if (sb_reset_dsp(sb)) 476 goto no; 477 mixer_init(d, &sb_mixer, sb); 478 479 if (sb->bd_flags & BD_F_ESS) 480 bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, ess_intr, sb, &ih); 481 else 482 bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih); 483 if ((sb->bd_flags & BD_F_SB16) && !(sb->bd_flags & BD_F_SB16X)) 484 pcm_setswap(dev, sb16_swap); 485 if (!sb->drq2) 486 pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 487 488 if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 489 /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 490 /*highaddr*/BUS_SPACE_MAXADDR, 491 /*filter*/NULL, /*filterarg*/NULL, 492 /*maxsize*/bs, /*nsegments*/1, 493 /*maxsegz*/0x3ffff, 494 /*flags*/0, &sb->parent_dmat) != 0) { 495 device_printf(dev, "unable to create dma tag\n"); 496 goto no; 497 } 498 499 snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld", 500 rman_get_start(sb->io_base), rman_get_start(sb->irq), 501 rman_get_start(sb->drq1)); 502 if (sb->drq2) 503 snprintf(status + strlen(status), SND_STATUSLEN - strlen(status), 504 ":%ld", rman_get_start(sb->drq2)); 505 506 if (pcm_register(dev, sb, 1, 1)) 507 goto no; 508 if (sb->bd_flags & BD_F_ESS) { 509 pcm_addchan(dev, PCMDIR_REC, &ess_chantemplate, sb); 510 pcm_addchan(dev, PCMDIR_PLAY, &ess_chantemplate, sb); 511 } else { 512 pcm_addchan(dev, PCMDIR_REC, &sb_chantemplate, sb); 513 pcm_addchan(dev, PCMDIR_PLAY, &sb_chantemplate, sb); 514 } 515 pcm_setstatus(dev, status); 516 517 return 0; 518 519no: 520 sb_release_resources(sb, dev); 521 return ENXIO; 522} 523 524static void 525sb_intr(void *arg) 526{ 527 struct sb_info *sb = (struct sb_info *)arg; 528 int reason = 3, c; 529 530 /* 531 * SB < 4.0 is half duplex and has only 1 bit for int source, 532 * so we fake it. SB 4.x (SB16) has the int source in a separate 533 * register. 534 * The Vibra16X has separate flags for 8 and 16 bit transfers, but 535 * I have no idea how to tell capture from playback interrupts... 536 */ 537 if (sb->bd_flags & BD_F_SB16) { 538 c = sb_getmixer(sb, IRQ_STAT); 539 /* this tells us if the source is 8-bit or 16-bit dma. We 540 * have to check the io channel to map it to read or write... 541 */ 542 reason = 0; 543 if (c & 1) { /* 8-bit dma */ 544 if (sb->pch.fmt & AFMT_U8) 545 reason |= 1; 546 if (sb->rch.fmt & AFMT_U8) 547 reason |= 2; 548 } 549 if (c & 2) { /* 16-bit dma */ 550 if (sb->pch.fmt & AFMT_S16_LE) 551 reason |= 1; 552 if (sb->rch.fmt & AFMT_S16_LE) 553 reason |= 2; 554 } 555 } else c = 1; 556#if 0 557 printf("sb_intr: reason=%d c=0x%x\n", reason, c); 558#endif 559 if ((reason & 1) && (sb->pch.buffer->dl > 0)) 560 chn_intr(sb->pch.channel); 561 if ((reason & 2) && (sb->rch.buffer->dl > 0)) 562 chn_intr(sb->rch.channel); 563 if (c & 1) 564 sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */ 565 if (c & 2) 566 sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */ 567} 568 569static void 570ess_intr(void *arg) 571{ 572 struct sb_info *sb = (struct sb_info *)arg; 573 574 sb_rd(sb, DSP_DATA_AVAIL); /* int ack */ 575#ifdef notyet 576 /* 577 * XXX 578 * for full-duplex mode: 579 * should read port 0x6 to identify where interrupt came from. 580 */ 581#endif 582 /* 583 * We are transferring data in DSP normal mode, 584 * so clear the dl to indicate the DMA is stopped. 585 */ 586 if (sb->pch.buffer->dl > 0) { 587 sb->pch.buffer->dl = -1; 588 chn_intr(sb->pch.channel); 589 } 590 if (sb->rch.buffer->dl > 0) { 591 sb->rch.buffer->dl = -1; 592 chn_intr(sb->rch.channel); 593 } 594} 595 596static int 597sb_speed(struct sb_chinfo *ch) 598{ 599 struct sb_info *sb = ch->parent; 600 int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 601 int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; 602 int speed = ch->spd; 603 604 if (sb->bd_flags & BD_F_SB16) { 605 RANGE(speed, 5000, 45000); 606 sb_cmd(sb, 0x42 - play); 607 sb_cmd(sb, speed >> 8); 608 sb_cmd(sb, speed & 0xff); 609 } else { 610 u_char tconst; 611 int max_speed = 45000, tmp; 612 u_long flags; 613 614 /* here enforce speed limitations - max 22050 on sb 1.x*/ 615 if (sb->bd_id <= 0x200) 616 max_speed = 22050; 617 618 /* 619 * SB models earlier than SB Pro have low limit for the 620 * input rate. Note that this is only for input, but since 621 * we do not support separate values for rec & play.... 622 */ 623 if (!play) { 624 if (sb->bd_id <= 0x200) 625 max_speed = 13000; 626 else 627 if (sb->bd_id < 0x300) 628 max_speed = 15000; 629 } 630 RANGE(speed, 4000, max_speed); 631 if (stereo) 632 speed <<= 1; 633 634 /* 635 * Now the speed should be valid. Compute the value to be 636 * programmed into the board. 637 */ 638 if (speed > 22050) { /* High speed mode on 2.01/3.xx */ 639 tconst = (u_char) 640 ((65536 - ((256000000 + speed / 2) / speed)) 641 >> 8); 642 sb->bd_flags |= BD_F_HISPEED; 643 tmp = 65536 - (tconst << 8); 644 speed = (256000000 + tmp / 2) / tmp; 645 } else { 646 sb->bd_flags &= ~BD_F_HISPEED; 647 tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; 648 tmp = 256 - tconst; 649 speed = (1000000 + tmp / 2) / tmp; 650 } 651 flags = spltty(); 652 sb_cmd1(sb, 0x40, tconst); /* set time constant */ 653 splx(flags); 654 if (stereo) 655 speed >>= 1; 656 } 657 ch->spd = speed; 658 return speed; 659} 660 661static int 662sb_start(struct sb_chinfo *ch) 663{ 664 struct sb_info *sb = ch->parent; 665 int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 666 int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; 667 int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; 668 int l = ch->buffer->dl; 669 int dh = ch->buffer->chan > 3; 670 u_char i1, i2; 671 672 if (b16 || dh) 673 l >>= 1; 674 l--; 675 676 if (play) 677 sb_cmd(sb, DSP_CMD_SPKON); 678 679 if (sb->bd_flags & BD_F_SB16) { 680 i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON; 681 i1 |= play? DSP_F16_DAC : DSP_F16_ADC; 682 i1 |= (b16 || dh)? DSP_DMA16 : DSP_DMA8; 683 i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0); 684 sb_cmd(sb, i1); 685 sb_cmd2(sb, i2, l); 686 } else { 687 if (sb->bd_flags & BD_F_HISPEED) 688 i1 = play? 0x90 : 0x98; 689 else 690 i1 = play? 0x1c : 0x2c; 691 sb_setmixer(sb, 0x0e, stereo? 2 : 0); 692 sb_cmd2(sb, 0x48, l); 693 sb_cmd(sb, i1); 694 } 695 sb->bd_flags |= BD_F_DMARUN << b16; 696 return 0; 697} 698 699static int 700sb_stop(struct sb_chinfo *ch) 701{ 702 struct sb_info *sb = ch->parent; 703 int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 704 int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; 705 706 if (sb->bd_flags & BD_F_HISPEED) 707 sb_reset_dsp(sb); 708 else { 709 sb_cmd(sb, b16? DSP_CMD_DMAPAUSE_16 : DSP_CMD_DMAPAUSE_8); 710 /* 711 * The above seems to have the undocumented side effect of 712 * blocking the other side as well. If the other 713 * channel was active (SB16) I have to re-enable it :( 714 */ 715 if (sb->bd_flags & (BD_F_DMARUN << (1 - b16))) 716 sb_cmd(sb, b16? 0xd4 : 0xd6 ); 717 } 718 if (play) 719 sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */ 720 sb->bd_flags &= ~(BD_F_DMARUN << b16); 721 return 0; 722} 723 724/* channel interface */ 725static void * 726sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 727{ 728 struct sb_info *sb = devinfo; 729 struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch; 730 int dch, dl, dh; 731 732 ch->parent = sb; 733 ch->channel = c; 734 ch->buffer = b; 735 ch->buffer->bufsize = DSP_BUFFSIZE; 736 if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) 737 return NULL; 738 dch = (dir == PCMDIR_PLAY)? 1 : 0; 739 if (sb->bd_flags & BD_F_SB16X) 740 dch = !dch; 741 dl = rman_get_start(sb->drq1); 742 dh = sb->drq2? rman_get_start(sb->drq2) : dl; 743 ch->buffer->chan = dch? dh : dl; 744 return ch; 745} 746 747static int 748sbchan_setdir(void *data, int dir) 749{ 750 struct sb_chinfo *ch = data; 751 752 ch->dir = dir; 753 return 0; 754} 755 756static int 757sbchan_setformat(void *data, u_int32_t format) 758{ 759 struct sb_chinfo *ch = data; 760 761 ch->fmt = format; 762 return 0; 763} 764 765static int 766sbchan_setspeed(void *data, u_int32_t speed) 767{ 768 struct sb_chinfo *ch = data; 769 770 ch->spd = speed; 771 return sb_speed(ch); 772} 773 774static int 775sbchan_setblocksize(void *data, u_int32_t blocksize) 776{ 777 return blocksize; 778} 779 780static int 781sbchan_trigger(void *data, int go) 782{ 783 struct sb_chinfo *ch = data; 784 785 if (go == PCMTRIG_EMLDMAWR) 786 return 0; 787 788 buf_isadma(ch->buffer, go); 789 if (go == PCMTRIG_START) 790 sb_start(ch); 791 else 792 sb_stop(ch); 793 return 0; 794} 795 796static int 797sbchan_getptr(void *data) 798{ 799 struct sb_chinfo *ch = data; 800 801 return buf_isadmaptr(ch->buffer); 802} 803 804static pcmchan_caps * 805sbchan_getcaps(void *data) 806{ 807 struct sb_chinfo *ch = data; 808 int p = (ch->dir == PCMDIR_PLAY)? 1 : 0; 809 810 if (ch->parent->bd_id < 0x300) 811 return p? &sb_playcaps : &sb_reccaps; 812 else if (ch->parent->bd_id < 0x400) 813 return p? &sbpro_playcaps : &sbpro_reccaps; 814 else if (ch->parent->bd_flags & BD_F_SB16X) 815 return &sb16x_caps; 816 else 817 return (ch->buffer->chan >= 4)? &sb16_hcaps : &sb16_lcaps; 818} 819 820/* utility functions for ESS */ 821static int 822ess_format(struct sb_chinfo *ch, u_int32_t format) 823{ 824 struct sb_info *sb = ch->parent; 825 int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 826 int b16 = (format & AFMT_S16_LE)? 1 : 0; 827 int stereo = (format & AFMT_STEREO)? 1 : 0; 828 u_char c; 829 830 ch->fmt = format; 831 sb_reset_dsp(sb); 832 /* auto-init DMA mode */ 833 ess_write(sb, 0xb8, play ? 0x04 : 0x0e); 834 /* mono/stereo */ 835 c = (ess_read(sb, 0xa8) & ~0x03) | 1; 836 if (!stereo) 837 c++; 838 ess_write(sb, 0xa8, c); 839 /* demand mode, 4 bytes/xfer */ 840 ess_write(sb, 0xb9, 2); 841 /* setup dac/adc */ 842 if (play) 843 ess_write(sb, 0xb6, b16? 0x00 : 0x80); 844 ess_write(sb, 0xb7, 0x51 | (b16? 0x20 : 0x00)); 845 ess_write(sb, 0xb7, 0x98 + (b16? 0x24 : 0x00) + (stereo? 0x00 : 0x38)); 846 /* irq/drq control */ 847 ess_write(sb, 0xb1, (ess_read(sb, 0xb1) & 0x0f) | 0x50); 848 ess_write(sb, 0xb2, (ess_read(sb, 0xb2) & 0x0f) | 0x50); 849 return 0; 850} 851 852static int 853ess_speed(struct sb_chinfo *ch, int speed) 854{ 855 struct sb_info *sb = ch->parent; 856 int t; 857 858 RANGE (speed, 5000, 49000); 859 if (speed > 22000) { 860 t = (795500 + speed / 2) / speed; 861 speed = (795500 + t / 2) / t; 862 t = (256 - t ) | 0x80; 863 } else { 864 t = (397700 + speed / 2) / speed; 865 speed = (397700 + t / 2) / t; 866 t = 128 - t; 867 } 868 ess_write(sb, 0xa1, t); /* set time constant */ 869#if 0 870 d->play_speed = d->rec_speed = speed; 871 speed = (speed * 9 ) / 20; 872#endif 873 t = 256 - 7160000 / ((speed * 9 / 20) * 82); 874 ess_write(sb, 0xa2, t); 875 return speed; 876} 877 878static int 879ess_start(struct sb_chinfo *ch) 880{ 881 struct sb_info *sb = ch->parent; 882 int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 883 short c = - ch->buffer->dl; 884 u_char c1; 885 886 /* 887 * clear bit 0 of register B8h 888 */ 889#if 1 890 c1 = play ? 0x04 : 0x0e; 891 ess_write(sb, 0xb8, c1++); 892#else 893 c1 = ess_read(sb, 0xb8) & 0xfe; 894 ess_write(sb, 0xb8, c1++); 895#endif 896 /* 897 * update ESS Transfer Count Register 898 */ 899 ess_write(sb, 0xa4, (u_char)((u_short)c & 0xff)); 900 ess_write(sb, 0xa5, (u_char)(((u_short)c >> 8) & 0xff)); 901 /* 902 * set bit 0 of register B8h 903 */ 904 ess_write(sb, 0xb8, c1); 905 if (play) 906 sb_cmd(sb, DSP_CMD_SPKON); 907 return 0; 908} 909 910static int 911ess_stop(struct sb_chinfo *ch) 912{ 913 struct sb_info *sb = ch->parent; 914 /* 915 * no need to send a stop command if the DMA has already stopped. 916 */ 917 if (ch->buffer->dl > 0) { 918 sb_cmd(sb, DSP_CMD_DMAPAUSE_8); /* pause dma. */ 919 } 920 return 0; 921} 922 923static int 924ess_abort(struct sb_chinfo *ch) 925{ 926 struct sb_info *sb = ch->parent; 927 int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 928 929 if (play) 930 sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */ 931 sb_reset_dsp(sb); 932 ess_format(ch, ch->fmt); 933 ess_speed(ch, ch->channel->speed); 934 return 0; 935} 936 937/* channel interface for ESS18xx */ 938static void * 939esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 940{ 941 struct sb_info *sb = devinfo; 942 struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch; 943 944 ch->parent = sb; 945 ch->channel = c; 946 ch->buffer = b; 947 ch->buffer->bufsize = ESS_BUFFSIZE; 948 if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) 949 return NULL; 950 ch->buffer->chan = rman_get_start(sb->drq1); 951 return ch; 952} 953 954static int 955esschan_setdir(void *data, int dir) 956{ 957 struct sb_chinfo *ch = data; 958 959 ch->dir = dir; 960 return 0; 961} 962 963static int 964esschan_setformat(void *data, u_int32_t format) 965{ 966 struct sb_chinfo *ch = data; 967 968 ess_format(ch, format); 969 return 0; 970} 971 972static int 973esschan_setspeed(void *data, u_int32_t speed) 974{ 975 struct sb_chinfo *ch = data; 976 977 return ess_speed(ch, speed); 978} 979 980static int 981esschan_setblocksize(void *data, u_int32_t blocksize) 982{ 983 return blocksize; 984} 985 986static int 987esschan_trigger(void *data, int go) 988{ 989 struct sb_chinfo *ch = data; 990 991 if (go == PCMTRIG_EMLDMAWR) 992 return 0; 993 switch (go) { 994 case PCMTRIG_START: 995 if (!ch->ess_dma_started) 996 buf_isadma(ch->buffer, go); 997 ch->ess_dma_started = 1; 998 ess_start(ch); 999 break; 1000 case PCMTRIG_STOP: 1001 if (ch->buffer->dl >= 0) { 1002 buf_isadma(ch->buffer, go); 1003 ch->ess_dma_started = 0; 1004 ess_stop(ch); 1005 } 1006 break; 1007 case PCMTRIG_ABORT: 1008 default: 1009 ch->ess_dma_started = 0; 1010 ess_abort(ch); 1011 buf_isadma(ch->buffer, go); 1012 break; 1013 } 1014 return 0; 1015} 1016 1017static int 1018esschan_getptr(void *data) 1019{ 1020 struct sb_chinfo *ch = data; 1021 1022 return buf_isadmaptr(ch->buffer); 1023} 1024 1025static pcmchan_caps * 1026esschan_getcaps(void *data) 1027{ 1028 struct sb_chinfo *ch = data; 1029 1030 return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps; 1031} 1032 1033/************************************************************/ 1034 1035static int 1036sbmix_init(snd_mixer *m) 1037{ 1038 struct sb_info *sb = mix_getdevinfo(m); 1039 1040 switch (sb->bd_flags & BD_F_MIX_MASK) { 1041 case BD_F_MIX_CT1345: /* SB 3.0 has 1345 mixer */ 1042 mix_setdevs(m, SBPRO_MIXER_DEVICES); 1043 mix_setrecdevs(m, SBPRO_RECORDING_DEVICES); 1044 sb_setmixer(sb, 0, 1); /* reset mixer */ 1045 if (!(sb->bd_flags & BD_F_ESS)) 1046 sb_setmixer(sb, MIC_VOL, 0x6); /* mic volume max */ 1047 sb_setmixer(sb, RECORD_SRC, 0x0); /* mic source */ 1048 sb_setmixer(sb, FM_VOL, 0x0); /* no midi */ 1049 break; 1050 1051 case BD_F_MIX_CT1745: /* SB16 mixer ... */ 1052 mix_setdevs(m, SB16_MIXER_DEVICES); 1053 mix_setrecdevs(m, SB16_RECORDING_DEVICES); 1054 sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */ 1055 sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */ 1056 sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */ 1057 } 1058 return 0; 1059} 1060 1061static int 1062sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 1063{ 1064 struct sb_info *sb = mix_getdevinfo(m); 1065 int regoffs; 1066 u_char val; 1067 mixer_tab *iomap; 1068 1069 switch (sb->bd_flags & BD_F_MIX_MASK) { 1070 case BD_F_MIX_CT1345: 1071 if (sb->bd_flags & BD_F_ESS) 1072 iomap = &ess_mix; 1073 else 1074 iomap = &sbpro_mix; 1075 break; 1076 1077 case BD_F_MIX_CT1745: 1078 iomap = &sb16_mix; 1079 break; 1080 1081 default: 1082 return -1; 1083 } 1084 1085 /* Change left channel */ 1086 regoffs = (*iomap)[dev][LEFT_CHN].regno; 1087 if (regoffs != 0) { 1088 val = sb_getmixer(sb, regoffs); 1089 change_bits(iomap, &val, dev, LEFT_CHN, left); 1090 sb_setmixer(sb, regoffs, val); 1091 } 1092 1093 /* Change right channel */ 1094 regoffs = (*iomap)[dev][RIGHT_CHN].regno; 1095 if (regoffs != 0) { 1096 val = sb_getmixer(sb, regoffs); /* Read the new one */ 1097 change_bits(iomap, &val, dev, RIGHT_CHN, right); 1098 sb_setmixer(sb, regoffs, val); 1099 } else 1100 right = left; 1101 1102 return left | (right << 8); 1103} 1104 1105static int 1106sbmix_setrecsrc(snd_mixer *m, u_int32_t src) 1107{ 1108 struct sb_info *sb = mix_getdevinfo(m); 1109 u_char recdev; 1110 1111 switch (sb->bd_flags & BD_F_MIX_MASK) { 1112 case BD_F_MIX_CT1345: 1113 if (src == SOUND_MASK_LINE) 1114 recdev = 0x06; 1115 else if (src == SOUND_MASK_CD) 1116 recdev = 0x02; 1117 else { /* default: mic */ 1118 src = SOUND_MASK_MIC; 1119 recdev = 0; 1120 } 1121 sb_setmixer(sb, RECORD_SRC, recdev | 1122 (sb_getmixer(sb, RECORD_SRC) & ~0x07)); 1123 break; 1124 1125 case BD_F_MIX_CT1745: /* sb16 */ 1126 recdev = 0; 1127 if (src & SOUND_MASK_MIC) 1128 recdev |= 0x01; /* mono mic */ 1129 if (src & SOUND_MASK_CD) 1130 recdev |= 0x06; /* l+r cd */ 1131 if (src & SOUND_MASK_LINE) 1132 recdev |= 0x18; /* l+r line */ 1133 if (src & SOUND_MASK_SYNTH) 1134 recdev |= 0x60; /* l+r midi */ 1135 sb_setmixer(sb, SB16_IMASK_L, recdev); 1136 sb_setmixer(sb, SB16_IMASK_R, recdev); 1137 /* 1138 * since the same volume controls apply to the input and 1139 * output sections, the best approach to have a consistent 1140 * behaviour among cards would be to disable the output path 1141 * on devices which are used to record. 1142 * However, since users like to have feedback, we only disable 1143 * the mic -- permanently. 1144 */ 1145 sb_setmixer(sb, SB16_OMASK, 0x1f & ~1); 1146 break; 1147 } 1148 return src; 1149} 1150 1151static int 1152sbsbc_probe(device_t dev) 1153{ 1154 char buf[64]; 1155 uintptr_t func, ver, r, f; 1156 1157 /* The parent device has already been probed. */ 1158 r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func); 1159 if (func != SCF_PCM) 1160 return (ENXIO); 1161 1162 r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver); 1163 f = (ver & 0xffff0000) >> 16; 1164 ver &= 0x0000ffff; 1165 snprintf(buf, sizeof buf, "SB DSP %d.%02d%s%s", (int) ver >> 8, (int) ver & 0xff, 1166 (f & BD_F_ESS)? " (ESS mode)" : "", 1167 (f & BD_F_SB16X)? " (ViBRA16X)" : ""); 1168 device_set_desc_copy(dev, buf); 1169 1170 return 0; 1171} 1172 1173static int 1174sbsbc_attach(device_t dev) 1175{ 1176 struct sb_info *sb; 1177 uintptr_t ver; 1178 1179 sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); 1180 if (!sb) 1181 return ENXIO; 1182 bzero(sb, sizeof *sb); 1183 1184 BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver); 1185 sb->bd_id = ver & 0x0000ffff; 1186 sb->bd_flags = (ver & 0xffff0000) >> 16; 1187 1188 return sb_doattach(dev, sb); 1189} 1190 1191static device_method_t sbsbc_methods[] = { 1192 /* Device interface */ 1193 DEVMETHOD(device_probe, sbsbc_probe), 1194 DEVMETHOD(device_attach, sbsbc_attach), 1195 1196 { 0, 0 } 1197}; 1198 1199static driver_t sbsbc_driver = { 1200 "pcm", 1201 sbsbc_methods, 1202 sizeof(snddev_info), 1203}; 1204 1205DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0); 1206 1207 1208 1209