Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • 61acfe9
  • /
  • sound
  • /
  • isa
  • /
  • msnd
  • /
  • msnd.c
Raw File Download
Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge Iframe embedding
swh:1:cnt:7c3203fe4869cffa9faa9d1a002092734fc576f4
directory badge Iframe embedding
swh:1:dir:1d34dcc47b219646b0b127bd9e7b953d8e943d12
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
msnd.c
/*********************************************************************
 *
 * 2002/06/30 Karsten Wiese:
 *	removed kernel-version dependencies.
 *	ripped from linux kernel 2.4.18 (OSS Implementation) by me.
 *	In the OSS Version, this file is compiled to a separate MODULE,
 *	that is used by the pinnacle and the classic driver.
 *	since there is no classic driver for alsa yet (i dont have a classic
 *	& writing one blindfold is difficult) this file's object is statically
 *	linked into the pinnacle-driver-module for now.	look for the string
 *		"uncomment this to make this a module again"
 *	to do guess what.
 *
 * the following is a copy of the 2.4.18 OSS FREE file-heading comment:
 *
 * msnd.c - Driver Base
 *
 * Turtle Beach MultiSound Sound Card Driver for Linux
 *
 * Copyright (C) 1998 Andrew Veliath
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 ********************************************************************/

#include <linux/kernel.h>
#include <linux/sched/signal.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/module.h>

#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>

#include "msnd.h"

#define LOGNAME			"msnd"


void snd_msnd_init_queue(void __iomem *base, int start, int size)
{
	writew(PCTODSP_BASED(start), base + JQS_wStart);
	writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
	writew(0, base + JQS_wHead);
	writew(0, base + JQS_wTail);
}
EXPORT_SYMBOL(snd_msnd_init_queue);

static int snd_msnd_wait_TXDE(struct snd_msnd *dev)
{
	unsigned int io = dev->io;
	int timeout = 1000;

	while (timeout-- > 0)
		if (inb(io + HP_ISR) & HPISR_TXDE)
			return 0;

	return -EIO;
}

static int snd_msnd_wait_HC0(struct snd_msnd *dev)
{
	unsigned int io = dev->io;
	int timeout = 1000;

	while (timeout-- > 0)
		if (!(inb(io + HP_CVR) & HPCVR_HC))
			return 0;

	return -EIO;
}

int snd_msnd_send_dsp_cmd(struct snd_msnd *dev, u8 cmd)
{
	unsigned long flags;

	spin_lock_irqsave(&dev->lock, flags);
	if (snd_msnd_wait_HC0(dev) == 0) {
		outb(cmd, dev->io + HP_CVR);
		spin_unlock_irqrestore(&dev->lock, flags);
		return 0;
	}
	spin_unlock_irqrestore(&dev->lock, flags);

	snd_printd(KERN_ERR LOGNAME ": Send DSP command timeout\n");

	return -EIO;
}
EXPORT_SYMBOL(snd_msnd_send_dsp_cmd);

int snd_msnd_send_word(struct snd_msnd *dev, unsigned char high,
		   unsigned char mid, unsigned char low)
{
	unsigned int io = dev->io;

	if (snd_msnd_wait_TXDE(dev) == 0) {
		outb(high, io + HP_TXH);
		outb(mid, io + HP_TXM);
		outb(low, io + HP_TXL);
		return 0;
	}

	snd_printd(KERN_ERR LOGNAME ": Send host word timeout\n");

	return -EIO;
}
EXPORT_SYMBOL(snd_msnd_send_word);

int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len)
{
	int i;

	if (len % 3 != 0) {
		snd_printk(KERN_ERR LOGNAME
			   ": Upload host data not multiple of 3!\n");
		return -EINVAL;
	}

	for (i = 0; i < len; i += 3)
		if (snd_msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]))
			return -EIO;

	inb(dev->io + HP_RXL);
	inb(dev->io + HP_CVR);

	return 0;
}
EXPORT_SYMBOL(snd_msnd_upload_host);

