cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

msnd.c (19207B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*********************************************************************
      3 *
      4 * 2002/06/30 Karsten Wiese:
      5 *	removed kernel-version dependencies.
      6 *	ripped from linux kernel 2.4.18 (OSS Implementation) by me.
      7 *	In the OSS Version, this file is compiled to a separate MODULE,
      8 *	that is used by the pinnacle and the classic driver.
      9 *	since there is no classic driver for alsa yet (i dont have a classic
     10 *	& writing one blindfold is difficult) this file's object is statically
     11 *	linked into the pinnacle-driver-module for now.	look for the string
     12 *		"uncomment this to make this a module again"
     13 *	to do guess what.
     14 *
     15 * the following is a copy of the 2.4.18 OSS FREE file-heading comment:
     16 *
     17 * msnd.c - Driver Base
     18 *
     19 * Turtle Beach MultiSound Sound Card Driver for Linux
     20 *
     21 * Copyright (C) 1998 Andrew Veliath
     22 *
     23 ********************************************************************/
     24
     25#include <linux/kernel.h>
     26#include <linux/sched/signal.h>
     27#include <linux/types.h>
     28#include <linux/interrupt.h>
     29#include <linux/io.h>
     30#include <linux/fs.h>
     31#include <linux/delay.h>
     32#include <linux/module.h>
     33
     34#include <sound/core.h>
     35#include <sound/initval.h>
     36#include <sound/pcm.h>
     37#include <sound/pcm_params.h>
     38
     39#include "msnd.h"
     40
     41#define LOGNAME			"msnd"
     42
     43
     44void snd_msnd_init_queue(void __iomem *base, int start, int size)
     45{
     46	writew(PCTODSP_BASED(start), base + JQS_wStart);
     47	writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
     48	writew(0, base + JQS_wHead);
     49	writew(0, base + JQS_wTail);
     50}
     51EXPORT_SYMBOL(snd_msnd_init_queue);
     52
     53static int snd_msnd_wait_TXDE(struct snd_msnd *dev)
     54{
     55	unsigned int io = dev->io;
     56	int timeout = 1000;
     57
     58	while (timeout-- > 0)
     59		if (inb(io + HP_ISR) & HPISR_TXDE)
     60			return 0;
     61
     62	return -EIO;
     63}
     64
     65static int snd_msnd_wait_HC0(struct snd_msnd *dev)
     66{
     67	unsigned int io = dev->io;
     68	int timeout = 1000;
     69
     70	while (timeout-- > 0)
     71		if (!(inb(io + HP_CVR) & HPCVR_HC))
     72			return 0;
     73
     74	return -EIO;
     75}
     76
     77int snd_msnd_send_dsp_cmd(struct snd_msnd *dev, u8 cmd)
     78{
     79	unsigned long flags;
     80
     81	spin_lock_irqsave(&dev->lock, flags);
     82	if (snd_msnd_wait_HC0(dev) == 0) {
     83		outb(cmd, dev->io + HP_CVR);
     84		spin_unlock_irqrestore(&dev->lock, flags);
     85		return 0;
     86	}
     87	spin_unlock_irqrestore(&dev->lock, flags);
     88
     89	snd_printd(KERN_ERR LOGNAME ": Send DSP command timeout\n");
     90
     91	return -EIO;
     92}
     93EXPORT_SYMBOL(snd_msnd_send_dsp_cmd);
     94
     95int snd_msnd_send_word(struct snd_msnd *dev, unsigned char high,
     96		   unsigned char mid, unsigned char low)
     97{
     98	unsigned int io = dev->io;
     99
    100	if (snd_msnd_wait_TXDE(dev) == 0) {
    101		outb(high, io + HP_TXH);
    102		outb(mid, io + HP_TXM);
    103		outb(low, io + HP_TXL);
    104		return 0;
    105	}
    106
    107	snd_printd(KERN_ERR LOGNAME ": Send host word timeout\n");
    108
    109	return -EIO;
    110}
    111EXPORT_SYMBOL(snd_msnd_send_word);
    112
    113int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len)
    114{
    115	int i;
    116
    117	if (len % 3 != 0) {
    118		snd_printk(KERN_ERR LOGNAME
    119			   ": Upload host data not multiple of 3!\n");
    120		return -EINVAL;
    121	}
    122
    123	for (i = 0; i < len; i += 3)
    124		if (snd_msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]))
    125			return -EIO;
    126
    127	inb(dev->io + HP_RXL);
    128	inb(dev->io + HP_CVR);
    129
    130	return 0;
    131}
    132EXPORT_SYMBOL(snd_msnd_upload_host);
    133
    134int snd_msnd_enable_irq(struct snd_msnd *dev)
    135{
    136	unsigned long flags;
    137
    138	if (dev->irq_ref++)
    139		return 0;
    140
    141	snd_printdd(LOGNAME ": Enabling IRQ\n");
    142
    143	spin_lock_irqsave(&dev->lock, flags);
    144	if (snd_msnd_wait_TXDE(dev) == 0) {
    145		outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
    146		if (dev->type == msndClassic)
    147			outb(dev->irqid, dev->io + HP_IRQM);
    148
    149		outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
    150		outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
    151		enable_irq(dev->irq);
    152		snd_msnd_init_queue(dev->DSPQ, dev->dspq_data_buff,
    153				    dev->dspq_buff_size);
    154		spin_unlock_irqrestore(&dev->lock, flags);
    155		return 0;
    156	}
    157	spin_unlock_irqrestore(&dev->lock, flags);
    158
    159	snd_printd(KERN_ERR LOGNAME ": Enable IRQ failed\n");
    160
    161	return -EIO;
    162}
    163EXPORT_SYMBOL(snd_msnd_enable_irq);
    164
    165int snd_msnd_disable_irq(struct snd_msnd *dev)
    166{
    167	unsigned long flags;
    168
    169	if (--dev->irq_ref > 0)
    170		return 0;
    171
    172	if (dev->irq_ref < 0)
    173		snd_printd(KERN_WARNING LOGNAME ": IRQ ref count is %d\n",
    174			   dev->irq_ref);
    175
    176	snd_printdd(LOGNAME ": Disabling IRQ\n");
    177
    178	spin_lock_irqsave(&dev->lock, flags);
    179	if (snd_msnd_wait_TXDE(dev) == 0) {
    180		outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
    181		if (dev->type == msndClassic)
    182			outb(HPIRQ_NONE, dev->io + HP_IRQM);
    183		disable_irq(dev->irq);
    184		spin_unlock_irqrestore(&dev->lock, flags);
    185		return 0;
    186	}
    187	spin_unlock_irqrestore(&dev->lock, flags);
    188
    189	snd_printd(KERN_ERR LOGNAME ": Disable IRQ failed\n");
    190
    191	return -EIO;
    192}
    193EXPORT_SYMBOL(snd_msnd_disable_irq);
    194
    195static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size)
    196{
    197	long tmp = (size * HZ * chip->play_sample_size) / 8;
    198	return tmp / (chip->play_sample_rate * chip->play_channels);
    199}
    200
    201static void snd_msnd_dsp_write_flush(struct snd_msnd *chip)
    202{
    203	if (!(chip->mode & FMODE_WRITE) || !test_bit(F_WRITING, &chip->flags))
    204		return;
    205	set_bit(F_WRITEFLUSH, &chip->flags);
    206/*	interruptible_sleep_on_timeout(
    207		&chip->writeflush,
    208		get_play_delay_jiffies(&chip, chip->DAPF.len));*/
    209	clear_bit(F_WRITEFLUSH, &chip->flags);
    210	if (!signal_pending(current))
    211		schedule_timeout_interruptible(
    212			get_play_delay_jiffies(chip, chip->play_period_bytes));
    213	clear_bit(F_WRITING, &chip->flags);
    214}
    215
    216void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file)
    217{
    218	if ((file ? file->f_mode : chip->mode) & FMODE_READ) {
    219		clear_bit(F_READING, &chip->flags);
    220		snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
    221		snd_msnd_disable_irq(chip);
    222		if (file) {
    223			snd_printd(KERN_INFO LOGNAME
    224				   ": Stopping read for %p\n", file);
    225			chip->mode &= ~FMODE_READ;
    226		}
    227		clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
    228	}
    229	if ((file ? file->f_mode : chip->mode) & FMODE_WRITE) {
    230		if (test_bit(F_WRITING, &chip->flags)) {
    231			snd_msnd_dsp_write_flush(chip);
    232			snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
    233		}
    234		snd_msnd_disable_irq(chip);
    235		if (file) {
    236			snd_printd(KERN_INFO
    237				   LOGNAME ": Stopping write for %p\n", file);
    238			chip->mode &= ~FMODE_WRITE;
    239		}
    240		clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
    241	}
    242}
    243EXPORT_SYMBOL(snd_msnd_dsp_halt);
    244
    245
    246int snd_msnd_DARQ(struct snd_msnd *chip, int bank)
    247{
    248	int /*size, n,*/ timeout = 3;
    249	u16 wTmp;
    250	/* void *DAQD; */
    251
    252	/* Increment the tail and check for queue wrap */
    253	wTmp = readw(chip->DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
    254	if (wTmp > readw(chip->DARQ + JQS_wSize))
    255		wTmp = 0;
    256	while (wTmp == readw(chip->DARQ + JQS_wHead) && timeout--)
    257		udelay(1);
    258
    259	if (chip->capturePeriods == 2) {
    260		void __iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF +
    261			     bank * DAQDS__size + DAQDS_wStart;
    262		unsigned short offset = 0x3000 + chip->capturePeriodBytes;
    263
    264		if (readw(pDAQ) != PCTODSP_BASED(0x3000))
    265			offset = 0x3000;
    266		writew(PCTODSP_BASED(offset), pDAQ);
    267	}
    268
    269	writew(wTmp, chip->DARQ + JQS_wTail);
    270
    271#if 0
    272	/* Get our digital audio queue struct */
    273	DAQD = bank * DAQDS__size + chip->mappedbase + DARQ_DATA_BUFF;
    274
    275	/* Get length of data */
    276	size = readw(DAQD + DAQDS_wSize);
    277
    278	/* Read data from the head (unprotected bank 1 access okay
    279	   since this is only called inside an interrupt) */
    280	outb(HPBLKSEL_1, chip->io + HP_BLKS);
    281	n = msnd_fifo_write(&chip->DARF,
    282			    (char *)(chip->base + bank * DAR_BUFF_SIZE),
    283			    size, 0);
    284	if (n <= 0) {
    285		outb(HPBLKSEL_0, chip->io + HP_BLKS);
    286		return n;
    287	}
    288	outb(HPBLKSEL_0, chip->io + HP_BLKS);
    289#endif
    290
    291	return 1;
    292}
    293EXPORT_SYMBOL(snd_msnd_DARQ);
    294
    295int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
    296{
    297	u16	DAPQ_tail;
    298	int	protect = start, nbanks = 0;
    299	void	__iomem *DAQD;
    300	static int play_banks_submitted;
    301	/* unsigned long flags;
    302	spin_lock_irqsave(&chip->lock, flags); not necessary */
    303
    304	DAPQ_tail = readw(chip->DAPQ + JQS_wTail);
    305	while (DAPQ_tail != readw(chip->DAPQ + JQS_wHead) || start) {
    306		int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);
    307
    308		if (start) {
    309			start = 0;
    310			play_banks_submitted = 0;
    311		}
    312
    313		/* Get our digital audio queue struct */
    314		DAQD = bank_num * DAQDS__size + chip->mappedbase +
    315			DAPQ_DATA_BUFF;
    316
    317		/* Write size of this bank */
    318		writew(chip->play_period_bytes, DAQD + DAQDS_wSize);
    319		if (play_banks_submitted < 3)
    320			++play_banks_submitted;
    321		else if (chip->playPeriods == 2) {
    322			unsigned short offset = chip->play_period_bytes;
    323
    324			if (readw(DAQD + DAQDS_wStart) != PCTODSP_BASED(0x0))
    325				offset = 0;
    326
    327			writew(PCTODSP_BASED(offset), DAQD + DAQDS_wStart);
    328		}
    329		++nbanks;
    330
    331		/* Then advance the tail */
    332		/*
    333		if (protect)
    334			snd_printd(KERN_INFO "B %X %lX\n",
    335				   bank_num, xtime.tv_usec);
    336		*/
    337
    338		DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
    339		writew(DAPQ_tail, chip->DAPQ + JQS_wTail);
    340		/* Tell the DSP to play the bank */
    341		snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_START);
    342		if (protect)
    343			if (2 == bank_num)
    344				break;
    345	}
    346	/*
    347	if (protect)
    348		snd_printd(KERN_INFO "%lX\n", xtime.tv_usec);
    349	*/
    350	/* spin_unlock_irqrestore(&chip->lock, flags); not necessary */
    351	return nbanks;
    352}
    353EXPORT_SYMBOL(snd_msnd_DAPQ);
    354
    355static void snd_msnd_play_reset_queue(struct snd_msnd *chip,
    356				      unsigned int pcm_periods,
    357				      unsigned int pcm_count)
    358{
    359	int	n;
    360	void	__iomem *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
    361
    362	chip->last_playbank = -1;
    363	chip->playLimit = pcm_count * (pcm_periods - 1);
    364	chip->playPeriods = pcm_periods;
    365	writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wHead);
    366	writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wTail);
    367
    368	chip->play_period_bytes = pcm_count;
    369
    370	for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
    371		writew(PCTODSP_BASED((u32)(pcm_count * n)),
    372			pDAQ + DAQDS_wStart);
    373		writew(0, pDAQ + DAQDS_wSize);
    374		writew(1, pDAQ + DAQDS_wFormat);
    375		writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
    376		writew(chip->play_channels, pDAQ + DAQDS_wChannels);
    377		writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
    378		writew(HIMT_PLAY_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
    379		writew(n, pDAQ + DAQDS_wFlags);
    380	}
    381}
    382
    383static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
    384					 unsigned int pcm_periods,
    385					 unsigned int pcm_count)
    386{
    387	int		n;
    388	void		__iomem *pDAQ;
    389	/* unsigned long	flags; */
    390
    391	/* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */
    392
    393	chip->last_recbank = 2;
    394	chip->captureLimit = pcm_count * (pcm_periods - 1);
    395	chip->capturePeriods = pcm_periods;
    396	writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DARQ + JQS_wHead);
    397	writew(PCTODSP_OFFSET(chip->last_recbank * DAQDS__size),
    398		chip->DARQ + JQS_wTail);
    399
    400#if 0 /* Critical section: bank 1 access. this is how the OSS driver does it:*/
    401	spin_lock_irqsave(&chip->lock, flags);
    402	outb(HPBLKSEL_1, chip->io + HP_BLKS);
    403	memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3);
    404	outb(HPBLKSEL_0, chip->io + HP_BLKS);
    405	spin_unlock_irqrestore(&chip->lock, flags);
    406#endif
    407
    408	chip->capturePeriodBytes = pcm_count;
    409	snd_printdd("snd_msnd_capture_reset_queue() %i\n", pcm_count);
    410
    411	pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
    412
    413	for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
    414		u32 tmp = pcm_count * n;
    415
    416		writew(PCTODSP_BASED(tmp + 0x3000), pDAQ + DAQDS_wStart);
    417		writew(pcm_count, pDAQ + DAQDS_wSize);
    418		writew(1, pDAQ + DAQDS_wFormat);
    419		writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
    420		writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
    421		writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
    422		writew(HIMT_RECORD_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
    423		writew(n, pDAQ + DAQDS_wFlags);
    424	}
    425}
    426
    427static const struct snd_pcm_hardware snd_msnd_playback = {
    428	.info =			SNDRV_PCM_INFO_MMAP_IOMEM |
    429				SNDRV_PCM_INFO_INTERLEAVED |
    430				SNDRV_PCM_INFO_MMAP_VALID |
    431				SNDRV_PCM_INFO_BATCH,
    432	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
    433	.rates =		SNDRV_PCM_RATE_8000_48000,
    434	.rate_min =		8000,
    435	.rate_max =		48000,
    436	.channels_min =		1,
    437	.channels_max =		2,
    438	.buffer_bytes_max =	0x3000,
    439	.period_bytes_min =	0x40,
    440	.period_bytes_max =	0x1800,
    441	.periods_min =		2,
    442	.periods_max =		3,
    443	.fifo_size =		0,
    444};
    445
    446static const struct snd_pcm_hardware snd_msnd_capture = {
    447	.info =			SNDRV_PCM_INFO_MMAP_IOMEM |
    448				SNDRV_PCM_INFO_INTERLEAVED |
    449				SNDRV_PCM_INFO_MMAP_VALID |
    450				SNDRV_PCM_INFO_BATCH,
    451	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
    452	.rates =		SNDRV_PCM_RATE_8000_48000,
    453	.rate_min =		8000,
    454	.rate_max =		48000,
    455	.channels_min =		1,
    456	.channels_max =		2,
    457	.buffer_bytes_max =	0x3000,
    458	.period_bytes_min =	0x40,
    459	.period_bytes_max =	0x1800,
    460	.periods_min =		2,
    461	.periods_max =		3,
    462	.fifo_size =		0,
    463};
    464
    465
    466static int snd_msnd_playback_open(struct snd_pcm_substream *substream)
    467{
    468	struct snd_pcm_runtime *runtime = substream->runtime;
    469	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    470
    471	set_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
    472	clear_bit(F_WRITING, &chip->flags);
    473	snd_msnd_enable_irq(chip);
    474
    475	runtime->dma_area = (__force void *)chip->mappedbase;
    476	runtime->dma_addr = chip->base;
    477	runtime->dma_bytes = 0x3000;
    478
    479	chip->playback_substream = substream;
    480	runtime->hw = snd_msnd_playback;
    481	return 0;
    482}
    483
    484static int snd_msnd_playback_close(struct snd_pcm_substream *substream)
    485{
    486	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    487
    488	snd_msnd_disable_irq(chip);
    489	clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
    490	return 0;
    491}
    492
    493
    494static int snd_msnd_playback_hw_params(struct snd_pcm_substream *substream,
    495					struct snd_pcm_hw_params *params)
    496{
    497	int	i;
    498	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    499	void	__iomem *pDAQ =	chip->mappedbase + DAPQ_DATA_BUFF;
    500
    501	chip->play_sample_size = snd_pcm_format_width(params_format(params));
    502	chip->play_channels = params_channels(params);
    503	chip->play_sample_rate = params_rate(params);
    504
    505	for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
    506		writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
    507		writew(chip->play_channels, pDAQ + DAQDS_wChannels);
    508		writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
    509	}
    510	/* dont do this here:
    511	 * snd_msnd_calibrate_adc(chip->play_sample_rate);
    512	 */
    513
    514	return 0;
    515}
    516
    517static int snd_msnd_playback_prepare(struct snd_pcm_substream *substream)
    518{
    519	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    520	unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
    521	unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
    522	unsigned int pcm_periods = pcm_size / pcm_count;
    523
    524	snd_msnd_play_reset_queue(chip, pcm_periods, pcm_count);
    525	chip->playDMAPos = 0;
    526	return 0;
    527}
    528
    529static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream,
    530				     int cmd)
    531{
    532	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    533	int	result = 0;
    534
    535	if (cmd == SNDRV_PCM_TRIGGER_START) {
    536		snd_printdd("snd_msnd_playback_trigger(START)\n");
    537		chip->banksPlayed = 0;
    538		set_bit(F_WRITING, &chip->flags);
    539		snd_msnd_DAPQ(chip, 1);
    540	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
    541		snd_printdd("snd_msnd_playback_trigger(STop)\n");
    542		/* interrupt diagnostic, comment this out later */
    543		clear_bit(F_WRITING, &chip->flags);
    544		snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
    545	} else {
    546		snd_printd(KERN_ERR "snd_msnd_playback_trigger(?????)\n");
    547		result = -EINVAL;
    548	}
    549
    550	snd_printdd("snd_msnd_playback_trigger() ENDE\n");
    551	return result;
    552}
    553
    554static snd_pcm_uframes_t
    555snd_msnd_playback_pointer(struct snd_pcm_substream *substream)
    556{
    557	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    558
    559	return bytes_to_frames(substream->runtime, chip->playDMAPos);
    560}
    561
    562
    563static const struct snd_pcm_ops snd_msnd_playback_ops = {
    564	.open =		snd_msnd_playback_open,
    565	.close =	snd_msnd_playback_close,
    566	.hw_params =	snd_msnd_playback_hw_params,
    567	.prepare =	snd_msnd_playback_prepare,
    568	.trigger =	snd_msnd_playback_trigger,
    569	.pointer =	snd_msnd_playback_pointer,
    570	.mmap =		snd_pcm_lib_mmap_iomem,
    571};
    572
    573static int snd_msnd_capture_open(struct snd_pcm_substream *substream)
    574{
    575	struct snd_pcm_runtime *runtime = substream->runtime;
    576	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    577
    578	set_bit(F_AUDIO_READ_INUSE, &chip->flags);
    579	snd_msnd_enable_irq(chip);
    580	runtime->dma_area = (__force void *)chip->mappedbase + 0x3000;
    581	runtime->dma_addr = chip->base + 0x3000;
    582	runtime->dma_bytes = 0x3000;
    583	memset(runtime->dma_area, 0, runtime->dma_bytes);
    584	chip->capture_substream = substream;
    585	runtime->hw = snd_msnd_capture;
    586	return 0;
    587}
    588
    589static int snd_msnd_capture_close(struct snd_pcm_substream *substream)
    590{
    591	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    592
    593	snd_msnd_disable_irq(chip);
    594	clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
    595	return 0;
    596}
    597
    598static int snd_msnd_capture_prepare(struct snd_pcm_substream *substream)
    599{
    600	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    601	unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
    602	unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
    603	unsigned int pcm_periods = pcm_size / pcm_count;
    604
    605	snd_msnd_capture_reset_queue(chip, pcm_periods, pcm_count);
    606	chip->captureDMAPos = 0;
    607	return 0;
    608}
    609
    610static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream,
    611				    int cmd)
    612{
    613	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    614
    615	if (cmd == SNDRV_PCM_TRIGGER_START) {
    616		chip->last_recbank = -1;
    617		set_bit(F_READING, &chip->flags);
    618		if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0)
    619			return 0;
    620
    621		clear_bit(F_READING, &chip->flags);
    622	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
    623		clear_bit(F_READING, &chip->flags);
    624		snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
    625		return 0;
    626	}
    627	return -EINVAL;
    628}
    629
    630
    631static snd_pcm_uframes_t
    632snd_msnd_capture_pointer(struct snd_pcm_substream *substream)
    633{
    634	struct snd_pcm_runtime *runtime = substream->runtime;
    635	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    636
    637	return bytes_to_frames(runtime, chip->captureDMAPos);
    638}
    639
    640
    641static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
    642					struct snd_pcm_hw_params *params)
    643{
    644	int		i;
    645	struct snd_msnd *chip = snd_pcm_substream_chip(substream);
    646	void		__iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
    647
    648	chip->capture_sample_size = snd_pcm_format_width(params_format(params));
    649	chip->capture_channels = params_channels(params);
    650	chip->capture_sample_rate = params_rate(params);
    651
    652	for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
    653		writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
    654		writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
    655		writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
    656	}
    657	return 0;
    658}
    659
    660
    661static const struct snd_pcm_ops snd_msnd_capture_ops = {
    662	.open =		snd_msnd_capture_open,
    663	.close =	snd_msnd_capture_close,
    664	.hw_params =	snd_msnd_capture_hw_params,
    665	.prepare =	snd_msnd_capture_prepare,
    666	.trigger =	snd_msnd_capture_trigger,
    667	.pointer =	snd_msnd_capture_pointer,
    668	.mmap =		snd_pcm_lib_mmap_iomem,
    669};
    670
    671
    672int snd_msnd_pcm(struct snd_card *card, int device)
    673{
    674	struct snd_msnd *chip = card->private_data;
    675	struct snd_pcm	*pcm;
    676	int err;
    677
    678	err = snd_pcm_new(card, "MSNDPINNACLE", device, 1, 1, &pcm);
    679	if (err < 0)
    680		return err;
    681
    682	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_msnd_playback_ops);
    683	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops);
    684
    685	pcm->private_data = chip;
    686	strcpy(pcm->name, "Hurricane");
    687
    688	return 0;
    689}
    690EXPORT_SYMBOL(snd_msnd_pcm);
    691
    692MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers");
    693MODULE_LICENSE("GPL");
    694