Index: sys/arch/i386/conf/GENERIC =================================================================== RCS file: /home/repos/netbsd-current/src/sys/arch/i386/conf/GENERIC,v retrieving revision 1.905 diff -u -r1.905 GENERIC --- sys/arch/i386/conf/GENERIC 21 Aug 2008 18:06:18 -0000 1.905 +++ sys/arch/i386/conf/GENERIC 19 Sep 2008 09:45:49 -0000 @@ -1312,7 +1312,8 @@ # The spkr driver provides a simple tone interface to the built in speaker. #spkr0 at pcppi? # PC speaker - +# (EXPERIMENTAL) PC speaker driven by PCM. Exclusive to spkr0 (above) +pcmaudio* at pcppi? # FM-Radio devices # ISA radio devices Index: sys/arch/x86/isa/clock.c =================================================================== RCS file: /home/repos/netbsd-current/src/sys/arch/x86/isa/clock.c,v retrieving revision 1.30 diff -u -r1.30 clock.c --- sys/arch/x86/isa/clock.c 11 May 2008 22:18:08 -0000 1.30 +++ sys/arch/x86/isa/clock.c 19 Sep 2008 08:45:07 -0000 @@ -175,6 +175,16 @@ static pcppi_tag_t ppicookie; #endif /* PCPPI */ +#include "pcmaudio.h" +#if NPCMAUDIO > 0 +#include +struct pcmaudio_softc *pcmaudio_softc; + +static u_long tvaldiv = 0; +static u_long hardskip = 0; +#endif + + #ifdef CLOCKDEBUG int clock_debug = 0; #define DPRINTF(arg) if (clock_debug) printf arg @@ -182,6 +192,8 @@ #define DPRINTF(arg) #endif +extern void (*initclock_func)(void); /* XXX put in header file */ + /* Used by lapic.c */ unsigned int gettick(void); void sysbeep(int, int); @@ -336,7 +348,30 @@ * set to. Also, correctly round * this by carrying an extra bit through the division. */ + +#if NPCMAUDIO > 0 + struct pcmaudio_softc *sc = pcmaudio_softc; + int samplingrate; + + if (sc == NULL) { /* Before pcmaudio(4) is attached. */ + samplingrate = hz; + } + else { /* After pcmaudio(4) has attached. */ + samplingrate = sc->sc_samplingrate; + /* Save a reference to i8254_timecounter */ + sc->sc_timecounter = &i8254_timecounter; + } + + /* Adjust the timecounter spec + * i8254_timecounter.quality = samplingrate + */ + tval = (freq * 2) / (u_long) samplingrate; + tvaldiv = samplingrate / (u_long) hz; + + +#else tval = (freq * 2) / (u_long) hz; +#endif tval = (tval / 2) + (tval & 0x1); /* initialize 8254 clock */ @@ -400,9 +435,61 @@ static int clockintr(void *arg, struct intrframe *frame) { + tickle_tc(); - hardclock((struct clockframe *)frame); +#if NPCMAUDIO > 0 + + struct pcmaudio_softc *sc = pcmaudio_softc; + u_long tval; + + if ((sc != NULL) && (sc->playing == true)) { + if (sc->blknow < sc->blkend) { + + /* Processing: signed int -> unsigned long, scale wrt rtclock_tval */ + tval = (((u_long)((1 << 15) + (signed long)*(sc->blknow++)) * rtclock_tval) >> 16) | 0x1; + + /* Adjust volume */ + tval = tval * sc->sc_spkrvolume / AUDIO_MAX_GAIN; + + /* Init counter 2, tied to the PC Speaker. */ + outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL2 | TIMER_16BIT | TIMER_INTTC); + outb(IO_TIMER1 + TIMER_CNTR2, tval % 256); + outb(IO_TIMER1 + TIMER_CNTR2, tval / 256); + } + else { + /* reset the blk pointers */ + sc->blknow = sc->blkstart; + } + + if (!((size_t) sc->blknow % sc->blksize)) { + /* schedule the audio(4) callback */ + + KASSERT(sc->sc_softintcookie != NULL); + softint_schedule(sc->sc_softintcookie); + } + } + + if (hardskip < tvaldiv){ + hardskip++; + goto eoi; + } + + hardskip = 0; + +#endif + + /* + * We only handle the system clock if we're really in charge + * of it. + */ + if (initclock_func == i8254_initclocks) { + hardclock((struct clockframe *)frame); + } + +#if NPCMAUDIO > 0 +eoi: +#endif #if NMCA > 0 if (MCA_system) { @@ -410,6 +497,7 @@ outb(0x61, inb(0x61) | 0x80); } #endif + return -1; } @@ -575,13 +663,14 @@ void i8254_initclocks(void) { - + /* * XXX If you're doing strange things with multiple clocks, you might * want to keep track of clock handlers. */ - (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, - (int (*)(void *))clockintr, 0); + i8254_timecounter.tc_priv = isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, + (int (*)(void *))clockintr, 0); + } static void Index: sys/dev/audio.c =================================================================== RCS file: /home/repos/netbsd-current/src/sys/dev/audio.c,v retrieving revision 1.243 diff -u -r1.243 audio.c --- sys/dev/audio.c 10 Jun 2008 22:53:08 -0000 1.243 +++ sys/dev/audio.c 17 Sep 2008 07:38:09 -0000 @@ -3583,6 +3583,10 @@ s = splaudio(); init_error = audio_initbufs(sc); if (init_error) goto err; + if (sc->sc_pustream == NULL || + sc->sc_rustream == NULL) { + goto err; + } if (sc->sc_pr.blksize != oldpblksize || sc->sc_rr.blksize != oldrblksize || sc->sc_pustream != oldpus || Index: sys/dev/ic/attimer.c =================================================================== RCS file: /home/repos/netbsd-current/src/sys/dev/ic/attimer.c,v retrieving revision 1.9 diff -u -r1.9 attimer.c --- sys/dev/ic/attimer.c 12 Jun 2008 22:30:30 -0000 1.9 +++ sys/dev/ic/attimer.c 14 Jul 2008 05:22:15 -0000 @@ -112,3 +112,19 @@ TIMER_DIV(pitch) / 256); splx(s); } + +void +attimer_set_pulse(device_t dev, int lowcnt) +{ + struct attimer_softc *sc = device_private(dev); + int s; + + s = splhigh(); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, TIMER_MODE, + TIMER_SEL2 | TIMER_16BIT | TIMER_INTTC); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, TIMER_CNTR2, + TIMER_DIV(lowcnt) % 256); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, TIMER_CNTR2, + TIMER_DIV(lowcnt) / 256); + splx(s); +} Index: sys/dev/ic/attimervar.h =================================================================== RCS file: /home/repos/netbsd-current/src/sys/dev/ic/attimervar.h,v retrieving revision 1.6 diff -u -r1.6 attimervar.h --- sys/dev/ic/attimervar.h 29 Apr 2008 06:53:02 -0000 1.6 +++ sys/dev/ic/attimervar.h 18 Jul 2008 10:31:30 -0000 @@ -40,3 +40,4 @@ int attimer_detach(device_t, int); device_t attimer_attach_speaker(void); void attimer_set_pitch(device_t, int); +void attimer_set_pulse(device_t, int); Index: sys/dev/isa/files.isa =================================================================== RCS file: /home/repos/netbsd-current/src/sys/dev/isa/files.isa,v retrieving revision 1.157 diff -u -r1.157 files.isa --- sys/dev/isa/files.isa 3 Apr 2008 22:46:22 -0000 1.157 +++ sys/dev/isa/files.isa 19 Sep 2008 08:03:30 -0000 @@ -443,6 +443,9 @@ file dev/isa/spkr.c spkr needs-flag attach midi at pcppi with midi_pcppi: midisyn file dev/isa/midi_pcppi.c midi_pcppi +device pcmaudio: audiobus, auconv, mulaw, aurateconv +attach pcmaudio at pcppi +file dev/isa/pcmaudio.c pcmaudio needs-flag # AT Timer (TIMER 1) attach attimer at isa with attimer_isa Index: sys/dev/isa/pcmaudio.c =================================================================== RCS file: sys/dev/isa/pcmaudio.c diff -N sys/dev/isa/pcmaudio.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/isa/pcmaudio.c 19 Sep 2008 14:03:36 -0000 @@ -0,0 +1,826 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Written by Cherry G. Mathew with bits and pieces + * from auich(4) and ym(4) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "pcmaudio.h" + +#include +#include +#include + +#include +#include + + +extern void (*initclock_func)(void); /* XXX put in header file */ + +static int pcmaudio_open(void *, int); +static void pcmaudio_close(void *); +static int pcmaudio_query_encoding(void *, audio_encoding_t *); +static int pcmaudio_set_params(void *, int, int, audio_params_t *, + audio_params_t *, stream_filter_list_t *, + stream_filter_list_t *); +static int pcmaudio_round_blocksize(void *, int, int, const audio_params_t *); +static int pcmaudio_halt_output(void *); +static int pcmaudio_halt_input(void *); + +static int pcmaudio_speaker_ctl(void *, int); +static int pcmaudio_getdev(void *, struct audio_device *); + +/* Mixer functions. Stubs for now. */ +static int pcmaudio_set_port(void *, mixer_ctrl_t *); +static int pcmaudio_get_port(void *, mixer_ctrl_t *); + +static int pcmaudio_query_devinfo(void *, mixer_devinfo_t *); + + +static size_t pcmaudio_round_buffersize(void *, int, size_t); +static paddr_t pcmaudio_mappage(void *, void *, off_t, int); +static int pcmaudio_get_props(void *); +static int pcmaudio_trigger_output(void *, void *, void *, int, void (*)(void *), + void *, const audio_params_t *); +static int pcmaudio_trigger_input(void *, void *, void *, int, void (*)(void *), + void *, const audio_params_t *); +static int pcmaudio_sysctl_verify(SYSCTLFN_ARGS); + +extern struct pcmaudio_softc *pcmaudio_softc; + +/* In the hypothetical case that there is more than one pcmaudio(4) + * instance attached during autoconf, the sampling rate is uniform + * across these instances. This situation is untested. + */ +static int pcmaudio_sampling_rate = 22000; + +static const struct audio_format pcmaudio_formats[PCMAUDIO_NFORMATS] = { + { + .driver_data = NULL, + .mode = AUMODE_PLAY, + .encoding = AUDIO_ENCODING_SLINEAR_LE, + .validbits = 16, + .precision = 16, + .channels = 1, + .channel_mask = AUFMT_MONAURAL, + .frequency_type = 1, + + /* Keep this in sync with pcmaudio_sampling_rate above */ + .frequency = {22000} + } +}; + +static const struct audio_hw_if pcmaudio_hw_if = { + .open = pcmaudio_open, + .close = pcmaudio_close, + .drain = NULL, + .query_encoding = pcmaudio_query_encoding, + .set_params = pcmaudio_set_params, + .round_blocksize = pcmaudio_round_blocksize, + .commit_settings = NULL, + .init_output = NULL, + .init_input = NULL, + .start_output = NULL, + .start_input = NULL, + .halt_output = pcmaudio_halt_output, + .halt_input = pcmaudio_halt_input, + .speaker_ctl = pcmaudio_speaker_ctl, + .getdev = pcmaudio_getdev, + .setfd = NULL, + + .set_port = pcmaudio_set_port, + .get_port = pcmaudio_get_port, + .query_devinfo = pcmaudio_query_devinfo, + + /* Memory allocation handled by audio(4) */ + .allocm = NULL, + .freem = NULL, + .round_buffersize = pcmaudio_round_buffersize, + .mappage = pcmaudio_mappage, + .get_props = pcmaudio_get_props, + .trigger_output = pcmaudio_trigger_output, + .trigger_input = pcmaudio_trigger_input, + .dev_ioctl = NULL, + .powerstate = NULL, +}; + +CFATTACH_DECL_NEW(pcmaudio, sizeof(struct pcmaudio_softc), + pcmaudio_match, pcmaudio_attach, pcmaudio_detach, NULL); + +int +pcmaudio_match(device_t parent, cfdata_t match, void *aux) +{ + return 1; +} + +/* Attachment is a bit complicated because we have in effect, two + * parent devices (pcppi(4) and attimer(4)). + * We therefore defer the audio(4) attachment to + * pcmaudio_pcppi_attach() See below: + * This ensures that both parents are attached, failing which we do + * not attach the driver to the audio subsystem. + */ + +void +pcmaudio_attach(device_t parent, device_t self, void *aux) +{ + + struct pcppi_softc *ppi_sc = device_private(parent); + struct pcmaudio_softc *sc = device_private(self); + + /* + * Global variable shim, because there can only be one + * IO_TIMER1 on isa busses + */ + pcmaudio_softc = sc; + + ppi_sc->sc_pcmaudiodev = self; + + /* Initialise some softc members to default values */ + sc->sc_audiodev = NULL; + sc->sc_open = 0; + sc->sc_dev = self; + sc->sc_samplingrate = hz; /* This is set to sampling rate on open(). */ + sc->sc_spkrvolume = AUDIO_MAX_GAIN / 2; + + /* Watch for power change */ + if (!pmf_device_register(sc->sc_dev, NULL, NULL)) + aprint_error_dev(sc->sc_dev, "couldn't establish power handler\n"); + + aprint_naive(": XT PC Speaker driven with PCM @ %dHz\n", pcmaudio_sampling_rate); + aprint_normal(": XT PC Speaker driven with PCM @ %dHz\n", pcmaudio_sampling_rate); + + +} + +int +pcmaudio_detach(device_t self, int flags) +{ + pmf_device_deregister(self); + return 0; +} + + +void +pcmaudio_pcppi_attach(device_t pcppidev) +{ + struct pcppi_softc *ppi_sc = device_private(pcppidev); + struct attimer_softc *attimer_sc = device_private(ppi_sc->sc_timer); + struct pcmaudio_softc *sc = device_private(ppi_sc->sc_pcmaudiodev); + + const struct sysctlnode *node, *node_samplingrate; + int err, node_pcmaudio; + + if (attimer_sc != NULL && (attimer_sc->sc_flags & ATT_ATTACHED)){ + + /* Final updates to the pcmaudio sc struct */ + sc->sc_ppidev = pcppidev; + sc->sc_timerdev = ppi_sc->sc_timer; + + aprint_normal_dev(ppi_sc->sc_pcmaudiodev, "attached to %s and %s\n", + device_xname(pcppidev), + device_xname(ppi_sc->sc_timer)); + + + /* auconv encodings */ + memcpy(sc->sc_audio_formats, pcmaudio_formats, sizeof(pcmaudio_formats)); + if (auconv_create_encodings(sc->sc_audio_formats, PCMAUDIO_NFORMATS, + &sc->sc_encodings) != 0){ + return; + } + + /* If all is well, attach to the audio subsystem */ + sc->sc_audiodev = audio_attach_mi(&pcmaudio_hw_if, sc, sc->sc_dev); + + + /* Kickstart the 8254. */ + initrtclock(TIMER_FREQ); + + /* If the main system timer is not the 8254, it has not been + * programmed to interrupt. See: x86/x86/lapic.c + * Do it now. + */ + if (sc->sc_timecounter->tc_priv == NULL) { + /* Register the interrupt handler, and enable + * interrupts. + */ + i8254_initclocks(); + } + + + /* sysctl nodes */ + err = sysctl_createv(&sc->sc_log, 0, NULL, NULL, 0, + CTLTYPE_NODE, "hw", NULL, NULL, 0, NULL, 0, + CTL_HW, CTL_EOL); + if (err != 0) + goto sysctl_err; + err = sysctl_createv(&sc->sc_log, 0, NULL, &node, 0, + CTLTYPE_NODE, device_xname(ppi_sc->sc_pcmaudiodev), NULL, NULL, 0, + NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); + if (err != 0) + goto sysctl_err; + + node_pcmaudio = node->sysctl_num; + + err = sysctl_createv(&sc->sc_log, 0, NULL, &node_samplingrate, + CTLFLAG_READWRITE, + CTLTYPE_INT, "samplingrate", + SYSCTL_DESCR("sampling rate (the 8254 interrupts at this rate)"), + pcmaudio_sysctl_verify, 0, sc, 0, + CTL_HW, node_pcmaudio, CTL_CREATE, CTL_EOL); + if (err != 0) + goto sysctl_err; + + sc->sc_sysctlnode = node_samplingrate->sysctl_num; + + return; + + sysctl_err: + printf("%s: failed to add sysctl nodes. (%d)\n", + device_xname(ppi_sc->sc_pcmaudiodev), err); + return; /* failure of sysctl is not fatal. */ + } +} + +static int +pcmaudio_sysctl_verify(SYSCTLFN_ARGS) +{ + int error, tmp; + struct sysctlnode node; + struct pcmaudio_softc *sc; + + node = *rnode; + sc = rnode->sysctl_data; + if (node.sysctl_num == sc->sc_sysctlnode) { + tmp = pcmaudio_sampling_rate; + node.sysctl_data = &tmp; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (tmp < 8000 || tmp > 44000) + return EINVAL; + pcmaudio_sampling_rate = tmp; + } + + return 0; +} + + +static int +pcmaudio_open(void *addr, int flags) +{ + struct pcmaudio_softc *sc = addr; + + if (!sc) { + return ENXIO; + } + if (sc->sc_open & FREAD) { + return EBUSY; + } + + if (flags & FREAD) { + return ENXIO; + } + + sc->sc_open |= FREAD; + + /* Sync up with sampling rate, if required. */ + if (sc->sc_samplingrate != pcmaudio_sampling_rate) { + + sc->sc_samplingrate = pcmaudio_sampling_rate; + + /* We need to re-do auconv filters if the sampling rate + * changes. + */ + + if (auconv_delete_encodings(sc->sc_encodings)) { + return ENXIO; + } + + sc->sc_audio_formats[PCMAUDIO_NFORMATS - 1].frequency[0] = sc->sc_samplingrate; + + if (auconv_create_encodings(sc->sc_audio_formats, PCMAUDIO_NFORMATS, + &sc->sc_encodings) != 0){ + return ENXIO; + } + + /* Crank up the 8254 to sampling rate. */ + initrtclock(TIMER_FREQ); + } + + return 0; +} + +static void +pcmaudio_close(void *addr) +{ + + struct pcmaudio_softc *sc = addr; + + sc->sc_open &= ~FREAD; + + /* Switch off speaker */ + pcmaudio_speaker_ctl(addr, SPKR_OFF); + + /* Minimise the 8254 interrupt rate when we're not using it + * for playback. + */ + + sc->sc_samplingrate = hz; + + if (timecounter != sc->sc_timecounter) { + initrtclock(0); + return; + } + + + initrtclock(TIMER_FREQ); + + return; + +} + +/* query_encoding: return current h/w encoding in + * audio_encoding_t *encp; + * + * INPUTS: addr -> points to the device sc, encp->index points to the + * index number of the encoding we're interested in. + * + * OUTPUTS: *encp is filled in with the current device encoding + * + * RETURNS: 0 on success, EINVAL on unsupported encoding index. + */ + +static int +pcmaudio_query_encoding(void *addr, audio_encoding_t *encp) +{ + + struct pcmaudio_softc *sc = addr; + + return auconv_query_encoding(sc->sc_encodings, encp); +} + + +/* + * set_params: set the hardware to operate under specified params. + * + * INPUTS: addr-> points to device sc, + * setmode is a subset of AUMODE_PLAY | AUMODE_RECORD, + * usemode is the current mode ( also a subset of above ) + * pparm is the requisite audio_params to be set ( encoding, + * precision, etc. ) for playback. + * rparm is as pparm, but for recording. + * pfil and rfil are stream filter hooks to be added for + * particular modes. + * + * OUTPUTS: None. Just update the h/w and return 0 if all goes well. + * + * RETURNS: return 0 if all is well. If an unsupported parameter is + * requested, return EINVAL. + */ + + +static int +pcmaudio_set_params(void *addr, int setmode, int usemode, audio_params_t *pparm, + audio_params_t *rparm, stream_filter_list_t *pfil, + stream_filter_list_t *rfil) +{ + struct pcmaudio_softc *sc = addr; + + int index; + + /* We don't support AUMODE_RECORD */ + /* Bail out, if AUMODE_PLAY is not asked for. */ + /* Note that we silently ignore the "record" aspect of + * AUMODE_PLAY | AUMODE_RECORD + */ + + if (setmode & AUMODE_RECORD && + !(setmode & AUMODE_PLAY)) { + return EINVAL; + } + + index = auconv_set_converter(sc->sc_audio_formats, PCMAUDIO_NFORMATS, + setmode, pparm, TRUE, pfil); + + if (index < 0) { + return EINVAL; + } + + return 0; + +} + +/* + * round_blocksize: A block is a DMA-able unit of memory. Some DMA h/w + * have alignment and size constraints, which are + * implemented via this hook. + * INPUTS: 'addr' is the h/w sc, 'blksize' is the current + * blocksize that will be requested. 'mode' is the + * current h/w mode ( AUMODE_PLAY | AUMODE_RECORD), + * 'param' is the current param settings of h/w. + * + * OUTPUTS/RETURNS: The rounded down size of a block of memory. + */ + +static int +pcmaudio_round_blocksize(void *addr, int blksize, int mode, + const audio_params_t *param) +{ + /* Our alignment constraints are slim. Since we aim to support + * 32bit linear PCM, we have block sizes which are a multiple + * of 4bytes, for now. + */ + + return blksize & -4; + +} + + +/* + * halt_output: Stop playing _now_ + * + * INPUTS: 'addr' is the h/w sc + * + * OUTPUTS/RETURNS: 0 on success. + */ + +static int +pcmaudio_halt_output(void *addr) +{ + struct pcmaudio_softc *sc = addr; + + /* Switch off speaker */ + pcmaudio_speaker_ctl(addr, SPKR_OFF); + + /* Flag the interrupt handler. */ + sc->playing = false; + + return 0; + +} + +/* + * halt_input: Stop recording _now_ + * + * INPUTS: 'addr' is the h/w sc + * + * OUTPUTS/RETURNS: ENXIO. We don't support record. + */ + +static int +pcmaudio_halt_input(void *addr) +{ + + /* We don't support input */ + return ENXIO; +} + + +/* + * speaker_ctl: Switch the speaker on/off + * + * INPUTS: 'addr' is the h/w sc. + * spkr_switch is SPKR_ON or SPKR_OFF + * + * OUTPUTS/RETURNS: 0 on success. EINVAL on invalid 'spkr_switch'. + */ + +static int +pcmaudio_speaker_ctl(void *addr, int spkr_switch) +{ + struct pcmaudio_softc *sc = addr; + + struct pcppi_softc *ppisc = device_private(sc->sc_ppidev); + + switch (spkr_switch) { + case SPKR_ON: + /* enable speaker */ + bus_space_write_1(ppisc->sc_iot, ppisc->sc_ppi_ioh, 0, + bus_space_read_1(ppisc->sc_iot, ppisc->sc_ppi_ioh, 0) + | PIT_SPKR); + break; + case SPKR_OFF: + bus_space_write_1(ppisc->sc_iot, ppisc->sc_ppi_ioh, 0, + bus_space_read_1(ppisc->sc_iot, ppisc->sc_ppi_ioh, 0) + & ~PIT_SPKR); + break; + default: + return EINVAL; + } + /* XXX: Untested. Remove me when done */ + return 0; +} + +/* + * getdev: Return information about the pcmaudio(4) driver. + * + * INPUTS: 'addr' is the h/w sc + * 'adev' is filled with descriptive information + * + * RETURNS: 0 on success + * + */ + +static int +pcmaudio_getdev(void *addr, struct audio_device *adev) +{ + + struct audio_device pcmaudiodev = { "PC Speaker (PCM)", + "0.1", + "pcmaudio" + }; + + *adev = pcmaudiodev; + return 0; +} + +/* Mixer */ + +enum { + PCMAUDIO_SPKRCLASS, + PCMAUDIO_SPKRVOLUME +}; + +/* + * set_port: Set the volume. + * + * INPUTS: 'addr' is the h/w sc + * 'mxc' contains volume information. + * + * OUTPUTS: EINVAL/0 on invalid input/success. + */ + +static int +pcmaudio_set_port(void *addr, mixer_ctrl_t *mxc) +{ + + struct pcmaudio_softc *sc = addr; + + if (mxc->dev == PCMAUDIO_SPKRVOLUME) { + mxc->type = AUDIO_MIXER_VALUE; + mxc->un.value.num_channels = 1; + sc->sc_spkrvolume = mxc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; + return 0; + } + + + return EINVAL; +} + +/* + * set_port: Set the volume. + * + * INPUTS: 'addr' is the h/w sc + * 'mxc' will be filled in with volume + * information. + * + * OUTPUTS: EINVAL/0 on invalid input/success. + * volume info, via 'mxc' + */ + +static int +pcmaudio_get_port(void *addr, mixer_ctrl_t *mxc) +{ + + struct pcmaudio_softc *sc = addr; + + if (mxc->dev == PCMAUDIO_SPKRVOLUME) { + mxc->type = AUDIO_MIXER_VALUE; + mxc->un.value.num_channels = 1; + mxc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_spkrvolume; + return 0; + } + + return EINVAL; +} + +/* + * query_devinfo: Pass up mixer information to the audio(4) + * layer. + * + * INPUTS: 'addr' is the h/w sc + * 'mxd' is the pointer to output to mixer info + * + * OUTPUTS/RETURNS: 'mxd' is filled in with mixer info. + * 0 on success, error, otherwise. + */ + +static int +pcmaudio_query_devinfo(void *addr, mixer_devinfo_t *mxd) +{ + + switch(mxd->index) { + case PCMAUDIO_SPKRCLASS: + mxd->type = AUDIO_MIXER_CLASS; + mxd->mixer_class = PCMAUDIO_SPKRCLASS; + strcpy(mxd->label.name, AudioCoutputs); + mxd->next = mxd->prev = AUDIO_MIXER_LAST; + break; + + case PCMAUDIO_SPKRVOLUME: + mxd->type = AUDIO_MIXER_VALUE; + mxd->mixer_class = PCMAUDIO_SPKRCLASS; + strcpy(mxd->label.name, AudioNspeaker); + strcpy(mxd->un.v.units.name, AudioNvolume); + mxd->un.v.num_channels = 1; + mxd->un.v.delta = PCM_MIXER_DELTA; + mxd->next = mxd->prev = AUDIO_MIXER_LAST; + break; + + default: + /* We don't support mixers just yet. */ + return ENXIO; + } + + return 0; +} + +/* + * round_buffersize: A block is a DMA-able unit of memory. Some DMA h/w + * have alignment and size constraints, which are + * implemented via this hook. + * INPUTS: 'addr' is the h/w sc, 'bufsize' is the current + * buffer size for the ring buffer in + * question. 'direction' is the mode (AUMODE_PLAY | + * AUMODE_RECORD) for which the ring buffer is + * specified. + * + * OUTPUTS/RETURNS: The rounded down size of the ring buffer. + */ + +static size_t +pcmaudio_round_buffersize(void *addr, int direction, size_t bufsize) +{ + + return bufsize & -4; + +} + +/* + * mappage: Backend to map DMA memory to userland. + * + * INPUTS: 'addr' is the h/w sc. 'start' with 'foffset' + * is the userspace address where the buffer + * mapping is requested. + * 'prot' is the page protections of the mapping. + * + * OUTPUTS: -1 on error. paddr of the mapping for the + * asking process, on success. + */ + +static paddr_t +pcmaudio_mappage(void *addr, void *start, off_t foffset, int prot) +{ + struct vm_map_entry *entry; + vsize_t mapoffset; + paddr_t pstart; + + vm_map_lock(kernel_map); + if (uvm_map_lookup_entry(kernel_map, (vaddr_t) start, &entry) == false) { + return 0; + } + + /* Confirm if the map we got is sane. */ + + if ((vaddr_t)start < entry->start) { + return -1; + } + + /* The offset in bytes from the map, entry start */ + mapoffset = ((vaddr_t)start - entry->start); + + pstart = entry->start + mapoffset; + vm_map_unlock(kernel_map); + + return pstart; +} + +/* + * get_props: 'addr' is the h/w sc + * + * INPUTS: None. + * + * OUTPUTS: properties passed up to the audio(4) driver. + */ + +static int +pcmaudio_get_props(void *addr) +{ + + return (AUDIO_PROP_MMAP); +} + +/* + * INPUTS: 'addr' -> the h/w sc. 'start' is the start address. + * 'end' is the end address of the 'DMA' ring buffer. + * 'blksize' is the number of bytes to be played by this 'DMA' + * operation. + * 'intp' is called after each block is processed. + * 'aparams' contains a description of the nature of the + * encoding of the audio data in the buffer. + * + * OUTPUTS: none. + * + * RETURNS: 0 on success. + * + */ + +static int pcmaudio_trigger_output(void *addr, void *start, void *end, int blksize, + void (*intp)(void *), void *arg, const audio_params_t *aparams) +{ + + struct pcmaudio_softc *sc = addr; + + if (start >= end) { + return EINVAL; + } + + + /* XXX: Set the PCM sampling rate */ + + if (sc->sc_pintr != intp || + sc->sc_parg != arg) { + sc->sc_pintr = intp; + sc->sc_parg = arg; + + /* softint */ + sc->sc_softintcookie = softint_establish(SOFTINT_CLOCK, intp, arg); + + } + + sc->blksize = blksize; + sc->blknow = sc->blkstart = start; + sc->blkend = end; + sc->playing = true; + + return 0; +} + +/* + * INPUTS: 'addr' -> the h/w sc. 'start' is the start address. + * 'end' is the end address of the 'DMA' ring buffer. + * 'blksize' is the number of bytes to be played by this 'DMA' + * operation. + * 'intp' is called after each block is processed. + * 'aparams' contains a description of the nature of the + * encoding of the audio data in the buffer. + * + * OUTPUTS: none. + * + * RETURNS: 0 on success. + * + */ + +static int pcmaudio_trigger_input(void *addrl, void *start, void *end, int blksize, + void (*intp)(void *), void *arg, const audio_params_t *aparams) +{ + /* Nope, we don't support capture */ + + return ENXIO; +} Index: sys/dev/isa/pcmaudiovar.h =================================================================== RCS file: sys/dev/isa/pcmaudiovar.h diff -N sys/dev/isa/pcmaudiovar.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/isa/pcmaudiovar.h 19 Sep 2008 13:51:18 -0000 @@ -0,0 +1,68 @@ +/* $NetBSD$ */ + +/* + * pcmaudiovar.h: definitions specific to the pcmaudio driver. + */ + +#ifndef _DEV_ISA_PCMAUDIOVAR_H_ +#define _DEV_ISA_PCMAUDIOVAR_H_ + +#ifndef NSPKR +#include "spkr.h" +#endif /* NSPKR */ +/* The pcmaudio driver is exclusive to the spkr tone driver: "spkr at pcppi" */ +#if NSPKR > 0 +#error spkr at pcppi, and pcmaudio at pcppi are mutually exclusive. Please check your config file. +#else + +#include +#include + +#define PCM_MIXER_DELTA 16 + +struct pcmaudio_softc { + device_t sc_dev; /* pcmaudio device instance struct */ + device_t sc_audiodev; /* audio(4) device instance struct which attaches to us */ + + device_t sc_ppidev; /* Parent pcppi device */ + device_t sc_timerdev; /* Parent 8254 dev */ + + int sc_open; /* We don't support multiple opens */ + int sc_samplingrate; /* Only updated at open() */ + u_char sc_spkrvolume; /* Mixer volume */ + + bool playing; /* Flag, polled by clock.c:clockintr() */ + + int blksize; /* Size of the DMA circular buffer */ + int16_t *blkstart; /* Pointer to the start of the buffer */ + int16_t *blknow; /* Pointer to current sample */ + int16_t *blkend; /* Pointer to the end of the buffer */ + + + void (*sc_pintr)(void *); /* Callback from audio(4) */ + void *sc_parg; /* The argument that the audio callback is called with. see audio_if.h */ + + void *sc_softintcookie; /* Cookie returned by softint_establish() */ + + struct timecounter *sc_timecounter; /* Pointer to the i8254 timer */ + +#define PCMAUDIO_NFORMATS 1 + + /* auconv encoding related */ + struct audio_format sc_audio_formats[PCMAUDIO_NFORMATS]; + struct audio_encoding_set *sc_encodings; + + struct sysctllog *sc_log; /* sysctl related */ + int sc_sysctlnode; + +}; + +int pcmaudio_match(device_t, cfdata_t, void *); +void pcmaudio_attach(device_t, device_t, void *); +int pcmaudio_detach(device_t, int); + +void pcmaudio_pcppi_attach(device_t); + + +#endif /* NSPKR */ +#endif /* _DEV_ISA_PCMAUDIOVAR_H_ */ Index: sys/dev/isa/pcppi.c =================================================================== RCS file: /home/repos/netbsd-current/src/sys/dev/isa/pcppi.c,v retrieving revision 1.32 diff -u -r1.32 pcppi.c --- sys/dev/isa/pcppi.c 5 Mar 2008 22:46:43 -0000 1.32 +++ sys/dev/isa/pcppi.c 19 Sep 2008 08:13:45 -0000 @@ -48,6 +48,11 @@ #include #include #include +#include "pcmaudio.h" + +#if NPCMAUDIO > 0 +#include +#endif #include "pckbd.h" #if NPCKBD > 0 @@ -207,7 +212,7 @@ sc->sc_bellactive = sc->sc_bellpitch = sc->sc_slp = 0; -#if NPCKBD > 0 +#if ((NPCKBD > 0) && (NPCMAUDIO == 0)) /* Provide a beeper for the PC Keyboard, if there isn't one already. */ pckbd_hookup_bell(pcppi_pckbd_bell, sc); #endif @@ -244,6 +249,9 @@ else { aprint_normal_dev(sc->sc_timer, "attached to %s\n", device_xname(self)); +#if NPCMAUDIO > 0 + pcmaudio_pcppi_attach(self); +#endif } } #endif Index: sys/dev/isa/pcppivar.h =================================================================== RCS file: /home/repos/netbsd-current/src/sys/dev/isa/pcppivar.h,v retrieving revision 1.9 diff -u -r1.9 pcppivar.h --- sys/dev/isa/pcppivar.h 4 Mar 2008 16:35:19 -0000 1.9 +++ sys/dev/isa/pcppivar.h 18 Jul 2008 11:33:36 -0000 @@ -49,6 +49,11 @@ int sc_bellactive, sc_bellpitch; int sc_slp; int sc_timeout; + +#if NPCMAUDIO > 0 + device_t sc_pcmaudiodev; +#endif + }; void pcppi_attach(struct pcppi_softc *);