int snd_msnd_enable_irq(struct snd_msnd *dev)
{
	unsigned long flags;

	if (dev->irq_ref++)
		return 0;

	snd_printdd(LOGNAME ": Enabling IRQ\n");

	spin_lock_irqsave(&dev->lock, flags);
	if (snd_msnd_wait_TXDE(dev) == 0) {
		outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
		if (dev->type == msndClassic)
			outb(dev->irqid, dev->io + HP_IRQM);

		outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
		outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
		enable_irq(dev->irq);
		snd_msnd_init_queue(dev->DSPQ, dev->dspq_data_buff,
				    dev->dspq_buff_size);
		spin_unlock_irqrestore(&dev->lock, flags);
		return 0;
	}
	spin_unlock_irqrestore(&dev->lock, flags);

	snd_printd(KERN_ERR LOGNAME ": Enable IRQ failed\n");

	return -EIO;
}
EXPORT_SYMBOL(snd_msnd_enable_irq);

int snd_msnd_disable_irq(struct snd_msnd *dev)
{
	unsigned long flags;

	if (--dev->irq_ref > 0)
		return 0;

	if (dev->irq_ref < 0)
		snd_printd(KERN_WARNING LOGNAME ": IRQ ref count is %d\n",
			   dev->irq_ref);

	snd_printdd(LOGNAME ": Disabling IRQ\n");

	spin_lock_irqsave(&dev->lock, flags);
	if (snd_msnd_wait_TXDE(dev) == 0) {
		outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
		if (dev->type == msndClassic)
			outb(HPIRQ_NONE, dev->io + HP_IRQM);
		disable_irq(dev->irq);
		spin_unlock_irqrestore(&dev->lock, flags);
		return 0;
	}
	spin_unlock_irqrestore(&dev->lock, flags);

	snd_printd(KERN_ERR LOGNAME ": Disable IRQ failed\n");

	return -EIO;
}
EXPORT_SYMBOL(snd_msnd_disable_irq);

static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size)
{
	long tmp = (size * HZ * chip->play_sample_size) / 8;
	return tmp / (chip->play_sample_rate * chip->play_channels);
}

static void snd_msnd_dsp_write_flush(struct snd_msnd *chip)
{
	if (!(chip->mode & FMODE_WRITE) || !test_bit(F_WRITING, &chip->flags))
		return;
	set_bit(F_WRITEFLUSH, &chip->flags);
/*	interruptible_sleep_on_timeout(
		&chip->writeflush,
		get_play_delay_jiffies(&chip, chip->DAPF.len));*/
	clear_bit(F_WRITEFLUSH, &chip->flags);
	if (!signal_pending(current))
		schedule_timeout_interruptible(
			get_play_delay_jiffies(chip, chip->play_period_bytes));
	clear_bit(F_WRITING, &chip->flags);
}

void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file)
{
	if ((file ? file->f_mode : chip->mode) & FMODE_READ) {
		clear_bit(F_READING, &chip->flags);
		snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
		snd_msnd_disable_irq(chip);
		if (file) {
			snd_printd(KERN_INFO LOGNAME
				   ": Stopping read for %p\n", file);
			chip->mode &= ~FMODE_READ;
		}
		clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
	}
	if ((file ? file->f_mode : chip->mode) & FMODE_WRITE) {
		if (test_bit(F_WRITING, &chip->flags)) {
			snd_msnd_dsp_write_flush(chip);
			snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
		}
		snd_msnd_disable_irq(chip);
		if (file) {
			snd_printd(KERN_INFO
				   LOGNAME ": Stopping write for %p\n", file);
			chip->mode &= ~FMODE_WRITE;
		}
		clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
	}
}
EXPORT_SYMBOL(snd_msnd_dsp_halt);


