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

ivtv-alsa-pcm.c (6893B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  ALSA PCM device for the
      4 *  ALSA interface to ivtv PCM capture streams
      5 *
      6 *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.net>
      7 *  Copyright (C) 2009  Devin Heitmueller <dheitmueller@kernellabs.com>
      8 *
      9 *  Portions of this work were sponsored by ONELAN Limited for the cx18 driver
     10 */
     11
     12#include "ivtv-driver.h"
     13#include "ivtv-queue.h"
     14#include "ivtv-streams.h"
     15#include "ivtv-fileops.h"
     16#include "ivtv-alsa.h"
     17#include "ivtv-alsa-pcm.h"
     18
     19#include <sound/core.h>
     20#include <sound/pcm.h>
     21
     22
     23static unsigned int pcm_debug;
     24module_param(pcm_debug, int, 0644);
     25MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
     26
     27#define dprintk(fmt, arg...) \
     28	do { \
     29		if (pcm_debug) \
     30			pr_info("ivtv-alsa-pcm %s: " fmt, __func__, ##arg); \
     31	} while (0)
     32
     33static const struct snd_pcm_hardware snd_ivtv_hw_capture = {
     34	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
     35		SNDRV_PCM_INFO_MMAP           |
     36		SNDRV_PCM_INFO_INTERLEAVED    |
     37		SNDRV_PCM_INFO_MMAP_VALID,
     38
     39	.formats = SNDRV_PCM_FMTBIT_S16_LE,
     40
     41	.rates = SNDRV_PCM_RATE_48000,
     42
     43	.rate_min = 48000,
     44	.rate_max = 48000,
     45	.channels_min = 2,
     46	.channels_max = 2,
     47	.buffer_bytes_max = 62720 * 8,	/* just about the value in usbaudio.c */
     48	.period_bytes_min = 64,		/* 12544/2, */
     49	.period_bytes_max = 12544,
     50	.periods_min = 2,
     51	.periods_max = 98,		/* 12544, */
     52};
     53
     54static void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc,
     55					u8 *pcm_data,
     56					size_t num_bytes)
     57{
     58	struct snd_pcm_substream *substream;
     59	struct snd_pcm_runtime *runtime;
     60	unsigned int oldptr;
     61	unsigned int stride;
     62	int period_elapsed = 0;
     63	int length;
     64
     65	dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zu\n", itvsc,
     66		pcm_data, num_bytes);
     67
     68	substream = itvsc->capture_pcm_substream;
     69	if (substream == NULL) {
     70		dprintk("substream was NULL\n");
     71		return;
     72	}
     73
     74	runtime = substream->runtime;
     75	if (runtime == NULL) {
     76		dprintk("runtime was NULL\n");
     77		return;
     78	}
     79
     80	stride = runtime->frame_bits >> 3;
     81	if (stride == 0) {
     82		dprintk("stride is zero\n");
     83		return;
     84	}
     85
     86	length = num_bytes / stride;
     87	if (length == 0) {
     88		dprintk("%s: length was zero\n", __func__);
     89		return;
     90	}
     91
     92	if (runtime->dma_area == NULL) {
     93		dprintk("dma area was NULL - ignoring\n");
     94		return;
     95	}
     96
     97	oldptr = itvsc->hwptr_done_capture;
     98	if (oldptr + length >= runtime->buffer_size) {
     99		unsigned int cnt =
    100			runtime->buffer_size - oldptr;
    101		memcpy(runtime->dma_area + oldptr * stride, pcm_data,
    102		       cnt * stride);
    103		memcpy(runtime->dma_area, pcm_data + cnt * stride,
    104		       length * stride - cnt * stride);
    105	} else {
    106		memcpy(runtime->dma_area + oldptr * stride, pcm_data,
    107		       length * stride);
    108	}
    109	snd_pcm_stream_lock(substream);
    110
    111	itvsc->hwptr_done_capture += length;
    112	if (itvsc->hwptr_done_capture >=
    113	    runtime->buffer_size)
    114		itvsc->hwptr_done_capture -=
    115			runtime->buffer_size;
    116
    117	itvsc->capture_transfer_done += length;
    118	if (itvsc->capture_transfer_done >=
    119	    runtime->period_size) {
    120		itvsc->capture_transfer_done -=
    121			runtime->period_size;
    122		period_elapsed = 1;
    123	}
    124
    125	snd_pcm_stream_unlock(substream);
    126
    127	if (period_elapsed)
    128		snd_pcm_period_elapsed(substream);
    129}
    130
    131static int snd_ivtv_pcm_capture_open(struct snd_pcm_substream *substream)
    132{
    133	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
    134	struct snd_pcm_runtime *runtime = substream->runtime;
    135	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
    136	struct ivtv *itv = to_ivtv(v4l2_dev);
    137	struct ivtv_stream *s;
    138	struct ivtv_open_id item;
    139	int ret;
    140
    141	/* Instruct the CX2341[56] to start sending packets */
    142	snd_ivtv_lock(itvsc);
    143
    144	if (ivtv_init_on_first_open(itv)) {
    145		snd_ivtv_unlock(itvsc);
    146		return -ENXIO;
    147	}
    148
    149	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
    150
    151	v4l2_fh_init(&item.fh, &s->vdev);
    152	item.itv = itv;
    153	item.type = s->type;
    154
    155	/* See if the stream is available */
    156	if (ivtv_claim_stream(&item, item.type)) {
    157		/* No, it's already in use */
    158		v4l2_fh_exit(&item.fh);
    159		snd_ivtv_unlock(itvsc);
    160		return -EBUSY;
    161	}
    162
    163	if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) ||
    164	    test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
    165		/* We're already streaming.  No additional action required */
    166		snd_ivtv_unlock(itvsc);
    167		return 0;
    168	}
    169
    170
    171	runtime->hw = snd_ivtv_hw_capture;
    172	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
    173	itvsc->capture_pcm_substream = substream;
    174	runtime->private_data = itv;
    175
    176	itv->pcm_announce_callback = ivtv_alsa_announce_pcm_data;
    177
    178	/* Not currently streaming, so start it up */
    179	set_bit(IVTV_F_S_STREAMING, &s->s_flags);
    180	ret = ivtv_start_v4l2_encode_stream(s);
    181	snd_ivtv_unlock(itvsc);
    182
    183	return ret;
    184}
    185
    186static int snd_ivtv_pcm_capture_close(struct snd_pcm_substream *substream)
    187{
    188	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
    189	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
    190	struct ivtv *itv = to_ivtv(v4l2_dev);
    191	struct ivtv_stream *s;
    192
    193	/* Instruct the ivtv to stop sending packets */
    194	snd_ivtv_lock(itvsc);
    195	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
    196	ivtv_stop_v4l2_encode_stream(s, 0);
    197	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
    198
    199	ivtv_release_stream(s);
    200
    201	itv->pcm_announce_callback = NULL;
    202	snd_ivtv_unlock(itvsc);
    203
    204	return 0;
    205}
    206
    207static int snd_ivtv_pcm_prepare(struct snd_pcm_substream *substream)
    208{
    209	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
    210
    211	itvsc->hwptr_done_capture = 0;
    212	itvsc->capture_transfer_done = 0;
    213
    214	return 0;
    215}
    216
    217static int snd_ivtv_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
    218{
    219	return 0;
    220}
    221
    222static
    223snd_pcm_uframes_t snd_ivtv_pcm_pointer(struct snd_pcm_substream *substream)
    224{
    225	unsigned long flags;
    226	snd_pcm_uframes_t hwptr_done;
    227	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
    228
    229	spin_lock_irqsave(&itvsc->slock, flags);
    230	hwptr_done = itvsc->hwptr_done_capture;
    231	spin_unlock_irqrestore(&itvsc->slock, flags);
    232
    233	return hwptr_done;
    234}
    235
    236static const struct snd_pcm_ops snd_ivtv_pcm_capture_ops = {
    237	.open		= snd_ivtv_pcm_capture_open,
    238	.close		= snd_ivtv_pcm_capture_close,
    239	.prepare	= snd_ivtv_pcm_prepare,
    240	.trigger	= snd_ivtv_pcm_trigger,
    241	.pointer	= snd_ivtv_pcm_pointer,
    242};
    243
    244int snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc)
    245{
    246	struct snd_pcm *sp;
    247	struct snd_card *sc = itvsc->sc;
    248	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
    249	struct ivtv *itv = to_ivtv(v4l2_dev);
    250	int ret;
    251
    252	ret = snd_pcm_new(sc, "CX2341[56] PCM",
    253			  0, /* PCM device 0, the only one for this card */
    254			  0, /* 0 playback substreams */
    255			  1, /* 1 capture substream */
    256			  &sp);
    257	if (ret) {
    258		IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",
    259			      __func__, ret);
    260		goto err_exit;
    261	}
    262
    263	spin_lock_init(&itvsc->slock);
    264
    265	snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
    266			&snd_ivtv_pcm_capture_ops);
    267	snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
    268	sp->info_flags = 0;
    269	sp->private_data = itvsc;
    270	strscpy(sp->name, itv->card_name, sizeof(sp->name));
    271
    272	return 0;
    273
    274err_exit:
    275	return ret;
    276}