xref: /netbsd-src/sys/arch/luna68k/dev/psgpam.c (revision bb671f5666350bf86ab3d8eff6be2864defe8cda)
1*bb671f56Stsutsui /*	$NetBSD: psgpam.c,v 1.2 2022/06/11 14:45:37 tsutsui Exp $	*/
28b8575d9Stsutsui 
38b8575d9Stsutsui /*
48b8575d9Stsutsui  * Copyright (c) 2018 Yosuke Sugahara. All rights reserved.
58b8575d9Stsutsui  *
68b8575d9Stsutsui  * Redistribution and use in source and binary forms, with or without
78b8575d9Stsutsui  * modification, are permitted provided that the following conditions
88b8575d9Stsutsui  * are met:
98b8575d9Stsutsui  * 1. Redistributions of source code must retain the above copyright
108b8575d9Stsutsui  *    notice, this list of conditions and the following disclaimer.
118b8575d9Stsutsui  * 2. Redistributions in binary form must reproduce the above copyright
128b8575d9Stsutsui  *    notice, this list of conditions and the following disclaimer in the
138b8575d9Stsutsui  *    documentation and/or other materials provided with the distribution.
148b8575d9Stsutsui  *
158b8575d9Stsutsui  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
168b8575d9Stsutsui  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
178b8575d9Stsutsui  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
188b8575d9Stsutsui  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
198b8575d9Stsutsui  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
208b8575d9Stsutsui  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
218b8575d9Stsutsui  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
228b8575d9Stsutsui  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
238b8575d9Stsutsui  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
248b8575d9Stsutsui  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
258b8575d9Stsutsui  * SUCH DAMAGE.
268b8575d9Stsutsui  */
278b8575d9Stsutsui 
288b8575d9Stsutsui #include <sys/cdefs.h>
29*bb671f56Stsutsui __KERNEL_RCSID(0, "$NetBSD: psgpam.c,v 1.2 2022/06/11 14:45:37 tsutsui Exp $");
308b8575d9Stsutsui 
318b8575d9Stsutsui #include <sys/param.h>
328b8575d9Stsutsui #include <sys/systm.h>
338b8575d9Stsutsui #include <sys/device.h>
348b8575d9Stsutsui #include <sys/endian.h>
358b8575d9Stsutsui #include <sys/kmem.h>
368b8575d9Stsutsui #include <sys/sysctl.h>
378b8575d9Stsutsui 
388b8575d9Stsutsui #include <sys/cpu.h>
398b8575d9Stsutsui #include <sys/audioio.h>
408b8575d9Stsutsui #include <dev/audio/audio_if.h>
418b8575d9Stsutsui 
428b8575d9Stsutsui #include <machine/autoconf.h>
438b8575d9Stsutsui 
448b8575d9Stsutsui #include <luna68k/dev/xpbusvar.h>
458b8575d9Stsutsui #include <luna68k/dev/psgpam_enc.h>
468b8575d9Stsutsui #include <luna68k/dev/xpcmd.h>
478b8575d9Stsutsui #include <luna68k/dev/xplx/xplxdefs.h>
488b8575d9Stsutsui 
498b8575d9Stsutsui #include <luna68k/luna68k/isr.h>
508b8575d9Stsutsui 
518b8575d9Stsutsui #include "ioconf.h"
528b8575d9Stsutsui 
538b8575d9Stsutsui /*
548b8575d9Stsutsui  * Debug level:
558b8575d9Stsutsui  * 0: No debug logs
568b8575d9Stsutsui  * 1: action changes like open/close/set_format...
578b8575d9Stsutsui  * 2: + normal operations like read/write/ioctl...
588b8575d9Stsutsui  * 3: + TRACEs except interrupt
598b8575d9Stsutsui  * 4: + TRACEs including interrupt
608b8575d9Stsutsui  */
618b8575d9Stsutsui /* Note AUDIO_DEBUG should be sync'ed with src/sys/dev/audio/audio.c */
628b8575d9Stsutsui /* #define AUDIO_DEBUG	1 */
638b8575d9Stsutsui 
648b8575d9Stsutsui #if defined(AUDIO_DEBUG)
658b8575d9Stsutsui #define DPRINTF(n, fmt...)	do {					\
668b8575d9Stsutsui 	if (psgpamdebug >= (n)) {					\
678b8575d9Stsutsui 		if (cpu_intr_p()) {					\
688b8575d9Stsutsui 			audio_mlog_printf(fmt);				\
698b8575d9Stsutsui 		} else {						\
708b8575d9Stsutsui 			audio_mlog_flush();				\
718b8575d9Stsutsui 			printf(fmt);					\
728b8575d9Stsutsui 		}							\
738b8575d9Stsutsui 	}								\
748b8575d9Stsutsui } while (0)
758b8575d9Stsutsui 
768b8575d9Stsutsui /* XXX Parasitic to audio.c... */
778b8575d9Stsutsui extern void audio_mlog_flush(void);
788b8575d9Stsutsui extern void audio_mlog_printf(const char *, ...);
798b8575d9Stsutsui 
808b8575d9Stsutsui static int	psgpamdebug = AUDIO_DEBUG;
818b8575d9Stsutsui #else
828b8575d9Stsutsui #define DPRINTF(n, fmt...)	__nothing
838b8575d9Stsutsui #endif
848b8575d9Stsutsui 
858b8575d9Stsutsui struct psgpam_softc {
868b8575d9Stsutsui 	device_t sc_dev;
878b8575d9Stsutsui 	vaddr_t sc_shm_base;
888b8575d9Stsutsui 	vsize_t sc_shm_size;
898b8575d9Stsutsui 
908b8575d9Stsutsui 	void (*sc_intr)(void *);
918b8575d9Stsutsui 	void *sc_arg;
928b8575d9Stsutsui 
938b8575d9Stsutsui 	kmutex_t sc_intr_lock;
948b8575d9Stsutsui 	kmutex_t sc_thread_lock;
958b8575d9Stsutsui 
968b8575d9Stsutsui 	int      sc_isopen;
978b8575d9Stsutsui 
988b8575d9Stsutsui 	int      sc_started;
998b8575d9Stsutsui 	int      sc_outcount;
1008b8575d9Stsutsui 	int      sc_xp_state;
1018b8575d9Stsutsui 	uint16_t sc_xp_addr;	/* XP buffer addr */
1028b8575d9Stsutsui 
1038b8575d9Stsutsui 	int      sc_xp_enc;
1048b8575d9Stsutsui 	int      sc_xp_rept;
1058b8575d9Stsutsui 	int      sc_xp_cycle_clk;
1068b8575d9Stsutsui 	int      sc_xp_rept_clk;
1078b8575d9Stsutsui 	int      sc_xp_rept_max;
1088b8575d9Stsutsui 
1098b8575d9Stsutsui 	u_int    sc_sample_rate;
1108b8575d9Stsutsui 	int      sc_stride;
1118b8575d9Stsutsui 	int      sc_dynamic;
1128b8575d9Stsutsui 
1138b8575d9Stsutsui 	uint8_t *sc_start_ptr;
1148b8575d9Stsutsui 	uint8_t *sc_end_ptr;
1158b8575d9Stsutsui 	int      sc_blksize;
1168b8575d9Stsutsui 	int      sc_blkcount;
1178b8575d9Stsutsui 	int      sc_cur_blk_id;
1188b8575d9Stsutsui 
1198b8575d9Stsutsui 	struct psgpam_codecvar sc_psgpam_codecvar;
1208b8575d9Stsutsui };
1218b8575d9Stsutsui 
1228b8575d9Stsutsui static int  psgpam_match(device_t, cfdata_t, void *);
1238b8575d9Stsutsui static void psgpam_attach(device_t, device_t, void *);
1248b8575d9Stsutsui 
1258b8575d9Stsutsui /* MI audio layer interface */
1268b8575d9Stsutsui static int  psgpam_open(void *, int);
1278b8575d9Stsutsui static void psgpam_close(void *);
1288b8575d9Stsutsui static int  psgpam_query_format(void *, audio_format_query_t *);
1298b8575d9Stsutsui static int  psgpam_set_format(void *, int,
1308b8575d9Stsutsui     const audio_params_t *, const audio_params_t *,
1318b8575d9Stsutsui     audio_filter_reg_t *, audio_filter_reg_t *);
1328b8575d9Stsutsui static int  psgpam_trigger_output(void *, void *, void *, int,
1338b8575d9Stsutsui     void (*)(void *), void *, const audio_params_t *);
1348b8575d9Stsutsui static int  psgpam_halt_output(void *);
1358b8575d9Stsutsui static int  psgpam_getdev(void *, struct audio_device *);
1368b8575d9Stsutsui static int  psgpam_set_port(void *, mixer_ctrl_t *);
1378b8575d9Stsutsui static int  psgpam_get_port(void *, mixer_ctrl_t *);
1388b8575d9Stsutsui static int  psgpam_query_devinfo(void *, mixer_devinfo_t *);
1398b8575d9Stsutsui static int  psgpam_get_props(void *);
1408b8575d9Stsutsui static void psgpam_get_locks(void *, kmutex_t **, kmutex_t **);
1418b8575d9Stsutsui static int  psgpam_round_blocksize(void *, int, int, const audio_params_t *);
1428b8575d9Stsutsui static size_t psgpam_round_buffersize(void *, int, size_t);
1438b8575d9Stsutsui 
1448b8575d9Stsutsui static int  psgpam_intr(void *);
1458b8575d9Stsutsui 
1468b8575d9Stsutsui #if defined(AUDIO_DEBUG)
1478b8575d9Stsutsui static int  psgpam_sysctl_debug(SYSCTLFN_PROTO);
1488b8575d9Stsutsui #endif
1498b8575d9Stsutsui static int  psgpam_sysctl_enc(SYSCTLFN_PROTO);
1508b8575d9Stsutsui static int  psgpam_sysctl_dynamic(SYSCTLFN_PROTO);
1518b8575d9Stsutsui 
1528b8575d9Stsutsui CFATTACH_DECL_NEW(psgpam, sizeof(struct psgpam_softc),
1538b8575d9Stsutsui     psgpam_match, psgpam_attach, NULL, NULL);
1548b8575d9Stsutsui 
1558b8575d9Stsutsui static int psgpam_matched;
1568b8575d9Stsutsui 
1578b8575d9Stsutsui static const struct audio_hw_if psgpam_hw_if = {
1588b8575d9Stsutsui 	.open			= psgpam_open,
1598b8575d9Stsutsui 	.close			= psgpam_close,
1608b8575d9Stsutsui 	.query_format		= psgpam_query_format,
1618b8575d9Stsutsui 	.set_format		= psgpam_set_format,
1628b8575d9Stsutsui 	.trigger_output		= psgpam_trigger_output,
1638b8575d9Stsutsui 	.halt_output		= psgpam_halt_output,
1648b8575d9Stsutsui 	.getdev			= psgpam_getdev,
1658b8575d9Stsutsui 	.set_port		= psgpam_set_port,
1668b8575d9Stsutsui 	.get_port		= psgpam_get_port,
1678b8575d9Stsutsui 	.query_devinfo		= psgpam_query_devinfo,
1688b8575d9Stsutsui 	.get_props		= psgpam_get_props,
1698b8575d9Stsutsui 	.get_locks		= psgpam_get_locks,
1708b8575d9Stsutsui 	.round_blocksize        = psgpam_round_blocksize,
1718b8575d9Stsutsui 	.round_buffersize	= psgpam_round_buffersize,
1728b8575d9Stsutsui };
1738b8575d9Stsutsui 
1748b8575d9Stsutsui static struct audio_device psgpam_device = {
1758b8575d9Stsutsui 	"PSG PAM",
1768b8575d9Stsutsui 	"0.2",
1778b8575d9Stsutsui 	"",
1788b8575d9Stsutsui };
1798b8575d9Stsutsui 
1808b8575d9Stsutsui static struct audio_format psgpam_format = {
1818b8575d9Stsutsui 	.mode		= AUMODE_PLAY,
1828b8575d9Stsutsui 	.encoding	= AUDIO_ENCODING_NONE,
1838b8575d9Stsutsui 	.validbits	= 0,		/* filled by query_format */
1848b8575d9Stsutsui 	.precision	= 0,		/* filled by query_format */
1858b8575d9Stsutsui 	.channels	= 1,
1868b8575d9Stsutsui 	.channel_mask	= AUFMT_MONAURAL,
1878b8575d9Stsutsui 	.frequency_type	= 0,		/* filled by query_format */
1888b8575d9Stsutsui 	.frequency	= { 0 },	/* filled by query_format */
1898b8575d9Stsutsui };
1908b8575d9Stsutsui 
1918b8575d9Stsutsui static int
psgpam_match(device_t parent,cfdata_t cf,void * aux)1928b8575d9Stsutsui psgpam_match(device_t parent, cfdata_t cf, void *aux)
1938b8575d9Stsutsui {
1948b8575d9Stsutsui 	struct xpbus_attach_args *xa = aux;
1958b8575d9Stsutsui 
1968b8575d9Stsutsui 	if (psgpam_matched)
1978b8575d9Stsutsui 		return 0;
1988b8575d9Stsutsui 
199*bb671f56Stsutsui 	/* Only the first generation LUNA has YM2149 at XP */
200*bb671f56Stsutsui 	if (machtype != LUNA_I)
201*bb671f56Stsutsui 		return 0;
202*bb671f56Stsutsui 
2038b8575d9Stsutsui 	if (strcmp(xa->xa_name, psgpam_cd.cd_name))
2048b8575d9Stsutsui 		return 0;
2058b8575d9Stsutsui 
2068b8575d9Stsutsui 	psgpam_matched = 1;
2078b8575d9Stsutsui 	return 1;
2088b8575d9Stsutsui }
2098b8575d9Stsutsui 
2108b8575d9Stsutsui static void
psgpam_attach(device_t parent,device_t self,void * aux)2118b8575d9Stsutsui psgpam_attach(device_t parent, device_t self, void *aux)
2128b8575d9Stsutsui {
2138b8575d9Stsutsui 	struct psgpam_softc *sc;
2148b8575d9Stsutsui 	const struct sysctlnode *node;
2158b8575d9Stsutsui 
2168b8575d9Stsutsui 	sc = device_private(self);
2178b8575d9Stsutsui 	sc->sc_dev = self;
2188b8575d9Stsutsui 
2198b8575d9Stsutsui 	aprint_normal(": HD647180X I/O processor as PSG PAM\n");
2208b8575d9Stsutsui 
2218b8575d9Stsutsui 	sc->sc_shm_base = XP_SHM_BASE;
2228b8575d9Stsutsui 	sc->sc_shm_size = XP_SHM_SIZE;
2238b8575d9Stsutsui 
2248b8575d9Stsutsui 	sc->sc_xp_enc = PAM_ENC_PAM2A;
2258b8575d9Stsutsui 	sc->sc_sample_rate = 8000;
2268b8575d9Stsutsui 	sc->sc_stride = 2;
2278b8575d9Stsutsui 	sc->sc_dynamic = 1;
2288b8575d9Stsutsui 
2298b8575d9Stsutsui 	mutex_init(&sc->sc_thread_lock, MUTEX_DEFAULT, IPL_NONE);
2308b8575d9Stsutsui 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
2318b8575d9Stsutsui 
2328b8575d9Stsutsui 	isrlink_autovec(psgpam_intr, sc, 5, ISRPRI_TTYNOBUF);
2338b8575d9Stsutsui 
2348b8575d9Stsutsui 	sysctl_createv(NULL, 0, NULL, &node,
2358b8575d9Stsutsui 		0,
2368b8575d9Stsutsui 		CTLTYPE_NODE, device_xname(sc->sc_dev),
2378b8575d9Stsutsui 		SYSCTL_DESCR("psgpam"),
2388b8575d9Stsutsui 		NULL, 0,
2398b8575d9Stsutsui 		NULL, 0,
2408b8575d9Stsutsui 		CTL_HW,
2418b8575d9Stsutsui 		CTL_CREATE, CTL_EOL);
2428b8575d9Stsutsui 	if (node != NULL) {
2438b8575d9Stsutsui #if defined(AUDIO_DEBUG)
2448b8575d9Stsutsui 		sysctl_createv(NULL, 0, NULL, NULL,
2458b8575d9Stsutsui 			CTLFLAG_READWRITE,
2468b8575d9Stsutsui 			CTLTYPE_INT, "debug",
2478b8575d9Stsutsui 			SYSCTL_DESCR("PSGPAM debug"),
2488b8575d9Stsutsui 			psgpam_sysctl_debug, 0, (void *)sc, 0,
2498b8575d9Stsutsui 			CTL_HW, node->sysctl_num,
2508b8575d9Stsutsui 			CTL_CREATE, CTL_EOL);
2518b8575d9Stsutsui #endif
2528b8575d9Stsutsui 		sysctl_createv(NULL, 0, NULL, NULL,
2538b8575d9Stsutsui 			CTLFLAG_READWRITE,
2548b8575d9Stsutsui 			CTLTYPE_INT, "enc",
2558b8575d9Stsutsui 			SYSCTL_DESCR("PSGPAM encoding"),
2568b8575d9Stsutsui 			psgpam_sysctl_enc, 0, (void *)sc, 0,
2578b8575d9Stsutsui 			CTL_HW, node->sysctl_num,
2588b8575d9Stsutsui 			CTL_CREATE, CTL_EOL);
2598b8575d9Stsutsui 		sysctl_createv(NULL, 0, NULL, NULL,
2608b8575d9Stsutsui 			CTLFLAG_READWRITE,
2618b8575d9Stsutsui 			CTLTYPE_INT, "dynamic",
2628b8575d9Stsutsui 			SYSCTL_DESCR("PSGPAM dynamic offset"),
2638b8575d9Stsutsui 			psgpam_sysctl_dynamic, 0, (void *)sc, 0,
2648b8575d9Stsutsui 			CTL_HW, node->sysctl_num,
2658b8575d9Stsutsui 			CTL_CREATE, CTL_EOL);
2668b8575d9Stsutsui 	}
2678b8575d9Stsutsui 
2688b8575d9Stsutsui 	audio_attach_mi(&psgpam_hw_if, sc, sc->sc_dev);
2698b8575d9Stsutsui }
2708b8575d9Stsutsui 
2718b8575d9Stsutsui /* private functions */
2728b8575d9Stsutsui 
2738b8575d9Stsutsui static void
psgpam_xp_query(struct psgpam_softc * sc)2748b8575d9Stsutsui psgpam_xp_query(struct psgpam_softc *sc)
2758b8575d9Stsutsui {
2768b8575d9Stsutsui 	u_int a;
2778b8575d9Stsutsui 	int r;
2788b8575d9Stsutsui 
2798b8575d9Stsutsui 	if (!sc->sc_isopen) {
2808b8575d9Stsutsui 		a = xp_acquire(DEVID_PAM, 0);
2818b8575d9Stsutsui 		if (a == 0) {
2828b8575d9Stsutsui 			sc->sc_xp_cycle_clk = 65535;
2838b8575d9Stsutsui 			sc->sc_xp_rept_clk = 255;
2848b8575d9Stsutsui 			sc->sc_xp_rept_max = 0;
2858b8575d9Stsutsui 			DPRINTF(1, "XPLX BUSY!\n");
2868b8575d9Stsutsui 			return;
2878b8575d9Stsutsui 		}
2888b8575d9Stsutsui 		xp_ensure_firmware();
2898b8575d9Stsutsui 	}
2908b8575d9Stsutsui 
2918b8575d9Stsutsui 	xp_writemem8(PAM_ENC, sc->sc_xp_enc);
2928b8575d9Stsutsui 	r = xp_cmd(DEVID_PAM, PAM_CMD_QUERY);
2938b8575d9Stsutsui 	if (r != XPLX_R_OK) {
2948b8575d9Stsutsui 		sc->sc_xp_cycle_clk = 65535;
2958b8575d9Stsutsui 		sc->sc_xp_rept_clk = 255;
2968b8575d9Stsutsui 		sc->sc_xp_rept_max = 0;
2978b8575d9Stsutsui 		DPRINTF(1, "XPLX QUERY FAIL: %d\n", r);
2988b8575d9Stsutsui 	} else {
2998b8575d9Stsutsui 		sc->sc_xp_cycle_clk = xp_readmem16le(PAM_CYCLE_CLK);
3008b8575d9Stsutsui 		sc->sc_xp_rept_clk = xp_readmem8(PAM_REPT_CLK);
3018b8575d9Stsutsui 		sc->sc_xp_rept_max = xp_readmem8(PAM_REPT_MAX);
3028b8575d9Stsutsui 		DPRINTF(1, "xp cycle_clk=%d rept_clk=%d rept_max=%d\n",
3038b8575d9Stsutsui 		    sc->sc_xp_cycle_clk,
3048b8575d9Stsutsui 		    sc->sc_xp_rept_clk,
3058b8575d9Stsutsui 		    sc->sc_xp_rept_max);
3068b8575d9Stsutsui 	}
3078b8575d9Stsutsui 	if (!sc->sc_isopen) {
3088b8575d9Stsutsui 		xp_release(DEVID_PAM);
3098b8575d9Stsutsui 	}
3108b8575d9Stsutsui }
3118b8575d9Stsutsui 
3128b8575d9Stsutsui static void
psgpam_xp_start(struct psgpam_softc * sc)3138b8575d9Stsutsui psgpam_xp_start(struct psgpam_softc *sc)
3148b8575d9Stsutsui {
3158b8575d9Stsutsui 
3168b8575d9Stsutsui 	DPRINTF(3, "XP PAM starting..");
3178b8575d9Stsutsui 	if (xp_readmem8(PAM_RUN) != 0) {
3188b8575d9Stsutsui 		DPRINTF(1, "XP PAM already started???\n");
3198b8575d9Stsutsui 	}
3208b8575d9Stsutsui 
3218b8575d9Stsutsui 	psgpam_xp_query(sc);
3228b8575d9Stsutsui 
3238b8575d9Stsutsui 	sc->sc_xp_rept = (XP_CPU_FREQ / sc->sc_sample_rate
3248b8575d9Stsutsui 	    - sc->sc_xp_cycle_clk) / sc->sc_xp_rept_clk;
3258b8575d9Stsutsui 	if (sc->sc_xp_rept < 0)
3268b8575d9Stsutsui 		sc->sc_xp_rept = 0;
3278b8575d9Stsutsui 	if (sc->sc_xp_rept > sc->sc_xp_rept_max)
3288b8575d9Stsutsui 		sc->sc_xp_rept = sc->sc_xp_rept_max;
3298b8575d9Stsutsui 	xp_writemem8(PAM_REPT, sc->sc_xp_rept);
3308b8575d9Stsutsui 	DPRINTF(3, "ENC=%d REPT=%d\n", sc->sc_xp_enc, sc->sc_xp_rept);
3318b8575d9Stsutsui 
3328b8575d9Stsutsui 	xp_intr5_enable();
3338b8575d9Stsutsui 	xp_cmd_nowait(DEVID_PAM, PAM_CMD_START);
3348b8575d9Stsutsui 
3358b8575d9Stsutsui 	DPRINTF(3, "XP PAM started\n");
3368b8575d9Stsutsui }
3378b8575d9Stsutsui 
3388b8575d9Stsutsui /* MI MD API */
3398b8575d9Stsutsui 
3408b8575d9Stsutsui static int
psgpam_open(void * hdl,int flags)3418b8575d9Stsutsui psgpam_open(void *hdl, int flags)
3428b8575d9Stsutsui {
3438b8575d9Stsutsui 	struct psgpam_softc *sc;
3448b8575d9Stsutsui 	u_int a;
3458b8575d9Stsutsui 
3468b8575d9Stsutsui 	DPRINTF(1, "%s: flags=0x%x\n", __func__, flags);
3478b8575d9Stsutsui 	sc = hdl;
3488b8575d9Stsutsui 
3498b8575d9Stsutsui 	a = xp_acquire(DEVID_PAM, 0);
3508b8575d9Stsutsui 	if (a == 0)
3518b8575d9Stsutsui 		return EBUSY;
3528b8575d9Stsutsui 
3538b8575d9Stsutsui 	/* firmware transfer */
3548b8575d9Stsutsui 	xp_ensure_firmware();
3558b8575d9Stsutsui 
3568b8575d9Stsutsui 	sc->sc_xp_state = 0;
3578b8575d9Stsutsui 	sc->sc_started = 0;
3588b8575d9Stsutsui 	sc->sc_outcount = 0;
3598b8575d9Stsutsui 	sc->sc_isopen = 1;
3608b8575d9Stsutsui 
3618b8575d9Stsutsui 	memset(xp_shmptr(PAM_BUF), XP_ATN_RESET, PAM_BUF_LEN);
3628b8575d9Stsutsui 
3638b8575d9Stsutsui 	return 0;
3648b8575d9Stsutsui }
3658b8575d9Stsutsui 
3668b8575d9Stsutsui static void
psgpam_close(void * hdl)3678b8575d9Stsutsui psgpam_close(void *hdl)
3688b8575d9Stsutsui {
3698b8575d9Stsutsui 	struct psgpam_softc *sc;
3708b8575d9Stsutsui 
3718b8575d9Stsutsui 	sc = hdl;
3728b8575d9Stsutsui 
3738b8575d9Stsutsui 	xp_intr5_disable();
3748b8575d9Stsutsui 
3758b8575d9Stsutsui 	xp_release(DEVID_PAM);
3768b8575d9Stsutsui 
3778b8575d9Stsutsui 	sc->sc_isopen = 0;
3788b8575d9Stsutsui 
3798b8575d9Stsutsui 	DPRINTF(1, "%s\n", __func__);
3808b8575d9Stsutsui }
3818b8575d9Stsutsui 
3828b8575d9Stsutsui static int
psgpam_query_format(void * hdl,audio_format_query_t * afp)3838b8575d9Stsutsui psgpam_query_format(void *hdl, audio_format_query_t *afp)
3848b8575d9Stsutsui {
3858b8575d9Stsutsui 	struct psgpam_softc *sc;
3868b8575d9Stsutsui 	u_int freq;
3878b8575d9Stsutsui 	uint8_t rept_max;
3888b8575d9Stsutsui 	int clk;
3898b8575d9Stsutsui 	int i, n;
3908b8575d9Stsutsui 
3918b8575d9Stsutsui #define XP_FREQ_MAXCOUNT	40
3928b8575d9Stsutsui 	int f[XP_FREQ_MAXCOUNT];
3938b8575d9Stsutsui 
3948b8575d9Stsutsui 	if (afp->index != 0)
3958b8575d9Stsutsui 		return EINVAL;
3968b8575d9Stsutsui 
3978b8575d9Stsutsui 	sc = hdl;
3988b8575d9Stsutsui 
3998b8575d9Stsutsui 	psgpam_xp_query(sc);
4008b8575d9Stsutsui 	switch (sc->sc_xp_enc) {
4018b8575d9Stsutsui 	case PAM_ENC_PAM2A:
4028b8575d9Stsutsui 	case PAM_ENC_PAM2B:
4038b8575d9Stsutsui 		psgpam_format.validbits = 16;
4048b8575d9Stsutsui 		psgpam_format.precision = 16;
4058b8575d9Stsutsui 		break;
4068b8575d9Stsutsui 	case PAM_ENC_PAM3A:
4078b8575d9Stsutsui 	case PAM_ENC_PAM3B:
4088b8575d9Stsutsui 		psgpam_format.validbits = 32;
4098b8575d9Stsutsui 		psgpam_format.precision = 32;
4108b8575d9Stsutsui 		break;
4118b8575d9Stsutsui 	}
4128b8575d9Stsutsui 
4138b8575d9Stsutsui 	/* convert xp's max to AUFMT's max */
4148b8575d9Stsutsui 	rept_max = sc->sc_xp_rept_max + 1;
4158b8575d9Stsutsui 
4168b8575d9Stsutsui 	if (rept_max <= AUFMT_MAX_FREQUENCIES) {
4178b8575d9Stsutsui 		/* all choice */
4188b8575d9Stsutsui 		for (i = 0; i < rept_max; i++) {
4198b8575d9Stsutsui 			clk = sc->sc_xp_cycle_clk + i * sc->sc_xp_rept_clk;
4208b8575d9Stsutsui 			freq = XP_CPU_FREQ / clk;
4218b8575d9Stsutsui 			psgpam_format.frequency[i] = freq;
4228b8575d9Stsutsui 		}
4238b8575d9Stsutsui 		n = rept_max;
4248b8575d9Stsutsui 	} else {
4258b8575d9Stsutsui 		if (rept_max > XP_FREQ_MAXCOUNT)
4268b8575d9Stsutsui 			rept_max = XP_FREQ_MAXCOUNT;
4278b8575d9Stsutsui 
4288b8575d9Stsutsui 		for (i = 0; i < rept_max; i++) {
4298b8575d9Stsutsui 			clk = sc->sc_xp_cycle_clk + i * sc->sc_xp_rept_clk;
4308b8575d9Stsutsui 			freq = XP_CPU_FREQ / clk;
4318b8575d9Stsutsui 			if (freq < 4000) break;
4328b8575d9Stsutsui 			f[i] = freq;
4338b8575d9Stsutsui 		}
4348b8575d9Stsutsui 		for (; i < XP_FREQ_MAXCOUNT; i++)
4358b8575d9Stsutsui 			f[i] = 0;
4368b8575d9Stsutsui 
4378b8575d9Stsutsui 		/*
4388b8575d9Stsutsui 		 * keep: first, last
4398b8575d9Stsutsui 		 * remove: any unusable freq
4408b8575d9Stsutsui 		 */
4418b8575d9Stsutsui 		for (i = 1; i < rept_max - 1; i++) {
4428b8575d9Stsutsui 			if (( 4000 <= f[i] && f[i] <  6000 &&
4438b8575d9Stsutsui 			     f[i - 1] <  6000 && f[i + 1] >  4000) ||
4448b8575d9Stsutsui 			    ( 6000 <= f[i]    && f[i] < 8000 &&
4458b8575d9Stsutsui 			     f[i - 1] <  8000 && f[i + 1] >  6000) ||
4468b8575d9Stsutsui 			    ( 8000 <= f[i] && f[i] < 12000 &&
4478b8575d9Stsutsui 			     f[i - 1] < 12000 && f[i + 1] >  8000) ||
4488b8575d9Stsutsui 			    (12000 <= f[i] && f[i] < 16000 &&
4498b8575d9Stsutsui 			     f[i - 1] < 16000 && f[i + 1] > 12000)) {
4508b8575d9Stsutsui 				f[i] = 0;
4518b8575d9Stsutsui 			}
4528b8575d9Stsutsui 		}
4538b8575d9Stsutsui 		n = 0;
4548b8575d9Stsutsui 		for (i = 0; i < rept_max; i++) {
4558b8575d9Stsutsui 			if (f[i] != 0) {
4568b8575d9Stsutsui 				psgpam_format.frequency[n] = f[i];
4578b8575d9Stsutsui 				n++;
4588b8575d9Stsutsui 				if (n == AUFMT_MAX_FREQUENCIES)
4598b8575d9Stsutsui 					break;
4608b8575d9Stsutsui 			}
4618b8575d9Stsutsui 		}
4628b8575d9Stsutsui 	}
4638b8575d9Stsutsui 
4648b8575d9Stsutsui 	psgpam_format.frequency_type = n;
4658b8575d9Stsutsui 
4668b8575d9Stsutsui 	afp->fmt = psgpam_format;
4678b8575d9Stsutsui 	return 0;
4688b8575d9Stsutsui }
4698b8575d9Stsutsui 
4708b8575d9Stsutsui static int
psgpam_set_format(void * hdl,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)4718b8575d9Stsutsui psgpam_set_format(void *hdl, int setmode,
4728b8575d9Stsutsui     const audio_params_t *play, const audio_params_t *rec,
4738b8575d9Stsutsui     audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
4748b8575d9Stsutsui {
4758b8575d9Stsutsui 	/* called before open */
4768b8575d9Stsutsui 
4778b8575d9Stsutsui 	struct psgpam_softc *sc;
4788b8575d9Stsutsui 
4798b8575d9Stsutsui 	sc = hdl;
4808b8575d9Stsutsui 	DPRINTF(1, "%s: mode=%d %s/%dbit/%dch/%dHz\n", __func__,
4818b8575d9Stsutsui 	    setmode, audio_encoding_name(play->encoding),
4828b8575d9Stsutsui 	    play->precision, play->channels, play->sample_rate);
4838b8575d9Stsutsui 
4848b8575d9Stsutsui 	sc->sc_sample_rate = play->sample_rate;
4858b8575d9Stsutsui 
4868b8575d9Stsutsui 	/* set filter */
4878b8575d9Stsutsui 	switch (sc->sc_xp_enc) {
4888b8575d9Stsutsui 	case PAM_ENC_PAM2A:
4898b8575d9Stsutsui 		if (sc->sc_dynamic) {
4908b8575d9Stsutsui 			pfil->codec = psgpam_aint_to_pam2a_d;
4918b8575d9Stsutsui 		} else {
4928b8575d9Stsutsui 			pfil->codec = psgpam_aint_to_pam2a;
4938b8575d9Stsutsui 		}
4948b8575d9Stsutsui 		sc->sc_stride = 2;
4958b8575d9Stsutsui 		break;
4968b8575d9Stsutsui 	case PAM_ENC_PAM2B:
4978b8575d9Stsutsui 		if (sc->sc_dynamic) {
4988b8575d9Stsutsui 			pfil->codec = psgpam_aint_to_pam2b_d;
4998b8575d9Stsutsui 		} else {
5008b8575d9Stsutsui 			pfil->codec = psgpam_aint_to_pam2b;
5018b8575d9Stsutsui 		}
5028b8575d9Stsutsui 		sc->sc_stride = 2;
5038b8575d9Stsutsui 		break;
5048b8575d9Stsutsui 	case PAM_ENC_PAM3A:
5058b8575d9Stsutsui 		if (sc->sc_dynamic) {
5068b8575d9Stsutsui 			pfil->codec = psgpam_aint_to_pam3a_d;
5078b8575d9Stsutsui 		} else {
5088b8575d9Stsutsui 			pfil->codec = psgpam_aint_to_pam3a;
5098b8575d9Stsutsui 		}
5108b8575d9Stsutsui 		sc->sc_stride = 4;
5118b8575d9Stsutsui 		break;
5128b8575d9Stsutsui 	case PAM_ENC_PAM3B:
5138b8575d9Stsutsui 		if (sc->sc_dynamic) {
5148b8575d9Stsutsui 			pfil->codec = psgpam_aint_to_pam3b_d;
5158b8575d9Stsutsui 		} else {
5168b8575d9Stsutsui 			pfil->codec = psgpam_aint_to_pam3b;
5178b8575d9Stsutsui 		}
5188b8575d9Stsutsui 		sc->sc_stride = 4;
5198b8575d9Stsutsui 		break;
5208b8575d9Stsutsui 	}
5218b8575d9Stsutsui 	psgpam_init_context(&sc->sc_psgpam_codecvar, sc->sc_sample_rate);
5228b8575d9Stsutsui 	pfil->context = &sc->sc_psgpam_codecvar;
5238b8575d9Stsutsui 
5248b8575d9Stsutsui 	return 0;
5258b8575d9Stsutsui }
5268b8575d9Stsutsui 
5278b8575d9Stsutsui /* marking block */
5288b8575d9Stsutsui static void
psgpam_mark_blk(struct psgpam_softc * sc,int blk_id)5298b8575d9Stsutsui psgpam_mark_blk(struct psgpam_softc *sc, int blk_id)
5308b8575d9Stsutsui {
5318b8575d9Stsutsui 	int markoffset;
5328b8575d9Stsutsui 	uint marker;
5338b8575d9Stsutsui 
5348b8575d9Stsutsui 	markoffset = sc->sc_blksize * (blk_id + 1);
5358b8575d9Stsutsui 
5368b8575d9Stsutsui 	if (blk_id == sc->sc_blkcount - 1) {
5378b8575d9Stsutsui 		marker = XP_ATN_RELOAD;
5388b8575d9Stsutsui 	} else {
5398b8575d9Stsutsui 		marker = XP_ATN_STAT;
5408b8575d9Stsutsui 	}
5418b8575d9Stsutsui 
5428b8575d9Stsutsui 	/* marking */
5438b8575d9Stsutsui 	uint8_t *start = sc->sc_start_ptr;
5448b8575d9Stsutsui 	if (sc->sc_stride == 2) {
5458b8575d9Stsutsui 		uint16_t *markptr = (uint16_t*)(start + markoffset);
5468b8575d9Stsutsui 		markptr -= 1;
5478b8575d9Stsutsui 		*markptr |= marker;
5488b8575d9Stsutsui 	} else {
5498b8575d9Stsutsui 		/* stride == 4 */
5508b8575d9Stsutsui 		uint32_t *markptr = (uint32_t*)(start + markoffset);
5518b8575d9Stsutsui 		markptr -= 1;
5528b8575d9Stsutsui 		*markptr |= marker;
5538b8575d9Stsutsui 	}
5548b8575d9Stsutsui }
5558b8575d9Stsutsui 
5568b8575d9Stsutsui static int
psgpam_trigger_output(void * hdl,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const audio_params_t * param)5578b8575d9Stsutsui psgpam_trigger_output(void *hdl, void *start, void *end, int blksize,
5588b8575d9Stsutsui     void (*intr)(void *), void *intrarg, const audio_params_t *param)
5598b8575d9Stsutsui {
5608b8575d9Stsutsui 	void *dp;
5618b8575d9Stsutsui 	struct psgpam_softc *sc;
5628b8575d9Stsutsui 
5638b8575d9Stsutsui 	sc = hdl;
5648b8575d9Stsutsui 
5658b8575d9Stsutsui 	DPRINTF(2, "%s start=%p end=%p blksize=%d\n", __func__,
5668b8575d9Stsutsui 		start, end, blksize);
5678b8575d9Stsutsui 
5688b8575d9Stsutsui 	sc->sc_outcount++;
5698b8575d9Stsutsui 
5708b8575d9Stsutsui 	sc->sc_intr = intr;
5718b8575d9Stsutsui 	sc->sc_arg = intrarg;
5728b8575d9Stsutsui 	sc->sc_blksize = blksize;
5738b8575d9Stsutsui 
5748b8575d9Stsutsui 	sc->sc_start_ptr = start;
5758b8575d9Stsutsui 	sc->sc_end_ptr = end;
5768b8575d9Stsutsui 	sc->sc_blkcount = (sc->sc_end_ptr - sc->sc_start_ptr) / sc->sc_blksize;
5778b8575d9Stsutsui 	sc->sc_xp_addr = PAM_BUF;
5788b8575d9Stsutsui 
5798b8575d9Stsutsui 	psgpam_mark_blk(sc, 0);
5808b8575d9Stsutsui 	psgpam_mark_blk(sc, 1);
5818b8575d9Stsutsui 
5828b8575d9Stsutsui 	/* transfer */
5838b8575d9Stsutsui 	dp = xp_shmptr(sc->sc_xp_addr);
5848b8575d9Stsutsui 	memcpy(dp, start, blksize * 2);
5858b8575d9Stsutsui 
5868b8575d9Stsutsui 	/* (preincrement variable in intr) */
5878b8575d9Stsutsui 	sc->sc_cur_blk_id = 1;
5888b8575d9Stsutsui 	sc->sc_xp_addr += blksize;
5898b8575d9Stsutsui 
5908b8575d9Stsutsui 	/* play start */
5918b8575d9Stsutsui 	if (sc->sc_started == 0) {
5928b8575d9Stsutsui 		/* set flag first */
5938b8575d9Stsutsui 		sc->sc_started = 1;
5948b8575d9Stsutsui 		psgpam_xp_start(sc);
5958b8575d9Stsutsui 	}
5968b8575d9Stsutsui 
5978b8575d9Stsutsui 	return 0;
5988b8575d9Stsutsui }
5998b8575d9Stsutsui 
6008b8575d9Stsutsui static int
psgpam_halt_output(void * hdl)6018b8575d9Stsutsui psgpam_halt_output(void *hdl)
6028b8575d9Stsutsui {
6038b8575d9Stsutsui 	struct psgpam_softc *sc = hdl;
6048b8575d9Stsutsui 
6058b8575d9Stsutsui 	DPRINTF(2, "%s\n", __func__);
6068b8575d9Stsutsui 
6078b8575d9Stsutsui 	xp_intr5_disable();
6088b8575d9Stsutsui 
6098b8575d9Stsutsui 	memset(xp_shmptr(PAM_BUF), XP_ATN_RESET, PAM_BUF_LEN);
6108b8575d9Stsutsui 
6118b8575d9Stsutsui 	sc->sc_started = 0;
6128b8575d9Stsutsui 	sc->sc_xp_state = 0;
6138b8575d9Stsutsui 
6148b8575d9Stsutsui 	return 0;
6158b8575d9Stsutsui }
6168b8575d9Stsutsui 
6178b8575d9Stsutsui static int
psgpam_getdev(void * hdl,struct audio_device * ret)6188b8575d9Stsutsui psgpam_getdev(void *hdl, struct audio_device *ret)
6198b8575d9Stsutsui {
6208b8575d9Stsutsui 
6218b8575d9Stsutsui 	*ret = psgpam_device;
6228b8575d9Stsutsui 	return 0;
6238b8575d9Stsutsui }
6248b8575d9Stsutsui 
6258b8575d9Stsutsui static int
psgpam_set_port(void * hdl,mixer_ctrl_t * mc)6268b8575d9Stsutsui psgpam_set_port(void *hdl, mixer_ctrl_t *mc)
6278b8575d9Stsutsui {
6288b8575d9Stsutsui 
6298b8575d9Stsutsui 	DPRINTF(2, "%s\n", __func__);
6308b8575d9Stsutsui 	return 0;
6318b8575d9Stsutsui }
6328b8575d9Stsutsui 
6338b8575d9Stsutsui static int
psgpam_get_port(void * hdl,mixer_ctrl_t * mc)6348b8575d9Stsutsui psgpam_get_port(void *hdl, mixer_ctrl_t *mc)
6358b8575d9Stsutsui {
6368b8575d9Stsutsui 
6378b8575d9Stsutsui 	DPRINTF(2, "%s\n", __func__);
6388b8575d9Stsutsui 	return 0;
6398b8575d9Stsutsui }
6408b8575d9Stsutsui 
6418b8575d9Stsutsui static int
psgpam_query_devinfo(void * hdl,mixer_devinfo_t * di)6428b8575d9Stsutsui psgpam_query_devinfo(void *hdl, mixer_devinfo_t *di)
6438b8575d9Stsutsui {
6448b8575d9Stsutsui 
6458b8575d9Stsutsui 	DPRINTF(2, "%s %d\n", __func__, di->index);
6468b8575d9Stsutsui 	switch (di->index) {
6478b8575d9Stsutsui 	default:
6488b8575d9Stsutsui 		return EINVAL;
6498b8575d9Stsutsui 	}
6508b8575d9Stsutsui 	return 0;
6518b8575d9Stsutsui }
6528b8575d9Stsutsui 
6538b8575d9Stsutsui static int
psgpam_get_props(void * hdl)6548b8575d9Stsutsui psgpam_get_props(void *hdl)
6558b8575d9Stsutsui {
6568b8575d9Stsutsui 
6578b8575d9Stsutsui 	return AUDIO_PROP_PLAYBACK;
6588b8575d9Stsutsui }
6598b8575d9Stsutsui 
6608b8575d9Stsutsui static void
psgpam_get_locks(void * hdl,kmutex_t ** intr,kmutex_t ** thread)6618b8575d9Stsutsui psgpam_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread)
6628b8575d9Stsutsui {
6638b8575d9Stsutsui 	struct psgpam_softc *sc = hdl;
6648b8575d9Stsutsui 
6658b8575d9Stsutsui 	*intr = &sc->sc_intr_lock;
6668b8575d9Stsutsui 	*thread = &sc->sc_thread_lock;
6678b8575d9Stsutsui }
6688b8575d9Stsutsui 
6698b8575d9Stsutsui static int
psgpam_round_blocksize(void * hdl,int bs,int mode,const audio_params_t * param)6708b8575d9Stsutsui psgpam_round_blocksize(void *hdl, int bs, int mode,
6718b8575d9Stsutsui     const audio_params_t *param)
6728b8575d9Stsutsui {
6738b8575d9Stsutsui 
6748b8575d9Stsutsui #if 0
6758b8575d9Stsutsui 	if (bs < 16384) {
6768b8575d9Stsutsui 		return (16384 / bs) * bs;
6778b8575d9Stsutsui 	} else {
6788b8575d9Stsutsui 		return 16384;
6798b8575d9Stsutsui 	}
6808b8575d9Stsutsui #else
6818b8575d9Stsutsui 	return bs;
6828b8575d9Stsutsui #endif
6838b8575d9Stsutsui }
6848b8575d9Stsutsui 
6858b8575d9Stsutsui static size_t
psgpam_round_buffersize(void * hdl,int direction,size_t bufsize)6868b8575d9Stsutsui psgpam_round_buffersize(void *hdl, int direction, size_t bufsize)
6878b8575d9Stsutsui {
6888b8575d9Stsutsui 
6898b8575d9Stsutsui 	if (bufsize > 28 * 1024) {
6908b8575d9Stsutsui 		bufsize = 28 * 1024;
6918b8575d9Stsutsui 	}
6928b8575d9Stsutsui 	return bufsize;
6938b8575d9Stsutsui }
6948b8575d9Stsutsui 
6958b8575d9Stsutsui static int
psgpam_intr(void * hdl)6968b8575d9Stsutsui psgpam_intr(void *hdl)
6978b8575d9Stsutsui {
6988b8575d9Stsutsui 	struct psgpam_softc *sc = hdl;
6998b8575d9Stsutsui 
7008b8575d9Stsutsui 	xp_intr5_acknowledge();
7018b8575d9Stsutsui 	DPRINTF(4, "psgpam intr\n");
7028b8575d9Stsutsui 
7038b8575d9Stsutsui 	sc->sc_cur_blk_id++;
7048b8575d9Stsutsui 	sc->sc_xp_addr += sc->sc_blksize;
7058b8575d9Stsutsui 	if (sc->sc_cur_blk_id == sc->sc_blkcount) {
7068b8575d9Stsutsui 		sc->sc_cur_blk_id = 0;
7078b8575d9Stsutsui 		sc->sc_xp_addr = PAM_BUF;
7088b8575d9Stsutsui 	}
7098b8575d9Stsutsui 	psgpam_mark_blk(sc, sc->sc_cur_blk_id);
7108b8575d9Stsutsui 	memcpy(xp_shmptr(sc->sc_xp_addr),
7118b8575d9Stsutsui 	    sc->sc_start_ptr + sc->sc_cur_blk_id * sc->sc_blksize,
7128b8575d9Stsutsui 	    sc->sc_blksize);
7138b8575d9Stsutsui 
7148b8575d9Stsutsui 	mutex_spin_enter(&sc->sc_intr_lock);
7158b8575d9Stsutsui 
7168b8575d9Stsutsui 	if (sc->sc_intr) {
7178b8575d9Stsutsui 		sc->sc_intr(sc->sc_arg);
7188b8575d9Stsutsui 	} else {
7198b8575d9Stsutsui 		DPRINTF(1, "psgpam_intr: spurious interrupt\n");
7208b8575d9Stsutsui 	}
7218b8575d9Stsutsui 
7228b8575d9Stsutsui 	mutex_spin_exit(&sc->sc_intr_lock);
7238b8575d9Stsutsui 
7248b8575d9Stsutsui 	/* handled */
7258b8575d9Stsutsui 	return 1;
7268b8575d9Stsutsui }
7278b8575d9Stsutsui 
7288b8575d9Stsutsui #if defined(AUDIO_DEBUG)
7298b8575d9Stsutsui /* sysctl */
7308b8575d9Stsutsui static int
psgpam_sysctl_debug(SYSCTLFN_ARGS)7318b8575d9Stsutsui psgpam_sysctl_debug(SYSCTLFN_ARGS)
7328b8575d9Stsutsui {
7338b8575d9Stsutsui 	struct sysctlnode node;
7348b8575d9Stsutsui 	int t, error;
7358b8575d9Stsutsui 
7368b8575d9Stsutsui 	node = *rnode;
7378b8575d9Stsutsui 
7388b8575d9Stsutsui 	t = psgpamdebug;
7398b8575d9Stsutsui 	node.sysctl_data = &t;
7408b8575d9Stsutsui 
7418b8575d9Stsutsui 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
7428b8575d9Stsutsui 	if (error || newp == NULL) {
7438b8575d9Stsutsui 		return error;
7448b8575d9Stsutsui 	}
7458b8575d9Stsutsui 
7468b8575d9Stsutsui 	if (t < 0)
7478b8575d9Stsutsui 		return EINVAL;
7488b8575d9Stsutsui 	if (t > 4)
7498b8575d9Stsutsui 		return EINVAL;
7508b8575d9Stsutsui 	psgpamdebug = t;
7518b8575d9Stsutsui 	return 0;
7528b8575d9Stsutsui }
7538b8575d9Stsutsui #endif
7548b8575d9Stsutsui 
7558b8575d9Stsutsui /* sysctl */
7568b8575d9Stsutsui static int
psgpam_sysctl_enc(SYSCTLFN_ARGS)7578b8575d9Stsutsui psgpam_sysctl_enc(SYSCTLFN_ARGS)
7588b8575d9Stsutsui {
7598b8575d9Stsutsui 	struct sysctlnode node;
7608b8575d9Stsutsui 	struct psgpam_softc *sc;
7618b8575d9Stsutsui 	int t, error;
7628b8575d9Stsutsui 
7638b8575d9Stsutsui 	node = *rnode;
7648b8575d9Stsutsui 	sc = node.sysctl_data;
7658b8575d9Stsutsui 
7668b8575d9Stsutsui 	t = sc->sc_xp_enc;
7678b8575d9Stsutsui 	node.sysctl_data = &t;
7688b8575d9Stsutsui 
7698b8575d9Stsutsui 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
7708b8575d9Stsutsui 	if (error || newp == NULL) {
7718b8575d9Stsutsui 		return error;
7728b8575d9Stsutsui 	}
7738b8575d9Stsutsui 
7748b8575d9Stsutsui 	if (t < PAM_ENC_PAM2A)
7758b8575d9Stsutsui 		return EINVAL;
7768b8575d9Stsutsui 	if (t > PAM_ENC_PAM3B)
7778b8575d9Stsutsui 		return EINVAL;
7788b8575d9Stsutsui 	sc->sc_xp_enc = t;
7798b8575d9Stsutsui 	return 0;
7808b8575d9Stsutsui }
7818b8575d9Stsutsui 
7828b8575d9Stsutsui static int
psgpam_sysctl_dynamic(SYSCTLFN_ARGS)7838b8575d9Stsutsui psgpam_sysctl_dynamic(SYSCTLFN_ARGS)
7848b8575d9Stsutsui {
7858b8575d9Stsutsui 	struct sysctlnode node;
7868b8575d9Stsutsui 	struct psgpam_softc *sc;
7878b8575d9Stsutsui 	int t, error;
7888b8575d9Stsutsui 
7898b8575d9Stsutsui 	node = *rnode;
7908b8575d9Stsutsui 	sc = node.sysctl_data;
7918b8575d9Stsutsui 
7928b8575d9Stsutsui 	t = sc->sc_dynamic;
7938b8575d9Stsutsui 	node.sysctl_data = &t;
7948b8575d9Stsutsui 
7958b8575d9Stsutsui 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
7968b8575d9Stsutsui 	if (error || newp == NULL) {
7978b8575d9Stsutsui 		return error;
7988b8575d9Stsutsui 	}
7998b8575d9Stsutsui 
8008b8575d9Stsutsui 	sc->sc_dynamic = t;
8018b8575d9Stsutsui 	return 0;
8028b8575d9Stsutsui }
803