int snd_msnd_DARQ(struct snd_msnd *chip, int bank)
{
	int /*size, n,*/ timeout = 3;
	u16 wTmp;
	/* void *DAQD; */

	/* Increment the tail and check for queue wrap */
	wTmp = readw(chip->DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
	if (wTmp > readw(chip->DARQ + JQS_wSize))
		wTmp = 0;
	while (wTmp == readw(chip->DARQ + JQS_wHead) && timeout--)
		udelay(1);

	if (chip->capturePeriods == 2) {
		void __iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF +
			     bank * DAQDS__size + DAQDS_wStart;
		unsigned short offset = 0x3000 + chip->capturePeriodBytes;

		if (readw(pDAQ) != PCTODSP_BASED(0x3000))
			offset = 0x3000;
		writew(PCTODSP_BASED(offset), pDAQ);
	}

	writew(wTmp, chip->DARQ + JQS_wTail);

#if 0
	/* Get our digital audio queue struct */
	DAQD = bank * DAQDS__size + chip->mappedbase + DARQ_DATA_BUFF;

	/* Get length of data */
	size = readw(DAQD + DAQDS_wSize);

	/* Read data from the head (unprotected bank 1 access okay
	   since this is only called inside an interrupt) */
	outb(HPBLKSEL_1, chip->io + HP_BLKS);
	n = msnd_fifo_write(&chip->DARF,
			    (char *)(chip->base + bank * DAR_BUFF_SIZE),
			    size, 0);
	if (n <= 0) {
		outb(HPBLKSEL_0, chip->io + HP_BLKS);
		return n;
	}
	outb(HPBLKSEL_0, chip->io + HP_BLKS);
#endif

	return 1;
}
EXPORT_SYMBOL(snd_msnd_DARQ);

int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
{
	u16	DAPQ_tail;
	int	protect = start, nbanks = 0;
	void	__iomem *DAQD;
	static int play_banks_submitted;
	/* unsigned long flags;
	spin_lock_irqsave(&chip->lock, flags); not necessary */

	DAPQ_tail = readw(chip->DAPQ + JQS_wTail);
	while (DAPQ_tail != readw(chip->DAPQ + JQS_wHead) || start) {
		int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);

		if (start) {
			start = 0;
			play_banks_submitted = 0;
		}

		/* Get our digital audio queue struct */
		DAQD = bank_num * DAQDS__size + chip->mappedbase +
			DAPQ_DATA_BUFF;

		/* Write size of this bank */
		writew(chip->play_period_bytes, DAQD + DAQDS_wSize);
		if (play_banks_submitted < 3)
			++play_banks_submitted;
		else if (chip->playPeriods == 2) {
			unsigned short offset = chip->play_period_bytes;

			if (readw(DAQD + DAQDS_wStart) != PCTODSP_BASED(0x0))
				offset = 0;

			writew(PCTODSP_BASED(offset), DAQD + DAQDS_wStart);
		}
		++nbanks;

		/* Then advance the tail */
		/*
		if (protect)
			snd_printd(KERN_INFO "B %X %lX\n",
				   bank_num, xtime.tv_usec);
		*/

		DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
		writew(DAPQ_tail, chip->DAPQ + JQS_wTail);
		/* Tell the DSP to play the bank */
		snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_START);
		if (protect)
			if (2 == bank_num)
				break;
	}
	/*
	if (protect)
		snd_printd(KERN_INFO "%lX\n", xtime.tv_usec);
	*/
	/* spin_unlock_irqrestore(&chip->lock, flags); not necessary */
	return nbanks;
}
EXPORT_SYMBOL(snd_msnd_DAPQ);

static void snd_msnd_play_reset_queue(struct snd_msnd *chip,
				      unsigned int pcm_periods,
				      unsigned int pcm_count)
{
	int	n;
	void	__iomem *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;

	chip->last_playbank = -1;
	chip->playLimit = pcm_count * (pcm_periods - 1);
	chip->playPeriods = pcm_periods;
	writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wHead);
	writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wTail);

	chip->play_period_bytes = pcm_count;

	for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
		writew(PCTODSP_BASED((u32)(pcm_count * n)),
			pDAQ + DAQDS_wStart);
		writew(0, pDAQ + DAQDS_wSize);
		writew(1, pDAQ + DAQDS_wFormat);
		writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
		writew(chip->play_channels, pDAQ + DAQDS_wChannels);
		writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
		writew(HIMT_PLAY_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
		writew(n, pDAQ + DAQDS_wFlags);
	}
}

static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
					 unsigned int pcm_periods,
					 unsigned int pcm_count)
{
	int		n;
	void		__iomem *pDAQ;
	/* unsigned long	flags; */

	/* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */

	chip->last_recbank = 2;
	chip->captureLimit = pcm_count * (pcm_periods - 1);
	chip->capturePeriods = pcm_periods;
	writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DARQ + JQS_wHead);
	writew(PCTODSP_OFFSET(chip->last_recbank * DAQDS__size),
		chip->DARQ + JQS_wTail);

#if 0 /* Critical section: bank 1 access. this is how the OSS driver does it:*/
	spin_lock_irqsave(&chip->lock, flags);
	outb(HPBLKSEL_1, chip->io + HP_BLKS);
	memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3);
	outb(HPBLKSEL_0, chip->io + HP_BLKS);
	spin_unlock_irqrestore(&chip->lock, flags);
#endif

	chip->capturePeriodBytes = pcm_count;
	snd_printdd("snd_msnd_capture_reset_queue() %i\n", pcm_count);

	pDAQ = chip->mappedbase + DARQ_DATA_BUFF;

	for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
		u32 tmp = pcm_count * n;

		writew(PCTODSP_BASED(tmp + 0x3000), pDAQ + DAQDS_wStart);
		writew(pcm_count, pDAQ + DAQDS_wSize);
		writew(1, pDAQ + DAQDS_wFormat);
		writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
		writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
		writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
		writew(HIMT_RECORD_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
		writew(n, pDAQ + DAQDS_wFlags);
	}
}

static const struct snd_pcm_hardware snd_msnd_playback = {
	.info =			SNDRV_PCM_INFO_MMAP |
				SNDRV_PCM_INFO_INTERLEAVED |
				SNDRV_PCM_INFO_MMAP_VALID |
				SNDRV_PCM_INFO_BATCH,
	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
	.rates =		SNDRV_PCM_RATE_8000_48000,
	.rate_min =		8000,
	.rate_max =		48000,
	.channels_min =		1,
	.channels_max =		2,
	.buffer_bytes_max =	0x3000,
	.period_bytes_min =	0x40,
	.period_bytes_max =	0x1800,
	.periods_min =		2,
	.periods_max =		3,
	.fifo_size =		0,
};

static const struct snd_pcm_hardware snd_msnd_capture = {
	.info =			SNDRV_PCM_INFO_MMAP |
				SNDRV_PCM_INFO_INTERLEAVED |
				SNDRV_PCM_INFO_MMAP_VALID |
				SNDRV_PCM_INFO_BATCH,
	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
	.rates =		SNDRV_PCM_RATE_8000_48000,
	.rate_min =		8000,
	.rate_max =		48000,
	.channels_min =		1,
	.channels_max =		2,
	.buffer_bytes_max =	0x3000,
	.period_bytes_min =	0x40,
	.period_bytes_max =	0x1800,
	.periods_min =		2,
	.periods_max =		3,
	.fifo_size =		0,
};


static int snd_msnd_playback_open(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);

	set_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
	clear_bit(F_WRITING, &chip->flags);
	snd_msnd_enable_irq(chip);

	runtime->dma_area = (__force void *)chip->mappedbase;
	runtime->dma_bytes = 0x3000;

	chip->playback_substream = substream;
	runtime->hw = snd_msnd_playback;
	return 0;
}

static int snd_msnd_playback_close(struct snd_pcm_substream *substream)
{
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);

	snd_msnd_disable_irq(chip);
	clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
	return 0;
}


static int snd_msnd_playback_hw_params(struct snd_pcm_substream *substream,
					struct snd_pcm_hw_params *params)
{
	int	i;
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
	void	__iomem *pDAQ =	chip->mappedbase + DAPQ_DATA_BUFF;

	chip->play_sample_size = snd_pcm_format_width(params_format(params));
	chip->play_channels = params_channels(params);
	chip->play_sample_rate = params_rate(params);

	for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
		writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
		writew(chip->play_channels, pDAQ + DAQDS_wChannels);
		writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
	}
	/* dont do this here:
	 * snd_msnd_calibrate_adc(chip->play_sample_rate);
	 */

	return 0;
}

static int snd_msnd_playback_prepare(struct snd_pcm_substream *substream)
{
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
	unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
	unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
	unsigned int pcm_periods = pcm_size / pcm_count;

	snd_msnd_play_reset_queue(chip, pcm_periods, pcm_count);
	chip->playDMAPos = 0;
	return 0;
}

static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream,
				     int cmd)
{
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
	int	result = 0;

	if (cmd == SNDRV_PCM_TRIGGER_START) {
		snd_printdd("snd_msnd_playback_trigger(START)\n");
		chip->banksPlayed = 0;
		set_bit(F_WRITING, &chip->flags);
		snd_msnd_DAPQ(chip, 1);
	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
		snd_printdd("snd_msnd_playback_trigger(STop)\n");
		/* interrupt diagnostic, comment this out later */
		clear_bit(F_WRITING, &chip->flags);
		snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
	} else {
		snd_printd(KERN_ERR "snd_msnd_playback_trigger(?????)\n");
		result = -EINVAL;
	}

	snd_printdd("snd_msnd_playback_trigger() ENDE\n");
	return result;
}

static snd_pcm_uframes_t
snd_msnd_playback_pointer(struct snd_pcm_substream *substream)
{
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);

	return bytes_to_frames(substream->runtime, chip->playDMAPos);
}


static const struct snd_pcm_ops snd_msnd_playback_ops = {
	.open =		snd_msnd_playback_open,
	.close =	snd_msnd_playback_close,
	.ioctl =	snd_pcm_lib_ioctl,
	.hw_params =	snd_msnd_playback_hw_params,
	.prepare =	snd_msnd_playback_prepare,
	.trigger =	snd_msnd_playback_trigger,
	.pointer =	snd_msnd_playback_pointer,
};

static int snd_msnd_capture_open(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);

	set_bit(F_AUDIO_READ_INUSE, &chip->flags);
	snd_msnd_enable_irq(chip);
	runtime->dma_area = (__force void *)chip->mappedbase + 0x3000;
	runtime->dma_bytes = 0x3000;
	memset(runtime->dma_area, 0, runtime->dma_bytes);
	chip->capture_substream = substream;
	runtime->hw = snd_msnd_capture;
	return 0;
}

static int snd_msnd_capture_close(struct snd_pcm_substream *substream)
{
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);

	snd_msnd_disable_irq(chip);
	clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
	return 0;
}

static int snd_msnd_capture_prepare(struct snd_pcm_substream *substream)
{
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
	unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
	unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
	unsigned int pcm_periods = pcm_size / pcm_count;

	snd_msnd_capture_reset_queue(chip, pcm_periods, pcm_count);
	chip->captureDMAPos = 0;
	return 0;
}

static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream,
				    int cmd)
{
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);

	if (cmd == SNDRV_PCM_TRIGGER_START) {
		chip->last_recbank = -1;
		set_bit(F_READING, &chip->flags);
		if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0)
			return 0;

		clear_bit(F_READING, &chip->flags);
	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
		clear_bit(F_READING, &chip->flags);
		snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
		return 0;
	}
	return -EINVAL;
}


static snd_pcm_uframes_t
snd_msnd_capture_pointer(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);

	return bytes_to_frames(runtime, chip->captureDMAPos);
}


static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
					struct snd_pcm_hw_params *params)
{
	int		i;
	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
	void		__iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF;

	chip->capture_sample_size = snd_pcm_format_width(params_format(params));
	chip->capture_channels = params_channels(params);
	chip->capture_sample_rate = params_rate(params);

	for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
		writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
		writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
		writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
	}
	return 0;
}


static const struct snd_pcm_ops snd_msnd_capture_ops = {
	.open =		snd_msnd_capture_open,
	.close =	snd_msnd_capture_close,
	.ioctl =	snd_pcm_lib_ioctl,
	.hw_params =	snd_msnd_capture_hw_params,
	.prepare =	snd_msnd_capture_prepare,
	.trigger =	snd_msnd_capture_trigger,
	.pointer =	snd_msnd_capture_pointer,
};


int snd_msnd_pcm(struct snd_card *card, int device)
{
	struct snd_msnd *chip = card->private_data;
	struct snd_pcm	*pcm;
	int err;

	err = snd_pcm_new(card, "MSNDPINNACLE", device, 1, 1, &pcm);
	if (err < 0)
		return err;

	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_msnd_playback_ops);
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops);

	pcm->private_data = chip;
	strcpy(pcm->name, "Hurricane");

	return 0;
}
EXPORT_SYMBOL(snd_msnd_pcm);

MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers");
MODULE_LICENSE("GPL");

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API